buildr 1.3.0-java

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.
Files changed (138) hide show
  1. data/CHANGELOG +780 -0
  2. data/DISCLAIMER +7 -0
  3. data/KEYS +151 -0
  4. data/LICENSE +176 -0
  5. data/NOTICE +31 -0
  6. data/README +173 -0
  7. data/Rakefile +63 -0
  8. data/addon/buildr/antlr.rb +65 -0
  9. data/addon/buildr/cobertura.rb +232 -0
  10. data/addon/buildr/hibernate.rb +142 -0
  11. data/addon/buildr/javacc.rb +85 -0
  12. data/addon/buildr/jdepend.rb +60 -0
  13. data/addon/buildr/jetty.rb +248 -0
  14. data/addon/buildr/nailgun.rb +892 -0
  15. data/addon/buildr/openjpa.rb +90 -0
  16. data/addon/buildr/org/apache/buildr/JettyWrapper$1.class +0 -0
  17. data/addon/buildr/org/apache/buildr/JettyWrapper$BuildrHandler.class +0 -0
  18. data/addon/buildr/org/apache/buildr/JettyWrapper.class +0 -0
  19. data/addon/buildr/org/apache/buildr/JettyWrapper.java +144 -0
  20. data/addon/buildr/xmlbeans.rb +93 -0
  21. data/bin/buildr +21 -0
  22. data/buildr.gemspec +50 -0
  23. data/doc/css/default.css +225 -0
  24. data/doc/css/print.css +95 -0
  25. data/doc/css/syntax.css +43 -0
  26. data/doc/images/apache-incubator-logo.png +0 -0
  27. data/doc/images/buildr-hires.png +0 -0
  28. data/doc/images/buildr.png +0 -0
  29. data/doc/images/note.png +0 -0
  30. data/doc/images/tip.png +0 -0
  31. data/doc/images/zbuildr.tif +0 -0
  32. data/doc/pages/artifacts.textile +317 -0
  33. data/doc/pages/building.textile +501 -0
  34. data/doc/pages/contributing.textile +178 -0
  35. data/doc/pages/download.textile +25 -0
  36. data/doc/pages/extending.textile +229 -0
  37. data/doc/pages/getting_started.textile +337 -0
  38. data/doc/pages/index.textile +63 -0
  39. data/doc/pages/mailing_lists.textile +17 -0
  40. data/doc/pages/more_stuff.textile +367 -0
  41. data/doc/pages/packaging.textile +592 -0
  42. data/doc/pages/projects.textile +449 -0
  43. data/doc/pages/recipes.textile +127 -0
  44. data/doc/pages/settings_profiles.textile +339 -0
  45. data/doc/pages/testing.textile +475 -0
  46. data/doc/pages/troubleshooting.textile +121 -0
  47. data/doc/pages/whats_new.textile +389 -0
  48. data/doc/print.haml +52 -0
  49. data/doc/print.toc.yaml +28 -0
  50. data/doc/scripts/buildr-git.rb +411 -0
  51. data/doc/scripts/install-jruby.sh +44 -0
  52. data/doc/scripts/install-linux.sh +64 -0
  53. data/doc/scripts/install-osx.sh +52 -0
  54. data/doc/site.haml +55 -0
  55. data/doc/site.toc.yaml +44 -0
  56. data/lib/buildr.rb +47 -0
  57. data/lib/buildr/core.rb +27 -0
  58. data/lib/buildr/core/application.rb +373 -0
  59. data/lib/buildr/core/application_cli.rb +134 -0
  60. data/lib/buildr/core/build.rb +262 -0
  61. data/lib/buildr/core/checks.rb +382 -0
  62. data/lib/buildr/core/common.rb +155 -0
  63. data/lib/buildr/core/compile.rb +594 -0
  64. data/lib/buildr/core/environment.rb +120 -0
  65. data/lib/buildr/core/filter.rb +258 -0
  66. data/lib/buildr/core/generate.rb +195 -0
  67. data/lib/buildr/core/help.rb +118 -0
  68. data/lib/buildr/core/progressbar.rb +156 -0
  69. data/lib/buildr/core/project.rb +890 -0
  70. data/lib/buildr/core/test.rb +690 -0
  71. data/lib/buildr/core/transports.rb +486 -0
  72. data/lib/buildr/core/util.rb +235 -0
  73. data/lib/buildr/ide.rb +19 -0
  74. data/lib/buildr/ide/eclipse.rb +181 -0
  75. data/lib/buildr/ide/idea.ipr.template +300 -0
  76. data/lib/buildr/ide/idea.rb +194 -0
  77. data/lib/buildr/ide/idea7x.ipr.template +290 -0
  78. data/lib/buildr/ide/idea7x.rb +210 -0
  79. data/lib/buildr/java.rb +26 -0
  80. data/lib/buildr/java/ant.rb +71 -0
  81. data/lib/buildr/java/bdd_frameworks.rb +267 -0
  82. data/lib/buildr/java/commands.rb +210 -0
  83. data/lib/buildr/java/compilers.rb +432 -0
  84. data/lib/buildr/java/deprecated.rb +141 -0
  85. data/lib/buildr/java/groovyc.rb +137 -0
  86. data/lib/buildr/java/jruby.rb +99 -0
  87. data/lib/buildr/java/org/apache/buildr/BuildrNail$Main.class +0 -0
  88. data/lib/buildr/java/org/apache/buildr/BuildrNail.class +0 -0
  89. data/lib/buildr/java/org/apache/buildr/BuildrNail.java +41 -0
  90. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.class +0 -0
  91. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.java +116 -0
  92. data/lib/buildr/java/packaging.rb +706 -0
  93. data/lib/buildr/java/pom.rb +178 -0
  94. data/lib/buildr/java/rjb.rb +142 -0
  95. data/lib/buildr/java/test_frameworks.rb +290 -0
  96. data/lib/buildr/java/version_requirement.rb +172 -0
  97. data/lib/buildr/packaging.rb +21 -0
  98. data/lib/buildr/packaging/artifact.rb +729 -0
  99. data/lib/buildr/packaging/artifact_namespace.rb +957 -0
  100. data/lib/buildr/packaging/artifact_search.rb +140 -0
  101. data/lib/buildr/packaging/gems.rb +102 -0
  102. data/lib/buildr/packaging/package.rb +233 -0
  103. data/lib/buildr/packaging/tar.rb +104 -0
  104. data/lib/buildr/packaging/zip.rb +719 -0
  105. data/rakelib/apache.rake +126 -0
  106. data/rakelib/changelog.rake +56 -0
  107. data/rakelib/doc.rake +103 -0
  108. data/rakelib/package.rake +44 -0
  109. data/rakelib/release.rake +53 -0
  110. data/rakelib/rspec.rake +81 -0
  111. data/rakelib/rubyforge.rake +45 -0
  112. data/rakelib/scm.rake +49 -0
  113. data/rakelib/setup.rake +59 -0
  114. data/rakelib/stage.rake +45 -0
  115. data/spec/application_spec.rb +316 -0
  116. data/spec/archive_spec.rb +494 -0
  117. data/spec/artifact_namespace_spec.rb +635 -0
  118. data/spec/artifact_spec.rb +738 -0
  119. data/spec/build_spec.rb +193 -0
  120. data/spec/checks_spec.rb +537 -0
  121. data/spec/common_spec.rb +579 -0
  122. data/spec/compile_spec.rb +561 -0
  123. data/spec/groovy_compilers_spec.rb +239 -0
  124. data/spec/java_bdd_frameworks_spec.rb +238 -0
  125. data/spec/java_compilers_spec.rb +446 -0
  126. data/spec/java_packaging_spec.rb +1042 -0
  127. data/spec/java_test_frameworks_spec.rb +414 -0
  128. data/spec/packaging_helper.rb +63 -0
  129. data/spec/packaging_spec.rb +589 -0
  130. data/spec/project_spec.rb +739 -0
  131. data/spec/sandbox.rb +116 -0
  132. data/spec/scala_compilers_spec.rb +239 -0
  133. data/spec/spec.opts +6 -0
  134. data/spec/spec_helpers.rb +283 -0
  135. data/spec/test_spec.rb +871 -0
  136. data/spec/transport_spec.rb +300 -0
  137. data/spec/version_requirement_spec.rb +115 -0
  138. metadata +324 -0
@@ -0,0 +1,118 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+
17
+ require 'buildr/core/common'
18
+ require 'buildr/core/project'
19
+
20
+
21
+ module Buildr
22
+
23
+ module Help #:nodoc:
24
+ class << self
25
+
26
+ def <<(arg)
27
+ if arg.respond_to?(:call)
28
+ texters << arg
29
+ else
30
+ texters << lambda { arg }
31
+ end
32
+ end
33
+
34
+ def to_s
35
+ texters.map(&:call).join("\n")
36
+ end
37
+
38
+ protected
39
+ def texters
40
+ @texters ||= []
41
+ end
42
+
43
+ end
44
+ end
45
+
46
+ class << self
47
+ def help(&block)
48
+ Help << block if block_given?
49
+ Help
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+
56
+ task 'help' do
57
+ # Greeater.
58
+ Buildr.application.usage
59
+ puts
60
+
61
+ # Show only the top-level projects.
62
+ projects.reject(&:parent).tap do |top_level|
63
+ unless top_level.empty?
64
+ puts 'Top-level projects (buildr help:projects for full list):'
65
+ width = [top_level.map(&:name).map(&:size), 20].flatten.max
66
+ top_level.each do |project|
67
+ puts project.comment.to_s.empty? ? project.name : (" %-#{width}s # %s" % [project.name, project.comment])
68
+ end
69
+ puts
70
+ end
71
+ end
72
+
73
+ # Show all the top-level tasks, excluding projects.
74
+ puts 'Common tasks:'
75
+ task('help:tasks').invoke
76
+ puts
77
+ puts 'For help on command line options:'
78
+ puts ' buildr --help'
79
+ puts Buildr.help.to_s
80
+ end
81
+
82
+
83
+ module Buildr
84
+
85
+ # :call-seq:
86
+ # help() { ... }
87
+ #
88
+ # Use this to enhance the help task, e.g. to print some important information about your build,
89
+ # configuration options, build instructions, etc.
90
+ def help(&block)
91
+ Buildr.help << block
92
+ end
93
+
94
+ end
95
+
96
+
97
+ namespace 'help' do
98
+
99
+ desc 'List all projects defined by this buildfile'
100
+ task 'projects' do
101
+ width = projects.map(&:name).map(&:size).max
102
+ projects.each do |project|
103
+ puts project.comment.to_s.empty? ? " #{project.name}" : (" %-#{width}s # %s" % [project.name, project.comment])
104
+ end
105
+ end
106
+
107
+ desc 'List all tasks available from this buildfile'
108
+ task 'tasks' do
109
+ Buildr.application.tasks.select(&:comment).reject { |task| Project === task }.tap do |tasks|
110
+ width = [tasks.map(&:name).map(&:size), 20].flatten.max
111
+ tasks.each do |task|
112
+ printf " %-#{width}s # %s\n", task.name, task.comment
113
+ end
114
+ puts
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,156 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+
17
+ class ProgressBar
18
+
19
+ class << self
20
+
21
+ def start(args, &block)
22
+ new(args).start &block
23
+ end
24
+
25
+ def width
26
+ @width ||= $terminal.output_cols || 80
27
+ end
28
+
29
+ end
30
+
31
+ def initialize(args = {})
32
+ @title = args[:title] || ''
33
+ @total = args[:total] || 0
34
+ @mark = args[:mark] || '.'
35
+ @format = args[:format] || default_format
36
+ @output = args[:output] || $stderr unless args[:hidden] || !$stdout.isatty
37
+ clear
38
+ end
39
+
40
+ def start
41
+ @start = @last_time = Time.now
42
+ @count = 0
43
+ @finished = false
44
+ render
45
+ if block_given?
46
+ result = yield(self) if block_given?
47
+ finish
48
+ result
49
+ else
50
+ self
51
+ end
52
+ end
53
+
54
+ def inc(count)
55
+ set @count + count
56
+ end
57
+
58
+ def <<(bytes)
59
+ inc bytes.size
60
+ end
61
+
62
+ def set(count)
63
+ @count = [count, 0].max
64
+ @count = [count, @total].min unless @total == 0
65
+ render if changed?
66
+ end
67
+
68
+ def title
69
+ @title.size > ProgressBar.width / 5 ? (@title[0, ProgressBar.width / 5 - 2] + '..') : @title
70
+ end
71
+
72
+ def count
73
+ human(@count)
74
+ end
75
+
76
+ def total
77
+ human(@total)
78
+ end
79
+
80
+ def percentage
81
+ '%3d%%' % (@total == 0 ? 100 : (@count * 100 / @total))
82
+ end
83
+
84
+ def time
85
+ @finished ? elapsed : eta
86
+ end
87
+
88
+ def eta
89
+ return 'ETA: --:--:--' if @count == 0
90
+ elapsed = Time.now - @start
91
+ eta = elapsed * @total / @count - elapsed
92
+ 'ETA: %s' % duration(eta.ceil)
93
+ end
94
+
95
+ def elapsed
96
+ 'Time: %s' % duration(Time.now - @start)
97
+ end
98
+
99
+ def rate
100
+ '%s/s' % human(@count / (Time.now - @start))
101
+ end
102
+
103
+ def progress(width)
104
+ width -= 2
105
+ marks = @total == 0 ? width : (@count * width / @total)
106
+ "|%-#{width}s|" % (@mark * marks)
107
+ end
108
+
109
+ def human(bytes)
110
+ magnitude = (0..3).find { |i| bytes < (1024 << i * 10) } || 3
111
+ return '%dB' % bytes if magnitude == 0
112
+ return '%.1f%s' % [ bytes.to_f / (1 << magnitude * 10), [nil, 'KB', 'MB', 'GB'][magnitude] ]
113
+ end
114
+
115
+ def duration(seconds)
116
+ '%02d:%02d:%02d' % [seconds / 3600, (seconds / 60) % 60, seconds % 60]
117
+ end
118
+
119
+ def finish
120
+ unless @finished
121
+ @finished = true
122
+ render
123
+ end
124
+ end
125
+
126
+ protected
127
+
128
+ def clear
129
+ return unless @output
130
+ @output.print "\r", " " * (ProgressBar.width - 1), "\r"
131
+ @output.flush
132
+ end
133
+
134
+ def render
135
+ return unless @output
136
+ format, *args = @format
137
+ line = format % args.map { |arg| send(arg) }
138
+ @output.print line.sub('|--|') { progress(ProgressBar.width - line.size + 3) }
139
+ @output.print @finished ? "\n" : "\r"
140
+ @output.flush
141
+ @previous = @count
142
+ @last_time = Time.now
143
+ end
144
+
145
+ def changed?
146
+ return false unless @output
147
+ return human(@count) != human(@previous) if @total == 0
148
+ return true if (@count - @previous) >= @total / 100
149
+ return Time.now - @last_time > 1
150
+ end
151
+
152
+ def default_format
153
+ @total == 0 ? ['%s %8s %s', :title, :count, :elapsed] : ['%s: %s |--| %8s/%s %s', :title, :percentage, :count, :total, :time]
154
+ end
155
+
156
+ end
@@ -0,0 +1,890 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+
17
+ require 'buildr/core/util'
18
+
19
+
20
+ module Buildr
21
+
22
+ # Symbolic mapping for directory layout. Used for both the default and custom layouts.
23
+ #
24
+ # For example, the default layout maps [:source, :main, :java] to 'src/main/java', and
25
+ # [:target, :main, :classes] to 'target/classes'. You can use this to change the layout
26
+ # of your projects.
27
+ #
28
+ # To map [:source, :main] into the 'sources' directory:
29
+ # my_layout = Layout.new
30
+ # my_layout[:source, :main] = 'sources'
31
+ #
32
+ # define 'foo', :layout=>my_layout do
33
+ # ...
34
+ # end
35
+ #
36
+ # To map [:source, :main, :java] to 'java/main':
37
+ # class MainLast < Layout
38
+ # def expand(*args)
39
+ # if args[0..1] == [:source, :main]
40
+ # super args[2], :main, *args[3,]
41
+ # else
42
+ # super
43
+ # end
44
+ # end
45
+ # end
46
+ #
47
+ # define 'foo', :layout=>MainLast do
48
+ # ...
49
+ # end
50
+ class Layout
51
+
52
+ class << self
53
+
54
+ # Default layout used by new projects.
55
+ attr_accessor :default
56
+
57
+ end
58
+
59
+ def initialize #:nodoc:
60
+ @mapping = {}
61
+ end
62
+
63
+ # Expands list of symbols and path names into a full path, for example:
64
+ # puts default.expand(:source, :main, :java)
65
+ # => "src/main/java"
66
+ def expand(*args)
67
+ return '' if args.empty?
68
+ @mapping[args] ||= File.join(*[expand(*args[0..-2]), args.last.to_s].reject(&:empty?)) if args.size > 1
69
+ return @mapping[args] || args.first.to_s
70
+ end
71
+
72
+ # Resolves a list of symbols into a path.
73
+ def [](*args)
74
+ @mapping[args.map(&:to_sym)]
75
+ end
76
+
77
+ # Specifies the path resolved from a list of symbols.
78
+ def []=(*args)
79
+ @mapping[args[0...-1].map(&:to_sym)] = args.last
80
+ end
81
+
82
+ def initialize_copy(copy)
83
+ copy.instance_variable_set :@mapping, @mapping.clone
84
+ end
85
+
86
+ # Default layout has the following properties:
87
+ # * :source maps to the 'src' directory.
88
+ # * Anything under :source maps verbatim (e.g. :source, :main becomes 'src/main')
89
+ # * :target maps to the 'target' directory.
90
+ # * :target, :main maps to the 'target' directory as well.
91
+ # * Anything under :target, :main maps verbatim (e.g. :target, :main, :classes becomes 'target/classes')
92
+ # * Anything else under :target also maps verbatim (e.g. :target, :test becomes 'target/test')
93
+ class Default < Layout
94
+
95
+ def initialize
96
+ super
97
+ self[:source] = 'src'
98
+ self[:target, :main] = 'target'
99
+ end
100
+
101
+ end
102
+
103
+ self.default = Default.new
104
+
105
+ end
106
+
107
+
108
+ # A project definition is where you define all the tasks associated with
109
+ # the project you're building.
110
+ #
111
+ # The project itself will define several life cycle tasks for you. For example,
112
+ # it automatically creates a compile task that will compile all the source files
113
+ # found in src/main/java into target/classes, a test task that will compile source
114
+ # files from src/test/java and run all the JUnit tests found there, and a build
115
+ # task to compile and then run the tests.
116
+ #
117
+ # You use the project definition to enhance these tasks, for example, telling the
118
+ # compile task which class path dependencies to use. Or telling the project how
119
+ # to package an artifact, e.g. creating a JAR using <tt>package :jar</tt>.
120
+ #
121
+ # You can also define additional tasks that are executed by project tasks,
122
+ # or invoked from rake.
123
+ #
124
+ # Tasks created by the project are all prefixed with the project name, e.g.
125
+ # the project foo creates the task foo:compile. If foo contains a sub-project bar,
126
+ # the later will define the task foo:bar:compile. Since the compile task is
127
+ # recursive, compiling foo will also compile foo:bar.
128
+ #
129
+ # If you run:
130
+ # buildr compile
131
+ # from the command line, it will execute the compile task of the current project.
132
+ #
133
+ # Projects and sub-projects follow a directory heirarchy. The Buildfile is assumed to
134
+ # reside in the same directory as the top-level project, and each sub-project is
135
+ # contained in a sub-directory in the same name. For example:
136
+ # /home/foo
137
+ # |__ Buildfile
138
+ # |__ src/main/java
139
+ # |__ foo
140
+ # |__ src/main/java
141
+ #
142
+ # The default structure of each project is assumed to be:
143
+ # src
144
+ # |__main
145
+ # | |__java <-- Source files to compile
146
+ # | |__resources <-- Resources to copy
147
+ # | |__webapp <-- For WARs
148
+ # |__test
149
+ # | |__java <-- Source files to compile (tests)
150
+ # | |__resources <-- Resources to copy (tests)
151
+ # |__target <-- Packages created here
152
+ # | |__classes <-- Generated when compiling
153
+ # | |__resources <-- Copied (and filtered) from resources
154
+ # | |__test/classes <-- Generated when compiling tests
155
+ # | |__test/resources <-- Copied (and filtered) from resources
156
+ # |__reports <-- Test, coverage and other reports
157
+ #
158
+ # You can change the project layout by passing a new Layout to the project definition.
159
+ #
160
+ # You can only define a project once using #define. Afterwards, you can obtain the project
161
+ # definition using #project. The order in which you define projects is not important,
162
+ # project definitions are evaluated when you ask for them. Circular dependencies will not
163
+ # work. Rake tasks are only created after the project is evaluated, so if you need to access
164
+ # a task (e.g. compile) use <code>project('foo').compile</code> instead of <code>task('foo:compile')</code>.
165
+ #
166
+ # For example:
167
+ # define 'myapp', :version=>'1.1' do
168
+ #
169
+ # define 'wepapp' do
170
+ # compile.with project('myapp:beans')
171
+ # package :war
172
+ # end
173
+ #
174
+ # define 'beans' do
175
+ # compile.with DEPENDS
176
+ # package :jar
177
+ # end
178
+ # end
179
+ #
180
+ # puts projects.map(&:name)
181
+ # => [ 'myapp', 'myapp:beans', 'myapp:webapp' ]
182
+ # puts project('myapp:webapp').parent.name
183
+ # => 'myapp'
184
+ # puts project('myapp:webapp').compile.classpath.map(&:to_spec)
185
+ # => 'myapp:myapp-beans:jar:1.1'
186
+ class Project < Rake::Task
187
+
188
+ class << self
189
+
190
+ # :call-seq:
191
+ # define(name, properties?) { |project| ... } => project
192
+ #
193
+ # See Buildr#define.
194
+ def define(name, properties, &block) #:nodoc:
195
+ # Make sure a sub-project is only defined within the parent project,
196
+ # to prevent silly mistakes that lead to inconsistencies (e.g.
197
+ # namespaces will be all out of whack).
198
+ Buildr.application.current_scope == name.split(':')[0...-1] or
199
+ raise "You can only define a sub project (#{name}) within the definition of its parent project"
200
+
201
+ @projects ||= {}
202
+ raise "You cannot define the same project (#{name}) more than once" if @projects[name]
203
+ # Projects with names like: compile, test, build are invalid, so we have
204
+ # to make sure the project has not the name of an already defined task
205
+ raise "Invalid project name: #{name.inspect} is already used for a task" if Buildr.application.lookup(name)
206
+
207
+ Project.define_task(name).tap do |project|
208
+ # Define the project to prevent duplicate definition.
209
+ @projects[name] = project
210
+ # Set the project properties first, actions may use them.
211
+ properties.each { |name, value| project.send "#{name}=", value } if properties
212
+ # Instantiate callbacks for this project, and setup to call before/after define.
213
+ # Don't cache list of callbacks, since project may add new callbacks.
214
+ project.enhance do |project|
215
+ project.send :call_callbacks, :before_define
216
+ project.enhance do |project|
217
+ project.send :call_callbacks, :after_define
218
+ end
219
+ end
220
+ project.enhance do |project|
221
+ @on_define.each { |callback| callback[project] }
222
+ end if @on_define
223
+ # Enhance the project using the definition block.
224
+ project.enhance { project.instance_eval &block } if block
225
+
226
+ # Top-level project? Invoke the project definition. Sub-project? We don't invoke
227
+ # the project definiton yet (allow project calls to establish order of evaluation),
228
+ # but must do so before the parent project's definition is done.
229
+ project.parent.enhance { project.invoke } if project.parent
230
+ end
231
+ end
232
+
233
+ # :call-seq:
234
+ # project(name) => project
235
+ #
236
+ # See Buildr#project.
237
+ def project(*args) #:nodoc:
238
+ options = args.pop if Hash === args.last
239
+ rake_check_options options, :scope if options
240
+ raise ArgumentError, 'Only one project name at a time' unless args.size == 1
241
+ @projects ||= {}
242
+ name = args.first
243
+ if options && options[:scope]
244
+ # We assume parent project is evaluated.
245
+ project = options[:scope].split(':').inject([[]]) { |scopes, scope| scopes << (scopes.last + [scope]) }.
246
+ map { |scope| @projects[(scope + [name]).join(':')] }.
247
+ select { |project| project }.last
248
+ end
249
+ unless project
250
+ # Parent project not evaluated.
251
+ name.split(':').tap { |parts| @projects[parts.first].invoke if parts.size > 1 }
252
+ project = @projects[name]
253
+ end
254
+ raise "No such project #{name}" unless project
255
+ project.invoke
256
+ project
257
+ end
258
+
259
+ # :call-seq:
260
+ # projects(*names) => projects
261
+ #
262
+ # See Buildr#projects.
263
+ def projects(*names) #:nodoc:
264
+ options = names.pop if Hash === names.last
265
+ rake_check_options options, :scope if options
266
+ @projects ||= {}
267
+ names = names.flatten
268
+ if options && options[:scope]
269
+ # We assume parent project is evaluated.
270
+ if names.empty?
271
+ parent = @projects[options[:scope].to_s] or raise "No such project #{options[:scope]}"
272
+ @projects.values.select { |project| project.parent == parent }.each { |project| project.invoke }.
273
+ map { |project| [project] + projects(:scope=>project) }.flatten.sort_by(&:name)
274
+ else
275
+ names.uniq.map { |name| project(name, :scope=>options[:scope]) }
276
+ end
277
+ elsif names.empty?
278
+ # Parent project(s) not evaluated so we don't know all the projects yet.
279
+ @projects.values.each(&:invoke)
280
+ @projects.keys.map { |name| project(name) or raise "No such project #{name}" }.sort_by(&:name)
281
+ else
282
+ # Parent project(s) not evaluated, for the sub-projects we may need to find.
283
+ names.map { |name| name.split(':') }.select { |name| name.size > 1 }.map(&:first).uniq.each { |name| project(name) }
284
+ names.uniq.map { |name| project(name) or raise "No such project #{name}" }.sort_by(&:name)
285
+ end
286
+ end
287
+
288
+ # :call-seq:
289
+ # clear
290
+ #
291
+ # Discard all project definitions.
292
+ def clear
293
+ @projects.clear if @projects
294
+ end
295
+
296
+ # :call-seq:
297
+ # local_task(name)
298
+ # local_task(name) { |name| ... }
299
+ #
300
+ # Defines a local task with an optional execution message.
301
+ #
302
+ # A local task is a task that executes a task with the same name, defined in the
303
+ # current project, the project's with a base directory that is the same as the
304
+ # current directory.
305
+ #
306
+ # Complicated? Try this:
307
+ # buildr build
308
+ # is the same as:
309
+ # buildr foo:build
310
+ # But:
311
+ # cd bar
312
+ # buildr build
313
+ # is the same as:
314
+ # buildr foo:bar:build
315
+ #
316
+ # The optional block is called with the project name when the task executes
317
+ # and returns a message that, for example "Building project #{name}".
318
+ def local_task(args, &block)
319
+ task args do |task|
320
+ local_projects do |project|
321
+ puts block.call(project.name) if block && verbose
322
+ task("#{project.name}:#{task.name}").invoke
323
+ end
324
+ end
325
+ end
326
+
327
+ # *Deprecated* Check the Extension module to see how extensions are handled.
328
+ def on_define(&block)
329
+ Buildr.application.deprecated 'This method is deprecated, see Extension'
330
+ (@on_define ||= []) << block if block
331
+ end
332
+
333
+ def scope_name(scope, task_name) #:nodoc:
334
+ task_name
335
+ end
336
+
337
+ def local_projects(dir = nil, &block) #:nodoc:
338
+ dir = File.expand_path(dir || Buildr.application.original_dir)
339
+ projects = Project.projects.select { |project| project.base_dir == dir }
340
+ if projects.empty? && dir != Dir.pwd && File.dirname(dir) != dir
341
+ local_projects(File.dirname(dir), &block)
342
+ elsif block
343
+ if projects.empty?
344
+ warn "No projects defined for directory #{Buildr.application.original_dir}" if verbose
345
+ else
346
+ projects.each { |project| block[project] }
347
+ end
348
+ else
349
+ projects
350
+ end
351
+ end
352
+
353
+ # :call-seq:
354
+ # parent_task(task_name) => task_name or nil
355
+ #
356
+ # Returns a parent task, basically a task in a higher namespace. For example, the parent
357
+ # of 'foo:test:compile' is 'foo:compile' and the parent of 'foo:compile' is 'compile'.
358
+ def parent_task(task_name) #:nodoc:
359
+ namespace = task_name.split(':')
360
+ last_name = namespace.pop
361
+ namespace.pop
362
+ Buildr.application.lookup((namespace + [last_name]).join(':'), []) unless namespace.empty?
363
+ end
364
+
365
+ # :call-seq:
366
+ # project_from_task(task) => project
367
+ #
368
+ # Figure out project associated to this task and return it.
369
+ def project_from_task(task) #:nodoc:
370
+ project = Buildr.application.lookup('rake:' + task.to_s.gsub(/:[^:]*$/, ''))
371
+ project if Project === project
372
+ end
373
+
374
+ # Callback classes.
375
+ def callbacks #:nodoc:
376
+ @callbacks ||= []
377
+ end
378
+
379
+ end
380
+
381
+
382
+ # Project has visibility to everything in the Buildr namespace.
383
+ include Buildr
384
+
385
+ # The project name. For example, 'foo' for the top-level project, and 'foo:bar'
386
+ # for its sub-project.
387
+ attr_reader :name
388
+
389
+ # The parent project if this is a sub-project.
390
+ attr_reader :parent
391
+
392
+ def initialize(*args) #:nodoc:
393
+ super
394
+ split = name.split(':')
395
+ if split.size > 1
396
+ # Get parent project, but do not invoke it's definition to prevent circular
397
+ # dependencies (it's being invoked right now, so calling project will fail).
398
+ @parent = task(split[0...-1].join(':'))
399
+ raise "No parent project #{split[0...-1].join(':')}" unless @parent && Project === parent
400
+ end
401
+ callbacks = Project.callbacks.uniq.map(&:new)
402
+ @callbacks = [:before_define, :after_define].inject({}) do |hash, state|
403
+ methods = callbacks.select { |callback| callback.respond_to?(state) }.map { |callback| callback.method(state) }
404
+ hash.update(state=>methods)
405
+ end
406
+ end
407
+
408
+ # :call-seq:
409
+ # base_dir => path
410
+ #
411
+ # Returns the project's base directory.
412
+ #
413
+ # The Buildfile defines top-level project, so it's logical that the top-level project's
414
+ # base directory is the one in which we find the Buildfile. And each sub-project has
415
+ # a base directory that is one level down, with the same name as the sub-project.
416
+ #
417
+ # For example:
418
+ # /home/foo/ <-- base_directory of project 'foo'
419
+ # /home/foo/Buildfile <-- builds 'foo'
420
+ # /home/foo/bar <-- sub-project 'foo:bar'
421
+ def base_dir
422
+ if @base_dir.nil?
423
+ if parent
424
+ # For sub-project, a good default is a directory in the parent's base_dir,
425
+ # using the same name as the project.
426
+ @base_dir = File.expand_path(name.split(':').last, parent.base_dir)
427
+ else
428
+ # For top-level project, a good default is the directory where we found the Buildfile.
429
+ @base_dir = Dir.pwd
430
+ end
431
+ end
432
+ @base_dir
433
+ end
434
+
435
+ # Returns the layout associated with this project.
436
+ def layout
437
+ @layout ||= (parent ? parent.layout : Layout.default).clone
438
+ end
439
+
440
+ # :call-seq:
441
+ # path_to(*names) => path
442
+ #
443
+ # Returns a path from a combination of name, relative to the project's base directory.
444
+ # Essentially, joins all the supplied names and expands the path relative to #base_dir.
445
+ # Symbol arguments are converted to paths based on the layout, so whenever possible stick
446
+ # to these. For example:
447
+ # path_to(:source, :main, :java)
448
+ # => 'src/main/java'
449
+ #
450
+ # Keep in mind that all tasks are defined and executed relative to the Buildfile directory,
451
+ # so you want to use #path_to to get the actual path within the project as a matter of practice.
452
+ #
453
+ # For example:
454
+ # path_to('foo', 'bar')
455
+ # => foo/bar
456
+ # path_to('/tmp')
457
+ # => /tmp
458
+ # path_to(:base_dir, 'foo') # same as path_to('foo")
459
+ # => /home/project1/foo
460
+ def path_to(*names)
461
+ File.expand_path(layout.expand(*names), base_dir)
462
+ end
463
+ alias :_ :path_to
464
+
465
+ # :call-seq:
466
+ # file(path) => Task
467
+ # file(path=>prereqs) => Task
468
+ # file(path) { |task| ... } => Task
469
+ #
470
+ # Creates and returns a new file task in the project. Similar to calling Rake's
471
+ # file method, but the path is expanded relative to the project's base directory,
472
+ # and the task executes in the project's base directory.
473
+ #
474
+ # For example:
475
+ # define 'foo' do
476
+ # define 'bar' do
477
+ # file('src') { ... }
478
+ # end
479
+ # end
480
+ #
481
+ # puts project('foo:bar').file('src').to_s
482
+ # => '/home/foo/bar/src'
483
+ def file(*args, &block)
484
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
485
+ task = Rake::FileTask.define_task(path_to(task_name))
486
+ task.set_arg_names(arg_names) unless arg_names.empty?
487
+ task.enhance Array(deps), &block
488
+ end
489
+
490
+ # :call-seq:
491
+ # task(name) => Task
492
+ # task(name=>prereqs) => Task
493
+ # task(name) { |task| ... } => Task
494
+ #
495
+ # Creates and returns a new task in the project. Similar to calling Rake's task
496
+ # method, but prefixes the task name with the project name and executes the task
497
+ # in the project's base directory.
498
+ #
499
+ # For example:
500
+ # define 'foo' do
501
+ # task 'doda'
502
+ # end
503
+ #
504
+ # puts project('foo').task('doda').name
505
+ # => 'foo:doda'
506
+ #
507
+ # When called from within the project definition, creates a new task if the task
508
+ # does not already exist. If called from outside the project definition, returns
509
+ # the named task and raises an exception if the task is not defined.
510
+ #
511
+ # As with Rake's task method, calling this method enhances the task with the
512
+ # prerequisites and optional block.
513
+ def task(*args, &block)
514
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
515
+ if task_name =~ /^:/
516
+ Buildr.application.switch_to_namespace [] do
517
+ task = Rake::Task.define_task(task_name[1..-1])
518
+ end
519
+ elsif Buildr.application.current_scope == name.split(':')
520
+ task = Rake::Task.define_task(task_name)
521
+ else
522
+ unless task = Buildr.application.lookup(task_name, name.split(':'))
523
+ raise "You cannot define a project task outside the project definition, and no task #{name}:#{task_name} defined in the project"
524
+ end
525
+ end
526
+ task.set_arg_names(arg_names) unless arg_names.empty?
527
+ task.enhance Array(deps), &block
528
+ end
529
+
530
+ # :call-seq:
531
+ # recursive_task(name=>prereqs) { |task| ... }
532
+ #
533
+ # Define a recursive task. A recursive task executes itself and the same task
534
+ # in all the sub-projects.
535
+ def recursive_task(*args, &block)
536
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
537
+ task = Buildr.options.parallel ? multitask(task_name) : task(task_name)
538
+ parent.task(task_name).enhance [task] if parent
539
+ task.set_arg_names(arg_names) unless arg_names.empty?
540
+ task.enhance Array(deps), &block
541
+ end
542
+
543
+ # :call-seq:
544
+ # project(name) => project
545
+ # project => self
546
+ #
547
+ # Same as Buildr#project. This method is called on a project, so a relative name is
548
+ # sufficient to find a sub-project.
549
+ #
550
+ # When called on a project without a name, returns the project itself. You can use that when
551
+ # setting project properties, for example:
552
+ # define 'foo' do
553
+ # project.version = '1.0'
554
+ # end
555
+ def project(*args)
556
+ if Hash === args.last
557
+ options = args.pop
558
+ else
559
+ options = {}
560
+ end
561
+ if args.empty?
562
+ self
563
+ else
564
+ Project.project *(args + [{ :scope=>self.name }.merge(options)])
565
+ end
566
+ end
567
+
568
+ # :call-seq:
569
+ # projects(*names) => projects
570
+ #
571
+ # Same as Buildr#projects. This method is called on a project, so relative names are
572
+ # sufficient to find sub-projects.
573
+ def projects(*args)
574
+ if Hash === args.last
575
+ options = args.pop
576
+ else
577
+ options = {}
578
+ end
579
+ Project.projects *(args + [{ :scope=>self.name }.merge(options)])
580
+ end
581
+
582
+ def inspect #:nodoc:
583
+ %Q{project(#{name.inspect})}
584
+ end
585
+
586
+ protected
587
+
588
+ # :call-seq:
589
+ # base_dir = dir
590
+ #
591
+ # Sets the project's base directory. Allows you to specify a base directory by calling
592
+ # this accessor, or with the :base_dir property when calling #define.
593
+ #
594
+ # You can only set the base directory once for a given project, and only before accessing
595
+ # the base directory (for example, by calling #file or #path_to).
596
+ # Set the base directory. Note: you can only do this once for a project,
597
+ # and only before accessing the base directory. If you try reading the
598
+ # value with #base_dir, the base directory cannot be set again.
599
+ def base_dir=(dir)
600
+ raise 'Cannot set base directory twice, or after reading its value' if @base_dir
601
+ @base_dir = File.expand_path(dir)
602
+ end
603
+
604
+ # Sets the project layout. Accepts Layout object or class (or for that matter, anything
605
+ # that can expand).
606
+ def layout=(layout)
607
+ raise 'Cannot set directory layout twice, or after reading its value' if @layout
608
+ @layout = layout.is_a?(Class) ? layout.new : layout
609
+ end
610
+
611
+ # :call-seq:
612
+ # define(name, properties?) { |project| ... } => project
613
+ #
614
+ # Define a new sub-project within this project. See Buildr#define.
615
+ def define(name, properties = nil, &block)
616
+ Project.define "#{self.name}:#{name}", properties, &block
617
+ end
618
+
619
+ def execute(args) #:nodoc:
620
+ Buildr.application.switch_to_namespace name.split(':') do
621
+ super
622
+ end
623
+ end
624
+
625
+ # Call all callbacks for a particular state, e.g. :before_define, :after_define.
626
+ def call_callbacks(state) #:nodoc:
627
+ methods = @callbacks.delete(state) || []
628
+ methods.each { |method| method.call(project) }
629
+ end
630
+
631
+ def add_callback(callback)
632
+ @callbacks[:after_define] << callback.method(:after_define) if callback.respond_to?(:after_define)
633
+ end
634
+
635
+ end
636
+
637
+
638
+ # The basic mechanism for extending projects in Buildr are Ruby modules. In fact,
639
+ # base features like compiling and testing are all developed in the form of modules,
640
+ # and then added to the core Project class.
641
+ #
642
+ # A module defines instance methods that are then mixed into the project and become
643
+ # instance methods of the project. There are two general ways for extending projects.
644
+ # You can extend all projects by including the module in Project:
645
+ # class Project
646
+ # include MyExtension
647
+ # end
648
+ # You can also extend a given project instance and only that instance by extending
649
+ # it with the module:
650
+ # define 'foo' do
651
+ # extend MyExtension
652
+ # end
653
+ #
654
+ # Some extensions require tighter integration with the project, specifically for
655
+ # setting up tasks and properties, or for configuring tasks based on the project
656
+ # definition. You can do that by adding callbacks to the process.
657
+ #
658
+ # The easiest way to add callbacks is by incorporating the Extension module in your
659
+ # own extension, and using the various class methods to define callback behavior:
660
+ # * first_time -- This block will be called once for any particular extension.
661
+ # You can use this to setup top-level and local tasks.
662
+ # * before_define -- This block is called once for the project with the project
663
+ # instance, right before running the project definition. You can use this
664
+ # to add tasks and set properties that will be used in the project definition.
665
+ # * after_define -- This block is called once for the project with the project
666
+ # instance, right after running the project definition. You can use this to
667
+ # do any post-processing that depends on the project definition.
668
+ #
669
+ # This example illustrates how to write a simple extension:
670
+ # module LinesOfCode
671
+ # include Extension
672
+ #
673
+ # first_time do
674
+ # # Define task not specific to any projet.
675
+ # desc 'Count lines of code in current project'
676
+ # Project.local_task('loc')
677
+ # end
678
+ #
679
+ # before_define do |project|
680
+ # # Define the loc task for this particular project.
681
+ # Rake::Task.define_task 'loc' do |task|
682
+ # lines = task.prerequisites.map { |path| Dir['#{path}/**/*'] }.flatten.uniq.
683
+ # inject(0) { |total, file| total + File.readlines(file).count }
684
+ # puts "Project #{project.name} has #{lines} lines of code"
685
+ # end
686
+ # end
687
+ #
688
+ # after_define do |project|
689
+ # # Now that we know all the source directories, add them.
690
+ # task('loc'=>compile.sources + compile.test.sources)
691
+ # end
692
+ #
693
+ # # To use this method in your project:
694
+ # # loc path_1, path_2
695
+ # def loc(*paths)
696
+ # task('loc'=>paths)
697
+ # end
698
+ #
699
+ # end
700
+ #
701
+ # class Buildr::Project
702
+ # include LinesOfCode
703
+ # end
704
+ module Extension
705
+
706
+ def self.included(base) #:nodoc:
707
+ base.extend ClassMethods
708
+ end
709
+
710
+ # Methods added to the extension module when including Extension.
711
+ module ClassMethods
712
+
713
+ def included(base) #:nodoc:
714
+ # When included in Project, add callback and call first_time.
715
+ if Project == base && !base.callbacks.include?(callbacks)
716
+ base.callbacks << callbacks
717
+ callbacks.first_time if callbacks.respond_to?(:first_time)
718
+ end
719
+ end
720
+
721
+ def extended(base) #:nodoc:
722
+ # When extending project, add instance and call before_define.
723
+ if Project === base
724
+ callbacks = self.send(:callbacks).new
725
+ callbacks.before_define(base) if callbacks.respond_to?(:before_define)
726
+ base.send :add_callback, callbacks
727
+ end
728
+ end
729
+
730
+ # This block will be called once for any particular extension.
731
+ # You can use this to setup top-level and local tasks.
732
+ def first_time(&block)
733
+ meta = class << callbacks ; self ; end
734
+ meta.send :define_method, :first_time, &block
735
+ end
736
+
737
+ # This block is called once for the project with the project instance,
738
+ # right before running the project definition. You can use this to add
739
+ # tasks and set properties that will be used in the project definition.
740
+ def before_define(&block)
741
+ callbacks.send :define_method, :before_define, &block
742
+ end
743
+
744
+ # This block is called once for the project with the project instance,
745
+ # right after running the project definition. You can use this to do
746
+ # any post-processing that depends on the project definition.
747
+ def after_define(&block)
748
+ callbacks.send :define_method, :after_define, &block
749
+ end
750
+
751
+ private
752
+
753
+ def callbacks
754
+ const_get('Callbacks') rescue const_set('Callbacks', Class.new)
755
+ end
756
+
757
+ end
758
+
759
+ end
760
+
761
+
762
+ # :call-seq:
763
+ # define(name, properties?) { |project| ... } => project
764
+ #
765
+ # Defines a new project.
766
+ #
767
+ # The first argument is the project name. Each project must have a unique name.
768
+ # For a sub-project, the actual project name is created by prefixing the parent
769
+ # project's name.
770
+ #
771
+ # The second argument is optional and contains a hash or properties that are set
772
+ # on the project. You can only use properties that are supported by the project
773
+ # definition, e.g. :group and :version. You can also set these properties from the
774
+ # project definition.
775
+ #
776
+ # You pass a block that is executed in the context of the project definition.
777
+ # This block is used to define the project and tasks that are part of the project.
778
+ # Do not perform any work inside the project itself, as it will execute each time
779
+ # the Buildfile is loaded. Instead, use it to create and extend tasks that are
780
+ # related to the project.
781
+ #
782
+ # For example:
783
+ # define 'foo', :version=>'1.0' do
784
+ #
785
+ # define 'bar' do
786
+ # compile.with 'org.apache.axis2:axis2:jar:1.1'
787
+ # end
788
+ # end
789
+ #
790
+ # puts project('foo').version
791
+ # => '1.0'
792
+ # puts project('foo:bar').compile.classpath.map(&:to_spec)
793
+ # => 'org.apache.axis2:axis2:jar:1.1'
794
+ # % buildr build
795
+ # => Compiling 14 source files in foo:bar
796
+ def define(name, properties = nil, &block) #:yields:project
797
+ Project.define(name, properties, &block)
798
+ end
799
+
800
+ # :call-seq:
801
+ # project(name) => project
802
+ #
803
+ # Returns a project definition.
804
+ #
805
+ # When called from outside a project definition, must reference the project by its
806
+ # full name, e.g. 'foo:bar' to access the sub-project 'bar' in 'foo'. When called
807
+ # from inside a project, relative names are sufficient, e.g. <code>project('foo').project('bar')</code>
808
+ # will find the sub-project 'bar' in 'foo'.
809
+ #
810
+ # You cannot reference a project before the project is defined. When working with
811
+ # sub-projects, the project definition is stored by calling #define, and evaluated
812
+ # before a call to the parent project's #define method returns.
813
+ #
814
+ # However, if you call #project with the name of another sub-project, its definition
815
+ # is evaluated immediately. So the returned project definition is always complete,
816
+ # and you can access its definition (e.g. to find files relative to the base directory,
817
+ # or packages created by that project).
818
+ #
819
+ # For example:
820
+ # define 'myapp' do
821
+ # self.version = '1.1'
822
+ #
823
+ # define 'webapp' do
824
+ # # webapp is defined first, but beans is evaluated first
825
+ # compile.with project('beans')
826
+ # package :war
827
+ # end
828
+ #
829
+ # define 'beans' do
830
+ # package :jar
831
+ # end
832
+ # end
833
+ #
834
+ # puts project('myapp:beans').version
835
+ def project(*args)
836
+ Project.project *args
837
+ end
838
+
839
+ # :call-seq:
840
+ # projects(*names) => projects
841
+ #
842
+ # With no arguments, returns a list of all projects defined so far. When called on a project,
843
+ # returns all its sub-projects (direct descendants).
844
+ #
845
+ # With arguments, returns a list of named projects, fails on any name that does not exist.
846
+ # As with #project, you can use relative names when calling this method on a project.
847
+ #
848
+ # Like #project, this method evaluates the definition of each project before returning it.
849
+ # Be advised of circular dependencies.
850
+ #
851
+ # For example:
852
+ # files = projects.map { |prj| FileList[prj.path_to('src/**/*.java') }.flatten
853
+ # puts "There are #{files.size} source files in #{projects.size} projects"
854
+ #
855
+ # puts projects('myapp:beans', 'myapp:webapp').map(&:name)
856
+ # Same as:
857
+ # puts project('myapp').projects.map(&:name)
858
+ def projects(*args)
859
+ Project.projects *args
860
+ end
861
+
862
+ # Forces all the projects to be evaluated before executing any other task.
863
+ # If we don't do that, we don't get to have tasks available when running Rake.
864
+ namespace 'buildr' do
865
+ task 'initialize' do
866
+ projects
867
+ end
868
+
869
+ desc "Freezes the Buildfile so it always uses Buildr version #{Buildr::VERSION}"
870
+ task 'freeze' do
871
+ puts "Freezing the Buildfile so it always uses Buildr version #{Buildr::VERSION}"
872
+ original = File.read(Buildr.application.buildfile)
873
+ if original =~ /gem\s*(["'])buildr\1/
874
+ modified = original.sub(/gem\s*(["'])buildr\1\s*,\s*(["']).*\2/, %{gem "buildr", "#{Buildr::VERSION}"})
875
+ else
876
+ modified = %{gem "buildr", "#{Buildr::VERSION}"\n} + original
877
+ end
878
+ File.open(Buildr.application.buildfile, "w") { |file| file.write modified }
879
+ end
880
+
881
+ desc 'Unfreezes the Buildfile to use the latest version of Buildr'
882
+ task 'unfreeze' do
883
+ puts 'Unfreezing the Buildfile to use the latest version of Buildr from your Gems repository.'
884
+ modified = File.read(Buildr.application.buildfile).sub(/^\s*gem\s*(["'])buildr\1.*\n/, "")
885
+ File.open(Buildr.application.buildfile, "w") { |file| file.write modified }
886
+ end
887
+ end
888
+
889
+
890
+ end