Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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.