PageRenderTime 158ms CodeModel.GetById 107ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/Ruby/lib/ruby/gems/1.8/gems/rake-0.9.2/lib/rake/file_list.rb

http://github.com/agross/netopenspace
Ruby | 403 lines | 233 code | 42 blank | 128 comment | 20 complexity | 0615673d86a56cd6cf26bd36d9a6874f MD5 | raw file
  1require 'rake/cloneable'
  2require 'rake/file_utils_ext'
  3require 'rake/pathmap'
  4
  5######################################################################
  6module 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
388end
389
390module 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
403end # module Rake