Permalink
Browse files

Added Task type to better handle needed? checks.

OnceTask is no longer needed once it has been run.
Microsecond tasks avoid 1s time resolution limitation on some file
systems (e.g. HFS+).
  • Loading branch information...
1 parent 8e68993 commit 31a9f21aad1f0510364406d77d9917b3fce4f805 @joeyates committed Apr 14, 2012
View
@@ -5,6 +5,7 @@
require 'rake/path'
require 'rake/local_config'
require 'rake/file_task_alias'
+require 'rake/microsecond'
require 'rake/once_task'
require 'compiler'
@@ -265,7 +266,7 @@ def define_default
end
def define
- task :environment do
+ once_task :environment do
logger.level = Logger::DEBUG if ENV[ 'DEBUG' ]
end
@@ -281,7 +282,7 @@ def define
FileTaskAlias.define_task( :build, @target )
desc "Build '#{ target_basename }'"
- file @target => [ scoped_task( :environment ),
+ microsecond_file @target => [ scoped_task( :environment ),
scoped_task( :compile ),
*@target_prerequisites ] do | t |
shell "rm -f #{ t.name }"
@@ -294,7 +295,7 @@ def define
desc "Compile all sources"
# Only import dependencies when we're compiling
# otherwise makedepend gets run on e.g. 'rake -T'
- task :compile => [ scoped_task( :environment ),
+ once_task :compile => [ scoped_task( :environment ),
@makedepend_file,
scoped_task( :load_makedepend ),
*object_files ]
@@ -303,7 +304,7 @@ def define
define_compile_task( src )
end
- directory @objects_path
+ microsecond_directory @objects_path
file scoped_task( local_config ) do
@logger.add( Logger::DEBUG, "Creating file '#{ local_config }'" )
@@ -313,7 +314,7 @@ def define
config.save
end
- file @makedepend_file => [ scoped_task( :load_local_config ),
+ microsecond_file @makedepend_file => [ scoped_task( :load_local_config ),
scoped_task( :missing_headers ),
@objects_path,
*project_files ] do
@@ -328,7 +329,7 @@ def define
@include_paths += Rake::Path.expand_all_with_root( config.include_paths, @rakefile_path )
end
- task :missing_headers => [ *generated_headers ] do
+ once_task scoped_task( :missing_headers ) => [ *generated_headers ] do
missing_headers
end
@@ -599,6 +600,7 @@ def object_files
def project_files
source_files + header_files
end
+ public :project_files
def file_list( files, delimiter = ' ' )
files.join( delimiter )
View
@@ -0,0 +1,70 @@
+require 'rubygems' if RUBY_VERSION < '1.9'
+require 'rake/tasklib'
+require 'fileutils'
+
+module Rake
+
+ module Microsecond
+ # Compensate for file systems with 1s resolution
+
+ class FileTask < Task
+
+ attr_accessor :timestamp
+
+ def self.define_task( *args, &block )
+ task = super( *args, &block )
+ task.timestamp = nil
+ task
+ end
+
+ def needed?
+ return true if ! File.exist?( self.name )
+ @timestamp = File.stat( self.name ).mtime if @timestamp.nil?
+ return self.prerequisites.any? { | n | ! application[n].timestamp.nil? && application[n].timestamp > @timestamp }
+ end
+
+ def execute(*args)
+ @timestamp = Time.now
+ super(*args)
+ end
+
+ end
+
+ class DirectoryTask < Task
+
+ include FileUtils
+
+ attr_accessor :timestamp
+
+ def self.define_task( *args, &block )
+ task = super( *args, &block )
+ task.timestamp = nil
+ task
+ end
+
+ def needed?
+ exists = File.directory?( self.name )
+ @timestamp = File.stat( self.name ).mtime if exists
+ ! exists
+ end
+
+ def execute(*args)
+ mkdir_p self.name, :verbose => false
+ @timestamp = Time.now
+ super(*args)
+ end
+
+ end
+
+ end
+
+end
+
+def microsecond_file(*args, &block)
+ Rake::Microsecond::FileTask.define_task(*args, &block)
+end
+
+def microsecond_directory(*args, &block)
+ Rake::Microsecond::DirectoryTask.define_task(*args, &block)
+end
+
View
@@ -7,14 +7,17 @@ module Rake
class OnceTask < Task
attr_accessor :invoked
+ attr_accessor :timestamp
def self.define_task( *args, &block )
task = super( *args, &block )
+ task.timestamp = nil
task.invoked = false
task
end
def execute(*args)
+ @timestamp = Time.now
@invoked = true
super(*args)
end
View
@@ -77,8 +77,8 @@
end
it 'creates the correct tasks' do
- expected_tasks = expected_tasks( [ @project.target ], 'my_namespace' )
- missing_tasks = expected_tasks - task_names
+ expected = expected_tasks( scoped_tasks( [ @project.target ], 'my_namespace' ), 'my_namespace' )
+ missing_tasks = expected - task_names
missing_tasks.should == []
end
View
@@ -1,27 +1,141 @@
load File.dirname(__FILE__) + '/spec_helper.rb'
+require 'fileutils'
describe 'the dependencies system' do
include RakeBuilderHelper
+ include FileUtils
before( :each ) do
Rake::Task.clear
@project = cpp_task( :executable )
Rake::Task[ 'clean' ].execute
+ rm_f @project.local_config, :verbose => false
end
after( :each ) do
Rake::Task[ 'clean' ].execute
end
- it 'says the target is up to date, if nothing changes' do
- Rake::Task[ 'build' ].invoke
- Rake::Task[ @project.target ].needed?.should_not be_true
+ context 'objects_path' do
+
+ it 'should not be needed after being called' do
+ Rake::Task[ @project.objects_path ].invoke
+
+ Rake::Task[ @project.objects_path ].needed?.should be_false
+ end
+
end
- it 'says the build is up to date, if nothing changes' do
- Rake::Task[ 'build' ].invoke
- Rake::Task[ 'build' ].needed?.should be_false
+ context 'missing_headers' do
+
+ it 'should not be needed after being invoked' do
+ Rake::Task[ 'missing_headers' ].needed?.should be_true
+
+ Rake::Task[ 'missing_headers' ].invoke
+
+ Rake::Task[ 'missing_headers' ].needed?.should be_false
+ end
+
+ end
+
+ context 'makedepend_file' do
+
+ it 'should create the makedepend_file' do
+ exist?( @project.makedepend_file ).should be_false
+
+ Rake::Task[ @project.makedepend_file ].invoke
+
+ exist?( @project.makedepend_file ).should be_true
+ end
+
+ it 'should not be older than its prerequisites' do
+ t = Rake::Task[ @project.makedepend_file ]
+
+ t.invoke
+
+ stamp = t.timestamp
+ t.prerequisites.any? { |n| t.application[n].timestamp > stamp }.should be_false
+ end
+
+ it 'should have all prerequisites satisfied' do
+ t = Rake::Task[ @project.makedepend_file ]
+
+ t.invoke
+
+ t.prerequisites.any? { |n| t.application[n].needed? }.should be_false
+ end
+
+ it 'should not say the makedepend_file is needed' do
+ t = Rake::Task[ @project.makedepend_file ]
+
+ t.needed?.should be_true
+
+ t.invoke
+
+ t.needed?.should be_false
+ end
+
+ end
+
+ context 'build' do
+
+ before :each do
+ @task = Rake::Task[ 'build' ]
+ end
+
+ it 'should have all prerequisites satisfied' do
+ @task.invoke
+
+ @task.prerequisites.any? { |n| @task.application[n].needed? }.should be_false
+ end
+
+ it 'should create the makedepend_file' do
+ exist?( @project.makedepend_file ).should be_false
+
+ Rake::Task[ 'build' ].invoke
+
+ exist?( @project.makedepend_file ).should be_true
+ end
+
+ it 'should create the target' do
+ Rake::Task[ 'build' ].invoke
+
+ exist?( @project.target ).should be_true
+ end
+
+ it 'should say the compile task is up to date' do
+ Rake::Task[ 'build' ].invoke
+
+ Rake::Task[ 'compile' ].needed?.should be_false
+ end
+
+ it 'should say the build is up to date' do
+ Rake::Task[ 'build' ].invoke
+
+ Rake::Task[ 'build' ].needed?.should be_false
+ end
+
+ it 'should say the makedepend_file is up to date' do
+ exist?( @project.makedepend_file ).should be_false
+
+ isolating_seconds do
+ Rake::Task[ 'build' ].invoke
+ end
+
+ Rake::Task[ @project.makedepend_file ].needed?.should be_false
+ end
+
+ it 'should say the target is up to date' do
+ Rake::Task[ 'build' ].invoke
+
+ target_mtime = File.stat( @project.target ).mtime
+ @project.target_prerequisites.each do | prerequisite |
+ File.stat( prerequisite ).mtime.should be < target_mtime
+ end
+ Rake::Task[ @project.target ].needed?.should be_false
+ end
+
end
it 'doesn\'t recompile objects, if nothing changes' do
@@ -47,7 +161,7 @@
end
end
- it 'recompiles source files, if header dependencies' do
+ it 'recompiles source files, if header dependencies are more recent' do
header_file_path = Rake::Path.expand_with_root( 'cpp_project/main.h', SPEC_PATH )
object_file_path = Rake::Path.expand_with_root( 'main.o', SPEC_PATH )
isolating_seconds do
@@ -82,19 +196,25 @@
Rake::Task[ @project.target ].prerequisites.include?( @project.rakefile ).should be_true
end
- # In our case this spec file is the spec_helper
- # i.e., the file that calls Rake::Builder.new
it 'should indicate the target is out of date, if the Rakefile is newer' do
Rake::Task[ 'build' ].invoke
+
Rake::Task[ @project.target ].needed?.should be_false
+
+ Rake::Task.clear
+ @project = cpp_task( :executable )
touching_temporarily( @project.target, File.mtime( @project.rakefile ) - 1 ) do
Rake::Task[ @project.target ].needed?.should be_true
end
end
it 'should indicate that a build is needed if the Rakefile changes' do
Rake::Task[ 'build' ].invoke
+
Rake::Task[ 'build' ].needed?.should be_false
+
+ Rake::Task.clear
+ @project = cpp_task( :executable )
touching_temporarily( @project.target, File.mtime( @project.rakefile ) - 1 ) do
Rake::Task[ 'build' ].needed?.should be_true
end
Oops, something went wrong.

0 comments on commit 31a9f21

Please sign in to comment.