rake 0.8.7 → 0.9.0.beta.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rake might be problematic. Click here for more details.

Files changed (122) hide show
  1. data/.gemtest +0 -0
  2. data/CHANGES +77 -9
  3. data/{README → README.rdoc} +14 -10
  4. data/Rakefile +113 -110
  5. data/TODO +1 -1
  6. data/doc/command_line_usage.rdoc +18 -6
  7. data/doc/glossary.rdoc +2 -2
  8. data/doc/jamis.rb +2 -2
  9. data/doc/proto_rake.rdoc +22 -22
  10. data/doc/rake.1.gz +0 -0
  11. data/doc/rakefile.rdoc +60 -28
  12. data/doc/rational.rdoc +6 -6
  13. data/doc/release_notes/rake-0.4.15.rdoc +1 -1
  14. data/doc/release_notes/rake-0.5.0.rdoc +1 -1
  15. data/doc/release_notes/rake-0.7.0.rdoc +1 -1
  16. data/doc/release_notes/rake-0.7.2.rdoc +3 -3
  17. data/doc/release_notes/rake-0.7.3.rdoc +2 -2
  18. data/doc/release_notes/rake-0.8.0.rdoc +1 -1
  19. data/doc/release_notes/rake-0.8.2.rdoc +3 -3
  20. data/doc/release_notes/rake-0.8.3.rdoc +2 -2
  21. data/doc/release_notes/rake-0.8.4.rdoc +1 -1
  22. data/doc/release_notes/rake-0.8.5.rdoc +1 -1
  23. data/doc/release_notes/rake-0.8.6.rdoc +1 -1
  24. data/doc/release_notes/rake-0.8.7.rdoc +1 -1
  25. data/install.rb +14 -12
  26. data/lib/rake.rb +28 -2470
  27. data/lib/rake/alt_system.rb +7 -6
  28. data/lib/rake/application.rb +585 -0
  29. data/lib/rake/classic_namespace.rb +1 -0
  30. data/lib/rake/clean.rb +14 -14
  31. data/lib/rake/cloneable.rb +25 -0
  32. data/lib/rake/contrib/compositepublisher.rb +2 -5
  33. data/lib/rake/contrib/ftptools.rb +5 -8
  34. data/lib/rake/contrib/publisher.rb +2 -8
  35. data/lib/rake/contrib/rubyforgepublisher.rb +2 -4
  36. data/lib/rake/contrib/sshpublisher.rb +4 -6
  37. data/lib/rake/contrib/sys.rb +7 -25
  38. data/lib/rake/default_loader.rb +10 -0
  39. data/lib/rake/dsl.rb +2 -0
  40. data/lib/rake/dsl_definition.rb +146 -0
  41. data/lib/rake/early_time.rb +18 -0
  42. data/lib/rake/environment.rb +40 -0
  43. data/lib/rake/ext/core.rb +27 -0
  44. data/lib/rake/ext/module.rb +39 -0
  45. data/lib/rake/ext/string.rb +167 -0
  46. data/lib/rake/ext/time.rb +14 -0
  47. data/lib/rake/file_creation_task.rb +24 -0
  48. data/lib/rake/file_list.rb +403 -0
  49. data/lib/rake/file_task.rb +47 -0
  50. data/lib/rake/file_utils.rb +112 -0
  51. data/lib/rake/file_utils_ext.rb +132 -0
  52. data/lib/rake/gempackagetask.rb +6 -90
  53. data/lib/rake/invocation_chain.rb +51 -0
  54. data/lib/rake/invocation_exception_mixin.rb +16 -0
  55. data/lib/rake/loaders/makefile.rb +13 -15
  56. data/lib/rake/multi_task.rb +16 -0
  57. data/lib/rake/name_space.rb +25 -0
  58. data/lib/rake/packagetask.rb +13 -12
  59. data/lib/rake/pathmap.rb +1 -0
  60. data/lib/rake/pseudo_status.rb +24 -0
  61. data/lib/rake/rake_module.rb +25 -0
  62. data/lib/rake/rake_test_loader.rb +10 -2
  63. data/lib/rake/rdoctask.rb +211 -190
  64. data/lib/rake/ruby182_test_unit_fix.rb +9 -7
  65. data/lib/rake/rule_recursion_overflow_error.rb +20 -0
  66. data/lib/rake/runtest.rb +4 -6
  67. data/lib/rake/task.rb +327 -0
  68. data/lib/rake/task_argument_error.rb +7 -0
  69. data/lib/rake/task_arguments.rb +74 -0
  70. data/lib/rake/task_manager.rb +329 -0
  71. data/lib/rake/tasklib.rb +1 -2
  72. data/lib/rake/testtask.rb +51 -26
  73. data/lib/rake/version.rb +12 -0
  74. data/lib/rake/win32.rb +4 -4
  75. data/test/contrib/test_sys.rb +7 -30
  76. data/test/data/comments/Rakefile +18 -0
  77. data/test/data/default/Rakefile +1 -1
  78. data/test/data/dryrun/Rakefile +1 -1
  79. data/test/data/file_creation_task/Rakefile +1 -1
  80. data/test/data/namespace/Rakefile +9 -0
  81. data/test/data/rakelib/test1.rb +4 -2
  82. data/test/data/verbose/Rakefile +34 -0
  83. data/test/functional/functional_test.rb +25 -0
  84. data/test/{session_functional.rb → functional/session_based_tests.rb} +134 -23
  85. data/test/in_environment.rb +6 -4
  86. data/test/{test_application.rb → lib/application_test.rb} +277 -136
  87. data/test/{test_clean.rb → lib/clean_test.rb} +1 -0
  88. data/test/{test_definitions.rb → lib/definitions_test.rb} +2 -2
  89. data/test/lib/dsl_test.rb +52 -0
  90. data/test/{test_earlytime.rb → lib/earlytime_test.rb} +1 -2
  91. data/test/lib/environment_test.rb +18 -0
  92. data/test/{test_extension.rb → lib/extension_test.rb} +2 -2
  93. data/test/{test_file_creation_task.rb → lib/file_creation_task_test.rb} +0 -0
  94. data/test/{test_file_task.rb → lib/file_task_test.rb} +3 -3
  95. data/test/{test_filelist.rb → lib/filelist_test.rb} +28 -24
  96. data/test/{test_fileutils.rb → lib/fileutils_test.rb} +26 -21
  97. data/test/{test_ftp.rb → lib/ftp_test.rb} +0 -0
  98. data/test/{test_invocation_chain.rb → lib/invocation_chain_test.rb} +0 -0
  99. data/test/{test_makefile_loader.rb → lib/makefile_loader_test.rb} +0 -0
  100. data/test/{test_multitask.rb → lib/multitask_test.rb} +14 -6
  101. data/test/{test_namespace.rb → lib/namespace_test.rb} +0 -0
  102. data/test/lib/package_task_test.rb +82 -0
  103. data/test/{test_pathmap.rb → lib/pathmap_test.rb} +3 -2
  104. data/test/{test_pseudo_status.rb → lib/pseudo_status_test.rb} +0 -0
  105. data/test/{test_rake.rb → lib/rake_test.rb} +1 -1
  106. data/test/{test_rdoc_task.rb → lib/rdoc_task_test.rb} +19 -23
  107. data/test/{test_require.rb → lib/require_test.rb} +8 -2
  108. data/test/{test_rules.rb → lib/rules_test.rb} +1 -2
  109. data/test/{test_task_arguments.rb → lib/task_arguments_test.rb} +5 -5
  110. data/test/{test_task_manager.rb → lib/task_manager_test.rb} +5 -5
  111. data/test/{test_tasks.rb → lib/task_test.rb} +69 -4
  112. data/test/{test_tasklib.rb → lib/tasklib_test.rb} +0 -0
  113. data/test/{test_test_task.rb → lib/test_task_test.rb} +3 -3
  114. data/test/lib/testtask_test.rb +49 -0
  115. data/test/{test_top_level_functions.rb → lib/top_level_functions_test.rb} +3 -3
  116. data/test/{test_win32.rb → lib/win32_test.rb} +19 -0
  117. data/test/rake_test_setup.rb +4 -9
  118. data/test/ruby_version_test.rb +3 -0
  119. data/test/test_helper.rb +12 -0
  120. metadata +100 -44
  121. data/test/functional.rb +0 -15
  122. data/test/test_package_task.rb +0 -118
@@ -0,0 +1,18 @@
1
+ module Rake
2
+
3
+ # EarlyTime is a fake timestamp that occurs _before_ any other time value.
4
+ class EarlyTime
5
+ include Comparable
6
+ include Singleton
7
+
8
+ def <=>(other)
9
+ -1
10
+ end
11
+
12
+ def to_s
13
+ "<EARLY TIME>"
14
+ end
15
+ end
16
+
17
+ EARLY = EarlyTime.instance
18
+ end
@@ -0,0 +1,40 @@
1
+ require 'rake/dsl_definition'
2
+
3
+ module Rake
4
+
5
+ # Rakefile are evaluated in the Rake::Environment module space. Top
6
+ # level rake functions (e.g. :task, :file) are available in this
7
+ # environment.
8
+ module Environment
9
+ extend Rake::DSL
10
+
11
+ class << self
12
+ # Load a rakefile from the given path. The Rakefile is loaded
13
+ # in an environment that includes the Rake DSL methods.
14
+ def load_rakefile(rakefile_path)
15
+ rakefile = open(rakefile_path) { |f| f.read }
16
+ load_string(rakefile, rakefile_path)
17
+ end
18
+
19
+ # Load a string of code in the Rake DSL environment. If the
20
+ # string comes from a file, include the file path so that proper
21
+ # line numbers references may be retained.
22
+ def load_string(code, file_name=nil)
23
+ module_eval(code, file_name || "(eval)")
24
+ end
25
+
26
+ # Run a block of code in the Rake DSL environment.
27
+ def run(&block)
28
+ module_eval(&block)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Run the code block in an environment including the Rake DSL
34
+ # commands.
35
+ def DSL.environment(&block)
36
+ Rake::Environment.run(&block)
37
+ end
38
+ end
39
+
40
+
@@ -0,0 +1,27 @@
1
+ ######################################################################
2
+ # Core extension library
3
+ #
4
+ class Module
5
+ # Check for an existing method in the current class before extending. IF
6
+ # the method already exists, then a warning is printed and the extension is
7
+ # not added. Otherwise the block is yielded and any definitions in the
8
+ # block will take effect.
9
+ #
10
+ # Usage:
11
+ #
12
+ # class String
13
+ # rake_extension("xyz") do
14
+ # def xyz
15
+ # ...
16
+ # end
17
+ # end
18
+ # end
19
+ #
20
+ def rake_extension(method)
21
+ if method_defined?(method)
22
+ $stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists"
23
+ else
24
+ yield
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ require 'rake/ext/core'
2
+ require 'rake/task'
3
+ require 'rake/file_task'
4
+ require 'rake/file_creation_task'
5
+ require 'rake/application'
6
+ require 'rake/task_manager'
7
+
8
+ ######################################################################
9
+ # Rake extensions to Module.
10
+ #
11
+ class Module
12
+
13
+ # Rename the original handler to make it available.
14
+ alias :rake_original_const_missing :const_missing
15
+
16
+ # Check for deprecated uses of top level (i.e. in Object) uses of
17
+ # Rake class names. If someone tries to reference the constant
18
+ # name, display a warning and return the proper object. Using the
19
+ # --classic-namespace command line option will define these
20
+ # constants in Object and avoid this handler.
21
+ def const_missing(const_name)
22
+ case const_name
23
+ when :Task
24
+ Rake.application.const_warning(const_name)
25
+ Rake::Task
26
+ when :FileTask
27
+ Rake.application.const_warning(const_name)
28
+ Rake::FileTask
29
+ when :FileCreationTask
30
+ Rake.application.const_warning(const_name)
31
+ Rake::FileCreationTask
32
+ when :RakeApp
33
+ Rake.application.const_warning(const_name)
34
+ Rake::Application
35
+ else
36
+ rake_original_const_missing(const_name)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,167 @@
1
+ require 'rake/ext/core'
2
+
3
+ ######################################################################
4
+ # Rake extension methods for String.
5
+ #
6
+ class String
7
+ rake_extension("ext") do
8
+ # Replace the file extension with +newext+. If there is no extension on
9
+ # the string, append the new extension to the end. If the new extension
10
+ # is not given, or is the empty string, remove any existing extension.
11
+ #
12
+ # +ext+ is a user added method for the String class.
13
+ def ext(newext='')
14
+ return self.dup if ['.', '..'].include? self
15
+ if newext != ''
16
+ newext = (newext =~ /^\./) ? newext : ("." + newext)
17
+ end
18
+ self.chomp(File.extname(self)) << newext
19
+ end
20
+ end
21
+
22
+ rake_extension("pathmap") do
23
+ # Explode a path into individual components. Used by +pathmap+.
24
+ def pathmap_explode
25
+ head, tail = File.split(self)
26
+ return [self] if head == self
27
+ return [tail] if head == '.' || tail == '/'
28
+ return [head, tail] if head == '/'
29
+ return head.pathmap_explode + [tail]
30
+ end
31
+ protected :pathmap_explode
32
+
33
+ # Extract a partial path from the path. Include +n+ directories from the
34
+ # front end (left hand side) if +n+ is positive. Include |+n+|
35
+ # directories from the back end (right hand side) if +n+ is negative.
36
+ def pathmap_partial(n)
37
+ dirs = File.dirname(self).pathmap_explode
38
+ partial_dirs =
39
+ if n > 0
40
+ dirs[0...n]
41
+ elsif n < 0
42
+ dirs.reverse[0...-n].reverse
43
+ else
44
+ "."
45
+ end
46
+ File.join(partial_dirs)
47
+ end
48
+ protected :pathmap_partial
49
+
50
+ # Preform the pathmap replacement operations on the given path. The
51
+ # patterns take the form 'pat1,rep1;pat2,rep2...'.
52
+ def pathmap_replace(patterns, &block)
53
+ result = self
54
+ patterns.split(';').each do |pair|
55
+ pattern, replacement = pair.split(',')
56
+ pattern = Regexp.new(pattern)
57
+ if replacement == '*' && block_given?
58
+ result = result.sub(pattern, &block)
59
+ elsif replacement
60
+ result = result.sub(pattern, replacement)
61
+ else
62
+ result = result.sub(pattern, '')
63
+ end
64
+ end
65
+ result
66
+ end
67
+ protected :pathmap_replace
68
+
69
+ # Map the path according to the given specification. The specification
70
+ # controls the details of the mapping. The following special patterns are
71
+ # recognized:
72
+ #
73
+ # * <b>%p</b> -- The complete path.
74
+ # * <b>%f</b> -- The base file name of the path, with its file extension,
75
+ # but without any directories.
76
+ # * <b>%n</b> -- The file name of the path without its file extension.
77
+ # * <b>%d</b> -- The directory list of the path.
78
+ # * <b>%x</b> -- The file extension of the path. An empty string if there
79
+ # is no extension.
80
+ # * <b>%X</b> -- Everything *but* the file extension.
81
+ # * <b>%s</b> -- The alternate file separator if defined, otherwise use
82
+ # the standard file separator.
83
+ # * <b>%%</b> -- A percent sign.
84
+ #
85
+ # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the
86
+ # number is positive, only return (up to) +n+ directories in the path,
87
+ # starting from the left hand side. If +n+ is negative, return (up to)
88
+ # |+n+| directories from the right hand side of the path.
89
+ #
90
+ # Examples:
91
+ #
92
+ # 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b'
93
+ # 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d'
94
+ #
95
+ # Also the %d, %p, %f, %n, %x, and %X operators can take a
96
+ # pattern/replacement argument to perform simple string substitutions on a
97
+ # particular part of the path. The pattern and replacement are separated
98
+ # by a comma and are enclosed by curly braces. The replacement spec comes
99
+ # after the % character but before the operator letter. (e.g.
100
+ # "%{old,new}d"). Multiple replacement specs should be separated by
101
+ # semi-colons (e.g. "%{old,new;src,bin}d").
102
+ #
103
+ # Regular expressions may be used for the pattern, and back refs may be
104
+ # used in the replacement text. Curly braces, commas and semi-colons are
105
+ # excluded from both the pattern and replacement text (let's keep parsing
106
+ # reasonable).
107
+ #
108
+ # For example:
109
+ #
110
+ # "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class")
111
+ #
112
+ # returns:
113
+ #
114
+ # "bin/org/onestepback/proj/A.class"
115
+ #
116
+ # If the replacement text is '*', then a block may be provided to perform
117
+ # some arbitrary calculation for the replacement.
118
+ #
119
+ # For example:
120
+ #
121
+ # "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext|
122
+ # ext.downcase
123
+ # }
124
+ #
125
+ # Returns:
126
+ #
127
+ # "/path/to/file.txt"
128
+ #
129
+ def pathmap(spec=nil, &block)
130
+ return self if spec.nil?
131
+ result = ''
132
+ spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
133
+ case frag
134
+ when '%f'
135
+ result << File.basename(self)
136
+ when '%n'
137
+ result << File.basename(self).ext
138
+ when '%d'
139
+ result << File.dirname(self)
140
+ when '%x'
141
+ result << File.extname(self)
142
+ when '%X'
143
+ result << self.ext
144
+ when '%p'
145
+ result << self
146
+ when '%s'
147
+ result << (File::ALT_SEPARATOR || File::SEPARATOR)
148
+ when '%-'
149
+ # do nothing
150
+ when '%%'
151
+ result << "%"
152
+ when /%(-?\d+)d/
153
+ result << pathmap_partial($1.to_i)
154
+ when /^%\{([^}]*)\}(\d*[dpfnxX])/
155
+ patterns, operator = $1, $2
156
+ result << pathmap('%' + operator).pathmap_replace(patterns, &block)
157
+ when /^%/
158
+ fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
159
+ else
160
+ result << frag
161
+ end
162
+ end
163
+ result
164
+ end
165
+ end
166
+ end # class String
167
+
@@ -0,0 +1,14 @@
1
+ # ###########################################################################
2
+ # Extensions to time to allow comparisons with an early time class.
3
+ #
4
+ class Time
5
+ alias rake_original_time_compare :<=>
6
+ def <=>(other)
7
+ if Rake::EarlyTime === other
8
+ - other.<=>(self)
9
+ else
10
+ rake_original_time_compare(other)
11
+ end
12
+ end
13
+ end # class Time
14
+
@@ -0,0 +1,24 @@
1
+ require 'rake/file_task'
2
+ require 'rake/early_time'
3
+
4
+ module Rake
5
+
6
+ # A FileCreationTask is a file task that when used as a dependency will be
7
+ # needed if and only if the file has not been created. Once created, it is
8
+ # not re-triggered if any of its dependencies are newer, nor does trigger
9
+ # any rebuilds of tasks that depend on it whenever it is updated.
10
+ #
11
+ class FileCreationTask < FileTask
12
+ # Is this file task needed? Yes if it doesn't exist.
13
+ def needed?
14
+ ! File.exist?(name)
15
+ end
16
+
17
+ # Time stamp for file creation task. This time stamp is earlier
18
+ # than any other time stamp.
19
+ def timestamp
20
+ Rake::EARLY
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,403 @@
1
+ require 'rake/cloneable'
2
+ require 'rake/file_utils_ext'
3
+ require 'rake/pathmap'
4
+
5
+ ######################################################################
6
+ module Rake
7
+
8
+ # #########################################################################
9
+ # A FileList is essentially an array with a few helper methods defined to
10
+ # make file manipulation a bit easier.
11
+ #
12
+ # FileLists are lazy. When given a list of glob patterns for possible files
13
+ # to be included in the file list, instead of searching the file structures
14
+ # to find the files, a FileList holds the pattern for latter use.
15
+ #
16
+ # This allows us to define a number of FileList to match any number of
17
+ # files, but only search out the actual files when then FileList itself is
18
+ # actually used. The key is that the first time an element of the
19
+ # FileList/Array is requested, the pending patterns are resolved into a real
20
+ # list of file names.
21
+ #
22
+ class FileList
23
+
24
+ include Cloneable
25
+
26
+ # == Method Delegation
27
+ #
28
+ # The lazy evaluation magic of FileLists happens by implementing all the
29
+ # array specific methods to call +resolve+ before delegating the heavy
30
+ # lifting to an embedded array object (@items).
31
+ #
32
+ # In addition, there are two kinds of delegation calls. The regular kind
33
+ # delegates to the @items array and returns the result directly. Well,
34
+ # almost directly. It checks if the returned value is the @items object
35
+ # itself, and if so will return the FileList object instead.
36
+ #
37
+ # The second kind of delegation call is used in methods that normally
38
+ # return a new Array object. We want to capture the return value of these
39
+ # methods and wrap them in a new FileList object. We enumerate these
40
+ # methods in the +SPECIAL_RETURN+ list below.
41
+
42
+ # List of array methods (that are not in +Object+) that need to be
43
+ # delegated.
44
+ ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
45
+
46
+ # List of additional methods that must be delegated.
47
+ MUST_DEFINE = %w[to_a inspect <=>]
48
+
49
+ # List of methods that should not be delegated here (we define special
50
+ # versions of them explicitly below).
51
+ MUST_NOT_DEFINE = %w[to_a to_ary partition *]
52
+
53
+ # List of delegated methods that return new array values which need
54
+ # wrapping.
55
+ SPECIAL_RETURN = %w[
56
+ map collect sort sort_by select find_all reject grep
57
+ compact flatten uniq values_at
58
+ + - & |
59
+ ]
60
+
61
+ DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
62
+
63
+ # Now do the delegation.
64
+ DELEGATING_METHODS.each_with_index do |sym, i|
65
+ if SPECIAL_RETURN.include?(sym)
66
+ ln = __LINE__+1
67
+ class_eval %{
68
+ def #{sym}(*args, &block)
69
+ resolve
70
+ result = @items.send(:#{sym}, *args, &block)
71
+ FileList.new.import(result)
72
+ end
73
+ }, __FILE__, ln
74
+ else
75
+ ln = __LINE__+1
76
+ class_eval %{
77
+ def #{sym}(*args, &block)
78
+ resolve
79
+ result = @items.send(:#{sym}, *args, &block)
80
+ result.object_id == @items.object_id ? self : result
81
+ end
82
+ }, __FILE__, ln
83
+ end
84
+ end
85
+
86
+ # Create a file list from the globbable patterns given. If you wish to
87
+ # perform multiple includes or excludes at object build time, use the
88
+ # "yield self" pattern.
89
+ #
90
+ # Example:
91
+ # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb')
92
+ #
93
+ # pkg_files = FileList.new('lib/**/*') do |fl|
94
+ # fl.exclude(/\bCVS\b/)
95
+ # end
96
+ #
97
+ def initialize(*patterns)
98
+ @pending_add = []
99
+ @pending = false
100
+ @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
101
+ @exclude_procs = DEFAULT_IGNORE_PROCS.dup
102
+ @items = []
103
+ patterns.each { |pattern| include(pattern) }
104
+ yield self if block_given?
105
+ end
106
+
107
+ # Add file names defined by glob patterns to the file list. If an array
108
+ # is given, add each element of the array.
109
+ #
110
+ # Example:
111
+ # file_list.include("*.java", "*.cfg")
112
+ # file_list.include %w( math.c lib.h *.o )
113
+ #
114
+ def include(*filenames)
115
+ # TODO: check for pending
116
+ filenames.each do |fn|
117
+ if fn.respond_to? :to_ary
118
+ include(*fn.to_ary)
119
+ else
120
+ @pending_add << fn
121
+ end
122
+ end
123
+ @pending = true
124
+ self
125
+ end
126
+ alias :add :include
127
+
128
+ # Register a list of file name patterns that should be excluded from the
129
+ # list. Patterns may be regular expressions, glob patterns or regular
130
+ # strings. In addition, a block given to exclude will remove entries that
131
+ # return true when given to the block.
132
+ #
133
+ # Note that glob patterns are expanded against the file system. If a file
134
+ # is explicitly added to a file list, but does not exist in the file
135
+ # system, then an glob pattern in the exclude list will not exclude the
136
+ # file.
137
+ #
138
+ # Examples:
139
+ # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
140
+ # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c']
141
+ #
142
+ # If "a.c" is a file, then ...
143
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
144
+ #
145
+ # If "a.c" is not a file, then ...
146
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
147
+ #
148
+ def exclude(*patterns, &block)
149
+ patterns.each do |pat|
150
+ @exclude_patterns << pat
151
+ end
152
+ if block_given?
153
+ @exclude_procs << block
154
+ end
155
+ resolve_exclude if ! @pending
156
+ self
157
+ end
158
+
159
+
160
+ # Clear all the exclude patterns so that we exclude nothing.
161
+ def clear_exclude
162
+ @exclude_patterns = []
163
+ @exclude_procs = []
164
+ self
165
+ end
166
+
167
+ # Define equality.
168
+ def ==(array)
169
+ to_ary == array
170
+ end
171
+
172
+ # Return the internal array object.
173
+ def to_a
174
+ resolve
175
+ @items
176
+ end
177
+
178
+ # Return the internal array object.
179
+ def to_ary
180
+ to_a
181
+ end
182
+
183
+ # Lie about our class.
184
+ def is_a?(klass)
185
+ klass == Array || super(klass)
186
+ end
187
+ alias kind_of? is_a?
188
+
189
+ # Redefine * to return either a string or a new file list.
190
+ def *(other)
191
+ result = @items * other
192
+ case result
193
+ when Array
194
+ FileList.new.import(result)
195
+ else
196
+ result
197
+ end
198
+ end
199
+
200
+ # Resolve all the pending adds now.
201
+ def resolve
202
+ if @pending
203
+ @pending = false
204
+ @pending_add.each do |fn| resolve_add(fn) end
205
+ @pending_add = []
206
+ resolve_exclude
207
+ end
208
+ self
209
+ end
210
+
211
+ def resolve_add(fn)
212
+ case fn
213
+ when %r{[*?\[\{]}
214
+ add_matching(fn)
215
+ else
216
+ self << fn
217
+ end
218
+ end
219
+ private :resolve_add
220
+
221
+ def resolve_exclude
222
+ reject! { |fn| exclude?(fn) }
223
+ self
224
+ end
225
+ private :resolve_exclude
226
+
227
+ # Return a new FileList with the results of running +sub+ against each
228
+ # element of the original list.
229
+ #
230
+ # Example:
231
+ # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
232
+ #
233
+ def sub(pat, rep)
234
+ inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
235
+ end
236
+
237
+ # Return a new FileList with the results of running +gsub+ against each
238
+ # element of the original list.
239
+ #
240
+ # Example:
241
+ # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
242
+ # => ['lib\\test\\file', 'x\\y']
243
+ #
244
+ def gsub(pat, rep)
245
+ inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
246
+ end
247
+
248
+ # Same as +sub+ except that the original file list is modified.
249
+ def sub!(pat, rep)
250
+ each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
251
+ self
252
+ end
253
+
254
+ # Same as +gsub+ except that the original file list is modified.
255
+ def gsub!(pat, rep)
256
+ each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
257
+ self
258
+ end
259
+
260
+ # Apply the pathmap spec to each of the included file names, returning a
261
+ # new file list with the modified paths. (See String#pathmap for
262
+ # details.)
263
+ def pathmap(spec=nil)
264
+ collect { |fn| fn.pathmap(spec) }
265
+ end
266
+
267
+ # Return a new FileList with <tt>String#ext</tt> method applied to
268
+ # each member of the array.
269
+ #
270
+ # This method is a shortcut for:
271
+ #
272
+ # array.collect { |item| item.ext(newext) }
273
+ #
274
+ # +ext+ is a user added method for the Array class.
275
+ def ext(newext='')
276
+ collect { |fn| fn.ext(newext) }
277
+ end
278
+
279
+
280
+ # Grep each of the files in the filelist using the given pattern. If a
281
+ # block is given, call the block on each matching line, passing the file
282
+ # name, line number, and the matching line of text. If no block is given,
283
+ # a standard emacs style file:linenumber:line message will be printed to
284
+ # standard out. Returns the number of matched items.
285
+ def egrep(pattern, *options)
286
+ matched = 0
287
+ each do |fn|
288
+ begin
289
+ open(fn, "rb", *options) do |inf|
290
+ count = 0
291
+ inf.each do |line|
292
+ count += 1
293
+ if pattern.match(line)
294
+ matched += 1
295
+ if block_given?
296
+ yield fn, count, line
297
+ else
298
+ puts "#{fn}:#{count}:#{line}"
299
+ end
300
+ end
301
+ end
302
+ end
303
+ rescue StandardError => ex
304
+ $stderr.puts "Error while processing '#{fn}': #{ex}"
305
+ end
306
+ end
307
+ matched
308
+ end
309
+
310
+ # Return a new file list that only contains file names from the current
311
+ # file list that exist on the file system.
312
+ def existing
313
+ select { |fn| File.exist?(fn) }
314
+ end
315
+
316
+ # Modify the current file list so that it contains only file name that
317
+ # exist on the file system.
318
+ def existing!
319
+ resolve
320
+ @items = @items.select { |fn| File.exist?(fn) }
321
+ self
322
+ end
323
+
324
+ # FileList version of partition. Needed because the nested arrays should
325
+ # be FileLists in this version.
326
+ def partition(&block) # :nodoc:
327
+ resolve
328
+ result = @items.partition(&block)
329
+ [
330
+ FileList.new.import(result[0]),
331
+ FileList.new.import(result[1]),
332
+ ]
333
+ end
334
+
335
+ # Convert a FileList to a string by joining all elements with a space.
336
+ def to_s
337
+ resolve
338
+ self.join(' ')
339
+ end
340
+
341
+ # Add matching glob patterns.
342
+ def add_matching(pattern)
343
+ Dir[pattern].each do |fn|
344
+ self << fn unless exclude?(fn)
345
+ end
346
+ end
347
+ private :add_matching
348
+
349
+ # Should the given file name be excluded?
350
+ def exclude?(fn)
351
+ return true if @exclude_patterns.any? do |pat|
352
+ case pat
353
+ when Regexp
354
+ fn =~ pat
355
+ when /[*?]/
356
+ File.fnmatch?(pat, fn, File::FNM_PATHNAME)
357
+ else
358
+ fn == pat
359
+ end
360
+ end
361
+ @exclude_procs.any? { |p| p.call(fn) }
362
+ end
363
+
364
+ DEFAULT_IGNORE_PATTERNS = [
365
+ /(^|[\/\\])CVS([\/\\]|$)/,
366
+ /(^|[\/\\])\.svn([\/\\]|$)/,
367
+ /\.bak$/,
368
+ /~$/
369
+ ]
370
+ DEFAULT_IGNORE_PROCS = [
371
+ proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) }
372
+ ]
373
+
374
+ def import(array)
375
+ @items = array
376
+ self
377
+ end
378
+
379
+ class << self
380
+ # Create a new file list including the files listed. Similar to:
381
+ #
382
+ # FileList.new(*args)
383
+ def [](*args)
384
+ new(*args)
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+ module Rake
391
+ class << self
392
+
393
+ # Yield each file or directory component.
394
+ def each_dir_parent(dir) # :nodoc:
395
+ old_length = nil
396
+ while dir != '.' && dir.length != old_length
397
+ yield(dir)
398
+ old_length = dir.length
399
+ dir = File.dirname(dir)
400
+ end
401
+ end
402
+ end
403
+ end # module Rake