Buildr 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|