Class: Buildr::Project
- Inherits:
-
Rake::Task
- Object
- Rake::Task
- Buildr::Project
- Includes:
- Attributes
- Defined in:
- lib/core/project.rb,
lib/java/test.rb,
lib/core/build.rb,
lib/java/jetty.rb,
lib/java/compile.rb,
lib/java/packaging.rb
Overview
A project is a convenient mechanism for managing all the tasks related to a given project. For complex applications, you may have several projects, or sub-projects for each of the modules.
A project definition creates its own set of tasks, prefixed with the project name. For example, each project has a clean, build and deploy task. For project foo
the task names are foo:clean
, foo:build
and foo:deploy
.
Projects have properties, some of which they inherit from their parent project. Built in tasks use these properties, for example, the clean
task will remove the target directory specified by the target_dir
property. The compile
tasks uses the compiler option: you can set these options on the parent project and they will be inherited by all sub-projects.
You can only define a project once using #define. Afterwards, you can obtain the project definition using #project. However, when working with sub-projects, one project may reference another ahead of its definition: the sub-project definitions are then evaluated based on their dependencies with each other. Circular dependencies are not allowed.
For example:
define "project1" do
self.version = "1.1"
define "module1" do
package :jar
end
define "module2" do
compile.with project("project1:module1")
package :jar
end
end
projects.map(&:name)
=> [ "project", "project:module1", "project1:module2" ]
project("project1").sub_projects.map(&:name)
=> [ "project1:module1", "project1:module2" ]
project("project1:module1").parent.name
=> "project1"
project("project1:module1").version
=> "1.1"
Each project has a base directory (see #base_dir). By default, a top-level project uses the current directory, and each sub-project uses a sub-directory relative to the parent project.
For the above example, the directory structure is: project1/ |__Rakefile |__module1/ |__module2/
The project definition tasks a block and yields by passing the project definition. For convenience, the block is also executed in the context of the project object, as if with instance_eval.
The following two are equivalent:
define "project1" do |project|
project.version = "1.1"
self.version = "1.1"
end
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
The project name.
-
#parent ⇒ Object
readonly
The parent project if this is a sub-project.
Class Method Summary collapse
-
.clear ⇒ Object
Discard all project definitions.
-
.define(*args, &block) ⇒ Object
See Buildr#define.
-
.local_task(task) ⇒ Object
Enhances this task into a local task.
-
.name_and_properties_from_args(*args) ⇒ Object
:nodoc:.
-
.on_define(&block) ⇒ Object
The Project class defines minimal behavior for new projects.
-
.project(name) ⇒ Object
See Buildr#project.
-
.projects(*args) ⇒ Object
See Buildr#projects.
- .scope_name(scope, task_name) ⇒ Object
-
.warnings ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#base_dir ⇒ Object
The base directory of this project.
-
#base_dir=(dir) ⇒ Object
Set the base directory.
- #build(*args, &block) ⇒ Object
- #clean(*args, &block) ⇒ Object
- #compile(*sources, &block) ⇒ Object
-
#define(*args, &block) ⇒ Object
Define a new sub-project within this project.
- #execute ⇒ Object
-
#file(args, &block) ⇒ Object
Create or return a file task.
-
#id ⇒ Object
The project ID is the project name, and for a sub-project the parent project ID followed by the project name, separated with a hyphen.
-
#initialize(*args) ⇒ Project
constructor
:nodoc:.
- #package(*args) ⇒ Object
- #packages ⇒ Object
-
#path_to(*args) ⇒ Object
Returns a path made from multiple arguments.
- #prepare(*tasks, &block) ⇒ Object
-
#project(name) ⇒ Object
Same as Buildr#project.
-
#projects(*args) ⇒ Object
Same as Buildr#projects.
-
#recursive_task(args, &block) ⇒ Object
Define a recursive task.
- #resources(*tasks, &block) ⇒ Object
- #sub_projects ⇒ Object
-
#task(args, &block) ⇒ Object
Create or return a task.
- #test(*tasks, &block) ⇒ Object
- #tests ⇒ Object
- #webserve(&block) ⇒ Object
Methods included from Attributes
Constructor Details
#initialize(*args) ⇒ Project
:nodoc:
225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/core/project.rb', line 225 def initialize(*args) super split = name.split(":") if split.size > 1 # Get parent project, but do not invoke it's definition to # prevent circular dependencies (it's being invoked right now). @parent = task(split[0...-1].join(":")) raise "No parent project #{split[0...-1].join(":")}" unless @parent && Project === parent end # We want to lazily evaluate base_dir, but default initialize # will set it to the current directory. @base_dir = nil end |
Instance Attribute Details
#name ⇒ Object (readonly)
The project name. If this is a sub-project, it will be prefixed by the parent project’s name. For example, “foo” and “foo:bar”.
219 220 221 |
# File 'lib/core/project.rb', line 219 def name @name end |
#parent ⇒ Object (readonly)
The parent project if this is a sub-project.
222 223 224 |
# File 'lib/core/project.rb', line 222 def parent @parent end |
Class Method Details
.clear ⇒ Object
Discard all project definitions.
120 121 122 |
# File 'lib/core/project.rb', line 120 def clear() @projects.clear if @projects end |
.define(*args, &block) ⇒ Object
See Buildr#define.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/core/project.rb', line 73 def define(*args, &block) name, properties = name_and_properties_from_args(*args) # Make sure a sub-project is only defined within the parent project, # to prevent silly mistakes that lead to inconsistencies (e.g. # namespaces will be all out of whack). Rake.application.current_scope == name.split(":")[0...-1] or raise "You can only define a sub project (#{name}) within the definition of its parent process" @projects ||= {} raise "You cannot define the same project (#{name}) more than once" if @projects[name] returning(Project.define_task(name)) do |project| @projects[name] = project project.enhance { |project| @on_define.each { |callback| callback[project] } } if @on_define # Set the project properties first, actions may use them. properties.each { |name, value| project.send "#{name}=", value } # Enhance the project definition with the block. if block # Evaluate in context of project, and pass project. project.enhance { project.instance_exec project, &block } end if project.parent project.parent.enhance { project.invoke } else project.invoke end end end |
.local_task(task) ⇒ Object
Enhances this task into a local task. A local task executes the same task on the project in the local directory.
For example, if the current directory project is foo
, then rake build executes rake foo:build.
The current directory project is a project with the base directory being the same as the current directory. For example:
cd
rake build
Will execute the foo:bar:build
task, after switching to the directory of the sub-project bar
.
136 137 138 139 140 141 142 143 144 145 |
# File 'lib/core/project.rb', line 136 def local_task(task) task.enhance do |task| projects = Project.projects.select { |project| project.base_dir == Rake.application.original_dir } if verbose && projects.empty? warn "No projects defined for directory #{Rake.application.original_dir}" end projects.each { |project| task("#{project.name}:#{task.name}").invoke } end task end |
.name_and_properties_from_args(*args) ⇒ Object
:nodoc:
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/core/project.rb', line 180 def name_and_properties_from_args(*args) if Hash === args.last properties = args.pop.clone else properties = {} end if String === args.first name = args.shift else name = properties.delete(:name) end raise ArgumentError, "Expected project name followed by (optional) project properties." unless args.empty? raise ArgumentError, "Missing project name, this is the first argument to the define method" unless name [ name, properties ] end |
.on_define(&block) ⇒ Object
The Project class defines minimal behavior for new projects. Use #on_define to add behavior when defining new projects. Whenever a new project is defined, it will yield to the block with the project object.
For example:
# Set the default version of each project to "1.0".
Project.on_define do |project|
project.version ||= "1.0"
end
Keep in mind that the order in which #on_define blocks are called is not determined. You cannot depend on a previous #on_define to set properties or create new tasks. You would want to use the #enhance method instead, by calling it from within #on_define.
For example:
Project.on_define do |project|
puts "defining"
project.enhance { puts "defined" }
end
define "foo" do
puts "block"
end
=> defining
block
defined
175 176 177 |
# File 'lib/core/project.rb', line 175 def on_define(&block) (@on_define ||= []) << block if block end |
.project(name) ⇒ Object
See Buildr#project.
103 104 105 106 |
# File 'lib/core/project.rb', line 103 def project(name) @projects && @projects[name] or raise "No such project #{name}" returning(@projects[name]) { |project| project.invoke } end |
.projects(*args) ⇒ Object
See Buildr#projects.
109 110 111 112 113 114 115 116 117 |
# File 'lib/core/project.rb', line 109 def projects(*args) @projects ||= {} if args.empty? @projects.keys.map { |name| project(name) }.sort_by(&:name) else args.map { |name| project(name) or raise "No such project #{name}" }. uniq.sort_by(&:name) end end |
.scope_name(scope, task_name) ⇒ Object
209 210 211 |
# File 'lib/core/project.rb', line 209 def scope_name(scope, task_name) task_name end |
.warnings ⇒ Object
:nodoc:
197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/core/project.rb', line 197 def warnings() returning([]) do |msgs| msgs << "There are no project definitions in your Rakefile" if @projects.nil? || @projects.empty? # Find all projects that: # * Are referenced but never defined. This is probably a typo. # * Do not have a base directory. (@projects || {}).each do |name, project| msgs << "Project #{name} refers to the directory #{project.base_dir}, which does not exist" unless File.exist?(project.base_dir) end end end |
Instance Method Details
#base_dir ⇒ Object
The base directory of this project. The default for a top-level project is the same directory that holds the Rakefile. The default for a sub-project is a child directory with the same name.
A project definition can change the base directory using the base_dir hash value. Be advised that the base directory and all values that depend on it can only be determined after the project is defined.
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/core/project.rb', line 246 def base_dir() unless @base_dir if @parent # For sub-project, a good default is a directory in the parent's base_dir, # using the same name as the project. sub_dir = File.join(@parent.base_dir, name.split(":").last) @base_dir = File.exist?(sub_dir) ? sub_dir : @parent.base_dir @base_dir = sub_dir else # For top-level project, a good default is the directory where we found the Rakefile. @base_dir = Dir.pwd end end @base_dir end |
#base_dir=(dir) ⇒ Object
Set the base directory. Note: you can only do this once for a project, and only before accessing the base directory. If you try reading the value with #base_dir, the base directory cannot be set again.
265 266 267 268 |
# File 'lib/core/project.rb', line 265 def base_dir=(dir) raise "Cannot set base directory twice, or after reading its value" if @base_dir @base_dir = File.(dir) end |
#build(*args, &block) ⇒ Object
23 24 25 26 27 |
# File 'lib/core/build.rb', line 23 def build(*args, &block) returning(@build_task ||= task("build")) do |task| task.enhance args, &block end end |
#clean(*args, &block) ⇒ Object
29 30 31 32 33 |
# File 'lib/core/build.rb', line 29 def clean(*args, &block) returning(@clean_task ||= task("clean")) do |task| task.enhance args, &block end end |
#compile(*sources, &block) ⇒ Object
275 276 277 278 279 280 |
# File 'lib/java/compile.rb', line 275 def compile(*sources, &block) returning(@compile_task ||= Java::CompileTask.define_task("compile")) do |task| task.sources |= sources task.enhance &block if block end end |
#define(*args, &block) ⇒ Object
Define a new sub-project within this project.
271 272 273 274 |
# File 'lib/core/project.rb', line 271 def define(*args, &block) name, properties = Project.name_and_properties_from_args(*args) Project.define "#{self.name}:#{name}", properties, &block end |
#execute ⇒ Object
381 382 383 384 385 386 |
# File 'lib/core/project.rb', line 381 def execute() Rake.application.in_namespace ":#{name}" do # Everything we do inside the project is relative to its working directory. Dir.chdir(base_dir) { super } end end |
#file(args, &block) ⇒ Object
Create or return a file task. This is similar to Rake’s file method, with the exception that all relative paths are resolved relative to the project’s base directory.
You can call this from within or outside the project definition.
313 314 315 316 317 318 319 320 321 |
# File 'lib/core/project.rb', line 313 def file(args, &block) task_name, deps = Rake.application.resolve_args(args) unless task = Rake.application.lookup(task_name, []) task = Rake::FileTask.define_task(File.(task_name, base_dir)=>deps, &block) task.base_dir = base_dir end deps = [deps] unless deps.respond_to?(:to_ary) task.enhance deps, &block end |
#id ⇒ Object
The project ID is the project name, and for a sub-project the parent project ID followed by the project name, separated with a hyphen. For example, “foo” and “foo-bar”.
93 94 95 |
# File 'lib/java/packaging.rb', line 93 def id() name.gsub(":", "-") end |
#package(*args) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/java/packaging.rb', line 98 def package(*args) if Hash === args.last = args.pop.dup else = {} end [:type] = args.shift.to_s if Symbol === args.first fail "No packaging type specified" unless [:type] [:group] ||= self.group [:version] ||= self.version [:id] ||= self.id if String === args.first file = args.shift else file = .delete(:file) || path_to(:target_dir, Artifact.hash_to_file_name()) end fail "One argument too many; expecting at most type, file name, and hash of options" unless args.empty? packager = method("package_as_#{[:type]}") rescue fail("Do not know how to create a package of type #{[:type]}") package = packager.call(file, ) or fail("Do not know how to create a package of type #{[:type]}") task "package"=>package package.enhance [ task("build")] task "install"=>(file(repositories.locate(package)=>package) { |task| mkpath File.dirname(task.name), :verbose=>false cp package.name, task.name }) task "install"=>package.pom task "uninstall" do |task| verbose(Rake.application..trace) do [ package, package.pom ].map { |artifact| repositories.locate(artifact) }. each { |file| rm file if File.exist?(file) } end end task("deploy") { deploy(package, package.pom) } packages << package Artifact.register package, package.pom package end |
#packages ⇒ Object
143 144 145 |
# File 'lib/java/packaging.rb', line 143 def packages() @packages ||= [] end |
#path_to(*args) ⇒ Object
Returns a path made from multiple arguments. Relative paths are turned into absolute paths using this project’s base directory.
Symbol arguments are converted to paths by calling the attribute accessor on the project. For example:
For example:
path_to("foo", "bar")
=> /projects/project1/foo/bar
path_to(:target_dir, "foo")
=> /projects/project1/target/foo
path_to("/tmp")
=> /tmp
289 290 291 |
# File 'lib/core/project.rb', line 289 def path_to(*args) File.(File.join(args.map { |arg| Symbol === arg ? send(arg) : arg.to_s }), base_dir) end |
#prepare(*tasks, &block) ⇒ Object
282 283 284 285 286 |
# File 'lib/java/compile.rb', line 282 def prepare(*tasks, &block) returning(@prepare_task ||= task("prepare")) do |task| task.enhance tasks, &block end end |
#project(name) ⇒ Object
Same as Buildr#project.
294 295 296 |
# File 'lib/core/project.rb', line 294 def project(name) Project.project(name) end |
#projects(*args) ⇒ Object
Same as Buildr#projects.
299 300 301 |
# File 'lib/core/project.rb', line 299 def projects(*args) Project.projects(*args) end |
#recursive_task(args, &block) ⇒ Object
Define a recursive task.
A recursive task executes the task with the same name in the project, and in all its sub-projects. In fact, a recursive task actually adds itself as a prerequisite on the parent task.
For example:
define "foo" do
define "bar" do
define "baz" do
end
end
end
rake foo:build
Will execute foo:build, foo:bar:build and foo:baz:build
Inside the bar directory:
rake build
Will execute foo:bar:build and foo:baz:build.
This method defines a RakeTask. If you need a different type of task, define the task first and then call #recursive_task.
369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/core/project.rb', line 369 def recursive_task(args, &block) task_name, deps = Rake.application.resolve_args(args) deps = [deps] unless deps.respond_to?(:to_ary) returning(task(task_name=>deps)) do |task| if parent Rake.application.lookup(task_name, parent.name.split(":")).enhance [task] #Rake::Task["^#{name}"].enhance([ task ]) end task.enhance &block end end |
#resources(*tasks, &block) ⇒ Object
269 270 271 272 273 |
# File 'lib/java/compile.rb', line 269 def resources(*tasks, &block) returning(@resources_task ||= Filter.define_task("resources")) do |task| task.enhance tasks, &block end end |
#sub_projects ⇒ Object
303 304 305 306 |
# File 'lib/core/project.rb', line 303 def sub_projects() prefix = name + ":" Project.projects.select { |project| project.name.starts_with?(prefix) }.sort_by(&:name) end |
#task(args, &block) ⇒ Object
Create or return a task. This is similar to Rake’s task method, with the exception that the task is always defined within the project’s namespace.
If called from within the project definition, it returns a task, creating a new one no such task exists. If called from outside the project definition, it returns a task and raises an error if the task does not exist.
331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/core/project.rb', line 331 def task(args, &block) task_name, deps = Rake.application.resolve_args(args) if Rake.application.current_scope == name.split(":") Rake::Task.define_task(task_name=>deps, &block) else if task = Rake.application.lookup(task_name, name.split(":")) deps = [deps] unless deps.respond_to?(:to_ary) task.enhance deps, &block else full_name = "#{name}:#{task_name}" raise "You cannot define a project task outside the project definition, and no task #{full_name} defined in the project" end end end |
#test(*tasks, &block) ⇒ Object
67 68 69 |
# File 'lib/java/test.rb', line 67 def test(*tasks, &block) @tests.junit *tasks, &block end |
#tests ⇒ Object
63 64 65 |
# File 'lib/java/test.rb', line 63 def tests() @tests ||= Tests.new end |
#webserve(&block) ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/java/jetty.rb', line 85 def webserve(&block) task "jetty:shutdown" do begin res = Jetty.jetty_call('/stop', :put, @webserve_task.) verbose { puts "Response #{res}" } rescue Exception => e if (e.class == Errno::ECONNREFUSED) puts "Jetty server couldn't be contacted, nothing done." elsif (e.class == EOFError) puts "Shutdown successful." else puts "Unexpected error: #{e.class}" end end end returning(@webserve_task ||= Jetty::JettyTask.define_task("jetty:bounce")) do |task| task.enhance &block if block end end |