mirror of
https://github.com/zhigang1992/RubyMotion.git
synced 2026-04-24 04:46:05 +08:00
add support for app bundle, codesign, .ipa archiving
This commit is contained in:
@@ -12,12 +12,13 @@ task :default => :simulator
|
||||
namespace :build do
|
||||
desc "Build the simulator version"
|
||||
task :simulator do
|
||||
Rubixir::BUILDER.compile(Rubixir::CONFIG, 'iPhoneSimulator')
|
||||
Rubixir::BUILDER.build(Rubixir::CONFIG, 'iPhoneSimulator')
|
||||
end
|
||||
|
||||
desc "Build the iOS version"
|
||||
task :ios do
|
||||
Rubixir::BUILDER.compile(Rubixir::CONFIG, 'iPhoneOS')
|
||||
Rubixir::BUILDER.build(Rubixir::CONFIG, 'iPhoneOS')
|
||||
Rubixir::BUILDER.codesign(Rubixir::CONFIG, 'iPhoneOS')
|
||||
end
|
||||
|
||||
desc "Build everything"
|
||||
@@ -27,8 +28,20 @@ end
|
||||
desc "Run the simulator"
|
||||
task :simulator => ['build:simulator'] do
|
||||
sim = File.join(Rubixir::CONFIG.platform_dir('iPhoneSimulator'), '/Developer/Applications/iPhone Simulator.app/Contents/MacOS/iPhone Simulator')
|
||||
app = File.join(Rubixir::CONFIG.build_dir, 'iPhoneSimulator/main')
|
||||
sh "\"#{sim}\" -SimulateApplication \"#{app}\""
|
||||
sh "\"#{sim}\" -SimulateApplication \"#{Rubixir::CONFIG.app_bundle('iPhoneSimulator', true)}\""
|
||||
end
|
||||
|
||||
desc "Create an .ipa package"
|
||||
task :package => ['build:ios'] do
|
||||
tmp = "/tmp/ipa_root"
|
||||
rm_rf tmp
|
||||
mkdir_p "#{tmp}/Payload"
|
||||
cp_r Rubixir::CONFIG.app_bundle('iPhoneOS'), "#{tmp}/Payload"
|
||||
Dir.chdir(tmp) do
|
||||
sh "/bin/chmod -R 755 Payload"
|
||||
sh "/usr/bin/zip -r archive.zip Payload"
|
||||
end
|
||||
cp "#{tmp}/archive.zip", Rubixir::CONFIG.archive
|
||||
end
|
||||
|
||||
desc "Deploy on the device"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module Rubixir
|
||||
class Builder
|
||||
def compile(config, platform)
|
||||
def build(config, platform)
|
||||
datadir = File.join(File.dirname(__FILE__), '../../../data')
|
||||
libstatic = File.join(datadir, 'libmacruby-static.a')
|
||||
archs = Dir.glob(File.join(datadir, platform, '*.bc')).map do |path|
|
||||
@@ -17,7 +17,8 @@ module Rubixir
|
||||
cxx = File.join(config.platform_dir(platform), 'Developer/usr/bin/g++')
|
||||
|
||||
build_dir = File.join(config.build_dir, platform)
|
||||
|
||||
|
||||
# Prepare the list of BridgeSupport files needed.
|
||||
bs_flags = ''
|
||||
config.frameworks.each do |framework|
|
||||
bs_path = File.join(datadir, 'BridgeSupport', framework + '.bridgesupport')
|
||||
@@ -26,10 +27,11 @@ module Rubixir
|
||||
end
|
||||
end
|
||||
|
||||
files_compiled = false
|
||||
objs = []
|
||||
objs_build_dir = File.join(build_dir, 'objs')
|
||||
FileUtils.mkdir_p(objs_build_dir)
|
||||
config.files.each do |path|
|
||||
obj = File.join(build_dir, "#{path}.o")
|
||||
obj = File.join(objs_build_dir, "#{path}.o")
|
||||
should_rebuild = (!File.exist?(obj) or File.mtime(path) > File.mtime(obj))
|
||||
|
||||
# Generate or retrieve init function.
|
||||
@@ -37,7 +39,6 @@ module Rubixir
|
||||
objs << [obj, init_func]
|
||||
|
||||
next unless should_rebuild
|
||||
files_compiled = true
|
||||
|
||||
arch_objs = []
|
||||
archs.each do |arch|
|
||||
@@ -46,14 +47,14 @@ module Rubixir
|
||||
raise "Can't locate kernel file" unless File.exist?(kernel)
|
||||
|
||||
# Prepare build_dir.
|
||||
bc = File.join(build_dir, "#{path}.#{arch}.bc")
|
||||
bc = File.join(objs_build_dir, "#{path}.#{arch}.bc")
|
||||
FileUtils.mkdir_p(File.dirname(bc))
|
||||
|
||||
# LLVM bitcode.
|
||||
sh "/usr/bin/env VM_KERNEL_PATH=\"#{kernel}\" #{ruby} #{bs_flags} --emit-llvm \"#{bc}\" #{init_func} \"#{path}\""
|
||||
|
||||
# Assembly.
|
||||
asm = File.join(build_dir, "#{path}.#{arch}.s")
|
||||
asm = File.join(objs_build_dir, "#{path}.#{arch}.s")
|
||||
llc_arch = case arch
|
||||
when 'i386'; 'x86'
|
||||
when 'x86_64'; 'x86-64'
|
||||
@@ -63,7 +64,7 @@ module Rubixir
|
||||
sh "#{llc} \"#{bc}\" -o=\"#{asm}\" -march=#{llc_arch} -relocation-model=pic -disable-fp-elim -jit-enable-eh"
|
||||
|
||||
# Object.
|
||||
arch_obj = File.join(build_dir, "#{path}.#{arch}.o")
|
||||
arch_obj = File.join(objs_build_dir, "#{path}.#{arch}.o")
|
||||
sh "#{cc} -fexceptions -c -arch #{arch} \"#{asm}\" -o \"#{arch_obj}\""
|
||||
|
||||
arch_objs << arch_obj
|
||||
@@ -74,10 +75,6 @@ module Rubixir
|
||||
sh "lipo -create #{arch_objs_list} -output \"#{obj}\""
|
||||
end
|
||||
|
||||
# No need to rebuild the main executable in case no source files changed as well as the static library.
|
||||
main_exec = File.join(build_dir, "main")
|
||||
return if !files_compiled and File.exist?(main_exec) and File.mtime(main_exec) > File.mtime(File.join(datadir, platform, 'libmacruby-static.a'))
|
||||
|
||||
# Generate main file.
|
||||
main_txt = <<EOS
|
||||
#import <UIKit/UIKit.h>
|
||||
@@ -126,21 +123,81 @@ EOS
|
||||
EOS
|
||||
|
||||
# Compile main file.
|
||||
main = File.join(build_dir, 'main.mm')
|
||||
File.open(main, 'w') { |io| io.write(main_txt) }
|
||||
main_o = File.join(build_dir, 'main.o')
|
||||
arch_flags = archs.map { |x| "-arch #{x}" }.join(' ')
|
||||
sh "#{cxx} \"#{main}\" #{arch_flags} -fexceptions -fblocks -isysroot \"#{sdk}\" -miphoneos-version-min=4.3 -fobjc-legacy-dispatch -fobjc-abi-version=2 -c -o \"#{main_o}\""
|
||||
main = File.join(objs_build_dir, 'main.mm')
|
||||
main_o = File.join(objs_build_dir, 'main.o')
|
||||
if !(File.exist?(main) and File.exist?(main_o) and File.read(main) == main_txt)
|
||||
File.open(main, 'w') { |io| io.write(main_txt) }
|
||||
sh "#{cxx} \"#{main}\" #{arch_flags} -fexceptions -fblocks -isysroot \"#{sdk}\" -miphoneos-version-min=4.3 -fobjc-legacy-dispatch -fobjc-abi-version=2 -c -o \"#{main_o}\""
|
||||
end
|
||||
|
||||
# Prepare bundle.
|
||||
bundle_path = File.join(build_dir, config.app_name + '.app')
|
||||
FileUtils.mkdir_p(bundle_path)
|
||||
|
||||
# Link executable.
|
||||
main_exec = File.join(bundle_path, config.app_name)
|
||||
objs_list = objs.map { |path, _| path }.unshift(main_o).map { |x| "\"#{x}\"" }.join(' ')
|
||||
frameworks = config.frameworks.map { |x| "-framework #{x}" }.join(' ')
|
||||
framework_stubs_objs = []
|
||||
config.frameworks.each do |framework|
|
||||
stubs_obj = File.join(datadir, platform, "#{framework}_stubs.o")
|
||||
framework_stubs_objs << stubs_obj if File.exist?(stubs_obj)
|
||||
framework_stubs_objs << "\"#{stubs_obj}\"" if File.exist?(stubs_obj)
|
||||
end
|
||||
sh "#{cxx} -o #{main_exec} #{objs_list} #{arch_flags} -isysroot \"#{sdk}\" -L#{File.join(datadir, platform)} -lmacruby-static -lobjc -licucore #{frameworks} #{framework_stubs_objs.join(' ')}"
|
||||
sh "#{cxx} -o \"#{main_exec}\" #{objs_list} #{arch_flags} -isysroot \"#{sdk}\" -L#{File.join(datadir, platform)} -lmacruby-static -lobjc -licucore #{frameworks} #{framework_stubs_objs.join(' ')}"
|
||||
|
||||
# Create bundle/Info.plist.
|
||||
bundle_info_plist = File.join(bundle_path, 'Info.plist')
|
||||
File.open(bundle_info_plist, 'w') { |io| io.write(config.plist_data) }
|
||||
sh "/usr/bin/plutil -convert binary1 \"#{bundle_info_plist}\""
|
||||
|
||||
# Create bundle/PkgInfo.
|
||||
File.open(File.join(bundle_path, 'PkgInfo'), 'w') { |io| io.write(config.pkginfo_data) }
|
||||
end
|
||||
|
||||
def codesign(config, platform)
|
||||
bundle_path = File.join(config.build_dir, platform, config.app_name + '.app')
|
||||
raise unless File.exist?(bundle_path)
|
||||
|
||||
# Create bundle/ResourceRules.plist.
|
||||
resource_rules_plist = File.join(bundle_path, 'ResourceRules.plist')
|
||||
File.open(resource_rules_plist, 'w') do |io|
|
||||
io.write(<<-PLIST)
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>rules</key>
|
||||
<dict>
|
||||
<key>.*</key>
|
||||
<true/>
|
||||
<key>Info.plist</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>10</real>
|
||||
</dict>
|
||||
<key>ResourceRules.plist</key>
|
||||
<dict>
|
||||
<key>omit</key>
|
||||
<true/>
|
||||
<key>weight</key>
|
||||
<real>100</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
end
|
||||
|
||||
# Copy the provisioning profile.
|
||||
File.open(File.join(bundle_path, "embedded.mobileprovision"), 'w') do |io|
|
||||
io.write(File.read(config.provisioning_profile))
|
||||
end
|
||||
|
||||
# Do the codesigning.
|
||||
sh "/usr/bin/codesign -f -s \"#{config.codesign_certificate}\" --resource-rules=\"#{resource_rules_plist}\" \"#{bundle_path}\""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
module Rubixir
|
||||
class Config
|
||||
attr_accessor :files, :platforms_dir, :sdk_version, :frameworks,
|
||||
:app_delegate_class, :app_name, :build_dir
|
||||
:app_delegate_class, :app_name, :build_dir, :resources_dir,
|
||||
:codesign_certificate, :provisioning_profile
|
||||
|
||||
def initialize(project_dir)
|
||||
@files = Dir.glob(File.join(project_dir, 'app/**/*.rb'))
|
||||
@@ -11,6 +12,8 @@ module Rubixir
|
||||
@app_delegate_class = 'AppDelegate'
|
||||
@app_name = 'My App'
|
||||
@build_dir = File.join(project_dir, 'build')
|
||||
@resources_dir = File.join(project_dir, 'resources')
|
||||
@bundle_signature = '????'
|
||||
end
|
||||
|
||||
def platform_dir(platform)
|
||||
@@ -21,5 +24,116 @@ module Rubixir
|
||||
File.join(platform_dir(platform), 'Developer/SDKs',
|
||||
platform + @sdk_version + '.sdk')
|
||||
end
|
||||
|
||||
def app_bundle(platform, exec=false)
|
||||
path = File.join(@build_dir, platform, @app_name + '.app')
|
||||
path = File.join(path, @app_name) if exec
|
||||
path
|
||||
end
|
||||
|
||||
def archive
|
||||
File.join(@build_dir, @app_name + '.ipa')
|
||||
end
|
||||
|
||||
def plist_data
|
||||
<<DATA
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>#{`sw_vers -buildVersion`.strip}</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>#{@app_name}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>#{@app_name}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.omgwtf.#{@app_name}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>#{@app_name}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleResourceSpecification</key>
|
||||
<string>ResourceRules.plist</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>#{@bundle_signature}</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>iPhoneOS</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvmgcc42</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>8H7</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>iphoneos</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>#{@sdk_version}</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>8H7</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>iphoneos#{@sdk_version}</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0402</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>4A2002a</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>#{@sdk_version}</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string></string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
DATA
|
||||
end
|
||||
|
||||
def pkginfo_data
|
||||
"AAPL#{@bundle_signature}"
|
||||
end
|
||||
|
||||
def codesign_certificate
|
||||
@codesign_certificate ||= begin
|
||||
certs = `/usr/bin/security -q find-certificate`.scan(/"iPhone Developer: [^"]+"/).uniq
|
||||
if certs.size == 0
|
||||
$stderr.puts "can't find any iPhone Developer certificate in the keychain"
|
||||
exit 1
|
||||
elsif certs.size > 1
|
||||
$stderr.puts "found #{certs.size} iPhone Developer certificates, will use the first one (#{certs[0]})"
|
||||
end
|
||||
certs[0][1..-2] # trim trailing `"` characters
|
||||
end
|
||||
end
|
||||
|
||||
def provisioning_profile
|
||||
@provisioning_profile ||= begin
|
||||
paths = Dir.glob(File.expand_path("~/Library/MobileDevice/Provisioning\ Profiles/*.mobileprovision"))
|
||||
if paths.size == 0
|
||||
$stderr.puts "can't find any provisioning profile"
|
||||
exit 1
|
||||
elsif paths.size > 1
|
||||
$stderr.puts "found #{paths.size} provisioning profiles, will use the first one (#{paths[0]})"
|
||||
end
|
||||
paths[0]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user