mirror of
https://github.com/zhigang1992/RubyMotion.git
synced 2026-06-13 17:46:31 +08:00
Added support to build iOS frameworks through the 'ios-framework' template
This commit is contained in:
338
lib/motion/project/template/ios-framework.rb
Normal file
338
lib/motion/project/template/ios-framework.rb
Normal file
@@ -0,0 +1,338 @@
|
||||
# encoding: utf-8
|
||||
|
||||
# Copyright (c) 2012, HipByte SPRL and contributors
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
require 'motion/project/app'
|
||||
require 'motion/util/version'
|
||||
|
||||
App = Motion::Project::App
|
||||
App.template = :'ios-framework'
|
||||
|
||||
require 'motion/project'
|
||||
require 'motion/project/template/ios-framework/config'
|
||||
require 'motion/project/template/ios-framework/builder'
|
||||
|
||||
desc "Build the project, then run the simulator"
|
||||
task :default => :simulator
|
||||
|
||||
desc "Build everything"
|
||||
task :build => ['build:simulator', 'build:device']
|
||||
|
||||
namespace :build do
|
||||
def pre_build_actions(platform)
|
||||
# TODO: Ensure Info.plist gets regenerated on each build so it has ints for
|
||||
# Instruments and strings for normal builds.
|
||||
rm_f File.join(App.config.app_bundle(platform), 'Info.plist')
|
||||
|
||||
# TODO this should go into a iOS specific Builder class which performs this
|
||||
# check before building.
|
||||
App.config.resources_dirs.flatten.each do |dir|
|
||||
next unless File.exist?(dir)
|
||||
Dir.entries(dir).grep(/^Resources$/i).each do |basename|
|
||||
path = File.join(dir, basename)
|
||||
if File.directory?(path)
|
||||
suggestion = basename == 'Resources' ? 'Assets' : 'assets'
|
||||
App.fail "An iOS application cannot be installed if it contains a " \
|
||||
"directory called `resources'. Please rename the " \
|
||||
"directory at path `#{path}' to, for instance, " \
|
||||
"`#{File.join(dir, suggestion)}'."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "Build the simulator version"
|
||||
task :simulator do
|
||||
pre_build_actions('iPhoneSimulator')
|
||||
App.build('iPhoneSimulator')
|
||||
end
|
||||
|
||||
desc "Build the device version"
|
||||
task :device do
|
||||
pre_build_actions('iPhoneOS')
|
||||
App.build('iPhoneOS')
|
||||
# App.codesign('iPhoneOS')
|
||||
end
|
||||
end
|
||||
|
||||
# desc "Run the simulator"
|
||||
# task :simulator do
|
||||
# deployment_target = Motion::Util::Version.new(App.config.deployment_target)
|
||||
|
||||
# target = ENV['target']
|
||||
# if target && Motion::Util::Version.new(target) < deployment_target
|
||||
# App.fail "It is not possible to simulate an SDK version (#{target}) " \
|
||||
# "lower than the app's deployment target (#{deployment_target})"
|
||||
# end
|
||||
# target ||= App.config.sdk_version
|
||||
|
||||
# family_int =
|
||||
# if family = ENV['device_family']
|
||||
# App.config.device_family_int(family.downcase.intern)
|
||||
# else
|
||||
# App.config.device_family_ints[0]
|
||||
# end
|
||||
|
||||
# retina = ENV['retina']
|
||||
# if retina && retina.strip.downcase == 'false' && family_int == 1 # iPhone only
|
||||
# ios_7 = Motion::Util::Version.new('7')
|
||||
# if Motion::Util::Version.new(target) >= ios_7
|
||||
# if deployment_target < ios_7
|
||||
# App.fail "In order to simulate on a non-retina device, please use " \
|
||||
# "the `target' option to specify the simulated SDK version. " \
|
||||
# "E.g.: rake target=#{deployment_target} retina=false"
|
||||
# else
|
||||
# App.fail "It is not possible to simulate on a non-retina device " \
|
||||
# "when not deploying to iOS < 7."
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# if ENV['background_fetch']
|
||||
# modes = App.config.info_plist['UIBackgroundModes']
|
||||
# if modes.nil? || !modes.include?('fetch')
|
||||
# App.fail "In order to launch the application in `background fetch' " \
|
||||
# "mode, you will need to configure your application to enable " \
|
||||
# "it by adding: app.info_plist['UIBackgroundModes'] = ['fetch']"
|
||||
# end
|
||||
# end
|
||||
|
||||
# unless ENV["skip_build"]
|
||||
# Rake::Task["build:simulator"].invoke
|
||||
# end
|
||||
# app = App.config.app_bundle('iPhoneSimulator')
|
||||
|
||||
# if ENV['TMUX']
|
||||
# tmux_default_command = `tmux show-options -g default-command`.strip
|
||||
# unless tmux_default_command.include?("reattach-to-user-namespace")
|
||||
# App.warn(<<END
|
||||
|
||||
# It appears you are using tmux without 'reattach-to-user-namespace', the simulator might not work properly. You can either disable tmux or run the following commands:
|
||||
|
||||
# $ brew install reattach-to-user-namespace
|
||||
# $ echo 'set-option -g default-command "reattach-to-user-namespace -l $SHELL"' >> ~/.tmux.conf
|
||||
|
||||
# END
|
||||
# )
|
||||
# end
|
||||
# end
|
||||
|
||||
# # Cleanup the simulator application sandbox, to avoid having old resource files there.
|
||||
# if ENV['clean']
|
||||
# sim_apps = File.expand_path("~/Library/Application Support/iPhone Simulator/#{target}/Applications")
|
||||
# Dir.glob("#{sim_apps}/**/*.app").each do |app_bundle|
|
||||
# if File.basename(app_bundle) == File.basename(app)
|
||||
# rm_rf File.dirname(app_bundle)
|
||||
# break
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# device_name = ENV["device_name"]
|
||||
# simulate_device = App.config.device_family_string(device_name, family_int, target, retina)
|
||||
|
||||
# # Launch the simulator.
|
||||
# xcode = App.config.xcode_dir
|
||||
# env = "DYLD_FRAMEWORK_PATH=\"#{xcode}/../Frameworks\":\"#{xcode}/../OtherFrameworks\""
|
||||
# env << ' SIM_SPEC_MODE=1' if App.config.spec_mode
|
||||
# sim = File.join(App.config.bindir, 'ios/sim')
|
||||
# debug = (ENV['debug'] ? 1 : (App.config.spec_mode ? '0' : '2'))
|
||||
# app_args = (ENV['args'] or '')
|
||||
# App.info 'Simulate', app
|
||||
# at_exit { system("stty echo") } if $stdout.tty? # Just in case the simulator launcher crashes and leaves the terminal without echo.
|
||||
# Signal.trap(:INT) { } if ENV['debug']
|
||||
# system "#{env} #{sim} #{debug} #{family_int} \"#{simulate_device}\" #{target} \"#{xcode}\" \"#{app}\" #{app_args}"
|
||||
# App.config.print_crash_message if $?.exitstatus != 0 && !App.config.spec_mode
|
||||
# exit($?.exitstatus)
|
||||
# end
|
||||
|
||||
desc "Create an .ipa archive"
|
||||
task :archive => ['build:device'] do
|
||||
App.archive
|
||||
end
|
||||
|
||||
namespace :archive do
|
||||
desc "Create an .ipa archive for distribution (AppStore)"
|
||||
task :distribution do
|
||||
App.config_without_setup.build_mode = :release
|
||||
App.config_without_setup.distribution_mode = true
|
||||
Rake::Task["archive"].invoke
|
||||
end
|
||||
end
|
||||
|
||||
# desc "Same as 'spec:simulator'"
|
||||
# task :spec => ['spec:simulator']
|
||||
|
||||
# namespace :spec do
|
||||
# desc "Run the test/spec suite on the simulator"
|
||||
# task :simulator do
|
||||
# App.config_without_setup.spec_mode = true
|
||||
# Rake::Task["simulator"].invoke
|
||||
# end
|
||||
|
||||
# desc "Run the test/spec suite on the device"
|
||||
# task :device do
|
||||
# App.config_without_setup.spec_mode = true
|
||||
# ENV['debug'] ||= '1'
|
||||
# Rake::Task["device"].invoke
|
||||
# end
|
||||
# end
|
||||
|
||||
# $deployed_app_path = nil
|
||||
|
||||
# desc "Deploy on the device"
|
||||
# task :device => :archive do
|
||||
# App.info 'Deploy', App.config.archive
|
||||
# device_id = (ENV['id'] or App.config.device_id)
|
||||
# unless App.config.provisioned_devices.include?(device_id)
|
||||
# App.fail "Device ID `#{device_id}' not provisioned in profile `#{App.config.provisioning_profile}'"
|
||||
# end
|
||||
# env = "XCODE_DIR=\"#{App.config.xcode_dir}\""
|
||||
# deploy = File.join(App.config.bindir, 'ios/deploy')
|
||||
# flags = Rake.application.options.trace ? '-d' : ''
|
||||
# Signal.trap(:INT) { } if ENV['debug']
|
||||
# cmd = "#{env} #{deploy} #{flags} \"#{device_id}\" \"#{App.config.archive}\""
|
||||
# if ENV['install_only']
|
||||
# $deployed_app_path = `#{cmd}`.strip
|
||||
# else
|
||||
# sh(cmd)
|
||||
# end
|
||||
# end
|
||||
|
||||
# desc "Create a .a static library"
|
||||
# task :static do
|
||||
# libs = %w{iPhoneSimulator iPhoneOS}.map do |platform|
|
||||
# '"' + App.build(platform, :static => true) + '"'
|
||||
# end
|
||||
# fat_lib = File.join(App.config.build_dir, App.config.name + '-universal.a')
|
||||
# App.info 'Create', fat_lib
|
||||
# sh "/usr/bin/lipo -create #{libs.join(' ')} -output \"#{fat_lib}\""
|
||||
# end
|
||||
|
||||
# desc "Create a .framework"
|
||||
# task :framework do
|
||||
# libs = %w{iPhoneSimulator iPhoneOS}.map do |platform|
|
||||
# '"' + App.build(platform, :framework => true) + '"'
|
||||
# end
|
||||
# # fat_lib = File.join(App.config.build_dir, App.config.name + '-universal.a')
|
||||
# # App.info 'Create', fat_lib
|
||||
# # sh "/usr/bin/lipo -create #{libs.join(' ')} -output \"#{fat_lib}\""
|
||||
# end
|
||||
|
||||
# IOS_SIM_INSTRUMENTS_TEMPLATES = [
|
||||
# 'Allocations', 'Leaks', 'Activity Monitor',
|
||||
# 'Zombies', 'Time Profiler', 'System Trace', 'Automation',
|
||||
# 'File Activity', 'Core Data'
|
||||
# ]
|
||||
|
||||
# IOS_DEVICE_INSTRUMENTS_TEMPLATES = [
|
||||
# 'Allocations', 'Leaks', 'Activity Monitor',
|
||||
# 'Zombies', 'Time Profiler', 'System Trace', 'Automation',
|
||||
# 'Energy Diagnostics', 'Network', 'System Usage', 'Core Animation',
|
||||
# 'OpenGL ES Driver', 'OpenGL ES Analysis'
|
||||
# ]
|
||||
|
||||
# desc "Same as profile:simulator"
|
||||
# task :profile => ['profile:simulator']
|
||||
|
||||
# namespace :profile do
|
||||
# desc "Run a build on the simulator through Instruments"
|
||||
# task :simulator do
|
||||
# ENV['__USE_DEVICE_INT__'] = '1'
|
||||
# Rake::Task['build:simulator'].invoke
|
||||
|
||||
# plist = App.config.profiler_config_plist('iPhoneSimulator', ENV['args'], ENV['template'], IOS_SIM_INSTRUMENTS_TEMPLATES)
|
||||
# plist['com.apple.xcode.simulatedDeviceFamily'] = App.config.device_family_ints.first
|
||||
# plist['com.apple.xcode.SDKPath'] = App.config.sdk('iPhoneSimulator')
|
||||
# plist['optionalData']['launchOptions']['architectureType'] = 0
|
||||
# plist['deviceIdentifier'] = App.config.sdk('iPhoneSimulator')
|
||||
# App.profile('iPhoneSimulator', plist)
|
||||
# end
|
||||
|
||||
# namespace :simulator do
|
||||
# desc 'List all built-in Simulator Instruments templates'
|
||||
# task :templates do
|
||||
# puts "Built-in Simulator Instruments templates:"
|
||||
# IOS_SIM_INSTRUMENTS_TEMPLATES.each do |template|
|
||||
# puts "* #{template}"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# desc "Run a build on the device through Instruments"
|
||||
# task :device do
|
||||
# ENV['__USE_DEVICE_INT__'] = '1'
|
||||
|
||||
# # Create a build that allows debugging but doesn't start a debugger on deploy.
|
||||
# App.config.entitlements['get-task-allow'] = true
|
||||
# ENV['install_only'] = '1'
|
||||
# Rake::Task['device'].invoke
|
||||
|
||||
# if $deployed_app_path.nil? || $deployed_app_path.empty?
|
||||
# App.fail 'Unable to determine remote app path'
|
||||
# end
|
||||
|
||||
# plist = App.config.profiler_config_plist('iPhoneOS', ENV['args'], ENV['template'], IOS_DEVICE_INSTRUMENTS_TEMPLATES, false)
|
||||
# plist['absolutePathOfLaunchable'] = File.join($deployed_app_path, App.config.bundle_name)
|
||||
# plist['deviceIdentifier'] = (ENV['id'] or App.config.device_id)
|
||||
# App.profile('iPhoneOS', plist)
|
||||
# end
|
||||
|
||||
# namespace :device do
|
||||
# desc 'List all built-in device Instruments templates'
|
||||
# task :templates do
|
||||
# puts "Built-in device Instruments templates:"
|
||||
# IOS_DEVICE_INSTRUMENTS_TEMPLATES.each do |template|
|
||||
# puts "* #{template}"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# desc "Same as crashlog:simulator"
|
||||
# task :crashlog => 'crashlog:simulator'
|
||||
|
||||
# namespace :crashlog do
|
||||
# desc "Open the latest crash report generated by the app in the simulator"
|
||||
# task :simulator => :__local_crashlog
|
||||
|
||||
# desc "Retrieve and symbolicate crash logs generated by the app on the device, and open the latest generated one"
|
||||
# task :device do
|
||||
# device_id = (ENV['id'] or App.config.device_id)
|
||||
# crash_reports_dir = File.expand_path("~/Library/Logs/RubyMotion Device")
|
||||
# mkdir_p crash_reports_dir
|
||||
# deploy = File.join(App.config.bindir, 'ios/deploy')
|
||||
# env = "XCODE_DIR=\"#{App.config.xcode_dir}\" CRASH_REPORTS_DIR=\"#{crash_reports_dir}\""
|
||||
# flags = Rake.application.options.trace ? '-d' : ''
|
||||
# cmd = "#{env} #{deploy} -l #{flags} \"#{device_id}\" \"#{App.config.archive}\""
|
||||
# system(cmd)
|
||||
|
||||
# # Open the latest generated one.
|
||||
# logs = Dir.glob(File.join(crash_reports_dir, "#{App.config.name}_*"))
|
||||
# unless logs.empty?
|
||||
# sh "/usr/bin/open -a Console \"#{logs.last}\""
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
696
lib/motion/project/template/ios-framework/builder.rb
Normal file
696
lib/motion/project/template/ios-framework/builder.rb
Normal file
@@ -0,0 +1,696 @@
|
||||
# encoding: utf-8
|
||||
|
||||
# Copyright (c) 2012, HipByte SPRL and contributors
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
require 'motion/project/builder'
|
||||
require 'motion/project/paralel_builder'
|
||||
require 'motion/project/dependency'
|
||||
|
||||
module Motion; module Project
|
||||
class Builder
|
||||
def archive(config)
|
||||
# Create .ipa archive.
|
||||
app_bundle = config.app_bundle('iPhoneOS')
|
||||
archive = config.archive
|
||||
if !File.exist?(archive) or File.mtime(app_bundle) > File.mtime(archive)
|
||||
App.info 'Create', archive
|
||||
tmp = "/tmp/ipa_root"
|
||||
sh "/bin/rm -rf #{tmp}"
|
||||
sh "/bin/mkdir -p #{tmp}/Payload"
|
||||
sh "/bin/cp -r \"#{app_bundle}\" #{tmp}/Payload"
|
||||
Dir.chdir(tmp) do
|
||||
sh "/bin/chmod -R 755 Payload"
|
||||
sh "/usr/bin/zip -q -r archive.zip Payload"
|
||||
end
|
||||
sh "/bin/cp #{tmp}/archive.zip \"#{archive}\""
|
||||
end
|
||||
|
||||
# Create manifest file (if needed).
|
||||
manifest_plist = File.join(config.versionized_build_dir('iPhoneOS'), 'manifest.plist')
|
||||
manifest_plist_data = config.manifest_plist_data
|
||||
if manifest_plist_data and (!File.exist?(manifest_plist) or File.mtime(config.project_file) > File.mtime(manifest_plist))
|
||||
App.info 'Create', manifest_plist
|
||||
File.open(manifest_plist, 'w') { |io| io.write(manifest_plist_data) }
|
||||
end
|
||||
end
|
||||
|
||||
def codesign(config, platform)
|
||||
bundle_path = config.app_bundle(platform)
|
||||
raise unless File.exist?(bundle_path)
|
||||
|
||||
# Create bundle/ResourceRules.plist.
|
||||
resource_rules_plist = File.join(bundle_path, 'ResourceRules.plist')
|
||||
unless File.exist?(resource_rules_plist)
|
||||
App.info 'Create', resource_rules_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
|
||||
end
|
||||
|
||||
# Copy the provisioning profile.
|
||||
bundle_provision = File.join(bundle_path, "embedded.mobileprovision")
|
||||
App.info 'Create', bundle_provision
|
||||
FileUtils.cp config.provisioning_profile, bundle_provision
|
||||
|
||||
# Codesign.
|
||||
codesign_cmd = "CODESIGN_ALLOCATE=\"#{File.join(config.platform_dir(platform), 'Developer/usr/bin/codesign_allocate')}\" /usr/bin/codesign"
|
||||
if File.mtime(config.project_file) > File.mtime(bundle_path) \
|
||||
or !system("#{codesign_cmd} --verify \"#{bundle_path}\" >& /dev/null")
|
||||
App.info 'Codesign', bundle_path
|
||||
entitlements = File.join(config.versionized_build_dir(platform), "Entitlements.plist")
|
||||
File.open(entitlements, 'w') { |io| io.write(config.entitlements_data) }
|
||||
sh "#{codesign_cmd} -f -s \"#{config.codesign_certificate}\" --resource-rules=\"#{resource_rules_plist}\" --entitlements #{entitlements} \"#{bundle_path}\""
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def build(config, platform, opts)
|
||||
datadir = config.datadir
|
||||
unless File.exist?(File.join(datadir, platform))
|
||||
$stderr.puts "This version of RubyMotion does not support `#{platform}'"
|
||||
exit 1
|
||||
end
|
||||
archs = config.archs[platform]
|
||||
|
||||
# static_library = opts.delete(:static)
|
||||
|
||||
ruby = File.join(config.bindir, 'ruby')
|
||||
llc = File.join(config.bindir, 'llc')
|
||||
@nfd = File.join(config.bindir, 'nfd')
|
||||
|
||||
# if config.spec_mode and (config.spec_files - config.spec_core_files).empty?
|
||||
# App.fail "No spec files in `#{config.specs_dir}'"
|
||||
# end
|
||||
|
||||
config.resources_dirs.flatten!
|
||||
config.resources_dirs.uniq!
|
||||
|
||||
# Locate SDK and compilers.
|
||||
sdk = config.sdk(platform)
|
||||
cc = config.locate_compiler(platform, 'clang')
|
||||
cxx = config.locate_compiler(platform, 'clang++')
|
||||
|
||||
build_dir = File.join(config.versionized_build_dir(platform))
|
||||
App.info 'Build', build_dir
|
||||
|
||||
# Prepare the list of BridgeSupport files needed.
|
||||
bs_files = config.bridgesupport_files
|
||||
|
||||
# Build vendor libraries.
|
||||
vendor_libs = []
|
||||
config.vendor_projects.each do |vendor_project|
|
||||
vendor_project.build(platform)
|
||||
vendor_libs.concat(vendor_project.libs)
|
||||
bs_files.concat(vendor_project.bs_files)
|
||||
end
|
||||
|
||||
# Validate common build directory.
|
||||
if !File.directory?(Builder.common_build_dir) or !File.writable?(Builder.common_build_dir)
|
||||
$stderr.puts "Cannot write into the `#{Builder.common_build_dir}' directory, please remove or check permissions and try again."
|
||||
exit 1
|
||||
end
|
||||
|
||||
# TODO Framework: Should don't accept embedded (umbrella) frameworks
|
||||
# Prepare embedded and external frameworks BridgeSupport files (OSX-only).
|
||||
if config.respond_to?(:embedded_frameworks) && config.respond_to?(:external_frameworks)
|
||||
embedded_frameworks = config.embedded_frameworks.map { |x| File.expand_path(x) }
|
||||
external_frameworks = config.external_frameworks.map { |x| File.expand_path(x) }
|
||||
(embedded_frameworks + external_frameworks).each do |path|
|
||||
headers = Dir.glob(File.join(path, 'Headers/**/*.h'))
|
||||
bs_file = File.join(Builder.common_build_dir, File.expand_path(path) + '.bridgesupport')
|
||||
if !File.exist?(bs_file) or File.mtime(path) > File.mtime(bs_file)
|
||||
FileUtils.mkdir_p(File.dirname(bs_file))
|
||||
config.gen_bridge_metadata(platform, headers, bs_file, '', [])
|
||||
end
|
||||
bs_files << bs_file
|
||||
end
|
||||
else
|
||||
embedded_frameworks = external_frameworks = []
|
||||
end
|
||||
|
||||
# Build object files.
|
||||
objs_build_dir = File.join(build_dir, 'objs')
|
||||
FileUtils.mkdir_p(objs_build_dir)
|
||||
any_obj_file_built = false
|
||||
project_files = Dir.glob("**/*.rb").map{ |x| File.expand_path(x) }
|
||||
is_default_archs = (archs == config.default_archs[platform])
|
||||
rubyc_bs_flags = bs_files.map { |x| "--uses-bs \"" + x + "\" " }.join(' ')
|
||||
|
||||
build_file = Proc.new do |files_build_dir, path|
|
||||
rpath = path
|
||||
path = File.expand_path(path)
|
||||
if is_default_archs && !project_files.include?(path)
|
||||
files_build_dir = File.expand_path(File.join(Builder.common_build_dir, files_build_dir))
|
||||
end
|
||||
obj = File.join(files_build_dir, "#{path}.o")
|
||||
should_rebuild = (!File.exist?(obj) \
|
||||
or File.mtime(path) > File.mtime(obj) \
|
||||
or File.mtime(ruby) > File.mtime(obj))
|
||||
|
||||
# Generate or retrieve init function.
|
||||
init_func = should_rebuild ? "MREP_#{`/usr/bin/uuidgen`.strip.gsub('-', '')}" : `#{config.locate_binary('nm')} \"#{obj}\"`.scan(/T\s+_(MREP_.*)/)[0][0]
|
||||
|
||||
if should_rebuild
|
||||
App.info 'Compile', rpath
|
||||
FileUtils.mkdir_p(File.dirname(obj))
|
||||
arch_objs = []
|
||||
archs.each do |arch|
|
||||
# Locate arch kernel.
|
||||
kernel = File.join(datadir, platform, "kernel-#{arch}.bc")
|
||||
raise "Can't locate kernel file" unless File.exist?(kernel)
|
||||
|
||||
# Assembly.
|
||||
asm = File.join(files_build_dir, "#{path}.#{arch}.s")
|
||||
arm64 = false
|
||||
compiler_exec_arch = case arch
|
||||
when /^arm/
|
||||
(arm64 = (arch == 'arm64')) ? 'x86_64' : 'i386'
|
||||
else
|
||||
arch
|
||||
end
|
||||
sh "/usr/bin/env VM_PLATFORM=\"#{platform}\" VM_KERNEL_PATH=\"#{kernel}\" VM_OPT_LEVEL=\"#{config.opt_level}\" /usr/bin/arch -arch #{compiler_exec_arch} #{ruby} #{rubyc_bs_flags} --emit-llvm \"#{asm}\" #{init_func} \"#{path}\""
|
||||
|
||||
# Object
|
||||
arch_obj = File.join(files_build_dir, "#{path}.#{arch}.o")
|
||||
if arm64
|
||||
# At the time of this writing Apple hasn't yet contributed the source code of the LLVM backend for the "arm64" architecture, so the RubyMotion compiler can't emit proper assembly yet. We work around this limitation by generating bitcode instead and giving it to the linker. Ugly but should be temporary (right?).
|
||||
@dummy_object_file ||= begin
|
||||
src_path = '/tmp/__dummy_object_file__.c'
|
||||
obj_path = '/tmp/__dummy_object_file__.o'
|
||||
File.open(src_path, 'w') { |io| io.puts "static int foo(void) { return 42; }" }
|
||||
sh "#{cc} -c #{src_path} -o #{obj_path} -arch arm64 -miphoneos-version-min=7.0"
|
||||
obj_path
|
||||
end
|
||||
ld_path = File.join(App.config.xcode_dir, 'Toolchains/XcodeDefault.xctoolchain/usr/bin/ld')
|
||||
sh "#{ld_path} \"#{asm}\" \"#{@dummy_object_file}\" -arch arm64 -r -o \"#{arch_obj}\""
|
||||
else
|
||||
sh "#{cc} -fexceptions -c -arch #{arch} \"#{asm}\" -o \"#{arch_obj}\""
|
||||
end
|
||||
|
||||
[asm].each { |x| File.unlink(x) } unless ENV['keep_temps']
|
||||
arch_objs << arch_obj
|
||||
end
|
||||
|
||||
# Assemble fat binary.
|
||||
arch_objs_list = arch_objs.map { |x| "\"#{x}\"" }.join(' ')
|
||||
sh "/usr/bin/lipo -create #{arch_objs_list} -output \"#{obj}\""
|
||||
end
|
||||
|
||||
any_obj_file_built = true
|
||||
[obj, init_func]
|
||||
end
|
||||
|
||||
# Resolve file dependencies.
|
||||
if config.detect_dependencies == true
|
||||
config.dependencies = Dependency.new(config.files - config.exclude_from_detect_dependencies, config.dependencies).run
|
||||
end
|
||||
|
||||
parallel = ParallelBuilder.new(objs_build_dir, build_file)
|
||||
parallel.files = config.ordered_build_files
|
||||
parallel.files += config.spec_files if config.spec_mode
|
||||
parallel.run
|
||||
|
||||
objs = app_objs = parallel.objects
|
||||
spec_objs = []
|
||||
if config.spec_mode
|
||||
app_objs = objs[0...config.ordered_build_files.size]
|
||||
spec_objs = objs[-(config.spec_files.size)..-1]
|
||||
end
|
||||
|
||||
FileUtils.touch(objs_build_dir) if any_obj_file_built
|
||||
|
||||
# # Generate init file.
|
||||
# init_txt = <<EOS
|
||||
# extern "C" {
|
||||
# void ruby_sysinit(int *, char ***);
|
||||
# void ruby_init(void);
|
||||
# void ruby_init_loadpath(void);
|
||||
# void ruby_script(const char *);
|
||||
# void ruby_set_argv(int, char **);
|
||||
# void rb_vm_init_compiler(void);
|
||||
# void rb_vm_init_jit(void);
|
||||
# void rb_vm_aot_feature_provide(const char *, void *);
|
||||
# void *rb_vm_top_self(void);
|
||||
# void rb_define_global_const(const char *, void *);
|
||||
# void rb_rb2oc_exc_handler(void);
|
||||
# void rb_exit(int);
|
||||
# EOS
|
||||
# app_objs.each do |_, init_func|
|
||||
# init_txt << "void #{init_func}(void *, void *);\n"
|
||||
# end
|
||||
# init_txt << <<EOS
|
||||
# }
|
||||
|
||||
# extern "C"
|
||||
# void
|
||||
# RubyMotionInit(int argc, char **argv)
|
||||
# {
|
||||
# static bool initialized = false;
|
||||
# if (!initialized) {
|
||||
# ruby_init();
|
||||
# ruby_init_loadpath();
|
||||
# if (argc > 0) {
|
||||
# const char *progname = argv[0];
|
||||
# ruby_script(progname);
|
||||
# }
|
||||
# #if !__LP64__
|
||||
# try {
|
||||
# #endif
|
||||
# void *self = rb_vm_top_self();
|
||||
# EOS
|
||||
# init_txt << config.define_global_env_txt
|
||||
# app_objs.each do |_, init_func|
|
||||
# init_txt << "#{init_func}(self, 0);\n"
|
||||
# end
|
||||
# init_txt << <<EOS
|
||||
# #if !__LP64__
|
||||
# }
|
||||
# catch (...) {
|
||||
# rb_rb2oc_exc_handler();
|
||||
# }
|
||||
# #endif
|
||||
# initialized = true;
|
||||
# }
|
||||
# }
|
||||
# EOS
|
||||
|
||||
# # Compile init file.
|
||||
# init = File.join(objs_build_dir, 'init.mm')
|
||||
# init_o = File.join(objs_build_dir, 'init.o')
|
||||
# if !(File.exist?(init) and File.exist?(init_o) and File.read(init) == init_txt)
|
||||
# File.open(init, 'w') { |io| io.write(init_txt) }
|
||||
# sh "#{cxx} \"#{init}\" #{config.cflags(platform, true)} -c -o \"#{init_o}\""
|
||||
# end
|
||||
|
||||
# librubymotion = File.join(datadir, platform, 'librubymotion-static.a')
|
||||
# if static_library
|
||||
# # Create a static archive with all object files + the runtime.
|
||||
# lib = File.join(config.versionized_build_dir(platform), config.name + '.a')
|
||||
# App.info 'Create', lib
|
||||
# objs_list = objs.map { |path, _| path }.unshift(init_o, *config.frameworks_stubs_objects(platform)).map { |x| "\"#{x}\"" }.join(' ')
|
||||
# sh "/usr/bin/libtool -static \"#{librubymotion}\" #{objs_list} -o \"#{lib}\""
|
||||
# return lib
|
||||
# end
|
||||
|
||||
# # Generate main file.
|
||||
# main_txt = config.main_cpp_file_txt(spec_objs)
|
||||
|
||||
# # Compile main file.
|
||||
# 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}\" #{config.cflags(platform, true)} -c -o \"#{main_o}\""
|
||||
# end
|
||||
|
||||
|
||||
init_txt = <<EOS
|
||||
#import <Foundation/Foundation.h>
|
||||
extern "C" {
|
||||
void *rb_vm_top_self(void);
|
||||
EOS
|
||||
|
||||
app_objs.each do |_, init_func|
|
||||
init_txt << "void #{init_func}(void *, void *);\n"
|
||||
end
|
||||
|
||||
init_txt << <<EOS
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void initializer(void)
|
||||
{
|
||||
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
void *self = rb_vm_top_self();
|
||||
EOS
|
||||
app_objs.each do |_, init_func|
|
||||
init_txt << "#{init_func}(self, 0);\n"
|
||||
end
|
||||
|
||||
init_txt << <<EOS
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
EOS
|
||||
|
||||
# Compile init file.
|
||||
init = File.join(objs_build_dir, 'init.mm')
|
||||
init_o = File.join(objs_build_dir, 'init.o')
|
||||
if !(File.exist?(init) and File.exist?(init_o) and File.read(init) == init_txt)
|
||||
File.open(init, 'w') { |io| io.write(init_txt) }
|
||||
sh "#{cxx} \"#{init}\" #{config.cflags(platform, true)} -c -o \"#{init_o}\""
|
||||
end
|
||||
|
||||
# Prepare bundle.
|
||||
bundle_path = config.app_bundle(platform)
|
||||
unless File.exist?(bundle_path)
|
||||
App.info 'Create', bundle_path
|
||||
FileUtils.mkdir_p(bundle_path)
|
||||
end
|
||||
|
||||
# Link executable.
|
||||
main_exec = config.app_bundle_executable(platform)
|
||||
unless File.exist?(File.dirname(main_exec))
|
||||
App.info 'Create', File.dirname(main_exec)
|
||||
FileUtils.mkdir_p(File.dirname(main_exec))
|
||||
end
|
||||
main_exec_created = false
|
||||
if !File.exist?(main_exec) \
|
||||
or File.mtime(config.project_file) > File.mtime(main_exec) \
|
||||
or objs.any? { |path, _| File.mtime(path) > File.mtime(main_exec) } \
|
||||
or File.mtime(init_o) > File.mtime(main_exec) \
|
||||
or vendor_libs.any? { |lib| File.mtime(lib) > File.mtime(main_exec) }
|
||||
# or File.mtime(librubymotion) > File.mtime(main_exec)
|
||||
App.info 'Link', main_exec
|
||||
objs_list = objs.map { |path, _| path }.unshift(init_o, *config.frameworks_stubs_objects(platform)).map { |x| "\"#{x}\"" }.join(' ')
|
||||
framework_search_paths = (config.framework_search_paths + (embedded_frameworks + external_frameworks).map { |x| File.dirname(x) }).uniq.map { |x| "-F '#{File.expand_path(x)}'" }.join(' ')
|
||||
frameworks = (config.frameworks_dependencies + (embedded_frameworks + external_frameworks).map { |x| File.basename(x, '.framework') }).map { |x| "-framework #{x}" }.join(' ')
|
||||
weak_frameworks = config.weak_frameworks.map { |x| "-weak_framework #{x}" }.join(' ')
|
||||
vendor_libs = config.vendor_projects.inject([]) do |libs, vendor_project|
|
||||
libs << vendor_project.libs.map { |x|
|
||||
(vendor_project.opts[:force_load] ? '-force_load ' : '-ObjC ') + "\"#{x}\""
|
||||
}
|
||||
end.join(' ')
|
||||
linker_option = begin
|
||||
m = config.deployment_target.match(/(\d+)/)
|
||||
if m[0].to_i < 7
|
||||
"-stdlib=libstdc++"
|
||||
end
|
||||
end || ""
|
||||
# sh "#{cxx} -o \"#{main_exec}\" #{objs_list} #{config.ldflags(platform)} -L#{File.join(datadir, platform)} -lrubymotion-static -lobjc -licucore #{linker_option} #{framework_search_paths} #{frameworks} #{weak_frameworks} #{config.libs.join(' ')} #{vendor_libs}"
|
||||
sh "#{cxx} -o \"#{main_exec}\" #{objs_list} #{config.ldflags(platform)} -lobjc -licucore #{linker_option} #{framework_search_paths} #{frameworks} #{weak_frameworks} #{config.libs.join(' ')} #{vendor_libs} -single_module -compatibility_version 1 -current_version 1 -undefined dynamic_lookup -dynamiclib -read_only_relocs suppress -dead_strip -install_name @rpath/#{config.name}.framework/#{config.name} -Xlinker -rpath -Xlinker @executable_path/Frameworks -Xlinker -rpath -Xlinker @loader_path/Frameworks"
|
||||
|
||||
# system %Q{ clang++ -O0 -g -o /tmp/liblol.dylib /tmp/main.o "/tmp/#{filename}.x86_64.o" -arch x86_64 -lobjc -licucore #{frameworks_flags} -dynamiclib -install_name /tmp/liblol.dylib -Xlinker -rpath -Xlinker /tmp/liblol.dylib -Xlinker -rpath -Xlinker /tmp/liblol.dylib -mmacosx-version-min=10.9 -single_module -compatibility_version 1 -current_version 1 -undefined suppress -flat_namespace }
|
||||
|
||||
|
||||
|
||||
# clang -arch armv7 -dynamiclib -isysroot [] -L[] -F[] -filelist [] -install_name @rpath/frameworktest.framework/frameworktest -Xlinker -rpath -Xlinker @executable_path/Frameworks -Xlinker -rpath -Xlinker @loader_path/Frameworks -dead_strip -fapplication-extension -miphoneos-version-min=8.0 -single_module -compatibility_version 1 -current_version 1 -Xlinker -dependency_info -Xlinker [] -o frameworktest
|
||||
|
||||
main_exec_created = true
|
||||
|
||||
# # Change the install name of embedded frameworks.
|
||||
# embedded_frameworks.each do |path|
|
||||
# res = `/usr/bin/otool -L \"#{main_exec}\"`.scan(/(.*#{File.basename(path)}.*)\s\(/)
|
||||
# if res and res[0] and res[0][0]
|
||||
# old_path = res[0][0].strip
|
||||
# if platform == "MacOSX"
|
||||
# exec_path = "@executable_path/../Frameworks/"
|
||||
# else
|
||||
# exec_path = "@executable_path/Frameworks/"
|
||||
# end
|
||||
# new_path = exec_path + old_path.scan(/#{File.basename(path)}.*/)[0]
|
||||
# sh "/usr/bin/install_name_tool -change \"#{old_path}\" \"#{new_path}\" \"#{main_exec}\""
|
||||
# else
|
||||
# App.warn "Cannot locate and fix install name path of embedded framework `#{path}' in executable `#{main_exec}', application might not start"
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
# # Create bundle/PkgInfo.
|
||||
# bundle_pkginfo = File.join(bundle_path, 'PkgInfo')
|
||||
# if !File.exist?(bundle_pkginfo) or File.mtime(config.project_file) > File.mtime(bundle_pkginfo)
|
||||
# App.info 'Create', bundle_pkginfo
|
||||
# File.open(bundle_pkginfo, 'w') { |io| io.write(config.pkginfo_data) }
|
||||
# end
|
||||
|
||||
# Compile IB resources.
|
||||
config.resources_dirs.each do |dir|
|
||||
if File.exist?(dir)
|
||||
ib_resources = []
|
||||
ib_resources.concat((Dir.glob(File.join(dir, '**', '*.xib')) + Dir.glob(File.join(dir, '*.lproj', '*.xib'))).map { |xib| [xib, xib.sub(/\.xib$/, '.nib')] })
|
||||
ib_resources.concat(Dir.glob(File.join(dir, '**', '*.storyboard')).map { |storyboard| [storyboard, storyboard.sub(/\.storyboard$/, '.storyboardc')] })
|
||||
ib_resources.each do |src, dest|
|
||||
if !File.exist?(dest) or File.mtime(src) > File.mtime(dest)
|
||||
App.info 'Compile', src
|
||||
sh "/usr/bin/ibtool --compile \"#{dest}\" \"#{src}\""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
preserve_resources = []
|
||||
|
||||
# Compile Asset Catalog bundles.
|
||||
assets_bundles = config.assets_bundles
|
||||
unless assets_bundles.empty?
|
||||
app_icons_asset_bundle = config.app_icons_asset_bundle
|
||||
if app_icons_asset_bundle
|
||||
app_icons_info_plist_path = config.app_icons_info_plist_path(platform)
|
||||
app_icons_options = "--output-partial-info-plist \"#{app_icons_info_plist_path}\" " \
|
||||
"--app-icon \"#{config.app_icon_name_from_asset_bundle}\""
|
||||
end
|
||||
|
||||
App.info 'Compile', assets_bundles.join(", ")
|
||||
app_resources_dir = File.expand_path(config.app_resources_dir(platform))
|
||||
FileUtils.mkdir_p(app_resources_dir)
|
||||
cmd = "\"#{config.xcode_dir}/usr/bin/actool\" --output-format human-readable-text " \
|
||||
"--notices --warnings --platform #{config.deploy_platform.downcase} " \
|
||||
"--minimum-deployment-target #{config.deployment_target} " \
|
||||
"#{Array(config.device_family).map { |d| "--target-device #{d}" }.join(' ')} " \
|
||||
"#{app_icons_options} --compress-pngs --compile \"#{app_resources_dir}\" " \
|
||||
"\"#{assets_bundles.map { |f| File.expand_path(f) }.join('" "')}\""
|
||||
$stderr.puts(cmd) if App::VERBOSE
|
||||
actool_output = `#{cmd} 2>&1`
|
||||
$stderr.puts(actool_output) if App::VERBOSE
|
||||
|
||||
# Split output in warnings and compiled files
|
||||
actool_output, actool_compilation_results = actool_output.split('/* com.apple.actool.compilation-results */')
|
||||
actool_compiled_files = actool_compilation_results.strip.split("\n")
|
||||
if actool_document_warnings = actool_output.split('/* com.apple.actool.document.warnings */').last
|
||||
# Propagate warnings to the user.
|
||||
actool_document_warnings.strip.split("\n").each { |w| App.warn(w) }
|
||||
end
|
||||
|
||||
# Remove the partial Info.plist line and preserve all other assets.
|
||||
actool_compiled_files.delete(app_icons_info_plist_path) if app_icons_asset_bundle
|
||||
preserve_resources.concat(actool_compiled_files.map { |f| File.basename(f) })
|
||||
|
||||
config.configure_app_icons_from_asset_bundle(platform) if app_icons_asset_bundle
|
||||
end
|
||||
|
||||
# Compile CoreData Model resources and SpriteKit atlas files.
|
||||
config.resources_dirs.each do |dir|
|
||||
if File.exist?(dir)
|
||||
Dir.glob(File.join(dir, '*.xcdatamodeld')).each do |model|
|
||||
momd = model.sub(/\.xcdatamodeld$/, '.momd')
|
||||
if !File.exist?(momd) or File.mtime(model) > File.mtime(momd)
|
||||
App.info 'Compile', model
|
||||
model = File.expand_path(model) # momc wants absolute paths.
|
||||
momd = File.expand_path(momd)
|
||||
sh "\"#{App.config.xcode_dir}/usr/bin/momc\" \"#{model}\" \"#{momd}\""
|
||||
end
|
||||
end
|
||||
if cmd = config.spritekit_texture_atlas_compiler
|
||||
Dir.glob(File.join(dir, '*.atlas')).each do |atlas|
|
||||
if File.directory?(atlas)
|
||||
App.info 'Compile', atlas
|
||||
sh "\"#{cmd}\" \"#{atlas}\" \"#{bundle_path}\""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# # Copy embedded frameworks.
|
||||
# unless embedded_frameworks.empty?
|
||||
# app_frameworks = File.join(config.app_bundle(platform), 'Frameworks')
|
||||
# FileUtils.mkdir_p(app_frameworks)
|
||||
# embedded_frameworks.each do |src_path|
|
||||
# dest_path = File.join(app_frameworks, File.basename(src_path))
|
||||
# if !File.exist?(dest_path) or File.mtime(src_path) > File.mtime(dest_path)
|
||||
# App.info 'Copy', src_path
|
||||
# FileUtils.cp_r(src_path, dest_path)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# Create bundle/Info.plist.
|
||||
bundle_info_plist = File.join(bundle_path, 'Info.plist')
|
||||
if !File.exist?(bundle_info_plist) or File.mtime(config.project_file) > File.mtime(bundle_info_plist)
|
||||
App.info 'Create', bundle_info_plist
|
||||
File.open(bundle_info_plist, 'w') { |io| io.write(config.info_plist_data(platform)) }
|
||||
sh "/usr/bin/plutil -convert binary1 \"#{bundle_info_plist}\""
|
||||
end
|
||||
|
||||
# Copy resources, handle subdirectories.
|
||||
app_resources_dir = config.app_resources_dir(platform)
|
||||
FileUtils.mkdir_p(app_resources_dir)
|
||||
reserved_app_bundle_files = [
|
||||
'_CodeSignature/CodeResources', 'CodeResources', 'embedded.mobileprovision',
|
||||
'Info.plist', 'PkgInfo', 'ResourceRules.plist',
|
||||
convert_filesystem_encoding(config.name)
|
||||
]
|
||||
resources_exclude_extnames = ['.xib', '.storyboard', '.xcdatamodeld', '.atlas', '.xcassets']
|
||||
resources_paths = []
|
||||
config.resources_dirs.each do |dir|
|
||||
if File.exist?(dir)
|
||||
resources_paths << Dir.chdir(dir) do
|
||||
Dir.glob('**{,/*/**}/*').reject do |x|
|
||||
# Find files with extnames to exclude or files inside bundles to
|
||||
# exclude (e.g. xcassets).
|
||||
File.extname(x) == '.lproj' ||
|
||||
resources_exclude_extnames.include?(File.extname(x)) ||
|
||||
resources_exclude_extnames.include?(File.extname(x.split('/').first))
|
||||
end.map { |file| File.join(dir, file) }
|
||||
end
|
||||
end
|
||||
end
|
||||
resources_paths.flatten!
|
||||
resources_paths.each do |res_path|
|
||||
res = path_on_resources_dirs(config.resources_dirs, res_path)
|
||||
if reserved_app_bundle_files.include?(res)
|
||||
App.fail "Cannot use `#{res_path}' as a resource file because it's a reserved application bundle file"
|
||||
end
|
||||
dest_path = File.join(app_resources_dir, res)
|
||||
copy_resource(res_path, dest_path)
|
||||
end
|
||||
|
||||
# Compile all .strings files
|
||||
Dir.glob(File.join(app_resources_dir, '{,**/}*.strings')).each do |strings_file|
|
||||
compile_resource_to_binary_plist(strings_file)
|
||||
end
|
||||
|
||||
# # Optional support for #eval (OSX-only).
|
||||
# if config.respond_to?(:eval_support) and config.eval_support
|
||||
# repl_dylib_path = File.join(datadir, '..', 'librubymotion-repl.dylib')
|
||||
# dest_path = File.join(app_resources_dir, File.basename(repl_dylib_path))
|
||||
# copy_resource(repl_dylib_path, dest_path)
|
||||
# preserve_resources << File.basename(repl_dylib_path)
|
||||
# end
|
||||
|
||||
# Delete old resource files.
|
||||
resources_files = resources_paths.map { |x| path_on_resources_dirs(config.resources_dirs, x) }
|
||||
Dir.chdir(app_resources_dir) do
|
||||
Dir.glob('*').each do |bundle_res|
|
||||
next if File.directory?(bundle_res)
|
||||
next if reserved_app_bundle_files.include?(bundle_res)
|
||||
next if resources_files.include?(bundle_res)
|
||||
next if preserve_resources.include?(File.basename(bundle_res))
|
||||
App.warn "File `#{bundle_res}' found in app bundle but not in resource directories, removing"
|
||||
FileUtils.rm_rf(bundle_res)
|
||||
end
|
||||
end
|
||||
|
||||
# Generate dSYM.
|
||||
dsym_path = config.app_bundle_dsym(platform)
|
||||
if !File.exist?(dsym_path) or File.mtime(main_exec) > File.mtime(dsym_path)
|
||||
App.info "Create", dsym_path
|
||||
sh "/usr/bin/dsymutil \"#{main_exec}\" -o \"#{dsym_path}\""
|
||||
end
|
||||
|
||||
# Strip all symbols. Only in distribution mode.
|
||||
if main_exec_created and (config.distribution_mode or ENV['__strip__'])
|
||||
App.info "Strip", main_exec
|
||||
sh "#{config.locate_binary('strip')} #{config.strip_args} \"#{main_exec}\""
|
||||
end
|
||||
end
|
||||
|
||||
def path_on_resources_dirs(dirs, path)
|
||||
dir = dirs.each do |dir|
|
||||
break dir if path =~ /^#{dir}/
|
||||
end
|
||||
path = path.sub(/^#{dir}\/*/, '') if dir
|
||||
path
|
||||
end
|
||||
|
||||
def convert_filesystem_encoding(string)
|
||||
if RUBY_VERSION < "2.1.0"
|
||||
return eval `#{@nfd} "#{string}"`
|
||||
else
|
||||
# Dir.glob on Ruby 2.1 returns file path as "Normalization Form C".
|
||||
# So, we do not convert to "Normalization Form D".
|
||||
# (Ruby 2.0 or below, Dir.glob returns "Normalization Form D").
|
||||
return string
|
||||
end
|
||||
end
|
||||
|
||||
def compile_resource_to_binary_plist(path)
|
||||
unless File.size(path) == 0
|
||||
App.info 'Compile', path
|
||||
sh "/usr/bin/plutil -convert binary1 \"#{path}\""
|
||||
end
|
||||
end
|
||||
|
||||
def copy_resource(res_path, dest_path)
|
||||
if !File.exist?(dest_path) or File.mtime(res_path) > File.mtime(dest_path)
|
||||
FileUtils.mkdir_p(File.dirname(dest_path))
|
||||
App.info 'Copy', res_path
|
||||
FileUtils.cp_r(res_path, dest_path)
|
||||
end
|
||||
end
|
||||
|
||||
# def profile(config, platform, config_plist)
|
||||
# plist_path = File.join(config.versionized_build_dir(platform), 'pbxperfconfig.plist')
|
||||
# App.info('Create', plist_path)
|
||||
# plist_path = File.expand_path(plist_path)
|
||||
# File.open(plist_path, 'w') { |f| f << Motion::PropertyList.to_s(config_plist) }
|
||||
|
||||
# instruments_app = File.expand_path('../Applications/Instruments.app', config.xcode_dir)
|
||||
# App.info('Profile', config.app_bundle(platform))
|
||||
# sh "'#{File.join(config.bindir, 'instruments')}' '#{instruments_app}' '#{plist_path}'"
|
||||
# end
|
||||
|
||||
class << self
|
||||
def common_build_dir
|
||||
dir = File.expand_path("~/Library/RubyMotion/build")
|
||||
unless File.exist?(dir)
|
||||
begin
|
||||
FileUtils.mkdir_p dir
|
||||
rescue
|
||||
end
|
||||
end
|
||||
dir
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end; end
|
||||
528
lib/motion/project/template/ios-framework/config.rb
Normal file
528
lib/motion/project/template/ios-framework/config.rb
Normal file
@@ -0,0 +1,528 @@
|
||||
# encoding: utf-8
|
||||
|
||||
# Copyright (c) 2012, HipByte SPRL and contributors
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
require 'motion/project/xcode_config'
|
||||
|
||||
module Motion; module Project;
|
||||
class IOSFrameworkConfig < XcodeConfig
|
||||
register :'ios-framework'
|
||||
|
||||
# variable :device_family, :interface_orientations, :background_modes,
|
||||
# :status_bar_style, :icons, :prerendered_icon, :fonts, :seed_id,
|
||||
# :provisioning_profile, :manifest_assets
|
||||
|
||||
variable :device_family, :manifest_assets
|
||||
|
||||
def initialize(project_dir, build_mode)
|
||||
super
|
||||
@frameworks = ['UIKit', 'Foundation', 'CoreGraphics']
|
||||
@device_family = :iphone
|
||||
# @interface_orientations = [:portrait, :landscape_left, :landscape_right]
|
||||
# @background_modes = []
|
||||
# @status_bar_style = :default
|
||||
# @icons = []
|
||||
# @prerendered_icon = false
|
||||
@manifest_assets = []
|
||||
end
|
||||
|
||||
def platforms; ['iPhoneSimulator', 'iPhoneOS']; end
|
||||
def local_platform; 'iPhoneSimulator'; end
|
||||
def deploy_platform; 'iPhoneOS'; end
|
||||
|
||||
def archs
|
||||
@archs ||= begin
|
||||
# By default, do not build with 64-bit, as it's still experimental.
|
||||
archs = super
|
||||
archs['iPhoneSimulator'].delete('x86_64')
|
||||
archs['iPhoneOS'].delete('arm64')
|
||||
archs
|
||||
end
|
||||
end
|
||||
|
||||
# TODO datadir should not depend on the template name
|
||||
def datadir(target=deployment_target)
|
||||
File.join(motiondir, 'data', 'ios', target)
|
||||
end
|
||||
|
||||
# def app_icons_info_plist_path(platform)
|
||||
# File.expand_path(File.join(versionized_build_dir(platform), 'AppIcon.plist'))
|
||||
# end
|
||||
|
||||
# def configure_app_icons_from_asset_bundle(platform)
|
||||
# path = app_icons_info_plist_path(platform)
|
||||
# if File.exist?(path)
|
||||
# content = `/usr/libexec/PlistBuddy -c 'Print :CFBundleIcons:CFBundlePrimaryIcon:CFBundleIconFiles' "#{path}"`.strip
|
||||
# self.icons = content.split("\n")[1..-2].map(&:strip)
|
||||
# end
|
||||
# end
|
||||
|
||||
def validate
|
||||
# icons
|
||||
# if !(icons.is_a?(Array) and icons.all? { |x| x.is_a?(String) })
|
||||
# App.fail "app.icons should be an array of strings."
|
||||
# end
|
||||
|
||||
# manifest_assets
|
||||
if !(manifest_assets.is_a?(Array) and manifest_assets.all? { |x| x.is_a?(Hash) and x.keys.include?(:kind) and x.keys.include?(:url) })
|
||||
App.fail "app.manifest_assets should be an array of hashes with values for the :kind and :url keys"
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def locate_compiler(platform, *execs)
|
||||
paths = [File.join(platform_dir(platform), 'Developer/usr/bin')]
|
||||
paths.unshift File.join(xcode_dir, 'Toolchains/XcodeDefault.xctoolchain/usr/bin')
|
||||
|
||||
execs.each do |exec|
|
||||
paths.each do |path|
|
||||
cc = File.join(path, exec)
|
||||
return escape_path(cc) if File.exist?(cc)
|
||||
end
|
||||
end
|
||||
App.fail "Can't locate compilers for platform `#{platform}'"
|
||||
end
|
||||
|
||||
# def archive_extension
|
||||
# '.ipa'
|
||||
# end
|
||||
|
||||
# def codesign_certificate
|
||||
# super('iPhone')
|
||||
# end
|
||||
|
||||
# def provisioning_profile(name = /iOS Team Provisioning Profile/)
|
||||
# @provisioning_profile ||= begin
|
||||
# paths = Dir.glob(File.expand_path("~/Library/MobileDevice/Provisioning\ Profiles/*.mobileprovision")).select do |path|
|
||||
# text = File.read(path)
|
||||
# text.force_encoding('binary') if RUBY_VERSION >= '1.9.0'
|
||||
# text.scan(/<key>\s*Name\s*<\/key>\s*<string>\s*([^<]+)\s*<\/string>/)[0][0].match(name)
|
||||
# end
|
||||
# if paths.size == 0
|
||||
# App.fail "Can't find a provisioning profile named `#{name}'"
|
||||
# elsif paths.size > 1
|
||||
# App.warn "Found #{paths.size} provisioning profiles named `#{name}'. Set the `provisioning_profile' project setting. Will use the first one: `#{paths[0]}'"
|
||||
# end
|
||||
# paths[0]
|
||||
# end
|
||||
# end
|
||||
|
||||
# def read_provisioned_profile_array(key)
|
||||
# text = File.read(provisioning_profile)
|
||||
# text.force_encoding('binary') if RUBY_VERSION >= '1.9.0'
|
||||
# text.scan(/<key>\s*#{key}\s*<\/key>\s*<array>(.*?)\s*<\/array>/m)[0][0].scan(/<string>(.*?)<\/string>/).map { |str| str[0].strip }
|
||||
# end
|
||||
# private :read_provisioned_profile_array
|
||||
|
||||
# def provisioned_devices
|
||||
# @provisioned_devices ||= read_provisioned_profile_array('ProvisionedDevices')
|
||||
# end
|
||||
|
||||
# def seed_id
|
||||
# @seed_id ||= begin
|
||||
# seed_ids = read_provisioned_profile_array('ApplicationIdentifierPrefix')
|
||||
# if seed_ids.size == 0
|
||||
# App.fail "Can't find an application seed ID in the provisioning profile `#{provisioning_profile}'"
|
||||
# elsif seed_ids.size > 1
|
||||
# App.warn "Found #{seed_ids.size} seed IDs in the provisioning profile. Set the `seed_id' project setting. Will use the last one: `#{seed_ids.last}'"
|
||||
# end
|
||||
# seed_ids.last
|
||||
# end
|
||||
# end
|
||||
|
||||
# def entitlements_data
|
||||
# dict = entitlements
|
||||
# if distribution_mode
|
||||
# dict['application-identifier'] ||= seed_id + '.' + identifier
|
||||
# else
|
||||
# # Required for gdb.
|
||||
# dict['get-task-allow'] = true if dict['get-task-allow'].nil?
|
||||
# end
|
||||
# Motion::PropertyList.to_s(dict)
|
||||
# end
|
||||
|
||||
def common_flags(platform)
|
||||
simulator_version = begin
|
||||
flag = " -miphoneos-version-min=#{deployment_target}"
|
||||
if platform == "iPhoneSimulator"
|
||||
ver = xcode_version[0].match(/(\d+)/)
|
||||
if ver[0].to_i >= 5
|
||||
flag = " -mios-simulator-version-min=#{deployment_target}"
|
||||
end
|
||||
end
|
||||
flag
|
||||
end
|
||||
super + simulator_version
|
||||
end
|
||||
|
||||
def cflags(platform, cplusplus)
|
||||
super + " -g -fobjc-legacy-dispatch -fobjc-abi-version=2"
|
||||
end
|
||||
|
||||
def ldflags(platform)
|
||||
ldflags = super
|
||||
ldflags << " -fobjc-arc" if deployment_target < '5.0'
|
||||
ldflags
|
||||
end
|
||||
|
||||
def bridgesupport_flags
|
||||
extra_flags = (OSX_VERSION >= 10.7 && sdk_version < '7.0') ? '--no-64-bit' : ''
|
||||
"--format complete #{extra_flags}"
|
||||
end
|
||||
|
||||
def bridgesupport_cflags
|
||||
a = sdk_version.scan(/(\d+)\.(\d+)/)[0]
|
||||
sdk_version_headers = ((a[0].to_i * 10000) + (a[1].to_i * 100)).to_s
|
||||
"-miphoneos-version-min=#{sdk_version} -D__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__=#{sdk_version_headers}"
|
||||
end
|
||||
|
||||
def device_family_int(family)
|
||||
case family
|
||||
when :iphone then 1
|
||||
when :ipad then 2
|
||||
else
|
||||
App.fail "Unknown device_family value: `#{family}'"
|
||||
end
|
||||
end
|
||||
|
||||
def device_family_string(device_name, family, target, retina)
|
||||
device = case family
|
||||
when :iphone, 1
|
||||
"iPhone"
|
||||
when :ipad, 2
|
||||
"iPad"
|
||||
end
|
||||
|
||||
ver = xcode_version[0].match(/(\d+)/)
|
||||
case ver[0].to_i
|
||||
when 6
|
||||
(device_name.nil?) ? device + device_retina_xcode6_string(family, target, retina) : device_name
|
||||
when 5
|
||||
device + device_retina_xcode5_string(family, target, retina)
|
||||
else
|
||||
device + device_retina_xcode4_string(family, target, retina)
|
||||
end
|
||||
end
|
||||
|
||||
# def device_retina_xcode4_string(family, target, retina)
|
||||
# case retina
|
||||
# when 'true'
|
||||
# (family == 1 and target >= '6.0') ? ' (Retina 4-inch)' : ' (Retina)'
|
||||
# when '3.5'
|
||||
# ' (Retina 3.5-inch)'
|
||||
# when '4'
|
||||
# ' (Retina 4-inch)'
|
||||
# else
|
||||
# ''
|
||||
# end
|
||||
# end
|
||||
|
||||
# def device_retina_xcode5_string(family, target, retina)
|
||||
# retina4_string = begin
|
||||
# if (target >= '7.0' && App.config.archs['iPhoneSimulator'].include?("x86_64"))
|
||||
# " Retina (4-inch 64-bit)"
|
||||
# else
|
||||
# " Retina (4-inch)"
|
||||
# end
|
||||
# end
|
||||
|
||||
# case retina
|
||||
# when 'true'
|
||||
# (family == 1 and target >= '6.0') ? retina4_string : ' Retina'
|
||||
# when '3.5'
|
||||
# ' Retina (3.5-inch)'
|
||||
# when '4'
|
||||
# ' Retina (4-inch)'
|
||||
# else
|
||||
# if target < '7.0'
|
||||
# ''
|
||||
# else
|
||||
# (family == 1) ? retina4_string : ''
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# def device_retina_xcode6_string(family, target, retina)
|
||||
# case retina
|
||||
# when '3.5'
|
||||
# ' 4s'
|
||||
# when '4'
|
||||
# ' 5s'
|
||||
# else
|
||||
# (family == 1) ? ' 5s' : ' Retina'
|
||||
# end
|
||||
# end
|
||||
|
||||
def device_family_ints
|
||||
ary = @device_family.is_a?(Array) ? @device_family : [@device_family]
|
||||
ary.map { |family| device_family_int(family) }
|
||||
end
|
||||
|
||||
# def interface_orientations_consts
|
||||
# @interface_orientations.map do |ori|
|
||||
# case ori
|
||||
# when :portrait then 'UIInterfaceOrientationPortrait'
|
||||
# when :landscape_left then 'UIInterfaceOrientationLandscapeLeft'
|
||||
# when :landscape_right then 'UIInterfaceOrientationLandscapeRight'
|
||||
# when :portrait_upside_down then 'UIInterfaceOrientationPortraitUpsideDown'
|
||||
# else
|
||||
# App.fail "Unknown interface_orientation value: `#{ori}'"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# def background_modes_consts
|
||||
# @background_modes.map do |mode|
|
||||
# case mode
|
||||
# when :audio then 'audio'
|
||||
# when :location then 'location'
|
||||
# when :voip then 'voip'
|
||||
# when :newsstand_content then 'newsstand-content'
|
||||
# when :external_accessory then 'external-accessory'
|
||||
# when :bluetooth_central then 'bluetooth-central'
|
||||
# else
|
||||
# App.fail "Unknown background_modes value: `#{mode}'"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# def status_bar_style_const
|
||||
# case @status_bar_style
|
||||
# when :default then 'UIStatusBarStyleDefault'
|
||||
# when :black_translucent then 'UIStatusBarStyleBlackTranslucent'
|
||||
# when :black_opaque then 'UIStatusBarStyleBlackOpaque'
|
||||
# when :light_content then 'UIStatusBarStyleLightContent'
|
||||
# else
|
||||
# App.fail "Unknown status_bar_style value: `#{@status_bar_style}'"
|
||||
# end
|
||||
# end
|
||||
|
||||
# def device_id
|
||||
# @device_id ||= begin
|
||||
# deploy = File.join(App.config.bindir, 'ios/deploy')
|
||||
# device_id = `#{deploy} -D`.strip
|
||||
# if device_id.empty?
|
||||
# App.fail "Can't find an iOS device connected on USB"
|
||||
# end
|
||||
# device_id
|
||||
# end
|
||||
# end
|
||||
|
||||
def app_bundle(platform)
|
||||
File.join(versionized_build_dir(platform), bundle_name + '.framework')
|
||||
end
|
||||
|
||||
def app_bundle_executable(platform)
|
||||
File.join(app_bundle(platform), name)
|
||||
end
|
||||
|
||||
def app_resources_dir(platform)
|
||||
app_bundle(platform)
|
||||
end
|
||||
|
||||
# def fonts
|
||||
# @fonts ||= begin
|
||||
# resources_dirs.flatten.inject([]) do |fonts, dir|
|
||||
# if File.exist?(dir)
|
||||
# Dir.chdir(dir) do
|
||||
# fonts.concat(Dir.glob('*.{otf,ttf}'))
|
||||
# end
|
||||
# else
|
||||
# fonts
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
def info_plist
|
||||
super.merge({ 'CFBundlePackageType' => 'FMWK' })
|
||||
end
|
||||
|
||||
def info_plist_data(platform)
|
||||
Motion::PropertyList.to_s({
|
||||
'MinimumOSVersion' => deployment_target,
|
||||
'CFBundleResourceSpecification' => 'ResourceRules.plist',
|
||||
# 'CFBundleSupportedPlatforms' => [deploy_platform],
|
||||
# 'CFBundleIconFiles' => icons,
|
||||
# 'CFBundleIcons' => {
|
||||
# 'CFBundlePrimaryIcon' => {
|
||||
# 'CFBundleIconFiles' => icons,
|
||||
# 'UIPrerenderedIcon' => prerendered_icon,
|
||||
# }
|
||||
# },
|
||||
# 'UIAppFonts' => fonts,
|
||||
# TODO temp hack to get ints for Instruments, but strings for normal builds.
|
||||
'UIDeviceFamily' => device_family_ints.map { |x| ENV['__USE_DEVICE_INT__'] ? x.to_i : x.to_s },
|
||||
# 'UISupportedInterfaceOrientations' => interface_orientations_consts,
|
||||
# 'UIStatusBarStyle' => status_bar_style_const,
|
||||
# 'UIBackgroundModes' => background_modes_consts,
|
||||
'DTXcode' => begin
|
||||
vers = xcode_version[0].gsub(/\./, '')
|
||||
if vers.length == 2
|
||||
'0' + vers + '0'
|
||||
else
|
||||
'0' + vers
|
||||
end
|
||||
end,
|
||||
'DTXcodeBuild' => xcode_version[1],
|
||||
'DTSDKName' => "#{platform.downcase}#{sdk_version}",
|
||||
'DTSDKBuild' => sdk_build_version(platform),
|
||||
'DTPlatformName' => platform.downcase,
|
||||
'DTCompiler' => 'com.apple.compilers.llvm.clang.1_0',
|
||||
'DTPlatformVersion' => sdk_version,
|
||||
'DTPlatformBuild' => sdk_build_version(platform),
|
||||
}.merge(generic_info_plist).merge(dt_info_plist).merge(info_plist))
|
||||
end
|
||||
|
||||
def manifest_plist_data
|
||||
return nil if manifest_assets.empty?
|
||||
Motion::PropertyList.to_s({
|
||||
'items' => [
|
||||
{ 'assets' => manifest_assets,
|
||||
'metadata' => {
|
||||
'bundle-identifier' => identifier,
|
||||
'bundle-version' => @version,
|
||||
'kind' => 'software',
|
||||
'title' => @name
|
||||
} }
|
||||
]
|
||||
})
|
||||
end
|
||||
|
||||
def supported_sdk_versions(versions)
|
||||
versions.reverse.find { |vers| File.exist?(datadir(vers)) }
|
||||
end
|
||||
|
||||
def sdk_build_version(platform)
|
||||
@sdk_build_version ||= begin
|
||||
sdk_path = sdk(platform)
|
||||
`xcodebuild -version -sdk '#{sdk_path}' ProductBuildVersion`.strip
|
||||
end
|
||||
end
|
||||
|
||||
# def main_cpp_file_txt(spec_objs)
|
||||
# main_txt = <<EOS
|
||||
# #import <UIKit/UIKit.h>
|
||||
|
||||
# extern "C" {
|
||||
# void rb_define_global_const(const char *, void *);
|
||||
# void rb_rb2oc_exc_handler(void);
|
||||
# void rb_exit(int);
|
||||
# void RubyMotionInit(int argc, char **argv);
|
||||
# EOS
|
||||
# if spec_mode
|
||||
# spec_objs.each do |_, init_func|
|
||||
# main_txt << "void #{init_func}(void *, void *);\n"
|
||||
# end
|
||||
# end
|
||||
# main_txt << <<EOS
|
||||
# }
|
||||
# EOS
|
||||
|
||||
# if spec_mode
|
||||
# main_txt << <<EOS
|
||||
# @interface SpecLauncher : NSObject
|
||||
# @end
|
||||
|
||||
# #include <dlfcn.h>
|
||||
|
||||
# @implementation SpecLauncher
|
||||
|
||||
# + (id)launcher
|
||||
# {
|
||||
# [UIApplication sharedApplication];
|
||||
|
||||
# // Enable simulator accessibility.
|
||||
# // Thanks http://www.stewgleadow.com/blog/2011/10/14/enabling-accessibility-for-ios-applications/
|
||||
# NSString *simulatorRoot = [[[NSProcessInfo processInfo] environment] objectForKey:@"IPHONE_SIMULATOR_ROOT"];
|
||||
# if (simulatorRoot != nil) {
|
||||
# void *appSupportLibrary = dlopen([[simulatorRoot stringByAppendingPathComponent:@"/System/Library/PrivateFrameworks/AppSupport.framework/AppSupport"] fileSystemRepresentation], RTLD_LAZY);
|
||||
# CFStringRef (*copySharedResourcesPreferencesDomainForDomain)(CFStringRef domain) = (CFStringRef (*)(CFStringRef)) dlsym(appSupportLibrary, "CPCopySharedResourcesPreferencesDomainForDomain");
|
||||
|
||||
# if (copySharedResourcesPreferencesDomainForDomain != NULL) {
|
||||
# CFStringRef accessibilityDomain = copySharedResourcesPreferencesDomainForDomain(CFSTR("com.apple.Accessibility"));
|
||||
|
||||
# if (accessibilityDomain != NULL) {
|
||||
# CFPreferencesSetValue(CFSTR("ApplicationAccessibilityEnabled"), kCFBooleanTrue, accessibilityDomain, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
|
||||
# CFRelease(accessibilityDomain);
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
# // Load the UIAutomation framework.
|
||||
# dlopen("/Developer/Library/PrivateFrameworks/UIAutomation.framework/UIAutomation", RTLD_LOCAL);
|
||||
|
||||
# SpecLauncher *launcher = [[self alloc] init];
|
||||
# [[NSNotificationCenter defaultCenter] addObserver:launcher selector:@selector(appLaunched:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
# return launcher;
|
||||
# }
|
||||
|
||||
# - (void)appLaunched:(id)notification
|
||||
# {
|
||||
# // unregister observer to avoid duplicate invocation
|
||||
# [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
# // Give a bit of time for the simulator to attach...
|
||||
# [self performSelector:@selector(runSpecs) withObject:nil afterDelay:0.3];
|
||||
# }
|
||||
|
||||
# - (void)runSpecs
|
||||
# {
|
||||
# EOS
|
||||
# spec_objs.each do |_, init_func|
|
||||
# main_txt << "#{init_func}(self, 0);\n"
|
||||
# end
|
||||
# main_txt << "[NSClassFromString(@\"Bacon\") performSelector:@selector(run)];\n"
|
||||
# main_txt << <<EOS
|
||||
# }
|
||||
|
||||
# @end
|
||||
# EOS
|
||||
# end
|
||||
# main_txt << <<EOS
|
||||
# int
|
||||
# main(int argc, char **argv)
|
||||
# {
|
||||
# NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
# int retval = 0;
|
||||
# EOS
|
||||
# if ENV['ARR_CYCLES_DISABLE']
|
||||
# main_txt << <<EOS
|
||||
# setenv("ARR_CYCLES_DISABLE", "1", true);
|
||||
# EOS
|
||||
# end
|
||||
# main_txt << "[SpecLauncher launcher];\n" if spec_mode
|
||||
# main_txt << <<EOS
|
||||
# RubyMotionInit(argc, argv);
|
||||
# EOS
|
||||
# main_txt << <<EOS
|
||||
# retval = UIApplicationMain(argc, argv, nil, @"#{delegate_class}");
|
||||
# rb_exit(retval);
|
||||
# [pool release];
|
||||
# return retval;
|
||||
# }
|
||||
# EOS
|
||||
# end
|
||||
end
|
||||
end; end
|
||||
17
lib/motion/project/template/ios-framework/files/.gitignore
vendored
Normal file
17
lib/motion/project/template/ios-framework/files/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
.repl_history
|
||||
build
|
||||
tags
|
||||
app/pixate_code.rb
|
||||
resources/*.nib
|
||||
resources/*.momd
|
||||
resources/*.storyboardc
|
||||
.DS_Store
|
||||
nbproject
|
||||
.redcar
|
||||
#*#
|
||||
*~
|
||||
*.sw[po]
|
||||
.eprj
|
||||
.sass-cache
|
||||
.idea
|
||||
.dat*.*
|
||||
4
lib/motion/project/template/ios-framework/files/Gemfile
Normal file
4
lib/motion/project/template/ios-framework/files/Gemfile
Normal file
@@ -0,0 +1,4 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rake'
|
||||
# Add your dependencies here:
|
||||
14
lib/motion/project/template/ios-framework/files/Rakefile.erb
Normal file
14
lib/motion/project/template/ios-framework/files/Rakefile.erb
Normal file
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
$:.unshift("/Library/RubyMotion/lib")
|
||||
require 'motion/project/template/ios-framework'
|
||||
|
||||
begin
|
||||
require 'bundler'
|
||||
Bundler.require
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
Motion::Project::App.setup do |app|
|
||||
# Use `rake config' to see complete project settings.
|
||||
app.name = '<%= name %>'
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
describe "Application '<%= name %>'" do
|
||||
before do
|
||||
@app = UIApplication.sharedApplication
|
||||
end
|
||||
|
||||
it "has one window" do
|
||||
@app.windows.size.should == 1
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user