Created
March 27, 2011 21:36
-
-
Save AlanQuatermain/889654 to your computer and use it in GitHub Desktop.
Updates root locations of .xcarchive bundles for given sub-packages or components within a PackageMaker document. Doesn't modify permissions, content inclusion, etc. Just the root content path, everywhere it lies.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
# == Usage | |
# | |
# Modifies an existing (and fully-set-up) PackageMaker document to point a package component | |
# at a new Xcode Archive root directory. | |
# | |
# This script is designed to work only with components based on Xcode 4 archives (folders ending | |
# in .xcarchive) containing a 'Products' subfolder which is used as the component's root folder. | |
# | |
# Each time a new build is made, a new .xcarchive folder is created. It is a fairly involved | |
# process to edit an existing PackageMaker document to point its root at the new archive and to | |
# set recommended permissions on everything within that folder. Internally however the document | |
# contains only relative path information along with (in a few different places) the path to the | |
# component root. This script will find the references to the package component designated by the | |
# --component-id parameter and replace the portion of that path ending in a .xcarchive item with a | |
# new .xcarchive path specified using the --component-root parameter. It can operate on multiple | |
# sub-packages/components at a time, meaning that you can supply multiple --component-id and | |
# --component-root parameters, however they *must* be paired, with each id preceding a root. | |
# | |
# Usage: | |
# | |
# update_package_archive [OPTIONS] | |
# | |
# -h, --help:: | |
# Show this message. | |
# -c, --component-id:: | |
# The identifier (i.e. com.mycompany.mycomponent) specifying the component to act upon. | |
# -r, --component-root:: | |
# The new .xcarchive folder to use as the component's root. Actually uses the 'Products' folder | |
# within the .xcarchive bundle. | |
# -d, --doc:: | |
# The PackageMaker document upon which to act. | |
# -v, --verbose:: | |
# Print out scanning/replacement information while running. | |
# -t, --trial-run:: | |
# Don't actually update any files. Implies -v. | |
# | |
# Example | |
# | |
# update_package_archive --doc /path/to/my.pmdoc --component-id com.me.opt1 --component-root /path/to/opt1.xcarchive --component-id com.me.opt2 --component-root /path/to/opt2.xcarchive | |
require 'getoptlong' | |
require 'rexml/document' | |
require 'rexml/formatters/transitive' | |
require 'rdoc/usage' | |
opts = GetoptLong.new( | |
['--help', '-h', GetoptLong::NO_ARGUMENT], | |
['--component-id', '-i', GetoptLong::REQUIRED_ARGUMENT], | |
['--component-root', '-r', GetoptLong::REQUIRED_ARGUMENT], | |
['--doc', '-d', GetoptLong::REQUIRED_ARGUMENT], | |
['--verbose', '-v', GetoptLong::NO_ARGUMENT], | |
['--trial-run', '-t', GetoptLong::NO_ARGUMENT] | |
) | |
@@Doc = nil | |
@@ComponentRefs = Hash.new | |
@@CurrentID = nil | |
@@ComponentPaths = Hash.new | |
@@Verbose = false | |
@@TrialRun = false | |
opts.each do |opt, arg| | |
case opt | |
when '--help' | |
RDoc::usage | |
return | |
when '--verbose' | |
@@Verbose = true | |
when '--trial-run' | |
@@TrialRun = true | |
@@Verbose = true | |
when '--doc' | |
@@Doc = arg | |
when '--component-id' | |
@@CurrentID = arg | |
when '--component-root' | |
if @@CurrentID.nil? | |
fail "Must specify a package ID before each package root." | |
return | |
end | |
path = arg.chomp('/') | |
fail "Root path '#{path}' does not refer to a .xcarchive file." unless File.basename(path) =~ /\.xcarchive$/ | |
@@ComponentRefs[@@CurrentID] = path | |
@@CurrentID = nil | |
else | |
fail "Unrecognized option '#{opt}'." | |
end | |
end | |
if @@ComponentRefs.empty? or @@Doc.nil? | |
RDoc::usage | |
return | |
end | |
# Look for the document & scan its contents | |
Dir.glob("#{@@Doc}/*.xml") do |filename| | |
# skip index.xml, it's not interesting to us | |
next if File.basename(filename) == 'index.xml' | |
# skip the contents files at this point | |
next if File.basename(filename) =~ /contents.xml$/ | |
# we now have a package info file, read it | |
File.open(filename) do |file| | |
xmldoc = REXML::Document.new(file) | |
# store component refs | |
xmldoc.elements.each("pkgref/contents/component") do |element| | |
unless element.attributes['path'] =~ /\.xcarchive\/Products/ | |
puts "Skipping non-xcarchive component '#{element.attributes['id']}'" if @@Verbose | |
next | |
end | |
puts "Found xcarchive component '#{element.attributes['id']}'" if @@Verbose | |
@@ComponentPaths[element.attributes['id']] = filename | |
end | |
# store package refs (not all packages install something PackageMaker considers a 'component') | |
xmldoc.elements.each("pkgref/config") do |config| | |
identifier = config.get_elements('identifier').first.text | |
puts "#{identifier}" | |
unless config.get_elements('installFrom').first.text =~ /\.xcarchive\/Products/ | |
puts "Skipping non-xcarchive package ref '#{identifier}" if @@Verbose | |
next | |
end | |
puts "Found xcarchive package '#{identifier}" if @@Verbose | |
@@ComponentPaths[identifier] = filename | |
end | |
end | |
end | |
# Now go through each requested package ID and update its root | |
@@ComponentRefs.each do |id, root| | |
filename = @@ComponentPaths[id] | |
fail "Package ID '#{id}' not found." if filename.nil? | |
xmldoc = nil | |
File.open(filename) do |file| | |
xmldoc = REXML::Document.new(file) | |
xmldoc.inspect | |
# might be a component that needs tweaking | |
xmldoc.elements.each("pkgref/contents/component[@id='#{id}']") do |element| | |
path = element.attributes['path'] | |
puts "Old path = #{path}" if @@Verbose | |
# Replace everything up to '.xcarchive' with the new root... | |
path.gsub!(/^.*\.xcarchive/, root) | |
puts "New path for component '#{id}' is '#{path}'" if @@Verbose | |
# ...and put it back into the element | |
element.add_attribute('path', path) unless @@TrialRun | |
puts "#{element.inspect}" if @@Verbose | |
end | |
# might be a package-- check if that's the case | |
next unless xmldoc.get_elements("/pkgref/config/identifier").first.text == id rescue next | |
# it's a package id-- almost certain to have an 'installFrom' | |
puts "#{xmldoc.get_elements('pkgref/config/installFrom')}" | |
xmldoc.elements.each('pkgref/config/installFrom') do |element| | |
path = element.text | |
puts "Old path = #{path}" if @@Verbose | |
# Replace everything up to '.xcarchive' with the new root... | |
path.gsub!(/^.*\.xcarchive/, root) | |
puts "New path for package '#{id}' is '#{path}'" if @@Verbose | |
# ...and put it back into the element | |
unless @@TrialRun | |
element.text = path | |
end | |
end | |
# close the file | |
file.close | |
end | |
# open it truncated for writing | |
unless @@TrialRun | |
File.open(filename, "w+") do |outfile| | |
# Write out the modified document | |
formatter = REXML::Formatters::Transitive.new(2) | |
formatter.write(xmldoc, outfile) | |
outfile.close | |
end | |
end | |
# now look at the xxx-contents.xml file -- only one change in here | |
contents = File.join(File.dirname(filename), File.basename(filename, '.xml') + '-contents.xml') | |
File.open(contents) do |file| | |
xmldoc = REXML::Document.new(file) | |
# first 'f' element below root (pkg-contents) is the one with a path (pt) attribute | |
element = xmldoc.get_elements("/pkg-contents/f").first | |
next if element.nil? | |
path = element.attributes['pt'] | |
puts "Old path = #{path}" if @@Verbose | |
if path =~ /\.xcarchive\/Products/ | |
# Replace everything up to '.xcarchive' with the new root | |
path.gsub!(/^.*\.xcarchive/, root) | |
puts "New path in '#{contents}' is '#{path}'" if @@Verbose | |
# ...and put it back into the element | |
element.add_attribute('pt', path) unless @@TrialRun | |
end | |
# close the file | |
file.close | |
end | |
# open it trancated for writing | |
unless @@TrialRun | |
File.open(contents, "w+") do |outfile| | |
# Write out the modified document | |
formatter = REXML::Formatters::Transitive.new(2) | |
formatter.write(xmldoc, outfile) | |
outfile.close | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment