Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

180 lines (150 sloc) 6.066 kB
#!/usr/bin/ruby
# MacRuby Deployer.
#
# This file is covered by the Ruby license.
#
# Copyright (C) 2009-2010, Apple Inc
require 'optparse'
require 'rbconfig'
class Deployer
NAME = File.basename(__FILE__)
def initialize(argv)
@stdlib = []
OptionParser.new do |opts|
opts.banner = "Usage: #{NAME} [options] application-bundle"
opts.on('--compile', 'Compile the bundle source code') { @compile = true }
opts.on('--embed', 'Embed MacRuby inside the bundle') { @embed = true }
opts.on('--no-stdlib', 'Do not embed the standard library') { @no_stdlib = true }
opts.on('--stdlib [LIB]', 'Embed only LIB from the standard library') { |lib| @stdlib << lib }
opts.on('--verbose', 'Log actions to standard out') { @verbose = true }
opts.on('-v', '--version', 'Display the version') do
puts RUBY_DESCRIPTION
exit 1
end
begin
opts.parse!(argv)
rescue OptionParser::InvalidOption => e
die e, opts
end
die opts if argv.size != 1
@app_bundle = argv[0]
end
ensure_path @app_bundle
ensure_path File.join(@app_bundle, 'Contents'), "Path `%s' doesn't seem to be a valid application bundle"
# Locate necessary programs.
@install_name_tool = locate('install_name_tool')
# Locate the MacRuby framework.
@macruby_framework_path = Config::CONFIG['libdir'].scan(/^.+MacRuby\.framework/)[0]
ensure_path @macruby_framework_path, "Cannot locate MacRuby.framework from rbconfig.rb"
@macruby_install_version = RbConfig::CONFIG["INSTALL_VERSION"]
end
def run
die "Nothing to do, please specify --compile or --embed" if !@compile and !@embed
die "--no-stdlib can only be used with --embed" if @no_stdlib and !@embed
die "--stdlib can only be used with --embed" if !@stdlib.empty? and !@embed
embed if @embed
compile if @compile
end
private
# FileUtils::Verbose doesn't work with MacRuby yet. However, it doesn't print
# out failures anyways, just the command. Use the command-line tools directly
# to circumvent this.
{ :cp_r => 'cp -R', :mkdir_p => 'mkdir -p', :rm_rf => 'rm -rf' }.each do |name, cmd|
module_eval <<-END, __FILE__, __LINE__ + 1
def #{name}(*args)
execute "#{cmd} \#{args.map { |a| "'\#{a}'" }.join(' ')}"
end
END
end
def app_frameworks
File.join(@app_bundle, 'Contents/Frameworks')
end
def app_macruby
File.join(app_frameworks, 'MacRuby.framework')
end
def app_macruby_usr
File.join(app_macruby, 'Versions', @macruby_install_version, 'usr')
end
def macruby_usr
@macruby_usr ||= ensure_path(File.join(@macruby_framework_path, 'Versions', @macruby_install_version, 'usr'))
end
def compile_files
Dir.glob(File.join(@app_bundle, 'Contents/Resources/*.rb'))
end
MACRUBYC = File.join(RbConfig::CONFIG['bindir'], 'macrubyc')
def compile
compile_files.each do |source|
base = File.basename(source, '.rb')
next if base == 'rb_main'
obj = File.join(File.dirname(source), base + '.rbo')
if !File.exist?(obj) or File.mtime(source) > File.mtime(obj)
compile_cmd = "#{MACRUBYC} -C \"#{source}\" -o \"#{obj}\""
# Use Xcode ARCHS env var to determine which archs to compile for
compile_cmd += ENV['ARCHS'].strip.split.map { |a| " -a #{a}" }.join if ENV['ARCHS']
execute(compile_cmd, "Can't compile \"#{source}\"")
end
rm_rf(source)
end
fix_install_name if File.exist?(app_macruby)
end
STDLIB_PATTERN = "lib/ruby/{,site_ruby/}1.9.*{,/universal-darwin*}"
KEEP_STDLIB_PATTERN = "{%s}{,{.,/**/*.}{rb,rbo,bundle}}"
def embed
# Copy MacRuby.framework inside MyApp.app/Contents/Frameworks.
mkdir_p(app_frameworks)
rm_rf(app_macruby)
cp_r(@macruby_framework_path, app_frameworks)
# Delete unnecessary things in the MacRuby.framework copy.
dirs = ['bin', 'include', 'lib/libmacruby-static.a', 'share']
dirs << 'lib/ruby' if @no_stdlib
dirs << 'lib/ruby/Gems' # TODO add gems support
dirs.each { |x| rm_rf(File.join(app_macruby_usr, x)) }
# Only keep specific libs from stdlib.
unless @stdlib.empty?
lib = File.join(app_macruby_usr, STDLIB_PATTERN)
all, keep = ["**/*", KEEP_STDLIB_PATTERN % @stdlib.join(',')].map { |p| Dir.glob(File.join(lib, p)) }
keep.reject! { |f| File.extname(f) == '.rb' && keep.include?("#{f}o") } # only keep .rbo for duplicates
all.select { |f| keep.grep(/^#{f}/).empty? }.each { |x| rm_rf(x) }
end
# Only keep the Current version of the MacRuby.framework copy.
Dir.glob(File.join(app_macruby, 'Versions/*')).select { |x|
base = File.basename(x)
base != @macruby_install_version and base != 'Current'
}.each { |x|
rm_rf(x)
}
# Wait with fixing install name until all binaries are available.
fix_install_name unless @compile
end
# Hack the application binaries to link against the MacRuby.framework copy.
def fix_install_name
patterns = [File.join(@app_bundle, 'Contents/MacOS/*'),
File.join(app_macruby_usr, 'lib/ruby/**/*.{bundle,rbo}'),
File.join(@app_bundle, 'Contents/Resources/*.rbo')]
patterns.each do |pat|
Dir.glob(pat).each do |bin|
execute("#{@install_name_tool} -change #{macruby_usr}/lib/libmacruby.dylib @executable_path/../Frameworks/MacRuby.framework/Versions/#{@macruby_install_version}/usr/lib/libmacruby.dylib '#{bin}'")
end
end
end
def execute(line, error_message = nil)
$stdout.puts(line) if @verbose
ret = `#{line}`
die(error_message || "Error when executing `#{line}'") unless $?.success?
ret
end
def locate(progname)
path = `which #{progname}`.strip
die "Can't locate program `#{progname}'" if path.empty?
path
end
def ensure_path(path, message = "Path does not exist `%s'")
die(message % path) unless File.exist?(path)
path
end
def die(*args)
$stderr.puts args
exit 1
end
end
Deployer.new(ARGV).run
Jump to Line
Something went wrong with that request. Please try again.