You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by bo...@apache.org on 2009/11/08 02:34:12 UTC

svn commit: r833791 - in /buildr/trunk: ./ doc/ lib/buildr/ lib/buildr/core/ lib/buildr/ide/ lib/buildr/java/ lib/buildr/packaging/ spec/ spec/core/

Author: boisvert
Date: Sun Nov  8 01:34:11 2009
New Revision: 833791

URL: http://svn.apache.org/viewvc?rev=833791&view=rev
Log:
Project extensions (before/after_define) now support dependency ordering similar to Rake
(e.g. before_define(:my_setup => :compile)


Modified:
    buildr/trunk/CHANGELOG
    buildr/trunk/doc/extending.textile
    buildr/trunk/lib/buildr/core/build.rb
    buildr/trunk/lib/buildr/core/checks.rb
    buildr/trunk/lib/buildr/core/compile.rb
    buildr/trunk/lib/buildr/core/project.rb
    buildr/trunk/lib/buildr/core/test.rb
    buildr/trunk/lib/buildr/ide/eclipse.rb
    buildr/trunk/lib/buildr/ide/idea.rb
    buildr/trunk/lib/buildr/ide/idea7x.rb
    buildr/trunk/lib/buildr/java/compiler.rb
    buildr/trunk/lib/buildr/java/packaging.rb
    buildr/trunk/lib/buildr/packaging/package.rb
    buildr/trunk/lib/buildr/shell.rb
    buildr/trunk/spec/core/extension_spec.rb
    buildr/trunk/spec/sandbox.rb

Modified: buildr/trunk/CHANGELOG
URL: http://svn.apache.org/viewvc/buildr/trunk/CHANGELOG?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/CHANGELOG (original)
+++ buildr/trunk/CHANGELOG Sun Nov  8 01:34:11 2009
@@ -1,4 +1,6 @@
 1.4.0 (Pending)
+* Added:  Project extensions (before/after_define) now support dependency ordering
+          similar to Rake (e.g. before_define(:my_setup => :compile)
 * Added:  BUILDR-328 Detect Eclipse plugin project with META-INF/MANIFEST.MF 
           and Bundle-SymbolicName: entry
 * Added:  Support for Eclipse classpath variables to avoid absolute pathnames in
@@ -37,7 +39,7 @@
 * Fixed:  BUILDR-315 Fix Eclipse .classpath for local libraries (Mat Schaffer)
 * Fixed:  BUILDR-304 Referencing an existing package task using the package
           method fails if the package has a custom filename (Rhett Sutphin)
-* Fixed:  BUILDR-322 When specifying files (instead of directories) as sources for compile task, 
+* Fixed:  BUILDR-322 When specifying files (instead of directories) as sources for compile task,
           Buildr uses target directory timestamp only (not compiled output timestamp)
 * Fixed:  BUILDR-324: Regression - baseDir system property is not set when executing tests [Alexis Midon]
 * Fixed:  BUILDR-325: Overriding package spec with classifer doesn't work (Antoine Toulme)
@@ -51,7 +53,7 @@
           for artifact jars.
 * Added:  BUILDR-222 Support Git as a version control system
 * Added   BUILDR-223 Release Task: customizable commit message
-* Added:  BUILDR-242 Include Scala-Tools Repository by Default.  
+* Added:  BUILDR-242 Include Scala-Tools Repository by Default.
 * Added:  BUILDR-268 Allow proxying for https connections (Joel Muzzerall).
 * Added:  Info message "Packaging filename.ext" now displayed for packaging tasks
 * Added:  Added Scala.version and Scala.version_str
@@ -65,13 +67,13 @@
 * Change: Upgraded to use JRuby 1.1.6 (when auto-installing).
 * Change: Buildr, no longer in incubation (hurray!): new site, mailing list, SVN, Git.
 * Change: BUILDR-171 Eclipse task generates meta-data files for projects with
-          test source code but no main source code. 
+          test source code but no main source code.
 * Change: BUILDR-177 Moved cobertura and emma extensions to lib directory.
 * Change: BUILDR-187 Source code attachment for Eclipse .classpath.
 * Change: BUILDR-188 Source code attachment for IDEA .iml file (Marko Sibakov).
 * Change: BUILDR-209 Scala Specs Should Use src/specs/scala/
 * Change: BUILDR-237 Use MacPorts Scala on OS X.
-* Change: BUILDR-260 Upgrade to Scala 2.7.3 compatible dependencies:  
+* Change: BUILDR-260 Upgrade to Scala 2.7.3 compatible dependencies:
           ScalaSpecs 1.4.3, ScalaCheck 1.5 and ScalaTest 0.9.5
 * Change: Buildr now uses Jekyll to generate Web site/documentation:
 http://github.com/mojombo/jekyll/ This replaces Docter so less code to
@@ -159,7 +161,7 @@
           like java.util.Properties (Lacton).
 * Fixed:  BUILDR-116: TestTask should include the main compile target in its
           dependencies, even when using non standard directories (Lacton).
-* Fixed:  BUILDR-117 Shared directory for both code and resources produces 
+* Fixed:  BUILDR-117 Shared directory for both code and resources produces
           duplicate Eclipse classpath entries (Nathan Hamblen)
 * Fixed:  BUILDR-119 Eclipse task does not accept test resource folders
           (Lacton)
@@ -176,7 +178,7 @@
 * Fixed:  BUILDR-138 ScalaTest premature use of Buildr::Repositories
           inconsistent with customizing locations.
 * Fixed:  BUILDR-152 Project.task fails when task name starts with a colon.
-* Fixed:  BUILDR-157 Tasks library not loaded from a submodule. 
+* Fixed:  BUILDR-157 Tasks library not loaded from a submodule.
 * Docs:   BUILDR-111 Troubleshoot tip when Buildr's bin directory shows up in
           RUBYLIB (Geoffrey Ruscoe).
 
@@ -235,7 +237,7 @@
 * Added: Cobertura tasks can be invoked for a single project using project
 name as prefix to cobetura tasks.
 * Added: Cobertura can exclude specified classes from instrumentation.
-* Added: ArchiveTask#clean can be used to remove content from a package. 
+* Added: ArchiveTask#clean can be used to remove content from a package.
 * Added: Groovy compiler.
 * Added: Mechanism to simplify creating extensions (see Extension module).
 * Added: To run all test cases 'rake spec'.  Test coverage reports will show
@@ -246,7 +248,7 @@
 * Added: EAR packaging (Victor Hugo Borja).
 * Added: Profiles(.yaml), based on the code provided by Yanko Ivanov.
 * Added: Resources task picks the default mapping from the filter element of
-the current profile (if specified). 
+the current profile (if specified).
 * Added: Consolidated API for RJB and JRuby, replacing the now deprecated
 JavaWrapper.
 * Added: JRuby 1.1 support (Victor Hugo Borja, Nick Sieger).

Modified: buildr/trunk/doc/extending.textile
URL: http://svn.apache.org/viewvc/buildr/trunk/doc/extending.textile?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/doc/extending.textile (original)
+++ buildr/trunk/doc/extending.textile Sun Nov  8 01:34:11 2009
@@ -16,7 +16,7 @@
   REQUIRES = [
     'org.apache.openjpa:openjpa-all:jar:0.9.7-incubating',
     'commons-collections:commons-collections:jar:3.1',
-    . . . 
+    . . .
     'net.sourceforge.serp:serp:jar:1.11.0' ]
   ant('openjpa') do |ant|
     ant.taskdef :name=>'mapping',
@@ -48,20 +48,20 @@
 
 If you want to share these pre-canned definitions between projects, you have a few more options.  You can share the @tasks@ directory using SVN externals, Git modules, or whichever cross-repository feature your source control system supports. Another mechanism with better version control is to package all these tasks, functions and modules into a "Gem":http://rubygems.org/ and require it from your Buildfile.  You can run your own internal Gem server for that.
 
-To summarize, there are several common ways to distribute extensions: 
+To summarize, there are several common ways to distribute extensions:
 * Put them in the same place (e.g. @~/.buildr@) and require them from your
 @buildfile@
 * Put them directly in the project, typically under the @tasks@ directory.
 * Put them in a shared code repository, and link to them from your project's @tasks@ directory
 * As Ruby gems and specify which gems are used in the settings file
 
-You can also get creative and devise your own way to distribute extensions.  
+You can also get creative and devise your own way to distribute extensions.
 "Sake":http://errtheblog.com/post/6069 is a good example of such initiative that lets you deploy Rake tasks on a system-wide basis.
 
 h2(#extensions).  Creating Extensions
 
 The basic mechanism for extending projects in Buildr are Ruby modules.  In fact, base features like compiling and testing are all developed in the form of modules, and then added to the core Project class.
-  
+
 A module defines instance methods that are then mixed into the project and become instance methods of the project.  There are two general ways for extending projects.  You can extend all projects by including the module in Project:
 
 {% highlight ruby %}
@@ -128,6 +128,25 @@
 
 You may find interesting that this Extension API is used pervasively inside Buildr itself.  Many of the standard tasks such as @compile@, @test@, @package@  are extensions to a very small core.
 
+Starting with Buildr 1.4, it's possible to define ordering between @before_define@ and @after_define@ code blocks in a way similar to Rake's dependencies.  For example, if you wanted to override @project.test.compile.from@ in @after_define@, you could do so by in
+
+{% highlight ruby %}
+after_define(:functional_tests) do |project|
+  # Change project.test.compile.from if it's not already pointing
+  # to a location with Java sources
+  if Dir["#{project.test.compile.from}/**/*.java"].size == 0 &&
+     Dir["#{project._(:src, 'test-functional', :java)}/**/*.java"].size > 0
+    project.test.compile.from project._(:src, 'test-functional', :java)
+  end
+end
+
+# make sure project.test.compile.from is updated before the
+# compile extension picks up its value
+after_define(:compile => :functional_test)
+{% endhighlight %}
+
+Core extensions provide the following named callbacks: @compile@, @test@, @build@, @package@ and @check@.
+
 h2(#layouts).  Using Alternative Layouts
 
 Buildr follows a common convention for project layouts: Java source files appear in @src/main/java@ and compile to @target/classes@, resources are copied over from @src/main/resources@ and so forth.  Not all projects follow this convention, so it's now possible to specify an alternative project layout.

Modified: buildr/trunk/lib/buildr/core/build.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/build.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/build.rb (original)
+++ buildr/trunk/lib/buildr/core/build.rb Sun Nov  8 01:34:11 2009
@@ -52,7 +52,7 @@
       task 'default'=>'build'
     end
 
-    before_define do |project|
+    before_define(:build => [:compile, :test]) do |project|
       project.recursive_task 'build'
       project.recursive_task 'clean'
       project.clean do
@@ -61,6 +61,7 @@
       end
     end
 
+    after_define(:build)
 
     # *Deprecated:* Use +path_to(:target)+ instead.
     def target

Modified: buildr/trunk/lib/buildr/core/checks.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/checks.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/checks.rb (original)
+++ buildr/trunk/lib/buildr/core/checks.rb Sun Nov  8 01:34:11 2009
@@ -139,7 +139,7 @@
 
     include Extension
 
-    before_define do |project|
+    before_define(:check => :package) do |project|
       # The check task can do any sort of interesting things, but the most important is running expectations.
       project.task("check") do |task|
         project.expectations.inject(true) do |passed, expect|

Modified: buildr/trunk/lib/buildr/core/compile.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/compile.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/compile.rb (original)
+++ buildr/trunk/lib/buildr/core/compile.rb Sun Nov  8 01:34:11 2009
@@ -37,7 +37,7 @@
       end
 
       # Adds a compiler to the list of supported compiler.
-      #   
+      #
       # For example:
       #   Buildr::Compiler << Buildr::Javac
       def add(compiler)
@@ -188,7 +188,7 @@
       end
 
     private
-      
+
       def findFirst(file, pattern)
         match = nil
         File.open(file, "r") do |infile|
@@ -383,7 +383,7 @@
       @project, @usage = project, usage
       guess_compiler
     end
-    
+
     # Try to guess if we have a compiler to match source files.
     def guess_compiler #:nodoc:
       candidate = Compiler.compilers.detect { |cls| cls.applies_to?(project, self) }
@@ -497,7 +497,7 @@
       Project.local_task('compile') { |name| "Compiling #{name}" }
     end
 
-    before_define do |project|
+    before_define(:compile) do |project|
       resources = ResourcesTask.define_task('resources')
       resources.send :associate_with, project, :main
       project.path_to(:source, :main, :resources).tap { |dir| resources.from dir if File.exist?(dir) }
@@ -507,7 +507,7 @@
       project.recursive_task('compile')
     end
 
-    after_define do |project|
+    after_define(:compile) do |project|
       if project.compile.target
         # This comes last because the target path is set inside the project definition.
         project.build project.compile.target
@@ -517,7 +517,7 @@
       end
     end
 
-      
+
     # :call-seq:
     #   compile(*sources) => CompileTask
     #   compile(*sources) { |task| .. } => CompileTask

Modified: buildr/trunk/lib/buildr/core/project.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/project.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/project.rb (original)
+++ buildr/trunk/lib/buildr/core/project.rb Sun Nov  8 01:34:11 2009
@@ -210,8 +210,8 @@
           @projects[name] = project
           # Set the project properties first, actions may use them.
           properties.each { |name, value| project.send "#{name}=", value } if properties
-          # Instantiate callbacks for this project, and setup to call before/after define.
-          # Don't cache list of callbacks, since project may add new callbacks.
+          # Setup to call before/after define extension callbacks
+          # Don't cache list of extensions, since project may add new extensions.
           project.enhance do |project|
             project.send :call_callbacks, :before_define
             project.enhance do |project|
@@ -219,14 +219,14 @@
             end
           end
           project.enhance do |project|
-            @on_define.each { |callback| callback[project] }
+            @on_define.each { |extension| extension[project] }
           end if @on_define
           # Enhance the project using the definition block.
           project.enhance { project.instance_exec project, &block } if block
 
           # Top-level project? Invoke the project definition. Sub-project? We don't invoke
           # the project definiton yet (allow project calls to establish order of evaluation),
-          # but must do so before the parent project's definition is done. 
+          # but must do so before the parent project's definition is done.
           project.parent.enhance { project.invoke } if project.parent
         end
       end
@@ -375,11 +375,15 @@
         project if Project === project
       end
 
-      # Callback classes.
-      def callbacks #:nodoc:
-        @callbacks ||= []
+      # Loaded extension modules.
+      def extension_modules #:nodoc:
+        @extension_modules ||= []
       end
 
+      # Extension callbacks that apply to all projects
+      def global_callbacks #:nodoc:
+        @global_callbacks ||= []
+      end
     end
 
 
@@ -402,11 +406,8 @@
         @parent = task(split[0...-1].join(':'))
         raise "No parent project #{split[0...-1].join(':')}" unless @parent && Project === parent
       end
-      callbacks = Project.callbacks.uniq.map(&:new)
-      @callbacks = [:before_define, :after_define].inject({}) do |hash, state|
-        methods = callbacks.select { |callback| callback.respond_to?(state) }.map { |callback| callback.method(state) }
-        hash.update(state=>methods)
-      end
+      # Inherit all global callbacks
+      @callbacks = Project.global_callbacks.dup
     end
 
     # :call-seq:
@@ -587,6 +588,16 @@
       %Q{project(#{name.inspect})}
     end
 
+    def callbacks #:nodoc:
+      # global + project_local callbacks for this project
+      @callbacks ||= []
+    end
+
+    def calledback #:nodoc:
+      # project-local callbacks that have been called
+      @calledback ||= {}
+    end
+
   protected
 
     # :call-seq:
@@ -626,14 +637,32 @@
       end
     end
 
-    # Call all callbacks for a particular state, e.g. :before_define, :after_define.
-    def call_callbacks(state) #:nodoc:
-      methods = @callbacks.delete(state) || []
-      methods.each { |method| method.call(project) }
-    end
+    # Call all extension callbacks for a particular phase, e.g. :before_define, :after_define.
+    def call_callbacks(phase) #:nodoc:
+      remaining = @callbacks.select { |cb| cb.phase == phase }
+      known_callbacks = remaining.map { |cb| cb.name }
+
+      # find first callback with satisfied dependencies
+      first_satisfied = lambda do
+        remaining_names = remaining.map { |cb| cb.name }
+        remaining.find do |cb|
+          cb.dependencies.each do |dep|
+            fail "Unknown #{phase.inspect} extension dependency: #{dep.inspect}" unless known_callbacks.index(dep)
+          end
+          satisfied = cb.dependencies.find { |dep| remaining_names.index(dep) } == nil
+          remaining.delete cb if satisfied
+        end
+      end
 
-    def add_callback(callback)
-      @callbacks[:after_define] << callback.method(:after_define) if callback.respond_to?(:after_define)
+      # call each extension in order
+      until remaining.empty?
+        callback = first_satisfied.call
+        if callback.nil?
+          hash = remaining.map { |cb| { cb.name => cb.dependencies} }
+          fail "Unsatisfied dependencies in extensions for #{phase}: #{hash.inspect}"
+        end
+        callback.blocks.each { |b| b.call(self) }
+      end
     end
 
   end
@@ -707,6 +736,22 @@
   #   end
   module Extension
 
+    # Extension callback details
+    class Callback #:nodoc:
+      attr_accessor :phase, :name, :dependencies, :blocks
+
+      def initialize(phase, name, dependencies, blocks)
+        @phase = phase
+        @name = name
+        @dependencies = dependencies
+        @blocks = (blocks ? (Array === blocks ? blocks : [blocks]) : [])
+      end
+
+      def merge(callback)
+        Callback.new(phase, name, @dependencies + callback.dependencies, @blocks + callback.blocks)
+      end
+    end
+
     def self.included(base) #:nodoc:
       base.extend ClassMethods
     end
@@ -715,49 +760,109 @@
     module ClassMethods
 
       def included(base) #:nodoc:
-        # When included in Project, add callback and call first_time.
-        if Project == base && !base.callbacks.include?(callbacks)
-          base.callbacks << callbacks
-          callbacks.first_time if callbacks.respond_to?(:first_time)
+        # When included in Project, add module instance, merge callbacks and call first_time.
+        if Project == base && !base.extension_modules.include?(module_callbacks)
+          base.extension_modules << module_callbacks
+          merge_callbacks(base.global_callbacks, module_callbacks)
+          first_time = module_callbacks.select { |c| c.phase == :first_time }
+          first_time.each do |c|
+            c.blocks.each { |b| b.call }
+          end
         end
       end
 
       def extended(base) #:nodoc:
-        # When extending project, add instance and call before_define.
+        # When extending project, merge after_define callbacks and call before_define callback(s)
+        # immediately
         if Project === base
-          callbacks = self.send(:callbacks).new
-          callbacks.before_define(base) if callbacks.respond_to?(:before_define)
-          base.send :add_callback, callbacks
+          merge_callbacks(base.callbacks, module_callbacks.select { |cb| cb.phase == :after_define })
+          calls = module_callbacks.select { |cb| cb.phase == :before_define }
+          calls.each do |cb|
+            cb.blocks.each { |b| b.call(base) } unless base.calledback[cb]
+            base.calledback[cb] = cb
+          end
         end
       end
 
-      # This block will be called once for any particular extension.
+      # This block will be called once for any particular extension included in Project.
       # You can use this to setup top-level and local tasks.
       def first_time(&block)
-        meta = class << callbacks ; self ; end
-        meta.send :define_method, :first_time, &block
+        module_callbacks << Callback.new(:first_time, self.name, [], block)
       end
 
       # This block is called once for the project with the project instance,
       # right before running the project definition.  You can use this to add
       # tasks and set properties that will be used in the project definition.
-      def before_define(&block)
-        callbacks.send :define_method, :before_define, &block
+      #
+      # The block may be named and dependencies may be declared similar to Rake
+      # task dependencies:
+      #
+      #   before_define(:my_setup) do |project|
+      #     # do stuff on project
+      #   end
+      #
+      #   # my_setup code must run before :compile
+      #   before_define(:compile => :my_setup)
+      #
+      def before_define(*args, &block)
+        if args.empty?
+          name = self.name
+          deps = []
+        else
+          name, args, deps = Buildr.application.resolve_args(args)
+        end
+        module_callbacks << Callback.new(:before_define, name, deps, block)
       end
 
       # This block is called once for the project with the project instance,
       # right after running the project definition.  You can use this to do
       # any post-processing that depends on the project definition.
-      def after_define(&block)
-        callbacks.send :define_method, :after_define, &block
+      #
+      # The block may be named and dependencies may be declared similar to Rake
+      # task dependencies:
+      #
+      #   after_define(:my_setup) do |project|
+      #     # do stuff on project
+      #   end
+      #
+      #   # my_setup code must run before :compile (but only after project is defined)
+      #   after_define(:compile => :my_setup)
+      #
+      def after_define(*args, &block)
+        if args.empty?
+          name = self.name
+          deps = []
+        else
+          name, args, deps = Buildr.application.resolve_args(args)
+        end
+        module_callbacks << Callback.new(:after_define, name, deps, block)
       end
 
     private
 
-      def callbacks
-        const_get('Callbacks') rescue const_set('Callbacks', Class.new)
+      def module_callbacks
+        begin
+          const_get('Callbacks')
+        rescue
+          callbacks = []
+          const_set('Callbacks', callbacks)
+        end
       end
 
+      def merge_callbacks(base, merge)
+        # index by phase and name
+        index = base.inject({}) { |hash,cb| { [cb.phase, cb.name] => cb } }
+        merge.each do |cb|
+          existing = index[[cb.phase, cb.name]]
+          if existing
+            base[base.index(existing)] = existing.merge(cb)
+          else
+            base << cb
+          end
+          index[[cb.phase, cb.name]] = cb
+        end
+        base
+      end
     end
 
   end

Modified: buildr/trunk/lib/buildr/core/test.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/test.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/test.rb (original)
+++ buildr/trunk/lib/buildr/core/test.rb Sun Nov  8 01:34:11 2009
@@ -568,7 +568,7 @@
 
     end
     
-    before_define do |project|
+    before_define(:test) do |project|
       # Define a recursive test task, and pass it a reference to the project so it can discover all other tasks.
       test = TestTask.define_task('test')
       test.send :associate_with, project
@@ -587,7 +587,7 @@
       test.setup ; test.teardown
     end
 
-    after_define do |project|
+    after_define(:test => :compile) do |project|
       test = project.test
       # Dependency on compiled tests and resources.  Dependencies added using with.
       test.dependencies.concat [test.compile.target, test.resources.target].compact

Modified: buildr/trunk/lib/buildr/ide/eclipse.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/ide/eclipse.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/ide/eclipse.rb (original)
+++ buildr/trunk/lib/buildr/ide/eclipse.rb Sun Nov  8 01:34:11 2009
@@ -163,7 +163,7 @@
       project.recursive_task('eclipse')
     end
 
-    after_define do |project|
+    after_define(:eclipse => :package) do |project|
       eclipse = project.task('eclipse')
 
       eclipse.enhance [ file(project.path_to('.classpath')), file(project.path_to('.project')) ]

Modified: buildr/trunk/lib/buildr/ide/idea.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/ide/idea.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/ide/idea.rb (original)
+++ buildr/trunk/lib/buildr/ide/idea.rb Sun Nov  8 01:34:11 2009
@@ -30,11 +30,11 @@
       Project.local_task "idea"=>"artifacts"
     end
 
-    before_define do |project|
+    before_define(:idea) do |project|
       project.recursive_task("idea")
     end
 
-    after_define do |project|
+    after_define(:idea => :package) do |project|
       idea = project.task("idea")
       # We need paths relative to the top project's base directory.
       root_path = lambda { |p| f = lambda { |p| p.parent ? f[p.parent] : p.base_dir }; f[p] }[project]

Modified: buildr/trunk/lib/buildr/ide/idea7x.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/ide/idea7x.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/ide/idea7x.rb (original)
+++ buildr/trunk/lib/buildr/ide/idea7x.rb Sun Nov  8 01:34:11 2009
@@ -39,11 +39,11 @@
       Project.local_task "idea7x"=>"artifacts"
     end
 
-    before_define do |project|
+    before_define(:idea7x) do |project|
       project.recursive_task("idea7x")
     end
 
-    after_define do |project|
+    after_define(:idea7x => :package) do |project|
       idea7x = project.task("idea7x")
 
       # We need paths relative to the top project's base directory.

Modified: buildr/trunk/lib/buildr/java/compiler.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/java/compiler.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/java/compiler.rb (original)
+++ buildr/trunk/lib/buildr/java/compiler.rb Sun Nov  8 01:34:11 2009
@@ -276,14 +276,14 @@
       Project.local_task('javadoc')
     end
 
-    before_define do |project|
+    before_define(:javadoc) do |project|
       JavadocTask.define_task('javadoc').tap do |javadoc|
         javadoc.into project.path_to(:target, :javadoc)
         javadoc.using :windowtitle=>project.comment || project.name
       end
     end
 
-    after_define do |project|
+    after_define(:javadoc) do |project|
       project.javadoc.from project
     end
 

Modified: buildr/trunk/lib/buildr/java/packaging.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/java/packaging.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/java/packaging.rb (original)
+++ buildr/trunk/lib/buildr/java/packaging.rb Sun Nov  8 01:34:11 2009
@@ -573,7 +573,7 @@
 
       include Extension
 
-      before_define do |project|
+      before_define(:package => :build) do |project|
         ::Java.load
         if project.parent && project.parent.manifest 
           project.manifest = project.parent.manifest.dup

Modified: buildr/trunk/lib/buildr/packaging/package.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/packaging/package.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/packaging/package.rb (original)
+++ buildr/trunk/lib/buildr/packaging/package.rb Sun Nov  8 01:34:11 2009
@@ -43,7 +43,7 @@
       end
     end
 
-    before_define do |project|
+    before_define(:package => :build) do |project|
       [ :package, :install, :uninstall, :upload ].each { |name| project.recursive_task name }
       # Need to run build before package, since package is often used as a dependency by tasks that
       # expect build to happen.
@@ -51,6 +51,8 @@
       project.group ||= project.parent && project.parent.group || project.name
       project.version ||= project.parent && project.parent.version
     end
+    
+    after_define(:package)
 
     # The project's identifier. Same as the project name, with colons replaced by dashes.
     # The ID for project foo:bar is foo-bar.

Modified: buildr/trunk/lib/buildr/shell.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/shell.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/shell.rb (original)
+++ buildr/trunk/lib/buildr/shell.rb Sun Nov  8 01:34:11 2009
@@ -117,7 +117,7 @@
       ShellProviders.each { |p| Project.local_task "shell:#{p.to_sym}" }    # TODO  not working
     end
     
-    before_define do |project|
+    before_define(:shell => :compile) do |project|
       ShellProviders.each do |p|
         name = p.to_sym
         
@@ -133,7 +133,7 @@
       end
     end
     
-    after_define do |project|
+    after_define(:shell => :compile) do |project|
       default_shell = project.shell.using
       
       if default_shell

Modified: buildr/trunk/spec/core/extension_spec.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/spec/core/extension_spec.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/spec/core/extension_spec.rb (original)
+++ buildr/trunk/spec/core/extension_spec.rb Sun Nov  8 01:34:11 2009
@@ -18,17 +18,31 @@
 
 
 describe Extension do
-  
+
+  before do
+    @saved_modules   = Project.class_eval { @extension_modules }.dup
+    @saved_callbacks = Project.class_eval { @global_callbacks }.dup
+  end
+
+  after do
+    modules   = @saved_modules
+    callbacks = @saved_callbacks
+    Project.class_eval do
+      @global_callbacks  = callbacks
+      @extension_modules = modules
+    end
+  end
+
   it 'should call Extension.first_time during include' do
     TestExtension.should_receive(:first_time_called).once
     class Buildr::Project
       include TestExtension
     end
   end
-  
+
   it 'should call before_define and after_define in order when project is defined' do
     begin
-      TestExtension.initialized do |extension|
+      TestExtension.callback do |extension|
         extension.should_receive(:before_define_called).once.ordered
         extension.should_receive(:after_define_called).once.ordered
       end
@@ -37,14 +51,14 @@
       end
       define('foo')
     ensure
-      TestExtension.initialized { |ignore| }
+      TestExtension.callback { |ignore| }
     end
   end
 
   it 'should call before_define and after_define for each project defined' do
     begin
       extensions = 0
-      TestExtension.initialized do |extension|
+      TestExtension.callback do |extension|
         extensions += 1
         extension.should_receive(:before_define_called).once.ordered
         extension.should_receive(:after_define_called).once.ordered
@@ -55,39 +69,133 @@
       define('foo')
       define('bar')
       extensions.should equal(2)
-    ensure  
-      TestExtension.initialized { |ignore| }
+    ensure
+      TestExtension.callback { |ignore| }
+    end
+  end
+
+  it 'should call before_define callbacks in dependency order' do
+    class Buildr::Project
+      include ExtensionOneTwo
+      include ExtensionThreeFour
+    end
+    define('foo')
+    project('foo').before_order.should == [ :one, :two, :three, :four ]
+    project('foo').after_order.should  == [ :four, :three, :two, :one ]
+  end
+
+  it 'should call before_define callbacks when extending project' do
+    define('foo') do
+      extend ExtensionOneTwo
+      extend ExtensionThreeFour
     end
+    project('foo').before_order.should == [ :two, :one, :four, :three ]
+    project('foo').after_order.should  == [ :four, :three, :two, :one ]
+  end
+
+  it 'should raise error when including if callback dependencies cannot be satisfied' do
+    class Buildr::Project
+      include ExtensionOneTwo # missing ExtensionThreeFour
+    end
+    lambda { define('foo') }.should raise_error
+  end
+
+  it 'should raise error when extending if callback dependencies cannot be satisfied' do
+    lambda {
+      define('foo') do |project|
+        extend ExtensionOneTwo # missing ExtensionThreeFour
+      end
+    }.should raise_error
+  end
+
+  it 'should ignore dependencies when extending project' do
+    define('bar') do |project|
+      extend ExtensionThreeFour # missing ExtensionOneTwo
+    end
+    project('bar').before_order.should == [:four, :three]
+    project('bar').after_order.should == [:four, :three]
   end
 end
 
 module TestExtension
   include Extension
-  
+
   def initialize(*args)
-    # callback is used to obtain extension instance created by buildr
-    @@initialized.call(self) if @@initialized
     super
+    # callback is used to obtain extension instance created by buildr
+    @@callback.call(self) if @@callback
   end
-  
-  def TestExtension.initialized(&block)
-    @@initialized = block
+
+  def self.callback(&block)
+    @@callback = block
   end
-  
+
   first_time do
-    TestExtension.first_time_called()
+    self.first_time_called()
   end
-  
+
   before_define do |project|
     project.before_define_called()
   end
-  
+
   after_define do |project|
     project.after_define_called()
   end
 
-  def TestExtension.first_time_called()
+  def self.first_time_called()
+  end
+
+end
+
+module BeforeAfter
+  def before_order
+    @before_order ||= []
+  end
+
+  def after_order
+    @after_order ||= []
+  end
+end
+
+module ExtensionOneTwo
+  include Extension, BeforeAfter
+
+  before_define(:two => :one) do |project|
+    project.before_order << :two
+  end
+
+  before_define(:one) do |project|
+    project.before_order << :one
+  end
+
+  after_define(:one => :two) do |project|
+    project.after_order << :one
+  end
+
+  after_define(:two => :three) do |project|
+    project.after_order << :two
+  end
+end
+
+module ExtensionThreeFour
+  include Extension, BeforeAfter
+
+  before_define(:three => :two)
+
+  before_define(:four => :three) do |project|
+    project.before_order << :four
+  end
+
+  before_define(:three) do |project|
+    project.before_order << :three
+  end
+
+  after_define(:three => :four) do |project|
+    project.after_order << :three
+  end
+
+  after_define(:four) do |project|
+    project.after_order << :four
   end
-  
 end
 

Modified: buildr/trunk/spec/sandbox.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/spec/sandbox.rb?rev=833791&r1=833790&r2=833791&view=diff
==============================================================================
--- buildr/trunk/spec/sandbox.rb (original)
+++ buildr/trunk/spec/sandbox.rb Sun Nov  8 01:34:11 2009
@@ -45,14 +45,14 @@
       spec.before(:each) { sandbox }
       spec.after(:each) { reset }
     end
-    
+
     # Require an optional extension without letting its callbacks pollute the Project class.
     def require_optional_extension(extension_require_path)
-      project_callbacks_without_extension = Project.class_eval { @callbacks }.dup
+      project_callbacks_without_extension = Project.class_eval { @global_callbacks }.dup
       begin
         require extension_require_path
       ensure
-        Project.class_eval { @callbacks = project_callbacks_without_extension }
+        Project.class_eval { @global_callbacks = project_callbacks_without_extension }
       end
     end
   end
@@ -71,7 +71,7 @@
 
   def sandbox
     @_sandbox = {}
-    
+
     # Create a temporary directory where we can create files, e.g,
     # for projects, compilation. We need a place that does not depend
     # on the current directory.
@@ -88,10 +88,11 @@
 
     @_sandbox[:load_path] = $LOAD_PATH.clone
     #@_sandbox[:loaded_features] = $LOADED_FEATURES.clone
-    
+
     # Later on we'll want to lose all the on_define created during the test.
     @_sandbox[:on_define] = Project.class_eval { (@on_define || []).dup }
-    @_sandbox[:callbacks] = Project.class_eval { (@callbacks || []).dup }
+    @_sandbox[:extension_modules] = Project.class_eval { (@extension_modules || []).dup }
+    @_sandbox[:global_callbacks] = Project.class_eval { (@global_callbacks || []).dup }
     @_sandbox[:layout] = Layout.default.clone
 
     # Create a local repository we can play with. However, our local repository will be void
@@ -120,10 +121,17 @@
   def reset
     # Get rid of all the projects and the on_define blocks we used.
     Project.clear
+
     on_define = @_sandbox[:on_define]
-    Project.class_eval { @on_define = on_define }
-    callbacks = @_sandbox[:callbacks]
-    Project.class_eval { @callbacks = callbacks }
+    extension_modules = @_sandbox[:extension_modules]
+    global_callbacks = @_sandbox[:global_callbacks]
+
+    Project.class_eval do
+      @on_define = on_define
+      @global_callbacks = global_callbacks
+      @extension_modules = extension_modules
+    end
+
     Layout.default = @_sandbox[:layout].clone
 
     $LOAD_PATH.replace @_sandbox[:load_path]



Re: svn commit: r833791 - in /buildr/trunk: ./ doc/ lib/buildr/ lib/buildr/core/ lib/buildr/ide/ lib/buildr/java/ lib/buildr/packaging/ spec/ spec/core/

Posted by Assaf Arkin <as...@labnotes.org>.
Awesome.

Assaf

On Sun, Nov 8, 2009 at 8:09 AM, Alex Boisvert <al...@gmail.com>wrote:

> Thanks :)   Yes, it gives external extensions practically the same
> opportunies as our core modules.
>
> alex
>
> On Sun, Nov 8, 2009 at 3:01 AM, lacton <la...@users.sourceforge.net>
> wrote:
>
> > Very impressive work, Alex.  I feel the buildr extension mechanism,
> > which was already very nice, has reached a new level.
> >
> > Lacton
> >
> > On Sun, Nov 8, 2009 at 2:34 AM,  <bo...@apache.org> wrote:
> > > Author: boisvert
> > > Date: Sun Nov  8 01:34:11 2009
> > > New Revision: 833791
> > >
> > > URL: http://svn.apache.org/viewvc?rev=833791&view=rev
> > > Log:
> > > Project extensions (before/after_define) now support dependency
> ordering
> > similar to Rake
> > > (e.g. before_define(:my_setup => :compile)
> >
>

Re: svn commit: r833791 - in /buildr/trunk: ./ doc/ lib/buildr/ lib/buildr/core/ lib/buildr/ide/ lib/buildr/java/ lib/buildr/packaging/ spec/ spec/core/

Posted by Alex Boisvert <al...@gmail.com>.
Thanks :)   Yes, it gives external extensions practically the same
opportunies as our core modules.

alex

On Sun, Nov 8, 2009 at 3:01 AM, lacton <la...@users.sourceforge.net> wrote:

> Very impressive work, Alex.  I feel the buildr extension mechanism,
> which was already very nice, has reached a new level.
>
> Lacton
>
> On Sun, Nov 8, 2009 at 2:34 AM,  <bo...@apache.org> wrote:
> > Author: boisvert
> > Date: Sun Nov  8 01:34:11 2009
> > New Revision: 833791
> >
> > URL: http://svn.apache.org/viewvc?rev=833791&view=rev
> > Log:
> > Project extensions (before/after_define) now support dependency ordering
> similar to Rake
> > (e.g. before_define(:my_setup => :compile)
>

Re: svn commit: r833791 - in /buildr/trunk: ./ doc/ lib/buildr/ lib/buildr/core/ lib/buildr/ide/ lib/buildr/java/ lib/buildr/packaging/ spec/ spec/core/

Posted by lacton <la...@users.sourceforge.net>.
Very impressive work, Alex.  I feel the buildr extension mechanism,
which was already very nice, has reached a new level.

Lacton

On Sun, Nov 8, 2009 at 2:34 AM,  <bo...@apache.org> wrote:
> Author: boisvert
> Date: Sun Nov  8 01:34:11 2009
> New Revision: 833791
>
> URL: http://svn.apache.org/viewvc?rev=833791&view=rev
> Log:
> Project extensions (before/after_define) now support dependency ordering similar to Rake
> (e.g. before_define(:my_setup => :compile)