You are aware that the Mozilla project has a script that does exactly this, right? IIRC it's called unify.pl. -Kevin Ballard On Jun 24, 2007, at 6:57 AM, source_changes@macosforge.org wrote:
Revision 26461 Author pipping@macports.org Date 2007-06-24 06:57:23 -0700 (Sun, 24 Jun 2007) Log Message
initial upload of <merge.rb>
merge.rb is designed to merge two or more trees of single-arch destdirs into a single destdir of universal binaries.
build_coreutils.sh serves as a test, 'port destroot openssl +universal' can be used as well (port install zlib +universal is required)
see ./merge.rb --help for the usage Added Paths
users/pipping/merge.rb Diff
Added: users/pipping/merge.rb (0 => 26461)
--- users/pipping/merge.rb (rev 0) +++ users/pipping/merge.rb 2007-06-24 13:57:23 UTC (rev 26461) @@ -0,0 +1,221 @@ +#!/usr/bin/env ruby -w + +# Copyright (c) 2007 Elias Pipping +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +require 'fileutils' +require 'find' +require 'optparse' +require 'set' + +### +# we need GNU file with Apple's patches applied, available here: +# http://www.opensource.apple.com/darwinsource/Current/file-6.2/ patches/ +# +# Its output might span multiple lines, causing problems when parsing, even +# with `file -b`, hence we'll assume we're not passed universal binaries +# TODO: do a real check, maybe using `lipo -info` (fails on non- Mach-O files) +FILE="/usr/bin/file" + +class MergeArguments < Hash + def initialize(args) + super() + + # set up defaults + self[:dry_run] = false + self[:force] = false + self[:verbose] = false + + self[:exclude] = Set.new << '.svn' << 'CVS' + self[:root] = Dir.pwd + + opts = OptionParser.new do |opts| + opts.banner = "Usage: #$0 [options] <arch> <arch> [<arch> ...]" + + opts.on('-r', '--root DIRECTORY', 'specify root directory') do |root| + self[:root] = root || Dir.pwd + end + +# opts.on('-o', '--output DIRECTORY', 'specify output directory') do |out| +# self[:output] = File.expand_path(out) || File.join(self [:root], 'out') +# end + + opts.on('-e', '--exclude NAMES', 'specify names of files/ directories to exclude') do |exclude| + self[:exclude] = Set.new + exclude.split(" ").each do |item| + self[:exclude] << item + end + end + + opts.on('-d', '--dry-run', 'perform a dry run') do + self[:dry_run] = true + end + + opts.on('-f', '--force', 'force writing, ignoring collisions') do + self[:force] = true + end + + opts.on('-v', '--verbose', 'enable verbose output') do + self[:verbose] = true + end + + opts.on_tail('-h', '--help', 'display this help and exit') do + puts opts + exit + end + end + + opts.parse!(args) + end +end + +arguments = MergeArguments.new(ARGV) + +ARGS=ARGV.uniq + +def true_for_all? (filepath, args, &block) + result=true + ARGS.each {|arch| + result=false unless yield(File.join(args[:root], arch), filepath, arch) + } + return result +end + +def lipo (filepath,architectures,args) + lipoargs = Array.new + architectures.each {|arch| + lipoargs << sprintf('-arch %s %s', arch, File.join( + args[:root], arch, filepath + )) + } + lipotarget=File.join(args[:root], 'out', filepath) + lipocmd = sprintf( + 'lipo %s -create -o %s', + lipoargs.join(' '), + lipotarget + ) + if !File.exist?(lipotarget) or args[:force] + puts lipocmd if args[:verbose] + system lipocmd if !args[:dry_run] + end +end + +original_dir=Dir.pwd +processed=Set.new + +if File.directory?(File.expand_path(arguments[:root])) + ARGS.each {|architecture| + FileUtils.cd original_dir, :verbose => false + if File.directory? File.join( + File.expand_path(arguments[:root]), architecture + ) + FileUtils.cd( + File.join(arguments[:root], architecture), + :verbose => arguments[:verbose] + ) + Find.find('.') {|path| + arguments[:exclude].each {|exclude_me| + Find.prune if File.basename(path) == exclude_me + } + unless processed.include? path + my_dir=File.dirname(File.join(arguments[:root], 'out', path)) + # TODO: what if ppc/foo is a dir and i386/foo is a file (symlink)? (1) + unless File.exist? my_dir + FileUtils.mkdir_p( + my_dir, + :verbose => arguments[:verbose], + :noop => arguments[:dry_run] + ) + end + if true_for_all?(path, arguments) {|dir,filename,arch| + File.exist?(File.join(dir, filename)) + } + unless true_for_all?(path, arguments) {| dir,filename,arch| + File.directory?(File.join(dir, filename)) + } + + if true_for_all?(path, arguments) {|dir,filename,arch| + FileUtils.identical?( + File.join(dir, path), + File.join(arguments[:root], ARGS[0], path) + ) + } + copytarget=File.join(arguments[:root],'out',path) + if !File.exist?(copytarget) or arguments[:force] + FileUtils.cp( + File.join(arguments[:root],ARGS[0],path), + copytarget, + :preserve => !arguments[:force], + :verbose => arguments[:verbose], + :noop => arguments [:dry_run] + ) + end + else + # TODO: make this not-blindly lipo but check filetypes / archs + # TODO: beware of symlinks! + case File.basename path + when /\.dylib(\.\d*)*/, /\.so(\.\d*)*/, /\.a$/ + lipo(path,ARGS,arguments) + when /\.h$/, /\.hpp$/ + # TODO: handle header files + when /\.pc$/ + # TODO: handle pkgconfig files + when /\.sh$/ + # TODO: handle shell scripts + else + case %x{#{FILE} -b "#{File.join(arguments [:root],ARGS[0],path)}"} + when sprintf("Mach-O executable %s\n", ARGS[0]) + if true_for_all?(path, arguments) {| dir,filename,arch| + %x{#{FILE} -b "#{File.join(dir,path)}"} == sprintf("Mach-O executable %s\n", arch) + } + lipo(path,ARGS,arguments) + else + # one of the files is a mach-o file that matches its + # desired architecture but one of the others doesn't + end + # else + # TODO: deal with whatever else can differ + end + end + end + else + # TODO: at least one of the files is a directory (1) + end + else + # TODO: a file is not present in all trees. what's wrong? + end + processed << path + end + } + else + raise sprintf( + 'architecture missing from root directory: %s', + architecture + ) + end + } +else + raise sprintf( + 'invalid root directory: %s', + arguments[:root] + ) +end + +FileUtils.cd original_dir, :verbose => false Property changes on: users/pipping/merge.rb ___________________________________________________________________ Name: svn:executable + * _______________________________________________ macports-changes mailing list macports-changes@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo/macports-changes
-- Kevin Ballard http://kevin.sb.org eridius@macports.org http://www.tildesoft.com