mirror of
https://github.com/zhigang1992/RubyMotion.git
synced 2026-04-02 17:57:26 +08:00
http://clang.llvm.org/docs/Modules.html This patch supports support Objective-C Modules when compile ObjC source codes without xcodeproj file, like https://github.com/Watson1978/RubyMotionKaigi2013
353 lines
12 KiB
Ruby
353 lines
12 KiB
Ruby
# 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.
|
|
|
|
module Motion; module Project;
|
|
class XcodeConfig < Config
|
|
variable :xcode_dir, :sdk_version, :deployment_target, :frameworks,
|
|
:weak_frameworks, :framework_search_paths, :libs, :identifier,
|
|
:codesign_certificate, :short_version, :entitlements, :delegate_class
|
|
|
|
def initialize(project_dir, build_mode)
|
|
super
|
|
@info_plist = {}
|
|
@dependencies = {}
|
|
@frameworks = []
|
|
@weak_frameworks = []
|
|
@framework_search_paths = []
|
|
@libs = []
|
|
@bundle_signature = '????'
|
|
@short_version = nil
|
|
@entitlements = {}
|
|
@delegate_class = 'AppDelegate'
|
|
@spec_mode = false
|
|
end
|
|
|
|
def xcode_dir
|
|
@xcode_dir ||= begin
|
|
xcode_dot_app_path = '/Applications/Xcode.app/Contents/Developer'
|
|
|
|
# First, honor /usr/bin/xcode-select
|
|
xcodeselect = '/usr/bin/xcode-select'
|
|
if File.exist?(xcodeselect)
|
|
path = `#{xcodeselect} -print-path`.strip
|
|
if path.match(/^\/Developer\//) and File.exist?(xcode_dot_app_path)
|
|
@xcode_error_printed ||= false
|
|
$stderr.puts(<<EOS) unless @xcode_error_printed
|
|
===============================================================================
|
|
It appears that you have a version of Xcode installed in /Applications that has
|
|
not been set as the default version. It is possible that RubyMotion may be
|
|
using old versions of certain tools which could eventually cause issues.
|
|
|
|
To fix this problem, you can type the following command in the terminal:
|
|
$ sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
|
|
===============================================================================
|
|
EOS
|
|
@xcode_error_printed = true
|
|
end
|
|
return path if File.exist?(path)
|
|
end
|
|
|
|
# Since xcode-select is borked, we assume the user installed Xcode
|
|
# as an app (new in Xcode 4.3).
|
|
return xcode_dot_app_path if File.exist?(xcode_dot_app_path)
|
|
|
|
App.fail "Can't locate any version of Xcode on the system."
|
|
end
|
|
unescape_path(@xcode_dir)
|
|
end
|
|
|
|
def xcode_version
|
|
@xcode_version ||= begin
|
|
txt = `#{locate_binary('xcodebuild')} -version`
|
|
vers = txt.scan(/Xcode\s(.+)/)[0][0]
|
|
build = txt.scan(/(BuildVersion:|Build version)\s(.+)/)[0][1]
|
|
[vers, build]
|
|
end
|
|
end
|
|
|
|
def platforms; raise; end
|
|
def local_platform; raise; end
|
|
def deploy_platform; raise; end
|
|
|
|
def validate
|
|
# Xcode version
|
|
App.fail "Xcode 4.x or greater is required" if xcode_version[0] < '4.0'
|
|
|
|
# sdk_version
|
|
platforms.each do |platform|
|
|
sdk_path = File.join(platforms_dir, platform + '.platform',
|
|
"Developer/SDKs/#{platform}#{sdk_version}.sdk")
|
|
unless File.exist?(sdk_path)
|
|
App.fail "Can't locate #{platform} SDK #{sdk_version} at `#{sdk_path}'"
|
|
end
|
|
end
|
|
|
|
# deployment_target
|
|
if deployment_target.to_f > sdk_version.to_f
|
|
App.fail "Deployment target `#{deployment_target}' must be equal or lesser than SDK version `#{sdk_version}'"
|
|
end
|
|
unless File.exist?(datadir)
|
|
App.fail "iOS deployment target #{deployment_target} is not supported by this version of RubyMotion"
|
|
end
|
|
|
|
super
|
|
end
|
|
|
|
def platforms_dir
|
|
File.join(xcode_dir, 'Platforms')
|
|
end
|
|
|
|
def platform_dir(platform)
|
|
File.join(platforms_dir, platform + '.platform')
|
|
end
|
|
|
|
def sdk_version
|
|
@sdk_version ||= begin
|
|
versions = Dir.glob(File.join(platforms_dir, "#{deploy_platform}.platform/Developer/SDKs/#{deploy_platform}*.sdk")).map do |path|
|
|
File.basename(path).scan(/#{deploy_platform}(.*)\.sdk/)[0][0]
|
|
end
|
|
if versions.size == 0
|
|
App.fail "Can't find an iOS SDK in `#{platforms_dir}'"
|
|
end
|
|
supported_vers = supported_sdk_versions(versions)
|
|
unless supported_vers
|
|
App.fail "RubyMotion doesn't support any of these SDK versions: #{versions.join(', ')}"
|
|
end
|
|
supported_vers
|
|
end
|
|
end
|
|
|
|
def deployment_target
|
|
@deployment_target ||= sdk_version
|
|
end
|
|
|
|
def sdk(platform)
|
|
path = File.join(platform_dir(platform), 'Developer/SDKs',
|
|
platform + sdk_version + '.sdk')
|
|
escape_path(path)
|
|
end
|
|
|
|
def frameworks_dependencies
|
|
@frameworks_dependencies ||= begin
|
|
# Compute the list of frameworks, including dependencies, that the project uses.
|
|
deps = frameworks.dup.uniq
|
|
slf = File.join(sdk(local_platform), 'System', 'Library', 'Frameworks')
|
|
deps.each do |framework|
|
|
framework_path = File.join(slf, framework + '.framework', framework)
|
|
if File.exist?(framework_path)
|
|
`#{locate_binary('otool')} -L \"#{framework_path}\"`.scan(/\t([^\s]+)\s\(/).each do |dep|
|
|
# Only care about public, non-umbrella frameworks (for now).
|
|
if md = dep[0].match(/^\/System\/Library\/Frameworks\/(.+)\.framework\/(Versions\/.\/)?(.+)$/) and md[1] == md[3]
|
|
if File.exist?(File.join(datadir, 'BridgeSupport', md[1] + '.bridgesupport'))
|
|
deps << md[1]
|
|
deps.uniq!
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if @framework_search_paths.empty?
|
|
deps = deps.select { |dep| File.exist?(File.join(datadir, 'BridgeSupport', dep + '.bridgesupport')) }
|
|
end
|
|
deps
|
|
end
|
|
end
|
|
|
|
def frameworks_stubs_objects(platform)
|
|
stubs = []
|
|
(frameworks_dependencies + weak_frameworks).uniq.each do |framework|
|
|
stubs_obj = File.join(datadir, platform, "#{framework}_stubs.o")
|
|
stubs << stubs_obj if File.exist?(stubs_obj)
|
|
end
|
|
stubs
|
|
end
|
|
|
|
def bridgesupport_files
|
|
@bridgesupport_files ||= begin
|
|
bs_files = []
|
|
deps = ['RubyMotion'] + (frameworks_dependencies + weak_frameworks).uniq
|
|
deps << 'UIAutomation' if spec_mode
|
|
deps.each do |framework|
|
|
supported_versions.each do |ver|
|
|
next if ver < deployment_target || sdk_version < ver
|
|
bs_path = File.join(datadir(ver), 'BridgeSupport', framework + '.bridgesupport')
|
|
if File.exist?(bs_path)
|
|
bs_files << bs_path
|
|
end
|
|
end
|
|
end
|
|
bs_files
|
|
end
|
|
end
|
|
|
|
def default_archs
|
|
h = {}
|
|
platforms.each do |platform|
|
|
h[platform] = Dir.glob(File.join(datadir, platform, '*.bc')).map do |path|
|
|
path.scan(/kernel-(.+).bc$/)[0][0]
|
|
end
|
|
end
|
|
h
|
|
end
|
|
|
|
def archs
|
|
@archs ||= default_archs
|
|
end
|
|
|
|
def arch_flags(platform)
|
|
archs[platform].map { |x| "-arch #{x}" }.join(' ')
|
|
end
|
|
|
|
def common_flags(platform)
|
|
"#{arch_flags(platform)} -isysroot \"#{unescape_path(sdk(platform))}\" -F#{sdk(platform)}/System/Library/Frameworks"
|
|
end
|
|
|
|
def cflags(platform, cplusplus)
|
|
"#{common_flags(platform)} -fexceptions -fblocks" + (cplusplus ? '' : ' -std=c99') + (xcode_version[0] < '5.0' ? '' : ' -fmodules')
|
|
end
|
|
|
|
def ldflags(platform)
|
|
common_flags(platform)
|
|
end
|
|
|
|
def bundle_name
|
|
@name + (spec_mode ? '_spec' : '')
|
|
end
|
|
|
|
def app_bundle_dsym(platform)
|
|
File.join(versionized_build_dir(platform), bundle_name + '.dSYM')
|
|
end
|
|
|
|
def archive_extension
|
|
raise "not implemented"
|
|
end
|
|
|
|
def archive
|
|
File.join(versionized_build_dir(deploy_platform), bundle_name + archive_extension)
|
|
end
|
|
|
|
def identifier
|
|
@identifier ||= "com.yourcompany.#{@name.gsub(/\s/, '')}"
|
|
spec_mode ? @identifier + '_spec' : @identifier
|
|
end
|
|
|
|
def info_plist
|
|
@info_plist
|
|
end
|
|
|
|
def dt_info_plist
|
|
{}
|
|
end
|
|
|
|
def generic_info_plist
|
|
{
|
|
'BuildMachineOSBuild' => `sw_vers -buildVersion`.strip,
|
|
'CFBundleDevelopmentRegion' => 'en',
|
|
'CFBundleName' => @name,
|
|
'CFBundleDisplayName' => @name,
|
|
'CFBundleIdentifier' => identifier,
|
|
'CFBundleExecutable' => @name,
|
|
'CFBundleInfoDictionaryVersion' => '6.0',
|
|
'CFBundlePackageType' => 'APPL',
|
|
'CFBundleShortVersionString' => (@short_version || @version),
|
|
'CFBundleSignature' => @bundle_signature,
|
|
'CFBundleVersion' => @version
|
|
}
|
|
end
|
|
|
|
def pkginfo_data
|
|
"AAPL#{@bundle_signature}"
|
|
end
|
|
|
|
def codesign_certificate(platform)
|
|
@codesign_certificate ||= begin
|
|
cert_type = (distribution_mode ? 'Distribution' : 'Developer')
|
|
certs = `/usr/bin/security -q find-certificate -a`.scan(/"#{platform} #{cert_type}: [^"]+"/).uniq
|
|
if certs.size == 0
|
|
App.fail "Cannot find any #{platform} #{cert_type} certificate in the keychain"
|
|
elsif certs.size > 1
|
|
App.warn "Found #{certs.size} #{platform} #{cert_type} certificates in the keychain. Set the `codesign_certificate' project setting. Will use the first certificate: `#{certs[0]}'"
|
|
end
|
|
certs[0][1..-2] # trim trailing `"` characters
|
|
end
|
|
end
|
|
|
|
def gen_bridge_metadata(platform, headers, bs_file, c_flags, exceptions=[])
|
|
sdk_path = self.sdk(local_platform)
|
|
includes = headers.map { |header| "-I'#{File.dirname(header)}'" }.uniq
|
|
exceptions = exceptions.map { |x| "\"#{x}\"" }.join(' ')
|
|
a = sdk_version.scan(/(\d+)\.(\d+)/)[0]
|
|
sdk_version_headers = ((a[0].to_i * 10000) + (a[1].to_i * 100)).to_s
|
|
extra_flags = begin
|
|
case platform
|
|
when "MacOSX"
|
|
'--64-bit'
|
|
else
|
|
(OSX_VERSION >= 10.7 && sdk_version < '7.0') ? '--no-64-bit' : ''
|
|
end
|
|
end
|
|
sh "RUBYOPT='' /usr/bin/gen_bridge_metadata --format complete #{extra_flags} --cflags \" #{c_flags} -isysroot \"#{sdk_path}\" -miphoneos-version-min=#{sdk_version} -D__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__=#{sdk_version_headers} -I. #{includes.join(' ')}\" #{headers.map { |x| "\"#{x}\"" }.join(' ')} -o \"#{bs_file}\" #{ "-e #{exceptions}" if exceptions.length != 0}"
|
|
end
|
|
|
|
def define_global_env_txt
|
|
rubymotion_env =
|
|
if spec_mode
|
|
'test'
|
|
else
|
|
development? ? 'development' : 'release'
|
|
end
|
|
"rb_define_global_const(\"RUBYMOTION_ENV\", @\"#{rubymotion_env}\");\nrb_define_global_const(\"RUBYMOTION_VERSION\", @\"#{Motion::Version}\");\n"
|
|
end
|
|
|
|
def spritekit_texture_atlas_compiler
|
|
path = File.join(xcode_dir, 'usr/bin/TextureAtlas')
|
|
File.exist?(path) ? path : nil
|
|
end
|
|
|
|
def assets_bundles
|
|
xcassets_bundles = []
|
|
resources_dirs.each do |dir|
|
|
if File.exist?(dir)
|
|
xcassets_bundles.concat(Dir.glob(File.join(dir, '*.xcassets')))
|
|
end
|
|
end
|
|
xcassets_bundles
|
|
end
|
|
|
|
def app_icons_asset_bundle
|
|
app_icons_asset_bundles = assets_bundles.map { |b| Dir.glob(File.join(b, '*.appiconset')) }.flatten
|
|
if app_icons_asset_bundles.size > 1
|
|
App.warn "Found #{app_icons_asset_bundles.size} app icon sets across all " \
|
|
"xcasset bundles. Only the first one (alphabetically) " \
|
|
"will be used."
|
|
end
|
|
app_icons_asset_bundles.sort.first
|
|
end
|
|
|
|
def app_icon_name_from_asset_bundle
|
|
File.basename(app_icons_asset_bundle, '.appiconset')
|
|
end
|
|
end
|
|
end; end
|