cxxproject 0.2 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/Rakefile.rb +55 -33
  2. data/bin/cxx +10 -0
  3. data/lib/cxxproject/buildingblocks/binary_library.rb +16 -0
  4. data/lib/cxxproject/buildingblocks/building_block.rb +93 -0
  5. data/lib/cxxproject/buildingblocks/command_line.rb +30 -0
  6. data/lib/cxxproject/buildingblocks/custom_building_block.rb +17 -0
  7. data/lib/cxxproject/buildingblocks/executable.rb +49 -0
  8. data/lib/cxxproject/buildingblocks/has_dependencies_mixin.rb +57 -0
  9. data/lib/cxxproject/buildingblocks/has_libraries_mixin.rb +35 -0
  10. data/lib/cxxproject/buildingblocks/has_sources_mixin.rb +85 -0
  11. data/lib/cxxproject/buildingblocks/makefile.rb +35 -0
  12. data/lib/cxxproject/buildingblocks/module.rb +14 -0
  13. data/lib/cxxproject/buildingblocks/single_source.rb +11 -0
  14. data/lib/cxxproject/buildingblocks/source_library.rb +31 -0
  15. data/lib/cxxproject/extensions/file_ext.rb +47 -0
  16. data/lib/cxxproject/extensions/rake_dirty_ext.rb +30 -0
  17. data/lib/cxxproject/extensions/rake_ext.rb +158 -0
  18. data/lib/cxxproject/extensions/rake_listener_ext.rb +53 -0
  19. data/lib/cxxproject/extensions/stdout_ext.rb +35 -0
  20. data/lib/cxxproject/extensions/string_ext.rb +9 -0
  21. data/lib/cxxproject/task_maker.rb +418 -0
  22. data/lib/cxxproject/testinstanceeval.rb +65 -0
  23. data/lib/cxxproject/toolchain/base.rb +98 -0
  24. data/lib/cxxproject/toolchain/diab.rb +47 -0
  25. data/lib/cxxproject/toolchain/gcc.rb +39 -0
  26. data/lib/cxxproject/toolchain/provider.rb +18 -0
  27. data/lib/cxxproject/torake/compiler.rb +10 -0
  28. data/lib/cxxproject/torake.rb +152 -0
  29. data/lib/cxxproject/utils/dot/building_block_graph_writer.rb +19 -0
  30. data/lib/cxxproject/utils/dot/graph_writer.rb +53 -0
  31. data/lib/cxxproject/utils/dot/task_graph_writer.rb +34 -0
  32. data/lib/cxxproject/utils/ubigraph.rb +237 -0
  33. data/lib/cxxproject/{utils.rb → utils/utils.rb} +19 -1
  34. data/lib/cxxproject.rb +13 -223
  35. data/lib/tools/Rakefile.rb.template +10 -0
  36. data/lib/tools/project.rb.template +6 -0
  37. data/lib/tools/projectWizard.rb +44 -0
  38. data/spec/build_dependencies_spec.rb +169 -0
  39. data/spec/building_block_spec.rb +12 -0
  40. data/spec/dir_spec.rb +13 -0
  41. data/spec/project_path_spec.rb +73 -0
  42. data/spec/rake_listener_ext_spec.rb +25 -0
  43. data/spec/string_spec.rb +11 -0
  44. data/spec/testdata/basic/exe12/project.rb +5 -0
  45. data/spec/testdata/basic/lib1/project.rb +5 -0
  46. data/spec/testdata/basic/lib2/project.rb +8 -0
  47. data/spec/testdata/multiple_levels/libs/lib1/project.rb +5 -0
  48. data/spec/testdata/multiple_levels/libs/lib2/project.rb +19 -0
  49. data/spec/testdata/multiple_levels/mainproject/basic/project.rb +8 -0
  50. data/spec/testdata/onlyOneHeader/Rakefile.rb +4 -0
  51. data/spec/testdata/onlyOneHeader/project.rb +4 -0
  52. metadata +83 -19
  53. data/lib/cxxproject/compiler.rb +0 -80
  54. data/lib/cxxproject/dependencies.rb +0 -41
  55. data/spec/dependencies_spec.rb +0 -19
  56. /data/lib/cxxproject/{gcccompiler.rb → torake/gcccompiler.rb} +0 -0
  57. /data/lib/cxxproject/{osxcompiler.rb → torake/osxcompiler.rb} +0 -0
data/Rakefile.rb CHANGED
@@ -1,47 +1,69 @@
1
1
  require 'rake/gempackagetask'
2
- require 'roodi'
3
- require 'roodi_task'
4
- require 'spec/rake/spectask'
2
+ begin
3
+ require 'roodi'
4
+ require 'roodi_task'
5
+ rescue LoadError # don't bail out when people do not have roodi installed!
6
+ warn "roodi not installed...will not be checked!"
7
+ end
8
+
9
+ begin
10
+ require 'spec/rake/spectask' # old rspec
11
+ rescue LoadError
12
+ begin
13
+ require 'rspec/core/rake_task' # rspec 2.5.x
14
+ rescue LoadError
15
+ warn "spec not installed...will not be checked!"
16
+ end
17
+ end
18
+
19
+
5
20
 
6
21
  desc "Default Task"
7
- task :default => [:package, :roodi]
22
+ task :default => [:install]
8
23
 
9
- PKG_VERSION = '0.2'
10
24
  PKG_FILES = FileList[
11
- 'lib/**/*.rb',
12
- 'Rakefile.rb',
13
- 'spec/**/*.rb'
14
- # 'doc/**/*'
15
- ]
16
-
17
- spec = Gem::Specification.new do |s|
18
- s.name = 'cxxproject'
19
- s.version = PKG_VERSION
20
- s.summary = "Cpp Support for Rake."
21
- s.description = <<-EOF
22
- Some more high level building blocks for cpp projects.
23
- EOF
24
- s.files = PKG_FILES.to_a
25
- s.require_path = 'lib'
26
- s.author = ''
27
- s.email = ''
28
- s.homepage = ''
29
- end
25
+ 'lib/**/*.rb',
26
+ 'lib/tools/**/*.template',
27
+ 'Rakefile.rb',
28
+ 'spec/**/*.rb'
29
+ ]
30
30
 
31
- RoodiTask.new
32
- Rake::GemPackageTask.new(spec) {|pkg|}
31
+ task :gem
32
+ spec = Gem::Specification.load('cxx.gemspec')
33
+ Rake::GemPackageTask.new(spec)
33
34
 
34
- task :gem => [:spec, :roodi]
35
+ if self.class.const_defined?(:RoodiTask) then
36
+ RoodiTask.new 'roodi', PKG_FILES, 'roodi.yml'
37
+ task :gem => [:roodi]
38
+ end
35
39
 
36
- desc "Run all examples"
37
- Spec::Rake::SpecTask.new() do |t|
38
- t.spec_files = FileList['spec/**/*.rb']
40
+ # old rspec
41
+ if self.class.const_defined?(:SpecTask) then
42
+ desc "Run all examples"
43
+ Spec::Rake::SpecTask.new() do |t|
44
+ t.spec_files = FileList['spec/**/*_spec.rb']
45
+ end
46
+ # task :gem => [:spec]
39
47
  end
40
48
 
41
- task :default => [:install]
49
+ # new rspec
50
+ begin # const_defined? did not work?
51
+ desc "Run all examples"
52
+ RSpec::Core::RakeTask.new() do |t|
53
+ puts Dir.glob 'spec/**/*_spec.rb'
54
+ t.pattern = 'spec/**/*_spec.rb'
55
+ end
56
+ # task :gem => [:spec]
57
+ rescue
58
+ end
59
+
60
+ desc 'build gem only'
61
+ task :gem_only do
62
+ sh "gem build cxx.gemspec"
63
+ mv FileList["*.gem"], "pkg"
64
+ end
42
65
 
43
66
  desc "install gem globally"
44
- task :install => :gem do
67
+ task :install => [:gem] do
45
68
  sh "gem install pkg/#{spec.name}-#{spec.version}.gem"
46
69
  end
47
-
data/bin/cxx ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/ruby
2
+ $:.unshift File.join(File.dirname(__FILE__),"..","lib")
3
+
4
+ require 'tools/projectWizard'
5
+
6
+ if ARGV.size < 1
7
+ puts "usage: cxx project-dir"
8
+ exit 0
9
+ end
10
+ prepare_project(ARGV[0])
@@ -0,0 +1,16 @@
1
+ require 'cxxproject/buildingblocks/building_block'
2
+ require 'cxxproject/buildingblocks/has_libraries_mixin'
3
+
4
+ class BinaryLibrary < BuildingBlock
5
+ include HasLibraries
6
+
7
+ def initialize(name)
8
+ super(name)
9
+ libs_to_search << name
10
+ end
11
+
12
+ def get_task_name()
13
+ libs_to_search[0]
14
+ end
15
+
16
+ end
@@ -0,0 +1,93 @@
1
+ require 'cxxproject/buildingblocks/has_dependencies_mixin'
2
+ require 'cxxproject/utils/dot/graph_writer'
3
+
4
+ # stores all defined buildingblocks by name (the name should be unique)
5
+ ALL_BUILDING_BLOCKS = {}
6
+
7
+ class BuildingBlock
8
+ include HasDependencies
9
+
10
+ attr_reader :name
11
+ attr_reader :graph_name
12
+ attr_reader :config_files
13
+ attr_reader :project_dir
14
+ attr_reader :output_dir
15
+ attr_reader :complete_output_dir
16
+
17
+ def set_name(x)
18
+ @name = x
19
+ self
20
+ end
21
+
22
+ def set_tcs(x)
23
+ @tcs = x
24
+ self
25
+ end
26
+
27
+ def has_tcs?
28
+ @tcs != nil
29
+ end
30
+
31
+ def tcs()
32
+ raise "Toolchain settings must be set before!" if @tcs.nil?
33
+ @tcs
34
+ end
35
+
36
+ def set_config_files(x)
37
+ @config_files = x
38
+ self
39
+ end
40
+
41
+ def set_project_dir(x)
42
+ @project_dir = x
43
+ calc_complete_output_dir
44
+ self
45
+ end
46
+
47
+ # if output dir is absolute, -L and -l is used for linker ("linux mode")
48
+ def set_output_dir(x)
49
+ @output_dir = x
50
+ @output_dir_abs = File.is_absolute?(@output_dir)
51
+ calc_complete_output_dir
52
+ self
53
+ end
54
+
55
+ def calc_complete_output_dir
56
+ if @output_dir_abs
57
+ @complete_output_dir = @output_dir
58
+ else
59
+ @complete_output_dir = @project_dir + "/" + @output_dir
60
+ end
61
+ end
62
+
63
+ def set_graph_name(x)
64
+ @graph_name = x
65
+ self
66
+ end
67
+
68
+ def initialize(name)
69
+ @name = name
70
+ @graph_name = name
71
+ @config_files = []
72
+ @project_dir = "."
73
+ @output_dir = "."
74
+ @complete_output_dir = "."
75
+ @tcs = nil
76
+ @output_dir_abs = false
77
+
78
+ begin
79
+ raise "building block already exists: #{name}" if ALL_BUILDING_BLOCKS.include?@name
80
+ ALL_BUILDING_BLOCKS[@name] = self
81
+ rescue Exception => e
82
+ puts e
83
+ end
84
+ end
85
+
86
+ def complete_init()
87
+ end
88
+
89
+ def get_task_name()
90
+ raise "this method must be implemented by decendants"
91
+ end
92
+
93
+ end
@@ -0,0 +1,30 @@
1
+ require 'cxxproject/buildingblocks/building_block'
2
+
3
+ class CommandLine < BuildingBlock
4
+
5
+ def set_command_line(x)
6
+ @line = x
7
+ self
8
+ end
9
+
10
+ def get_command_line
11
+ @line
12
+ end
13
+
14
+ def get_target
15
+ @target
16
+ end
17
+
18
+ @@command_line_num = 0
19
+ def initialize(name)
20
+ super(name)
21
+ @line = name
22
+ @@command_line_num = @@command_line_num + 1
23
+ @num = @@command_line_num
24
+ end
25
+
26
+ def get_task_name()
27
+ "command line (#{@num}): " + get_command_line
28
+ end
29
+
30
+ end
@@ -0,0 +1,17 @@
1
+ require 'cxxproject/buildingblocks/building_block'
2
+
3
+ # todo...
4
+
5
+ class CustomBuildingBlock < BuildingBlock
6
+ attr_reader :custom_command
7
+
8
+ def set_custom_command(c)
9
+ @custom_command = c
10
+ self
11
+ end
12
+
13
+ def get_task_name()
14
+ raise "todo"
15
+ end
16
+
17
+ end
@@ -0,0 +1,49 @@
1
+ require 'cxxproject/buildingblocks/building_block'
2
+ require 'cxxproject/buildingblocks/has_libraries_mixin'
3
+ require 'cxxproject/buildingblocks/has_sources_mixin'
4
+
5
+ class Executable < BuildingBlock
6
+ include HasLibraries
7
+ include HasSources
8
+
9
+ attr_reader :linker_script
10
+ attr_reader :mapfile
11
+ attr_reader :output_file
12
+
13
+ def set_linker_script(x)
14
+ @linker_script = x
15
+ self
16
+ end
17
+
18
+ def set_mapfile(x)
19
+ @mapfile = x
20
+ self
21
+ end
22
+
23
+ # set during creating the task - note: depends on the used tcs
24
+ def set_output_file(x)
25
+ @output_file = x
26
+ self
27
+ end
28
+
29
+ def initialize(name)
30
+ super(name)
31
+ @linker_script = nil
32
+ @mapfile = nil
33
+ end
34
+
35
+ def linker_libs_string
36
+ @linkerString ||= ""
37
+ end
38
+
39
+
40
+ def get_executable_name()
41
+ File.relFromTo(@complete_output_dir + "/" + @name + @tcs[:LINKER][:OUTPUT_ENDING], @project_dir)
42
+ end
43
+
44
+ def get_task_name()
45
+ get_executable_name()
46
+ end
47
+
48
+
49
+ end
@@ -0,0 +1,57 @@
1
+ module HasDependencies
2
+
3
+ def dependencies
4
+ @dependencies ||= []
5
+ end
6
+ def set_dependencies(deps)
7
+ @dependencies = deps.map { |dep| dep.instance_of?(String) ? dep : dep.name }
8
+ self
9
+ end
10
+
11
+ # if set, includes and libs are taken from this array, not from @dependencies.
12
+ # task deps are still taken from @dependencies.
13
+ # use case: circular deps are allowed on "include-level", but not on "task-level".
14
+ def helper_dependencies
15
+ @helper_dependencies ||= []
16
+ end
17
+
18
+ def set_helper_dependencies(deps)
19
+ @helper_dependencies = deps.map { |dep| dep.instance_of?(String) ? dep : dep.name }
20
+ end
21
+
22
+ # will be calculated at the beginning of creating the building block task
23
+ def all_dependencies
24
+ @all_dependencies ||= []
25
+ end
26
+
27
+ # inclusive self!!
28
+ def calc_transitive_dependencies
29
+ deps = [self.name]
30
+ @all_dependencies = get_transitive_dependencies_internal(deps)
31
+ end
32
+
33
+ def get_transitive_dependencies_internal(deps)
34
+ depsToCheck = []
35
+ depList = helper_dependencies.length > 0 ? helper_dependencies : dependencies
36
+ depList.each do |d|
37
+ if not deps.include?d
38
+ deps << d
39
+ depsToCheck << d
40
+ end
41
+ end
42
+
43
+ # two-step needed to keep order of dependencies for includes, lib dirs, etc
44
+ depsToCheck.each do |d|
45
+ begin
46
+ raise "ERROR: while reading config file for #{self.name}: dependent building block \"#{d}\" was specified but not found!" unless ALL_BUILDING_BLOCKS[d]
47
+ ALL_BUILDING_BLOCKS[d].get_transitive_dependencies_internal(deps)
48
+ rescue Exception => e
49
+ puts e
50
+ exit
51
+ end
52
+ end
53
+ deps
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,35 @@
1
+ module HasLibraries
2
+
3
+ def user_libs
4
+ @user_libs ||= []
5
+ end
6
+ def set_user_libs(x)
7
+ @user_libs = x
8
+ self
9
+ end
10
+
11
+ def libs_to_search
12
+ @libs_to_search ||= []
13
+ end
14
+ def set_libs_to_search(x)
15
+ @libs_to_search = x
16
+ self
17
+ end
18
+
19
+ def lib_searchpaths
20
+ @lib_searchpaths ||= []
21
+ end
22
+ def set_lib_searchpaths(x)
23
+ @lib_searchpaths = x
24
+ self
25
+ end
26
+
27
+ def libs_with_path
28
+ @libs_with_path ||= []
29
+ end
30
+ def set_libs_with_path(x)
31
+ @libs_with_path = x
32
+ self
33
+ end
34
+
35
+ end
@@ -0,0 +1,85 @@
1
+ module HasSources
2
+
3
+ def sources
4
+ @sources ||= []
5
+ end
6
+ def set_sources(x)
7
+ @sources = x
8
+ self
9
+ end
10
+
11
+ def includes
12
+ @includes ||= []
13
+ end
14
+ def set_includes(x)
15
+ @includes = x
16
+ self
17
+ end
18
+
19
+ # used when a source file shall have different tcs than the project default
20
+ def tcs4source
21
+ @tcs4source ||= {}
22
+ end
23
+ def set_tcs4source(x)
24
+ @tcs4source = x
25
+ self
26
+ end
27
+
28
+ def include_string(type)
29
+ @include_string[type] ||= ""
30
+ end
31
+
32
+ def define_string(type)
33
+ @define_string[type] ||= ""
34
+ end
35
+
36
+ def calc_compiler_strings()
37
+ @include_string = {}
38
+ @define_string = {}
39
+
40
+ @incArray = []
41
+ all_dependencies.each do |e|
42
+ d = ALL_BUILDING_BLOCKS[e]
43
+ next if not HasSources === d
44
+ if d.includes.length == 0
45
+ @incArray << File.relFromTo("include", d.project_dir)
46
+ else
47
+ d.includes.each { |k| @incArray << File.relFromTo(k, d.project_dir) }
48
+ end
49
+ end
50
+
51
+ [:CPP, :C, :ASM].each do |type|
52
+ @include_string[type] = get_include_string(@tcs, type)
53
+ @define_string[type] = get_define_string(@tcs, type)
54
+ end
55
+ end
56
+
57
+ def get_include_string(tcs, type)
58
+ @incArray.uniq.map!{|k| "#{tcs[:COMPILER][type][:INCLUDE_PATH_FLAG]}#{k}"}.join(" ")
59
+ end
60
+
61
+ def get_define_string(tcs, type)
62
+ @tcs[:COMPILER][type][:DEFINES].map {|k| "#{tcs[:COMPILER][type][:DEFINE_FLAG]}#{k}"}.join(" ")
63
+ end
64
+
65
+ def get_object_file(source)
66
+ File.relFromTo(source, @complete_output_dir + (@output_dir_abs ? ("/" + @name) : "") ) + ".o"
67
+ end
68
+
69
+ def get_dep_file(object)
70
+ object + ".d"
71
+ end
72
+
73
+ def get_source_type(source)
74
+ ex = File.extname(source)
75
+ [:CPP, :C, :ASM].each do |t|
76
+ return t if tcs[:COMPILER][t][:SOURCE_FILE_ENDINGS].include?(ex)
77
+ end
78
+ nil
79
+ end
80
+
81
+ def get_sources_task_name
82
+ "Sources of #{name}"
83
+ end
84
+
85
+ end
@@ -0,0 +1,35 @@
1
+ require 'cxxproject/buildingblocks/building_block'
2
+ require 'cxxproject/buildingblocks/has_libraries_mixin'
3
+
4
+ class Makefile < BuildingBlock
5
+ include HasLibraries
6
+
7
+ def set_target(x)
8
+ @target = x
9
+ self
10
+ end
11
+
12
+ def set_makefile(x)
13
+ @makefile = x
14
+ self
15
+ end
16
+
17
+ def get_makefile
18
+ File.relFromTo(@makefile, @project_dir)
19
+ end
20
+
21
+ def get_target
22
+ @target
23
+ end
24
+
25
+ def initialize(name)
26
+ super(name)
27
+ @target = "all"
28
+ @makefile = nil
29
+ end
30
+
31
+ def get_task_name()
32
+ get_makefile+"_"+get_target
33
+ end
34
+
35
+ end
@@ -0,0 +1,14 @@
1
+ require 'cxxproject/buildingblocks/building_block'
2
+ require 'cxxproject/buildingblocks/has_libraries_mixin'
3
+ require 'cxxproject/buildingblocks/has_sources_mixin'
4
+
5
+ # can be used as wrapper for other tasks
6
+ class ModuleBuildingBlock < BuildingBlock
7
+ include HasLibraries
8
+ include HasSources
9
+
10
+ def get_task_name()
11
+ name
12
+ end
13
+
14
+ end
@@ -0,0 +1,11 @@
1
+ require 'cxxproject/buildingblocks/building_block'
2
+ require 'cxxproject/buildingblocks/has_sources_mixin'
3
+
4
+ class SingleSource < BuildingBlock
5
+ include HasSources
6
+
7
+ def get_task_name()
8
+ get_sources_task_name
9
+ end
10
+
11
+ end
@@ -0,0 +1,31 @@
1
+ require 'cxxproject/buildingblocks/building_block'
2
+ require 'cxxproject/buildingblocks/has_libraries_mixin'
3
+ require 'cxxproject/buildingblocks/has_sources_mixin'
4
+
5
+ class SourceLibrary < BuildingBlock
6
+ include HasLibraries
7
+ include HasSources
8
+
9
+ def initialize(name)
10
+ super(name)
11
+ @search_for_lib = false # false: use libs_with_path ("Eclipse mode"), true: use libs_to_search and lib_searchpaths ("Linux mode")
12
+ end
13
+
14
+ def complete_init()
15
+ if @output_dir_abs
16
+ libs_to_search << @name
17
+ lib_searchpaths << @output_dir
18
+ else
19
+ libs_with_path << File.join(@output_dir,"lib#{@name}.a")
20
+ end
21
+ end
22
+
23
+ def get_archive_name()
24
+ File.relFromTo(@complete_output_dir + "/lib" + @name + ".a", @project_dir)
25
+ end
26
+
27
+ def get_task_name()
28
+ get_archive_name
29
+ end
30
+
31
+ end
@@ -0,0 +1,47 @@
1
+ class File
2
+
3
+ @@oldRuby = RUBY_VERSION[0..2] == "1.8"
4
+
5
+ def self.is_absolute?(filename)
6
+ if @@oldRuby
7
+ filename[0] == 47 or filename[1] == 58 # 47 = /, 58 = :
8
+ else
9
+ filename[0] == '/' or filename[1] == ':'
10
+ end
11
+ end
12
+
13
+ # filename relative to nowRelToThisDir (if absolute, nowRelToThisDir can be nil)
14
+ # return: filename which is relative to thenRelToThisDir
15
+ def self.relFromTo(filename,nowRelToThisDir,thenRelToThisDirOrg = Dir.pwd)
16
+
17
+ absFilename = filename
18
+ thenRelToThisDir = thenRelToThisDirOrg + "/"
19
+
20
+ if not File.is_absolute?(filename)
21
+ absFilename = File.expand_path(nowRelToThisDir + "/" + filename)
22
+ end
23
+
24
+ maxLength = thenRelToThisDir.length > absFilename.length ? absFilename.length : thenRelToThisDir.length
25
+
26
+ lastEqDir = -1
27
+ for i in 0..maxLength-1
28
+ break if thenRelToThisDir[i] != absFilename[i]
29
+ end
30
+ lastEqDir = thenRelToThisDir.rindex("/",i)
31
+
32
+ if lastEqDir
33
+ dotdot = thenRelToThisDir[lastEqDir+1..-1].split("/").length
34
+ res = ""
35
+ dotdot.times { res << "../" }
36
+ res << absFilename[lastEqDir+1..-1]
37
+ return absFilename if res.length > absFilename.length # avoid something like "../../../../../../usr/local/lib"
38
+ return res
39
+ else
40
+ return absFilename
41
+ end
42
+
43
+
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,30 @@
1
+ require 'rake'
2
+ module Rake
3
+ class Task
4
+ # return true if this or one of the prerequisites is dirty
5
+ def dirty?
6
+ return calc_dirty_for_prerequsites if apply?(name)
7
+
8
+ if needed?
9
+ return true
10
+ end
11
+ return calc_dirty_for_prerequsites
12
+ end
13
+
14
+ def calc_dirty_for_prerequsites
15
+ res = prerequisites.find do |p|
16
+ t = Task[p]
17
+ if t != nil
18
+ if t.dirty?
19
+ true
20
+ else
21
+ false
22
+ end
23
+ else
24
+ false
25
+ end
26
+ end
27
+ return res != nil
28
+ end
29
+ end
30
+ end