# 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(< 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