Buildr 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +189 -0
- data/LICENSE +202 -0
- data/README +0 -0
- data/lib/buildr.rb +55 -0
- data/lib/core/artifact.rb +405 -0
- data/lib/core/build.rb +153 -0
- data/lib/core/core.rb +152 -0
- data/lib/core/project.rb +486 -0
- data/lib/core/transports.rb +383 -0
- data/lib/java/compile.rb +310 -0
- data/lib/java/eclipse.rb +78 -0
- data/lib/java/java.rb +114 -0
- data/lib/java/javacc.rb +88 -0
- data/lib/java/jetty.rb +118 -0
- data/lib/java/jetty/JettyWrapper$1.class +0 -0
- data/lib/java/jetty/JettyWrapper$BuildrHandler.class +0 -0
- data/lib/java/jetty/JettyWrapper.class +0 -0
- data/lib/java/jetty/JettyWrapper.java +107 -0
- data/lib/java/openjpa.rb +57 -0
- data/lib/java/packaging.rb +214 -0
- data/lib/java/test.rb +105 -0
- data/lib/java/xmlbeans.rb +66 -0
- data/lib/tasks/download.rb +38 -0
- data/lib/tasks/filter.rb +96 -0
- data/lib/tasks/zip.rb +422 -0
- metadata +142 -0
data/lib/core/build.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
require "open3"
|
2
|
+
|
3
|
+
|
4
|
+
module Buildr
|
5
|
+
|
6
|
+
BUILD_TASKS = {
|
7
|
+
:build =>"Build the project",
|
8
|
+
:clean =>"Clean files generated during a build",
|
9
|
+
:package =>"Create packages",
|
10
|
+
:install =>"Install packages created by the project",
|
11
|
+
:uninstall=>"Remove previously installed packages",
|
12
|
+
:deploy =>"Deploy packages created by the project"
|
13
|
+
}
|
14
|
+
|
15
|
+
# Handles the build and clean tasks.
|
16
|
+
BUILD_TASKS.each { |name, comment| Project.local_task(task(name)).add_comment(comment) }
|
17
|
+
|
18
|
+
Project.on_define do |project|
|
19
|
+
BUILD_TASKS.each { |name, comment| project.recursive_task name }
|
20
|
+
end
|
21
|
+
|
22
|
+
class Project
|
23
|
+
def build(*args, &block)
|
24
|
+
returning(@build_task ||= task("build")) do |task|
|
25
|
+
task.enhance args, &block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def clean(*args, &block)
|
30
|
+
returning(@clean_task ||= task("clean")) do |task|
|
31
|
+
task.enhance args, &block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Project.on_define do |project|
|
37
|
+
project.build
|
38
|
+
project.clean
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
class ReleaseTask < Rake::Task
|
43
|
+
|
44
|
+
VERSION_NUMBER_PATTERN = /VERSION_NUMBER\s*=\s*(["'])(.*)\1/
|
45
|
+
NEXT_VERSION_PATTERN = /NEXT_VERSION\s*=\s*(["'])(.*)\1/
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def svn_ignores()
|
49
|
+
@ignores = (@ignores || []).map { |pat| pat.is_a?(Regexp) ? pat : Regexp.new("^.*\s+#{Regexp.escape pat}$") }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(*args)
|
54
|
+
super
|
55
|
+
enhance do |task|
|
56
|
+
# Make sure we don't have anything uncommitted in SVN.
|
57
|
+
check_status
|
58
|
+
# Update current version to next version before deploying.
|
59
|
+
next_ver = update_version
|
60
|
+
# Run the deployment externally using the new version number
|
61
|
+
# (from the modified Rakefile).
|
62
|
+
sh "rake deploy"
|
63
|
+
# Update the next version as well to the next increment and commit.
|
64
|
+
update_next_version next_ver
|
65
|
+
# Tag the repository for this release.
|
66
|
+
tag_repository next_ver
|
67
|
+
# Update the next version to end with -SNAPSHOT.
|
68
|
+
update_version_to_snapshot next_ver
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def check_status()
|
73
|
+
ignores = ReleaseTask.svn_ignores
|
74
|
+
status = svn("status", "--ignore-externals", :verbose=>false).
|
75
|
+
reject { |line| line =~ /^X\s/ || ignores.any? { |pat| line =~ pat } }
|
76
|
+
fail "Uncommitted SVN files violate the First Principle Of Release!\n#{status}" unless
|
77
|
+
status.empty?
|
78
|
+
end
|
79
|
+
|
80
|
+
# Change the Rakefile and update the current version number to the
|
81
|
+
# next version number (VERSION_NUMBER = NEXT_VERSION). We need this
|
82
|
+
# before making a release with the next version. Return the next version.
|
83
|
+
def update_version()
|
84
|
+
rakefile = File.read(Rake.application.rakefile)
|
85
|
+
version = rakefile.scan(VERSION_NUMBER_PATTERN)[0][1] or
|
86
|
+
fail "Looking for VERSION_NUMBER = \"...\" in your Rakefile, none found"
|
87
|
+
next_ver = rakefile.scan(NEXT_VERSION_PATTERN)[0][1] or
|
88
|
+
fail "Looking for NEXT_VERSION = \"...\" in your Rakefile, none found"
|
89
|
+
if verbose
|
90
|
+
puts "Current version: #{version}"
|
91
|
+
puts "Next version: #{next_ver}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Switch version numbers.
|
95
|
+
rakefile.gsub!(VERSION_NUMBER_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{next_ver}"}) }
|
96
|
+
File.open(Rake.application.rakefile, "w") { |file| file.write rakefile }
|
97
|
+
|
98
|
+
next_ver
|
99
|
+
end
|
100
|
+
|
101
|
+
# Change the Rakefile and update the next version number to one after
|
102
|
+
# (NEXT_VERSION = NEXT_VERSION + 1). We do this to automatically increment
|
103
|
+
# future version number after each successful release.
|
104
|
+
def update_next_version(version)
|
105
|
+
# Update to new version number.
|
106
|
+
nums = version.split(".")
|
107
|
+
nums[-1] = nums[-1].to_i + 1
|
108
|
+
next_ver = nums.join(".")
|
109
|
+
rakefile = File.read(Rake.application.rakefile)
|
110
|
+
rakefile.gsub!(NEXT_VERSION_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{next_ver}"}) }
|
111
|
+
File.open(Rake.application.rakefile, "w") { |file| file.write rakefile }
|
112
|
+
|
113
|
+
# Commit new version number.
|
114
|
+
svn "commit", "-m", "Changed version number to #{version}", Rake.application.rakefile
|
115
|
+
end
|
116
|
+
|
117
|
+
# Create a tag in the SVN repository.
|
118
|
+
def tag_repository(version)
|
119
|
+
# Copy to tag.
|
120
|
+
cur_url = svn("info").scan(/URL: (.*)/)[0][0]
|
121
|
+
new_url = cur_url.sub(/trunk$/, "tags/#{version}")
|
122
|
+
svn "remove", new_url, "-m", "Removing old copy" rescue nil
|
123
|
+
svn "copy", cur_url, new_url, "-m", "Release #{version}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def update_version_to_snapshot(version)
|
127
|
+
version += "-SNAPSHOT"
|
128
|
+
rakefile = File.read(Rake.application.rakefile)
|
129
|
+
rakefile.gsub!(VERSION_NUMBER_PATTERN) { |ver| ver.sub(/(["']).*\1/, %Q{"#{version}"}) }
|
130
|
+
File.open(Rake.application.rakefile, "w") { |file| file.write rakefile }
|
131
|
+
# Commit new version number.
|
132
|
+
svn "commit", "-m", "Changed version number to #{version}", Rake.application.rakefile
|
133
|
+
end
|
134
|
+
|
135
|
+
def svn(*args)
|
136
|
+
if Hash === args.last
|
137
|
+
options = args.pop
|
138
|
+
else
|
139
|
+
options = { :verbose=>verbose }
|
140
|
+
end
|
141
|
+
puts ["svn", *args].join(" ") if options[:verbose]
|
142
|
+
Open3.popen3("svn", *args) do |stdin, stdout, stderr|
|
143
|
+
stdin.close
|
144
|
+
error = stderr.read
|
145
|
+
fail error unless error.empty?
|
146
|
+
returning(stdout.read) { |output| puts output if Rake.application.options.trace }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
desc "Make a release"
|
152
|
+
ReleaseTask.define_task "release"
|
153
|
+
end
|
data/lib/core/core.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require "highline"
|
2
|
+
|
3
|
+
|
4
|
+
module Kernel
|
5
|
+
def warn_with_color(message)
|
6
|
+
warn_without_color HighLine.new.color(message.to_s, :red)
|
7
|
+
end
|
8
|
+
alias_method_chain :warn, :color
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
module Buildr
|
13
|
+
module Attributes
|
14
|
+
|
15
|
+
def self.included(mod)
|
16
|
+
mod.extend(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
# An inherited attribute gets it value from an instance variable
|
20
|
+
# with the same name. If the value is not set it will defer to the
|
21
|
+
# parent object. If there is no parent object, it will use the
|
22
|
+
# default value; with a block, it evaluates the block by calling
|
23
|
+
# instance_eval on the object.
|
24
|
+
#
|
25
|
+
# For example:
|
26
|
+
# inherited_attr :version
|
27
|
+
# inherited_attr :src_dir, "src"
|
28
|
+
# inherited_attr :java_src_dir do src_dir + "/main/java"
|
29
|
+
def inherited_attr(symbol, default = nil, &block)
|
30
|
+
block ||= proc { default }
|
31
|
+
define_method symbol do
|
32
|
+
instance_variable_get("@#{symbol}") || (parent ? parent.send(symbol) : self.instance_eval(&block))
|
33
|
+
end
|
34
|
+
define_method "#{symbol}=" do |value|
|
35
|
+
instance_variable_set("@#{symbol}", value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class Rake::Task
|
43
|
+
def invoke
|
44
|
+
if application.options.trace
|
45
|
+
puts "** Invoke #{name} #{format_trace_flags}"
|
46
|
+
end
|
47
|
+
tasks = (Thread.current[:tasks] || [])
|
48
|
+
if tasks.include?(name)
|
49
|
+
fail "Circular dependency " + (tasks + [name]).join("=>")
|
50
|
+
end
|
51
|
+
@lock.synchronize do
|
52
|
+
return if @already_invoked
|
53
|
+
begin
|
54
|
+
Thread.current[:tasks] = tasks + [name]
|
55
|
+
@already_invoked = true
|
56
|
+
invoke_prerequisites
|
57
|
+
execute if needed?
|
58
|
+
ensure
|
59
|
+
Thread.current[:tasks] = tasks
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Rake::Task
|
66
|
+
|
67
|
+
# Access the base directory. The base directory is set when the class
|
68
|
+
# is defined from the current directory. The current directory is set
|
69
|
+
# to the base directory when the class is executed.
|
70
|
+
attr_accessor :base_dir
|
71
|
+
|
72
|
+
# :nodoc:
|
73
|
+
def invoke_with_base_dir()
|
74
|
+
Dir.chdir(@base_dir || Dir.pwd) { invoke_without_base_dir }
|
75
|
+
end
|
76
|
+
alias_method_chain :invoke, :base_dir
|
77
|
+
|
78
|
+
# :nodoc:
|
79
|
+
def initialize_with_base_dir(*args)
|
80
|
+
@base_dir = Dir.pwd
|
81
|
+
initialize_without_base_dir *args
|
82
|
+
end
|
83
|
+
alias_method_chain :initialize, :base_dir
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
class Rake::Application
|
89
|
+
|
90
|
+
def in_namespace_with_global_scope(name, &block)
|
91
|
+
if name =~ /^:/
|
92
|
+
begin
|
93
|
+
scope, @scope = @scope, name.split(":")[1...-1]
|
94
|
+
in_namespace_without_global_scope name.split(":").last, &block
|
95
|
+
ensure
|
96
|
+
@scope = scope
|
97
|
+
end
|
98
|
+
else
|
99
|
+
in_namespace_without_global_scope name, &block
|
100
|
+
end
|
101
|
+
end
|
102
|
+
alias_method_chain :in_namespace, :global_scope
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
class CheckTask < Rake::Task
|
108
|
+
|
109
|
+
def execute()
|
110
|
+
@warnings = []
|
111
|
+
super
|
112
|
+
report if verbose
|
113
|
+
end
|
114
|
+
|
115
|
+
def note(*msg)
|
116
|
+
@warnings += msg
|
117
|
+
end
|
118
|
+
|
119
|
+
def report()
|
120
|
+
if @warnings.empty?
|
121
|
+
puts HighLine.new.color("No warnings", :green)
|
122
|
+
else
|
123
|
+
warn "These are possible problems with your Rakefile"
|
124
|
+
@warnings.each { |msg| warn " #{msg}" }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
desc "Check your Rakefile for common errors"
|
132
|
+
CheckTask.define_task "check"
|
133
|
+
|
134
|
+
# Check for circular dependencies
|
135
|
+
task "check" do
|
136
|
+
depends = {}
|
137
|
+
expand = lambda do |stack, checking|
|
138
|
+
if depends[checking]
|
139
|
+
if stack.include?(checking)
|
140
|
+
fail "Circular " + (stack + [checking]).join("=>")
|
141
|
+
end
|
142
|
+
else
|
143
|
+
depends[checking] = []
|
144
|
+
depends[checking] |= Rake.application[checking].prerequisites.
|
145
|
+
map { |prereq| expand[stack + [checking.to_s], prereq.to_s] }.flatten.map(&:to_s)
|
146
|
+
end
|
147
|
+
depends[checking]
|
148
|
+
end
|
149
|
+
Rake.application.tasks.each do |checking|
|
150
|
+
expand[ [], checking.to_s ]
|
151
|
+
end
|
152
|
+
end
|
data/lib/core/project.rb
ADDED
@@ -0,0 +1,486 @@
|
|
1
|
+
module Buildr
|
2
|
+
|
3
|
+
# A project is a convenient mechanism for managing all the tasks
|
4
|
+
# related to a given project. For complex applications, you may have
|
5
|
+
# several projects, or sub-projects for each of the modules.
|
6
|
+
#
|
7
|
+
# A project definition creates its own set of tasks, prefixed with
|
8
|
+
# the project name. For example, each project has a clean, build
|
9
|
+
# and deploy task. For project +foo+ the task names are +foo:clean+,
|
10
|
+
# +foo:build+ and +foo:deploy+.
|
11
|
+
#
|
12
|
+
# Projects have properties, some of which they inherit from their
|
13
|
+
# parent project. Built in tasks use these properties, for example,
|
14
|
+
# the +clean+ task will remove the target directory specified by
|
15
|
+
# the +target_dir+ property. The +compile+ tasks uses the compiler
|
16
|
+
# option: you can set these options on the parent project and they
|
17
|
+
# will be inherited by all sub-projects.
|
18
|
+
#
|
19
|
+
# You can only define a project once using #define. Afterwards, you
|
20
|
+
# can obtain the project definition using #project. However, when
|
21
|
+
# working with sub-projects, one project may reference another ahead
|
22
|
+
# of its definition: the sub-project definitions are then evaluated
|
23
|
+
# based on their dependencies with each other. Circular dependencies
|
24
|
+
# are not allowed.
|
25
|
+
#
|
26
|
+
# For example:
|
27
|
+
# define "project1" do
|
28
|
+
# self.version = "1.1"
|
29
|
+
#
|
30
|
+
# define "module1" do
|
31
|
+
# package :jar
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# define "module2" do
|
35
|
+
# compile.with project("project1:module1")
|
36
|
+
# package :jar
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# projects.map(&:name)
|
41
|
+
# => [ "project", "project:module1", "project1:module2" ]
|
42
|
+
# project("project1").sub_projects.map(&:name)
|
43
|
+
# => [ "project1:module1", "project1:module2" ]
|
44
|
+
# project("project1:module1").parent.name
|
45
|
+
# => "project1"
|
46
|
+
# project("project1:module1").version
|
47
|
+
# => "1.1"
|
48
|
+
#
|
49
|
+
# Each project has a base directory (see #base_dir). By default,
|
50
|
+
# a top-level project uses the current directory, and each sub-project
|
51
|
+
# uses a sub-directory relative to the parent project.
|
52
|
+
#
|
53
|
+
# For the above example, the directory structure is:
|
54
|
+
# project1/
|
55
|
+
# |__Rakefile
|
56
|
+
# |__module1/
|
57
|
+
# |__module2/
|
58
|
+
#
|
59
|
+
# The project definition tasks a block and yields by passing the project
|
60
|
+
# definition. For convenience, the block is also executed in the context
|
61
|
+
# of the project object, as if with instance_eval.
|
62
|
+
#
|
63
|
+
# The following two are equivalent:
|
64
|
+
# define "project1" do |project|
|
65
|
+
# project.version = "1.1"
|
66
|
+
# self.version = "1.1"
|
67
|
+
# end
|
68
|
+
class Project < Rake::Task
|
69
|
+
|
70
|
+
class << self
|
71
|
+
|
72
|
+
# See Buildr#define.
|
73
|
+
def define(*args, &block)
|
74
|
+
name, properties = name_and_properties_from_args(*args)
|
75
|
+
# Make sure a sub-project is only defined within the parent project,
|
76
|
+
# to prevent silly mistakes that lead to inconsistencies (e.g.
|
77
|
+
# namespaces will be all out of whack).
|
78
|
+
Rake.application.current_scope == name.split(":")[0...-1] or
|
79
|
+
raise "You can only define a sub project (#{name}) within the definition of its parent process"
|
80
|
+
|
81
|
+
@projects ||= {}
|
82
|
+
raise "You cannot define the same project (#{name}) more than once" if @projects[name]
|
83
|
+
returning(Project.define_task(name)) do |project|
|
84
|
+
@projects[name] = project
|
85
|
+
project.enhance { |project| @on_define.each { |callback| callback[project] } } if @on_define
|
86
|
+
# Set the project properties first, actions may use them.
|
87
|
+
properties.each { |name, value| project.send "#{name}=", value }
|
88
|
+
# Enhance the project definition with the block.
|
89
|
+
if block
|
90
|
+
# Evaluate in context of project, and pass project.
|
91
|
+
project.enhance { project.instance_exec project, &block }
|
92
|
+
end
|
93
|
+
|
94
|
+
if project.parent
|
95
|
+
project.parent.enhance { project.invoke }
|
96
|
+
else
|
97
|
+
project.invoke
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# See Buildr#project.
|
103
|
+
def project(name)
|
104
|
+
@projects && @projects[name] or raise "No such project #{name}"
|
105
|
+
returning(@projects[name]) { |project| project.invoke }
|
106
|
+
end
|
107
|
+
|
108
|
+
# See Buildr#projects.
|
109
|
+
def projects(*args)
|
110
|
+
@projects ||= {}
|
111
|
+
if args.empty?
|
112
|
+
@projects.keys.map { |name| project(name) }.sort_by(&:name)
|
113
|
+
else
|
114
|
+
args.map { |name| project(name) or raise "No such project #{name}" }.
|
115
|
+
uniq.sort_by(&:name)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Discard all project definitions.
|
120
|
+
def clear()
|
121
|
+
@projects.clear if @projects
|
122
|
+
end
|
123
|
+
|
124
|
+
# Enhances this task into a local task. A local task executes the same
|
125
|
+
# task on the project in the local directory.
|
126
|
+
#
|
127
|
+
# For example, if the current directory project is +foo+, then
|
128
|
+
# +rake build+ executes +rake foo:build+.
|
129
|
+
#
|
130
|
+
# The current directory project is a project with the base directory
|
131
|
+
# being the same as the current directory. For example:
|
132
|
+
# cd bar
|
133
|
+
# rake build
|
134
|
+
# Will execute the +foo:bar:build+ task, after switching to the directory
|
135
|
+
# of the sub-project +bar+.
|
136
|
+
def local_task(task)
|
137
|
+
task.enhance do |task|
|
138
|
+
projects = Project.projects.select { |project| project.base_dir == Rake.application.original_dir }
|
139
|
+
if verbose && projects.empty?
|
140
|
+
warn "No projects defined for directory #{Rake.application.original_dir}"
|
141
|
+
end
|
142
|
+
projects.each { |project| task("#{project.name}:#{task.name}").invoke }
|
143
|
+
end
|
144
|
+
task
|
145
|
+
end
|
146
|
+
|
147
|
+
# The Project class defines minimal behavior for new projects.
|
148
|
+
# Use #on_define to add behavior when defining new projects.
|
149
|
+
# Whenever a new project is defined, it will yield to the block
|
150
|
+
# with the project object.
|
151
|
+
#
|
152
|
+
# For example:
|
153
|
+
# # Set the default version of each project to "1.0".
|
154
|
+
# Project.on_define do |project|
|
155
|
+
# project.version ||= "1.0"
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# Keep in mind that the order in which #on_define blocks are
|
159
|
+
# called is not determined. You cannot depend on a previous
|
160
|
+
# #on_define to set properties or create new tasks. You would
|
161
|
+
# want to use the #enhance method instead, by calling it
|
162
|
+
# from within #on_define.
|
163
|
+
#
|
164
|
+
# For example:
|
165
|
+
# Project.on_define do |project|
|
166
|
+
# puts "defining"
|
167
|
+
# project.enhance { puts "defined" }
|
168
|
+
# end
|
169
|
+
# define "foo" do
|
170
|
+
# puts "block"
|
171
|
+
# end
|
172
|
+
# => defining
|
173
|
+
# block
|
174
|
+
# defined
|
175
|
+
def on_define(&block)
|
176
|
+
(@on_define ||= []) << block if block
|
177
|
+
end
|
178
|
+
|
179
|
+
# :nodoc:
|
180
|
+
def name_and_properties_from_args(*args)
|
181
|
+
if Hash === args.last
|
182
|
+
properties = args.pop.clone
|
183
|
+
else
|
184
|
+
properties = {}
|
185
|
+
end
|
186
|
+
if String === args.first
|
187
|
+
name = args.shift
|
188
|
+
else
|
189
|
+
name = properties.delete(:name)
|
190
|
+
end
|
191
|
+
raise ArgumentError, "Expected project name followed by (optional) project properties." unless args.empty?
|
192
|
+
raise ArgumentError, "Missing project name, this is the first argument to the define method" unless name
|
193
|
+
[ name, properties ]
|
194
|
+
end
|
195
|
+
|
196
|
+
# :nodoc:
|
197
|
+
def warnings()
|
198
|
+
returning([]) do |msgs|
|
199
|
+
msgs << "There are no project definitions in your Rakefile" if @projects.nil? || @projects.empty?
|
200
|
+
# Find all projects that:
|
201
|
+
# * Are referenced but never defined. This is probably a typo.
|
202
|
+
# * Do not have a base directory.
|
203
|
+
(@projects || {}).each do |name, project|
|
204
|
+
msgs << "Project #{name} refers to the directory #{project.base_dir}, which does not exist" unless File.exist?(project.base_dir)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def scope_name(scope, task_name)
|
210
|
+
task_name
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
include Attributes
|
216
|
+
|
217
|
+
# The project name. If this is a sub-project, it will be prefixed
|
218
|
+
# by the parent project's name. For example, "foo" and "foo:bar".
|
219
|
+
attr_reader :name
|
220
|
+
|
221
|
+
# The parent project if this is a sub-project.
|
222
|
+
attr_reader :parent
|
223
|
+
|
224
|
+
# :nodoc:
|
225
|
+
def initialize(*args)
|
226
|
+
super
|
227
|
+
split = name.split(":")
|
228
|
+
if split.size > 1
|
229
|
+
# Get parent project, but do not invoke it's definition to
|
230
|
+
# prevent circular dependencies (it's being invoked right now).
|
231
|
+
@parent = task(split[0...-1].join(":"))
|
232
|
+
raise "No parent project #{split[0...-1].join(":")}" unless @parent && Project === parent
|
233
|
+
end
|
234
|
+
# We want to lazily evaluate base_dir, but default initialize
|
235
|
+
# will set it to the current directory.
|
236
|
+
@base_dir = nil
|
237
|
+
end
|
238
|
+
|
239
|
+
# The base directory of this project. The default for a top-level project
|
240
|
+
# is the same directory that holds the Rakefile. The default for a
|
241
|
+
# sub-project is a child directory with the same name.
|
242
|
+
#
|
243
|
+
# A project definition can change the base directory using the base_dir
|
244
|
+
# hash value. Be advised that the base directory and all values that
|
245
|
+
# depend on it can only be determined after the project is defined.
|
246
|
+
def base_dir()
|
247
|
+
unless @base_dir
|
248
|
+
if @parent
|
249
|
+
# For sub-project, a good default is a directory in the parent's base_dir,
|
250
|
+
# using the same name as the project.
|
251
|
+
sub_dir = File.join(@parent.base_dir, name.split(":").last)
|
252
|
+
@base_dir = File.exist?(sub_dir) ? sub_dir : @parent.base_dir
|
253
|
+
@base_dir = sub_dir
|
254
|
+
else
|
255
|
+
# For top-level project, a good default is the directory where we found the Rakefile.
|
256
|
+
@base_dir = Dir.pwd
|
257
|
+
end
|
258
|
+
end
|
259
|
+
@base_dir
|
260
|
+
end
|
261
|
+
|
262
|
+
# Set the base directory. Note: you can only do this once for a project,
|
263
|
+
# and only before accessing the base directory. If you try reading the
|
264
|
+
# value with #base_dir, the base directory cannot be set again.
|
265
|
+
def base_dir=(dir)
|
266
|
+
raise "Cannot set base directory twice, or after reading its value" if @base_dir
|
267
|
+
@base_dir = File.expand_path(dir)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Define a new sub-project within this project.
|
271
|
+
def define(*args, &block)
|
272
|
+
name, properties = Project.name_and_properties_from_args(*args)
|
273
|
+
Project.define "#{self.name}:#{name}", properties, &block
|
274
|
+
end
|
275
|
+
|
276
|
+
# Returns a path made from multiple arguments. Relative paths are turned into
|
277
|
+
# absolute paths using this project's base directory.
|
278
|
+
#
|
279
|
+
# Symbol arguments are converted to paths by calling the attribute accessor
|
280
|
+
# on the project. For example:
|
281
|
+
#
|
282
|
+
# For example:
|
283
|
+
# path_to("foo", "bar")
|
284
|
+
# => /projects/project1/foo/bar
|
285
|
+
# path_to(:target_dir, "foo")
|
286
|
+
# => /projects/project1/target/foo
|
287
|
+
# path_to("/tmp")
|
288
|
+
# => /tmp
|
289
|
+
def path_to(*args)
|
290
|
+
File.expand_path(File.join(args.map { |arg| Symbol === arg ? send(arg) : arg.to_s }), base_dir)
|
291
|
+
end
|
292
|
+
|
293
|
+
# Same as Buildr#project.
|
294
|
+
def project(name)
|
295
|
+
Project.project(name)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Same as Buildr#projects.
|
299
|
+
def projects(*args)
|
300
|
+
Project.projects(*args)
|
301
|
+
end
|
302
|
+
|
303
|
+
def sub_projects()
|
304
|
+
prefix = name + ":"
|
305
|
+
Project.projects.select { |project| project.name.starts_with?(prefix) }.sort_by(&:name)
|
306
|
+
end
|
307
|
+
|
308
|
+
# Create or return a file task. This is similar to Rake's file method,
|
309
|
+
# with the exception that all relative paths are resolved relative to
|
310
|
+
# the project's base directory.
|
311
|
+
#
|
312
|
+
# You can call this from within or outside the project definition.
|
313
|
+
def file(args, &block)
|
314
|
+
task_name, deps = Rake.application.resolve_args(args)
|
315
|
+
unless task = Rake.application.lookup(task_name, [])
|
316
|
+
task = Rake::FileTask.define_task(File.expand_path(task_name, base_dir)=>deps, &block)
|
317
|
+
task.base_dir = base_dir
|
318
|
+
end
|
319
|
+
deps = [deps] unless deps.respond_to?(:to_ary)
|
320
|
+
task.enhance deps, &block
|
321
|
+
end
|
322
|
+
|
323
|
+
# Create or return a task. This is similar to Rake's task method,
|
324
|
+
# with the exception that the task is always defined within the project's
|
325
|
+
# namespace.
|
326
|
+
#
|
327
|
+
# If called from within the project definition, it returns a task,
|
328
|
+
# creating a new one no such task exists. If called from outside the
|
329
|
+
# project definition, it returns a task and raises an error if the
|
330
|
+
# task does not exist.
|
331
|
+
def task(args, &block)
|
332
|
+
task_name, deps = Rake.application.resolve_args(args)
|
333
|
+
if Rake.application.current_scope == name.split(":")
|
334
|
+
Rake::Task.define_task(task_name=>deps, &block)
|
335
|
+
else
|
336
|
+
if task = Rake.application.lookup(task_name, name.split(":"))
|
337
|
+
deps = [deps] unless deps.respond_to?(:to_ary)
|
338
|
+
task.enhance deps, &block
|
339
|
+
else
|
340
|
+
full_name = "#{name}:#{task_name}"
|
341
|
+
raise "You cannot define a project task outside the project definition, and no task #{full_name} defined in the project"
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# Define a recursive task.
|
347
|
+
#
|
348
|
+
# A recursive task executes the task with the same name in the project,
|
349
|
+
# and in all its sub-projects. In fact, a recursive task actually adds
|
350
|
+
# itself as a prerequisite on the parent task.
|
351
|
+
#
|
352
|
+
# For example:
|
353
|
+
# define "foo" do
|
354
|
+
# define "bar" do
|
355
|
+
# define "baz" do
|
356
|
+
# end
|
357
|
+
# end
|
358
|
+
# end
|
359
|
+
#
|
360
|
+
# rake foo:build
|
361
|
+
# Will execute foo:build, foo:bar:build and foo:baz:build
|
362
|
+
#
|
363
|
+
# Inside the bar directory:
|
364
|
+
# rake build
|
365
|
+
# Will execute foo:bar:build and foo:baz:build.
|
366
|
+
#
|
367
|
+
# This method defines a RakeTask. If you need a different type of task,
|
368
|
+
# define the task first and then call #recursive_task.
|
369
|
+
def recursive_task(args, &block)
|
370
|
+
task_name, deps = Rake.application.resolve_args(args)
|
371
|
+
deps = [deps] unless deps.respond_to?(:to_ary)
|
372
|
+
returning(task(task_name=>deps)) do |task|
|
373
|
+
if parent
|
374
|
+
Rake.application.lookup(task_name, parent.name.split(":")).enhance [task]
|
375
|
+
#Rake::Task["^#{name}"].enhance([ task ])
|
376
|
+
end
|
377
|
+
task.enhance &block
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def execute()
|
382
|
+
Rake.application.in_namespace ":#{name}" do
|
383
|
+
# Everything we do inside the project is relative to its working directory.
|
384
|
+
Dir.chdir(base_dir) { super }
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
end
|
389
|
+
|
390
|
+
# :call-seq:
|
391
|
+
# define name { |project| ... }
|
392
|
+
# define name, properties { |project| ... }
|
393
|
+
# define properties { |project| ... }
|
394
|
+
#
|
395
|
+
# Defines a new project.
|
396
|
+
#
|
397
|
+
# The first argument is the project name. Each project must have a unique name,
|
398
|
+
# and you can only define a project once.
|
399
|
+
#
|
400
|
+
# The second argument contains any number of properties that are set on the
|
401
|
+
# project. The project must have attribute accessors to support these properties.
|
402
|
+
# You can also pass the project name in the properties hash.
|
403
|
+
#
|
404
|
+
# The easiest way to define a project and configure its tasks is by passing
|
405
|
+
# a block. The #define method executes the block within the context of the
|
406
|
+
# project, as if with instance_eval. It also passes the project to the block.
|
407
|
+
#
|
408
|
+
# For example:
|
409
|
+
# define "foo", :version=>"1.0" do
|
410
|
+
# . . .
|
411
|
+
# end
|
412
|
+
#
|
413
|
+
# define "bar" do |project|
|
414
|
+
# project.version = "1.0"
|
415
|
+
# . . .
|
416
|
+
# end
|
417
|
+
#
|
418
|
+
# define "baz" do
|
419
|
+
# self.version = "1.0"
|
420
|
+
# end
|
421
|
+
#
|
422
|
+
# Each project also has a #define method that operates the same way, but
|
423
|
+
# defines a sub-project. A sub-project has a compound name using the parent
|
424
|
+
# project's name, and also inherits some of its properties. You can only
|
425
|
+
# define a sub-project as part of the parent project's definition.
|
426
|
+
#
|
427
|
+
# For example:
|
428
|
+
# define "foo", :version=>"1.0" do
|
429
|
+
# define "bar"
|
430
|
+
# puts name
|
431
|
+
# puts version
|
432
|
+
# end
|
433
|
+
# end
|
434
|
+
# => "foo:bar"
|
435
|
+
# "1.0"
|
436
|
+
def define(*args, &block)
|
437
|
+
Project.define(*args, &block)
|
438
|
+
end
|
439
|
+
|
440
|
+
# Returns the named project.
|
441
|
+
#
|
442
|
+
# For a sub-project, use the full name, for example "foo:bar" to find
|
443
|
+
# the sub-project "bar" of the parent project "foo".
|
444
|
+
#
|
445
|
+
# You cannot reference a project before the project is defined, with one
|
446
|
+
# exception. When working with sub-projects, the project definitions are
|
447
|
+
# stored but not executed until the parent project is defined. So within
|
448
|
+
# a sub-project definition you can reference another sub-project definition.
|
449
|
+
# The definitions are then performed (invoked) based on that dependency.
|
450
|
+
# You cannot have circular references between project definitions.
|
451
|
+
#
|
452
|
+
# For example:
|
453
|
+
# define "project1" do
|
454
|
+
# self.version = "1.1"
|
455
|
+
#
|
456
|
+
# define "module1" do
|
457
|
+
# package :jar
|
458
|
+
# end
|
459
|
+
#
|
460
|
+
# define "module2" do
|
461
|
+
# compile.with project("project1:module1")
|
462
|
+
# package :jar
|
463
|
+
# end
|
464
|
+
# end
|
465
|
+
def project(name)
|
466
|
+
Project.project(name)
|
467
|
+
end
|
468
|
+
|
469
|
+
# With no arguments, returns a list of all the projects defined so far.
|
470
|
+
#
|
471
|
+
# With arguments, returns a list of these projects. Equivalent to calling #project
|
472
|
+
# for each named project.
|
473
|
+
#
|
474
|
+
# For example:
|
475
|
+
# files = projects.map { |prj| FileList[prj.path_to("src/**/*.java") }.flatten
|
476
|
+
# puts "There are #{files.size} source files in #{projects.size} projects"
|
477
|
+
#
|
478
|
+
# projects("project1", "project2").map(&:base_dir)
|
479
|
+
def projects(*args)
|
480
|
+
Project.projects *args
|
481
|
+
end
|
482
|
+
|
483
|
+
# Add project definition tests.
|
484
|
+
task("check") { |task| task.note *Project.warnings }
|
485
|
+
|
486
|
+
end
|