[27541] users/pipping/merge.rb

source_changes at macosforge.org source_changes at macosforge.org
Tue Aug 7 03:12:53 PDT 2007


Revision: 27541
          http://trac.macosforge.org/projects/macports/changeset/27541
Author:   pipping at macports.org
Date:     2007-08-07 03:12:53 -0700 (Tue, 07 Aug 2007)

Log Message:
-----------
please do have a look at this repository for details:
http://elias.svn.binera.de

r66: minor changes
r67: add comment, minor changes
r68: trivial changes
r69: new structure: Merge class, FileHandler subclass, Helpers module | simplification of multiple functions' interfaces | partial rewrite of multiple functions, including copy, create_directory | new function special_copy for the time being | major code cleanup, including all kinds of formatting changes
r70: rewrite function true_for_all?
r71: formatting change
r72: rename check_consistency to consistent?, reorganize it and make it into a predicate | restructure | unify comments
r73: use/define String#start_with? and String#end_with? from Ruby 1.9
r74: use String#start_with?

Modified Paths:
--------------
    users/pipping/merge.rb

Modified: users/pipping/merge.rb
===================================================================
--- users/pipping/merge.rb	2007-08-07 09:46:01 UTC (rev 27540)
+++ users/pipping/merge.rb	2007-08-07 10:12:53 UTC (rev 27541)
@@ -20,380 +20,425 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # THE SOFTWARE.
 
-require 'fileutils'
-require 'find'
-require 'logger'
-require 'optparse'
-require 'ostruct'
-require 'pathname'
-require 'set'
-require 'tempfile'
-require 'zlib'
+libs = %w{ fileutils find logger optparse ostruct pathname set tempfile zlib }
 
-###
-# we need GNU File with Apple's patches applied, otherwise we will not be
-# able to recognize x86_64 mach-o files
-FILE = '/usr/bin/file'
+libs.each {|lib| require lib}
 
-class MergeArguments
-  def self.parse( args )
+module Helpers
+  def true_for_all?( filePath, includeFirst )
+  # checks if a predicate is true for all (but the first) architecture
+    iterand = includeFirst ? @args : @args[1..-1]
+    iterand.all? {|arch| yield( @options.input + arch + filePath, arch )}
+  end
+end
 
-    # set up defaults
-    options = OpenStruct.new
-    options.dryrun  = false
+# these ruby 1.9 extensions come in handy. in case we are running ruby 1.8 we
+# just define them ourselves
+class String
+  unless self.method_defined?( :start_with? )
+    def start_with?( string )
+      self.slice( 0...string.length ) == string
+    end
+  end
+  unless self.method_defined?( :end_with? )
+    def end_with?( string )
+      self.slice( -string.length..-1 ) == string
+    end
+  end
+end
 
+class Merge
 
-    options.level   = Logger::INFO
-    options.log     = 'STDOUT'
+  private
 
-    options.exclude = %w{.svn CVS}
+  include Helpers
 
-    options.input   = Pathname.pwd
-    options.output  = Pathname.pwd + 'out'
+  class MergeArguments
+    def self.parse( arguments )
 
-    opts = OptionParser.new {|opts|
-      opts.banner = 'Usage: %s [options] arch arch [arch [arch ...]]' % $0
+      # set up defaults
+      options = OpenStruct.new
+      options.dryrun  = false
 
-      opts.on( '-i', '--input DIRECTORY',
-        'Change input directory', %{Defaults to '.'.} ) {|i|
-        options.input = Pathname.new( i ).expand_path || options.input
-      }
+      options.level   = Logger::INFO
+      options.log     = 'STDOUT'
 
-      opts.on( '-o', '--output DIRECTORY',
-        'Change output directory', %{Defaults to './out'.} ) {|o|
-        options.output = Pathname.new( o ).expand_path || options.output
-      }
+      options.exclude = %w{ .svn CVS }
 
-      opts.on( '-v', '--verbosity LEVEL', 'Change level of verbosity',
-         %{Valid arguments are 'debug', 'info',},
-         %{'warn', 'error', and 'fatal'.}, %{Defaults to 'info'.} ) {|v|
-        case v
-        when 'debug'
-          options.level = Logger::DEBUG
-        when 'info'
-          options.level = Logger::INFO
-        when 'warn'
-          options.level = Logger::WARN
-        when 'error'
-          options.level = Logger::ERROR
-        when 'fatal'
-          options.level = Logger::FATAL
-        end
-      }
+      options.input   = Pathname.pwd
+      options.output  = Pathname.pwd + 'out'
 
-      opts.on( '-l', '--log TARGET', 'Change target to log to',
-        %{Valid arguments are 'STDOUT', 'STDERR',},
-        %{and '/path/to/file'.}, %{Defaults to 'STDOUT'.} ) {|l|
-        options.log = l
-      }
+      opts = OptionParser.new {|opts|
+        opts.banner = 'Usage: %s [options] arch arch [arch [arch ...]]' % $0
 
-      opts.on( '-e', '--exclude PATTERN1,PATTERN2', Array,
-        'Exclude files/directories (glob-style)') {|e|
-        options.exclude = e
-      }
+        opts.on( '-i', '--input DIRECTORY',
+          'Change input directory', %q{Defaults to '.'} ) {|i|
+          options.input = Pathname.new( i ).expand_path || options.input
+        }
 
-      opts.on( '-d', '--[no-]dry-run', 'Perform a dry run' ) {|d|
-        options.dryrun = d
-      }
+        opts.on( '-o', '--output DIRECTORY',
+          'Change output directory', %q{Defaults to './out'} ) {|o|
+          options.output = Pathname.new( o ).expand_path || options.output
+        }
 
-      opts.on_tail( '-h', '--help', 'Show this message' ) {
-        puts opts
-        exit
-      }
-    }
-    opts.parse!( args )
-    options
-  end
-end
+        opts.on( '-v', '--verbosity LEVEL', 'Change level of verbosity',
+           %q{Valid arguments are 'debug', 'info',},
+           %q{'warn', 'error', and 'fatal'.}, %q{Defaults to 'info'} ) {|v|
+          case v
+          when 'debug', 'info', 'warn', 'error', 'fatal'
+            options.level = Logger.const_get( v.upcase )
+          end
+        }
 
-options = MergeArguments.parse( ARGV )
+        opts.on( '-l', '--log TARGET', 'Change target to log to',
+          %q{Valid arguments are 'STDOUT', 'STDERR',},
+          %q{and '/path/to/file'.}, %q{Defaults to 'STDOUT'} ) {|l|
+          options.log = l
+        }
 
-# ignore duplicates and trailing slashes
-ARGS = ARGV.collect {|arg| arg.chomp( '/' )}.uniq
+        opts.on( '-e', '--exclude PATTERN1,PATTERN2', Array,
+          'Exclude files/directories (glob-style)') {|e|
+          options.exclude = e
+        }
 
-def true_for_all? ( filePath, includeFirst, opts )
-# checks if a predicate is true for all (but the first) architecture
-  result = true
-  iterable = includeFirst ? ARGS : ARGS[1..-1]
-  iterable.each {|arch|
-    unless yield( opts.input + arch + filePath, arch )
-      result = false
-      break
+        opts.on( '-d', '--[no-]dry-run', 'Perform a dry run' ) {|d|
+          options.dryrun = d
+        }
+
+        opts.on_tail( '-h', '--help', 'Show this message' ) {
+          puts opts
+          exit
+        }
+      }
+      opts.parse!( arguments )
+      options
     end
-  }
-  return result
-end
-
-def create_directory ( dir, logPath, opts )
-# creates a directory
-  unless dir.exist?
-    dir.mkpath unless opts.dryrun
-    $log.debug( 'created : %s' % logPath )
-  else
-    $log.debug( 'exists  : %s' % logPath )
   end
-end
 
-def copy ( origin, target, logPath, opts )
-# copies files
-  unless target.exist?
-    FileUtils.cp( origin, target,
-      :noop => opts.dryrun
-    )
-    $log.debug( 'copied  : %s' % logPath )
-  else
-    $log.debug( 'exists  : %s' % logPath )
-  end
-end
+  class FileHandler
 
-def check_consistency( path, regex, opts )
-# checks if a file has the correct type and architecture across all trees
-  type_matches = true_for_all?( path, true, opts ) {|filePath, arch|
-    fileCallOutput = %x{#{ FILE } -b "#{ filePath }"}.chomp
-    fileCallOutput =~ regex
-  }
-  arch_matches = true_for_all?( path, true, opts ) {|filePath, arch|
-    lipoCallOutput = %x{lipo -info "#{ filePath }"}.chomp
-    lipoCallOutput =~ %r{is architecture: #{ arch }$}
-  }
-  unless type_matches and arch_matches
-    $log.error( 'type or architecture mismatch' )
-    raise( 'an error occurred. see the log for details.' )
-  end
-end
+    include Helpers
 
-def lipo ( filePath, opts )
-# glues single-architecture files together using lipo(1)
-  lipoArgs = Array.new
-  ARGS.each {|arch|
-    lipoArgs << sprintf( '-arch %s %s', arch, opts.input + arch + filePath )
-  }
-  lipoTarget = opts.output + filePath
-  lipoCommand = sprintf(
-    'lipo %s -create -o %s', lipoArgs.join( ' ' ), lipoTarget
-  )
-  unless lipoTarget.exist?
-    system lipoCommand unless opts.dryrun
-    $log.debug( 'merged  : %s' % filePath )
-  else
-    $log.debug( 'exists  : %s' % filePath )
-  end
-end
+    private
 
-def make_wrapper ( filePath, first, opts )
-# creates a wrapper for config scripts that differ across trees
-  wrapperTarget = opts.output + filePath
-  unless wrapperTarget.exist?
-    wrapperTarget.open( 'w' ) {|wrapper|
-      wrapper.puts '#! /bin/sh'
-      wrapper.puts 'DIR="/%s"' % filePath.dirname
-      wrapper.puts 'args=$@'
-      wrapper.puts 'if [ "${args/-arch/}" != "$args" ]; then'
-      wrapper.puts '  arch=`echo "$args" | ' +
-                   %q{sed 's!.*-arch  *\([^ ][^ ]*\).*!\1!'`;}
-      wrapper.puts 'else'
-      wrapper.puts '  arch=`uname -p`'
-      wrapper.puts 'fi'
-      wrapper.puts 'args=`echo $@ | sed "s!-arch  *${arch}!!"`'
-      wrapper.puts 'exec $DIR/${arch}/%s ${args}' % filePath.basename
-    }
-    wrapperTarget.chmod( first.stat.mode )
-  end
-end
+    def initialize( options, log, args )
+      @options, @log, @args = options, log, args
+    end
 
+    def create_directory( prefix, dir )
+    # creates a directory
+      prefixed = prefix + dir
+      unless prefixed.exist?
+        prefixed.mkpath unless @options.dryrun
+        @log.debug( 'created : %s' % dir )
+      else
+        @log.debug( 'exists  : %s' % dir )
+      end
+    end
 
-ORIGIN = Pathname.pwd
-processed = Set.new
+    def copy( filePath )
+    # copies a file
+      origin = @options.input + @args[0] + filePath
+      target = @options.output + filePath
+      unless target.exist?
+        FileUtils.cp( origin, target, :noop => @options.dryrun )
+        @log.debug( 'copied  : %s' % filePath )
+      else
+        @log.debug( 'exists  : %s' % filePath )
+      end
+    end
 
-case options.log
-when 'STDERR'
-  $log = Logger.new( STDERR )
-when 'STDOUT'
-  $log = Logger.new( STDOUT )
-else
-  logTarget = Pathname.new( options.log ).expand_path
-  if logTarget.writable?
-    $log = Logger.new( logTarget )
-  elsif !logTarget.exist? and logTarget.dirname.writable?
-    $log = Logger.new( logTarget )
-  else
-    $log = Logger.new( STDOUT )
-    $log.error( 'cannot create log file' )
-    raise( 'an error occurred.' )
-  end
-end
-$log.level = options.level
-$log.info( 'starting up' )
+    def consistent?( path, string )
+    # checks if a file has the correct type and architecture across all trees
+      type_matches = true_for_all?( path, true ) {|filePath, arch|
+        fileCallOutput = %x{ #{ $FILE } -b "#{ filePath }" }.chomp
+        fileCallOutput.start_with?( string )
+      }
+      arch_matches = true_for_all?( path, true ) {|filePath, arch|
+        lipoCallOutput = %x{ lipo -info "#{ filePath }" }.chomp
+        lipoCallOutput.end_with?( 'is architecture: %s' % arch )
+      }
+      type_matches and arch_matches
+    end
 
-# make sure we're given a valid root directory
-unless options.input.directory?
-  $log.fatal( 'invalid input directory: %s' % options.input )
-  raise( 'an error occurred. see the log for details.' )
-end
+    def lipo( filePath )
+    # glues single-architecture files together using lipo(1)
+      lipoArgs = Array.new
+      @args.each {|arch|
+        lipoArgs << '-arch %s %s' % [ arch, @options.input + arch + filePath ]
+      }
+      lipoTarget = @options.output + filePath
+      lipoCommand = 'lipo %s -create -o %s' % [ lipoArgs.join( ' ' ), lipoTarget ]
+      unless lipoTarget.exist?
+        system lipoCommand unless @options.dryrun
+        @log.debug( 'merged  : %s' % filePath )
+      else
+        @log.debug( 'exists  : %s' % filePath )
+      end
+    end
 
-# make sure the requested architectures have corresponding subdirectories
-# in the the given input directory
-unless true_for_all?( '.', true, options) {|filePath, arch|
-  filePath.directory?
-}
-  $log.fatal( 'architecture missing from input directory' )
-  raise( 'an error occurred. see the log for details.' )
-end
+    def make_wrapper( filePath, first )
+    # creates a wrapper for config scripts that differ across trees
+      wrapperTarget = @options.output + filePath
+      unless wrapperTarget.exist?
+        wrapperTarget.open( 'w' ) {|wrapper|
+          wrapper.puts(
+            '#! /bin/sh',
+            'DIR="/%s"' % filePath.dirname,
+            'args=$@',
+            'if [ "${args/-arch/}" != "$args" ]; then',
+            %q{  arch=`echo "$args" | sed 's!.*-arch  *\([^ ][^ ]*\).*!\1!'`;},
+            'else',
+            '  arch=`uname -p`',
+            'fi',
+            'args=`echo $@ | sed "s!-arch  *${arch}!!"`',
+            'exec $DIR/${arch}/%s ${args}' % filePath.basename
+          )
+        }
+        wrapperTarget.chmod( first.stat.mode )
+      end
+    end
 
-ARGS.each {|architecture|
-  FileUtils.cd options.input + architecture
-  Pathname.new( '.' ).find {|subPath|
-    options.exclude.each {|excludedPattern|
-      if subPath.basename.fnmatch? excludedPattern
-        Find.prune
-        break
+    def special_copy( origin, target, logPath )
+    # copies files
+      unless target.exist?
+        FileUtils.cp( origin, target, :noop => @options.dryrun )
+        @log.debug( 'forked  : %s' % logPath )
+      else
+        @log.debug( 'exists  : %s' % logPath )
       end
-    }
-    unless processed.include? subPath
-      processed << subPath
-      firstPath = options.input + ARGS[0] + subPath
-      unless true_for_all?( subPath, false, options ) {|filePath, arch|
+    end
+
+    public
+
+    def handle_file( subPath )
+      # firstPath is used so we have something we can compare other
+      # architecture's versions of the file we are processing with.
+      firstPath = @options.input + @args[0] + subPath
+      unless true_for_all?( subPath, false ) {|filePath, arch|
         begin
           filePath.ftype == firstPath.ftype
         rescue Errno::ENOENT
           false
         end
       }
-        $log.warn( 'skipped: %s' % subPath )
-        next
-      end
-
-      case firstPath.ftype
-      # handle file type: directory
-      when 'directory'
-        create_directory( options.output + subPath, subPath, options )
-      # handle file type: symlinks
-      when 'link'
-        linkTarget = firstPath.readlink
-        if true_for_all?( subPath, false, options ) {|filePath, arch|
-          pathToSymlink = options.input + arch + subPath
-          pathToSymlink.readlink == linkTarget
-        }
-          linkDestination = options.output + subPath
-          unless linkDestination.symlink? or linkDestination.exist?
-            FileUtils.copy_entry(
-              firstPath,
-              linkDestination
-            )
-          end
-        else
-          # DEBUG: link targets differ
-        end
-      when 'file'
-        if true_for_all?( subPath, false, options ) {|filePath, arch|
-          FileUtils.identical?( filePath, firstPath )
-        }
-          copy( firstPath, options.output + subPath, subPath, options )
-          next
-        end
-
-        case subPath.extname
-        # handle file type: header files
-        when '.h', '.hpp'
-          unless ( options.output + subPath ).exist?
-            open( options.output + subPath, 'w' ) {|headerTarget|
-              ARGS.each {|arch|
-                headerInput = options.input + arch + subPath
-                headerTarget.puts '#ifdef __%s__' % arch
-                headerTarget.puts headerInput.open( 'r' ).read
-                headerTarget.puts '#endif'
-              }
-            }
-            $log.debug( 'merged: %s' % subPath )
+        # A file is either missing from at least on of the single-architecture
+        # directories or not all versions of the file have the same type.
+        @log.warn( 'skipped: %s' % subPath )
+      else
+        case firstPath.ftype
+        # Handle file type: directory
+        when 'directory'
+          create_directory( @options.output, subPath )
+        # Handle file type: symlink
+        when 'link'
+          firstLinkTarget = firstPath.readlink
+          if true_for_all?( subPath, false ) {|filePath, arch|
+            linkTarget = @options.input + arch + subPath
+            linkTarget.readlink == firstLinkTarget
+          }
+            linkDestination = @options.output + subPath
+            unless linkDestination.symlink? or linkDestination.exist?
+              FileUtils.copy_entry( firstPath, linkDestination )
+              @log.debug( 'copied  : %s' % subPath )
+            else
+              @log.debug( 'exists  : %s' % subPath )
+            end
           else
-            $log.debug( 'exists: %s' % subPath )
+            # The links point at different targets
+            @log.warn( 'skipped: %s' % subPath )
           end
-        # handle file type: pkg-config files
-        when '.pc'
-          ARGS.each {|arch|
-            subArch = subPath.dirname + arch + subPath.basename
-            copyTarget = options.output + subArch
-            create_directory( copyTarget.dirname, subArch.dirname, options )
-            copy( options.input + arch + subPath, copyTarget, subArch, options )
+        when 'file'
+          if true_for_all?( subPath, false ) {|filePath, arch|
+            FileUtils.identical?( filePath, firstPath )
           }
-          $log.debug( 'spread  : %s' % subPath )
-        # handle file type: gzip-compressed man pages
-        when '.gz'
-          temporaryFiles = Hash.new
-          ARGS.each {|arch|
-            compressed = options.input + arch + subPath
-            uncompressed = Tempfile.new( compressed.basename )
-            uncompressed.write( Zlib::GzipReader.open( compressed ).read )
-            uncompressed.close
-            temporaryFiles[arch] = uncompressed.path
-          }
-          if true_for_all?( subPath, false, options ) {|filePath, arch|
-            FileUtils.identical?( temporaryFiles[arch], temporaryFiles[ARGS[0]] )
-          }
-            copy( firstPath, options.output + subPath, subPath, options )
+            copy( subPath )
           else
-            $log.warn( 'skipped: %s' % subPath )
-          end
-        # handle file type: other files
-        else
-          fileOutput = %x{ #{ FILE } -b "#{ firstPath }" }.chomp
-          case fileOutput
-          # handle file type: ar archives
-          when %r{^current ar archive}
-            check_consistency( subPath, %r{^current ar archive}, options )
-            lipo( subPath, options )
-          # handle file type: mach-o files
-          when %r{^Mach-O}
-            check_consistency( subPath, %r{^Mach-O}, options )
-            links = Hash.new
-            ARGS.each {|arch|
-              links[arch] = %x{
-                #{
-                  arch[-2..-1] == '64' ? 'otool64' : 'otool'
-                } -arch #{ arch } -LX #{
-                  options.input + arch + subPath
+            case subPath.extname
+            # Handle file type: header file
+            when '.h', '.hpp'
+              unless ( @options.output + subPath ).exist?
+                open( @options.output + subPath, 'w' ) {|headerTarget|
+                  @args.each {|arch|
+                    headerInput = @options.input + arch + subPath
+                    headerTarget.puts(
+                      '#ifdef __%s__' % arch,
+                      headerInput.open( 'r' ).read,
+                      '#endif'
+                    )
+                  }
                 }
-              }.entries.collect {|dependencyLine|
-                dependencyLine.strip.gsub(
-                  Regexp.new(
-                    ' \(compatibility version \d+(\.\d+){2}, ' +
-                    'current version \d+(\.\d+){2}\)'
-                  ), ''
+                @log.debug( 'merged: %s' % subPath )
+              else
+                @log.debug( 'exists: %s' % subPath )
+              end
+            # Handle file type: pkg-config file
+            when '.pc'
+              @args.each {|arch|
+                subArch = subPath.dirname + arch + subPath.basename
+                create_directory( @options.output, subArch.dirname )
+                special_copy(
+                  @options.input + arch + subPath,
+                  @options.output + subArch, subArch
                 )
-              }.reject {|dep|
-                dep[0..7] == '/usr/lib'
-              }.to_set
-            }
-            unless true_for_all?( subPath, false, options ) {|filePath, arch|
-              links[arch] == links[ARGS[0]]
-            }
-              $log.warn( 'skipped: %s' % subPath )
-              next
+              }
+            # Handle file type: gzip-compressed man page
+            when '.gz'
+              temporaryFiles = Hash.new
+              @args.each {|arch|
+                compressed = @options.input + arch + subPath
+                uncompressed = Tempfile.new( compressed.basename )
+                uncompressed.write( Zlib::GzipReader.open( compressed ).read )
+                uncompressed.close
+                temporaryFiles[arch] = uncompressed.path
+              }
+              if true_for_all?( subPath, false ) {|filePath, arch|
+                FileUtils.identical?(
+                  temporaryFiles[arch], temporaryFiles[@args[0]]
+                )
+              }
+                copy( subPath )
+              else
+                # The content of the compressed files differs
+                @log.warn( 'skipped: %s' % subPath )
+              end
+            else
+              fileOutput = %x{ #{ $FILE } -b "#{ firstPath }" }.chomp
+              case fileOutput
+              # Handle file type: ar archive
+              when %r{^current ar archive}
+                if consistent?( subPath, 'current ar archive' )
+                  lipo( subPath )
+                else
+                  @log.warn( 'skipped: %s' % subPath )
+                end
+              # Handle file type: mach-o binary
+              when %r{^Mach-O}
+                if consistent?( subPath, 'Mach-O' )
+                  links = Hash.new
+                  # Obtain the output of `otool -L`, get rid of everything we do
+                  # not need and stuff it into a set for later comparison
+                  @args.each {|arch|
+                    links[arch] = %x{
+                      #{
+                        arch.end_with?( '64' ) ? 'otool64' : 'otool'
+                      } -arch #{ arch } -LX #{
+                        @options.input + arch + subPath
+                      }
+                    }.entries.collect {|library|
+                      library.strip.gsub( %r{ \(compatibility version.+\)}, '' )
+                    }.reject {|line|
+                      line.start_with?( '/usr/lib' )
+                    }.to_set
+                  }
+                  unless true_for_all?( subPath, false ) {|filePath, arch|
+                    links[arch] == links[@args[0]]
+                  }
+                    # At least one single-architecture file was linked against a
+                    # library not all of the others were.
+                    @log.warn( 'skipped: %s' % subPath )
+                  else
+                    lipo( subPath )
+                  end
+                else
+                  @log.warn( 'skipped: %s' % subPath )
+                end
+              when 'Bourne shell script text executable'
+                if subPath.basename.to_s.end_with?( '-config' )
+                  # Handle file type: config script
+                  @args.each {|arch|
+                    subArch = subPath.dirname + arch + subPath.basename
+                    create_directory( @options.output, subArch.dirname )
+                    special_copy(
+                      @options.input + arch + subPath,
+                      @options.output + subArch, subArch
+                    )
+                  }
+                  make_wrapper( subPath, firstPath )
+                  @log.debug( 'wrapped : %s' % subPath )
+                end
+              else
+                # The file is a Bourne shell script, but we do not know how to
+                # merge it.
+                @log.warn( 'skipped: %s' % subPath )
+              end
             end
-            lipo( subPath, options )
-          when 'Bourne shell script text executable'
-            # DEBUG: no way to handle a shell script in general
-            next unless subPath.basename.to_s[-7..-1] == '-config'
-            # handle file type: config script
-            ARGS.each {|arch|
-              subArch = subPath.dirname + arch + subPath.basename
-              copyTarget = options.output + subArch
-              create_directory( copyTarget.dirname, subArch.dirname, options )
-              copy(
-                options.input + arch + subPath, copyTarget, subArch, options
-              )
-            }
-            make_wrapper( subPath, firstPath, options )
-            $log.debug( 'wrapped : %s' % subPath )
-          else
-            # DEBUG: unable to determine file type
-            $log.warn( 'skipped: %s' % subPath )
           end
         end
       end
     end
-  }
-}
+  end
 
-FileUtils.cd ORIGIN
-$log.info( 'shutting down' )
+
+  def initialize
+    @options = MergeArguments.parse( ARGV )
+
+    # ignore duplicates and trailing slashes
+    @args = ARGV.collect {|arg| arg.chomp( '/' )}.uniq
+
+    @ORIGIN = Pathname.pwd
+    processed = Set.new
+
+    # We need File with Apple's patches applied, otherwise we will not be able
+    # to recognize x86_64 Mach-O files
+    $FILE = '/usr/bin/file'
+
+    # Start logging
+    case @options.log
+    when 'STDERR'
+      @log = Logger.new( STDERR )
+    when 'STDOUT'
+      @log = Logger.new( STDOUT )
+    else
+      logTarget = Pathname.new( @options.log ).expand_path
+      if logTarget.writable?
+        @log = Logger.new( logTarget )
+      elsif !logTarget.exist? and logTarget.dirname.writable?
+        @log = Logger.new( logTarget )
+      else
+        @log = Logger.new( STDOUT )
+        @log.error( 'cannot create log file' )
+        raise( 'an error occurred.' )
+      end
+    end
+    @log.level = @options.level
+    @log.info( 'starting up' )
+
+    # Make sure we are given a valid root directory
+    unless @options.input.directory?
+      @log.fatal( 'invalid input directory: %s' % @options.input )
+      raise( 'an error occurred. see the log for details.' )
+    end
+
+    # Make sure the requested architectures have corresponding subdirectories
+    # in the the given input directory
+    unless true_for_all?( '.', true ) {|filePath, arch|
+      filePath.directory?
+    }
+      @log.fatal( 'architecture missing from input directory' )
+      raise( 'an error occurred. see the log for details.' )
+    end
+
+    # Walk the trees
+    @args.each {|architecture|
+      FileUtils.cd @options.input + architecture
+      Pathname.new( '.' ).find {|subPath|
+        @options.exclude.each {|excludedPattern|
+          if subPath.basename.fnmatch? excludedPattern
+            Find.prune
+            break
+          end
+        }
+        unless processed.include? subPath
+          processed << subPath
+          FileHandler.new( @options, @log, @args).handle_file( subPath )
+        end
+      }
+    }
+
+    FileUtils.cd @ORIGIN
+    @log.info( 'shutting down' )
+  end
+end
+
+Merge.new

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20070807/f9cfb201/attachment.html


More information about the macports-changes mailing list