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 2010/11/29 06:33:40 UTC
svn commit: r1040014 - in /buildr/trunk: CHANGELOG lib/buildr/core/cc.rb
spec/core/cc_spec.rb
Author: boisvert
Date: Mon Nov 29 05:33:39 2010
New Revision: 1040014
URL: http://svn.apache.org/viewvc?rev=1040014&view=rev
Log:
BUILDR-551 Continuous compilation not working for project trees
Modified:
buildr/trunk/CHANGELOG
buildr/trunk/lib/buildr/core/cc.rb
buildr/trunk/spec/core/cc_spec.rb
Modified: buildr/trunk/CHANGELOG
URL: http://svn.apache.org/viewvc/buildr/trunk/CHANGELOG?rev=1040014&r1=1040013&r2=1040014&view=diff
==============================================================================
--- buildr/trunk/CHANGELOG (original)
+++ buildr/trunk/CHANGELOG Mon Nov 29 05:33:39 2010
@@ -14,6 +14,7 @@
with classifier
* Fixed: BUILDR-522 Send notifications when continuous compilation
succeeds/fails.
+* Fixed: BUILDR-551 Continuous compilation not working for project trees
* Change: Upgrade to Groovy 1.7.5
* Change: BUILDR-545 Add the ability to specify the description element in in
application.xml contained within an ear.
Modified: buildr/trunk/lib/buildr/core/cc.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/cc.rb?rev=1040014&r1=1040013&r2=1040014&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/cc.rb (original)
+++ buildr/trunk/lib/buildr/core/cc.rb Mon Nov 29 05:33:39 2010
@@ -33,6 +33,15 @@ module Buildr
private
+ # run block on sub-projects depth-first, then on this project
+ def each_project(&block)
+ depth_first = lambda do |p|
+ p.projects.each { |c| depth_first.call(c, &block) }
+ block.call(p)
+ end
+ depth_first.call(@project)
+ end
+
def associate_with(project)
@project = project
end
@@ -40,7 +49,7 @@ module Buildr
def monitor_and_compile
# we don't want to actually fail if our dependencies don't succeed
begin
- [:compile, 'test:compile'].each { |name| project.task(name).invoke }
+ each_project { |p| p.test.compile.invoke }
build_completed(project)
rescue Exception => ex
$stderr.puts $terminal.color(ex.message, :red)
@@ -48,60 +57,54 @@ module Buildr
build_failed(project, ex)
end
- main_dirs = project.compile.sources.map(&:to_s)
- test_dirs = project.task('test:compile').sources.map(&:to_s)
- res_dirs = project.resources.sources.map(&:to_s)
-
- source_ext = lambda { |compiler| Array(Buildr::Compiler.select(compiler).source_ext).map(&:to_s) }
- main_ext = source_ext.call(project.compile.compiler) unless project.compile.compiler.nil?
- test_ext = source_ext.call(project.task('test:compile').compiler) unless project.task('test:compile').compiler.nil?
-
- test_tail = if test_dirs.empty? then '' else ",{#{test_dirs.join ','}}/**/*.{#{test_ext.join ','}}" end
- res_tail = if res_dirs.empty? then '' else ",{#{res_dirs.join ','}}/**/*" end
- pattern = "{{#{main_dirs.join ','}}/**/*.{#{main_ext.join ','}}#{test_tail}#{res_tail}}"
-
- times, _ = check_mtime pattern, {} # establish baseline
-
- dir_names = (main_dirs + test_dirs + res_dirs).map { |file| strip_filename project, file }
- if dir_names.length == 1
- info "Monitoring directory: #{dir_names.first}"
+ dirs = []
+ each_project do |p|
+ dirs += p.compile.sources.map(&:to_s)
+ dirs += p.test.compile.sources.map(&:to_s)
+ dirs += p.resources.sources.map(&:to_s)
+ end
+ if dirs.length == 1
+ info "Monitoring directory: #{dirs.first}"
else
- info "Monitoring directories: [#{dir_names.join ', '}]"
+ info "Monitoring directories: [#{dirs.join ', '}]"
+ end
+
+ timestamps = lambda do
+ times = {}
+ dirs.each { |d| Dir.glob("#{d}/**/*").map { |f| times[f] = File.mtime f } }
+ times
end
- trace "Monitoring extensions: [#{main_ext.join ', '}]"
+
+ old_times = timestamps.call()
while true
sleep delay
- times, changed = check_mtime pattern, times
+ new_times = timestamps.call()
+ changed = changed(new_times, old_times)
+ old_times = new_times
+
unless changed.empty?
info '' # better spacing
changed.each do |file|
- info "Detected changes in #{strip_filename project, file}"
- end
-
- in_main = main_dirs.any? do |dir|
- changed.any? { |file| file.index(dir) == 0 }
- end
-
- in_test = test_dirs.any? do |dir|
- changed.any? { |file| file.index(dir) == 0 }
+ info "Detected changes in #{file}"
end
- in_res = res_dirs.any? do |dir|
- changed.any? { |file| file.index(dir) == 0 }
+ each_project do |p|
+ # transitively reenable prerequisites
+ reenable = lambda do |t|
+ t = task(t)
+ t.reenable
+ t.prerequisites.each { |c| reenable.call(c) }
+ end
+ reenable.call(p.test.compile)
end
- project.task(:compile).reenable if in_main
- project.task('test:compile').reenable if in_test || in_main
-
successful = true
begin
- project.task(:resources).filter.run if in_res
- project.task(:compile).invoke if in_main
- project.task('test:compile').invoke if in_test || in_main
+ each_project { |p| p.test.compile.invoke }
build_completed(project)
rescue Exception => ex
$stderr.puts $terminal.color(ex.message, :red)
@@ -122,27 +125,20 @@ module Buildr
Buildr.application.build_failed('Compilation failed', project.path_to, ex)
end
- def check_mtime(pattern, old_times)
- times = {}
+ def changed(new_times, old_times)
changed = []
-
- Dir.glob pattern do |fname|
- times[fname] = File.mtime fname
- if old_times[fname].nil? || old_times[fname] < File.mtime(fname)
+ new_times.each do |(fname,newtime)|
+ if old_times[fname].nil? || old_times[fname] < newtime
changed << fname
end
end
# detect deletion (slower than it could be)
old_times.each_key do |fname|
- changed << fname unless times.has_key? fname
+ changed << fname unless new_times.has_key? fname
end
- [times, changed]
- end
-
- def strip_filename(project, name)
- name.gsub project.base_dir + File::SEPARATOR, ''
+ changed
end
end
@@ -157,7 +153,6 @@ module Buildr
before_define do |project|
cc = CCTask.define_task :cc
cc.send :associate_with, project
- project.recursive_task(:cc)
end
def cc
Modified: buildr/trunk/spec/core/cc_spec.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/spec/core/cc_spec.rb?rev=1040014&r1=1040013&r2=1040014&view=diff
==============================================================================
--- buildr/trunk/spec/core/cc_spec.rb (original)
+++ buildr/trunk/spec/core/cc_spec.rb Mon Nov 29 05:33:39 2010
@@ -17,50 +17,38 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helpers'))
module CCHelper
- def setup_cc
- project = define('foo')
- cc = project.cc
- project.stub!(:task).with(:cc).and_return(cc)
-
- compile = mock 'compile'
- project.stub!(:task).with(:compile).and_return(compile)
-
- test_compile = mock 'test:compile'
- project.stub!(:task).with('test:compile').and_return(test_compile)
-
- filter = mock('resources').tap do |resources|
- project.stub!(:task).with(:resources).and_return(resources)
-
- back = mock 'filter'
- resources.stub!(:filter).and_return(back)
-
- back
+ # monkey-patch task instance to track number of times it is run
+ def instrument_task(task)
+ class << task
+ attr_accessor :run_count
end
-
- sources
- tests
- resources
-
- [ project, compile, test_compile, filter ]
+ task.run_count = 0
+ task.enhance do |t|
+ t.run_count += 1
+ end
+ task
end
- def sources
- @sources ||= ['Test1.java', 'Test2.java'].map { |f| File.join('src/main/java/thepackage', f) }.
- each { |src| write src, "package thepackage; class #{src.pathmap('%n')} {}" }
+ def instrument_project(project)
+ instrument_task project.compile
+ instrument_task project.test.compile
+ instrument_task project.resources
+ project
end
- def tests
- @tests ||= ['Test1.java', 'Test2.java'].map { |f| File.join('src/test/java/thepackage', f) }.
- each { |src| write src, "package thepackage; class #{src.pathmap('%n')} {}" }
+ def define_foo()
+ @foo = define('foo')
+ instrument_project @foo
+ @foo
end
- def resources
- @resources ||= ['Test1.html', 'Test2.html'].map { |f| File.join('src/main/resources', f) }.
- each { |src| write src, '<html></html>' }
+ def foo()
+ @foo
end
end
+
describe Buildr::CCTask do
include CCHelper
@@ -69,101 +57,168 @@ describe Buildr::CCTask do
end
it 'should compile and test:compile on initial start' do
- project, compile, test_compile, filter = setup_cc
+ ['Test1.java', 'Test2.java'].map { |f| File.join('src/main/java/thepackage', f) }.
+ each { |src| write src, "package thepackage; class #{src.pathmap('%n')} {}" }
- compile.should_receive :invoke
- test_compile.should_receive :invoke
- filter.should_not_receive :run
+ ['Test1.java', 'Test2.java'].map { |f| File.join('src/test/java/thepackage', f) }.
+ each { |src| write src, "package thepackage; class #{src.pathmap('%n')} {}" }
+
+ ['Test1.html', 'Test2.html'].map { |f| File.join('src/main/resources', f) }.
+ each { |src| write src, '<html></html>' }
+
+ define_foo()
thread = Thread.new do
- project.cc.invoke
+ foo.cc.invoke
end
- sleep 0.5
+ sleep 1
+
+ foo.compile.run_count.should == 1
+ foo.test.compile.run_count.should == 1
thread.exit
end
it 'should detect a file change' do |spec|
-
+ write 'src/main/resources/my.properties', "# comment"
write 'src/main/java/Example.java', "public class Example {}"
write 'src/test/java/ExampleTest.java', "public class ExampleTest {}"
-
- project = define("foo")
- compile = project.compile
- test_compile = project.test.compile
- filter = project.resources
-
- # After first period:
- compile.should_receive :invoke
- test_compile.should_receive :invoke
- filter.should_not_receive :run
-
+
+ define_foo
+
thread = Thread.new do
begin
- project.cc.invoke
+ foo.cc.invoke
rescue => e
p "unexpected exception #{e.inspect}"
p e.backtrace.join("\n").inspect
end
end
-
- sleep 0.5
-
- compile.should_receive :reenable
- compile.should_receive :invoke
- test_compile.should_receive :reenable
- test_compile.should_receive :invoke
+ sleep 1
+
+ foo.compile.run_count.should == 1
+ foo.test.compile.run_count.should == 1
+ foo.resources.run_count.should == 1
- filter.should_not_receive :run
-
sleep 1 # Wait one sec as the timestamp needs to be different.
+
touch File.join(Dir.pwd, 'src/main/java/Example.java')
- sleep 0.3# Wait one standard delay and half
-
+
+ sleep 1
+
+ foo.compile.run_count.should == 2
+ foo.test.compile.run_count.should == 2
+ foo.resources.run_count.should == 2
+
thread.exit
end
-
-
+
it 'should support subprojects' do |spec|
-
write 'foo/src/main/java/Example.java', "public class Example {}"
write 'foo/src/test/java/ExampleTest.java', "public class ExampleTest {}"
-
+
define 'container' do
define('foo')
end
-
- project = project("container:foo")
- compile = project.compile
- test_compile = project.test.compile
- filter = project.resources
-
- # After first period:
- compile.should_receive :invoke
- test_compile.should_receive :invoke
- filter.should_not_receive :run
-
+
+ foo = instrument_project project("container:foo")
+
thread = Thread.new do
- project("container").cc.invoke
+ begin
+ project("container").cc.invoke
+ rescue => e
+ p "unexpected exception #{e.inspect}"
+ p e.backtrace.join("\n").inspect
+ end
end
-
- sleep 0.5
-
- # After we changed the file:
- compile.should_receive :reenable
- compile.should_receive :invoke
-
- test_compile.should_receive :reenable
- test_compile.should_receive :invoke
-
- filter.should_not_receive :run
-
- sleep 1 # Wait one sec as the timestamp needs to be different.
+
+ sleep 1
+
+ foo.compile.run_count.should == 1
+ foo.test.compile.run_count.should == 1
+ foo.resources.run_count.should == 1
+
+ file("foo/target/classes/Example.class").should exist
+ tstamp = File.mtime("foo/target/classes/Example.class")
touch File.join(Dir.pwd, 'foo/src/main/java/Example.java')
- sleep 0.3 # Wait one standard delay and half
-
+
+ sleep 1
+
+ foo.compile.run_count.should == 2
+ foo.test.compile.run_count.should == 2
+ foo.resources.run_count.should == 2
+ File.mtime("foo/target/classes/Example.class").should_not == tstamp
+
+ thread.exit
+ end
+
+ it 'should support parent and subprojects' do |spec|
+ write 'foo/src/main/java/Example.java', "public class Example {}"
+ write 'foo/src/test/java/ExampleTest.java', "public class ExampleTest {}"
+
+ write 'bar/src/main/java/Example.java', "public class Example {}"
+ write 'bar/src/test/java/ExampleTest.java', "public class ExampleTest {}"
+
+ write 'src/main/java/Example.java', "public class Example {}"
+ write 'src/test/java/ExampleTest.java', "public class ExampleTest {}"
+
+ write 'src/main/resources/foo.txt', "content"
+
+ define 'container' do
+ define('foo')
+ define('bar')
+ end
+
+ all = projects("container", "container:foo", "container:bar")
+ all.each { |p| instrument_project(p) }
+
+ thread = Thread.new do
+ begin
+ project("container").cc.invoke
+ rescue => e
+ p "unexpected exception #{e.inspect}"
+ p e.backtrace.join("\n").inspect
+ end
+ end
+
+ sleep 2
+
+ all.each do |p|
+ p.compile.run_count.should == 1
+ p.test.compile.run_count.should == 1
+ p.resources.run_count.should == 1
+ end
+
+ file("foo/target/classes/Example.class").should exist
+ tstamp = File.mtime("foo/target/classes/Example.class")
+
+ touch 'foo/src/main/java/Example.java'
+ p "after touch"
+ sleep 2
+
+ project("container:foo").tap do |p|
+ p.compile.run_count.should == 2
+ p.test.compile.run_count.should == 2
+ p.resources.run_count.should == 2
+ end
+ project("container").tap do |p|
+ p.compile.run_count.should == 1 # not_needed
+ p.test.compile.run_count.should == 1 # not_needed
+ p.resources.run_count.should == 2
+ end
+ File.mtime("foo/target/classes/Example.class").should_not == tstamp
+
+ touch 'src/main/java/Example.java'
+ sleep 2
+
+ project("container").tap do |p|
+ p.compile.run_count.should == 2
+ p.test.compile.run_count.should == 2
+ p.resources.run_count.should == 3
+ end
+
thread.exit
end
end