Class: Gym::XcodebuildFixes
- Inherits:
-
Object
- Object
- Gym::XcodebuildFixes
- Defined in:
- lib/gym/xcodebuild_fixes/swift_fix.rb,
lib/gym/xcodebuild_fixes/watchkit_fix.rb,
lib/gym/xcodebuild_fixes/watchkit2_fix.rb,
lib/gym/xcodebuild_fixes/generic_archive_fix.rb,
lib/gym/xcodebuild_fixes/package_application_fix.rb
Class Method Summary collapse
-
.check_for_swift(pcg) ⇒ Object
True if swift.
-
.generic_archive_fix ⇒ Object
Determine IPAs for the Watch App which aren’t inside of a containing iOS App and removes them.
-
.is_watchkit_app?(app_path) ⇒ Boolean
Does this application have a WatchKit target.
-
.patch_package_application ⇒ Object
Fix PackageApplication Perl script by Xcode to create the IPA from the archive.
-
.should_apply_watchkit1_fix? ⇒ Boolean
Should only be applied if watchkit app is not a watchkit2 app.
-
.swift_library_fix ⇒ Object
Determine whether it is a Swift project and, eventually, include all required libraries to copy from Xcode’s toolchain directory.
- .toolchain_dir_name_for_toolchain(toolchain) ⇒ Object
-
.watchkit2? ⇒ Boolean
Does this application have a WatchKit target.
-
.watchkit2_fix ⇒ Object
Determine whether this app has WatchKit2 support and manually package up the WatchKit2 framework.
-
.watchkit? ⇒ Boolean
Does this application have a WatchKit target.
-
.watchkit_fix ⇒ Object
Determine whether this app has WatchKit support and manually package up the WatchKit framework.
-
.wrap_xcodebuild ⇒ Object
Wrap xcodebuild to work-around ipatool dependecy to system ruby.
-
.zip_entries_matching(zipfile, file_pattern) ⇒ Object
return the entries (files or directories) in the zip matching the pattern.
Class Method Details
.check_for_swift(pcg) ⇒ Object
Returns true if swift.
79 80 81 82 83 |
# File 'lib/gym/xcodebuild_fixes/swift_fix.rb', line 79 def check_for_swift(pcg) UI.verbose "Checking for Swift framework" default_swift_libs = "#{pcg.appfile_path}/Frameworks/libswift.*" # note the extra ., this is a string representation of a regexp zip_entries_matching(pcg.ipa_path, /#{default_swift_libs}/).count > 0 end |
.generic_archive_fix ⇒ Object
Determine IPAs for the Watch App which aren’t inside of a containing iOS App and removes them.
In the future it may be nice to modify the plist file for the archive itself so that it points to the correct IPA as well.
This is a workaround for this bug github.com/CocoaPods/CocoaPods/issues/4178
14 15 16 17 18 19 20 21 22 23 |
# File 'lib/gym/xcodebuild_fixes/generic_archive_fix.rb', line 14 def generic_archive_fix UI.verbose "Looking For Orphaned WatchKit2 Applications" Dir.glob("#{BuildCommandGenerator.archive_path}/Products/Applications/*.app").each do |app_path| if is_watchkit_app?(app_path) UI.verbose "Removing Orphaned WatchKit2 Application #{app_path}" FileUtils.rm_rf(app_path) end end end |
.is_watchkit_app?(app_path) ⇒ Boolean
Does this application have a WatchKit target
26 27 28 29 |
# File 'lib/gym/xcodebuild_fixes/generic_archive_fix.rb', line 26 def is_watchkit_app?(app_path) plist_path = "#{app_path}/Info.plist" `/usr/libexec/PlistBuddy -c 'Print :DTSDKName' #{plist_path.shellescape} 2>&1`.match(/^\s*watchos2\.\d+\s*$/) != nil end |
.patch_package_application ⇒ Object
Fix PackageApplication Perl script by Xcode to create the IPA from the archive
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/gym/xcodebuild_fixes/package_application_fix.rb', line 5 def patch_package_application require 'fileutils' # Initialization @patched_package_application_path = File.join("/tmp", "PackageApplication4Gym") return @patched_package_application_path if File.exist?(@patched_package_application_path) Dir.mktmpdir do |tmpdir| # Check current set of PackageApplication MD5 hashes require 'digest' path = File.join(Gym::ROOT, "lib/assets/package_application_patches/PackageApplication_MD5") expected_md5_hashes = File.read(path).split("\n") # If that location changes, search it using xcrun --sdk iphoneos -f PackageApplication package_application_path = "#{Xcode.xcode_path}/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication" UI.crash!("Unable to patch the `PackageApplication` script bundled in Xcode. This is not supported.") unless expected_md5_hashes.include?(Digest::MD5.file(package_application_path).hexdigest) # Duplicate PackageApplication script to PackageApplication4Gym FileUtils.copy_file(package_application_path, @patched_package_application_path) # Apply patches to PackageApplication4Gym from patches folder Dir[File.join(Gym::ROOT, "lib/assets/package_application_patches/*.diff")].each do |patch| UI.verbose "Applying Package Application patch: #{File.basename(patch)}" command = ["patch '#{@patched_package_application_path}' < '#{patch}'"] Runner.new.print_command(command, "Applying Package Application patch: #{File.basename(patch)}") if $verbose FastlaneCore::CommandExecutor.execute(command: command, print_all: false, print_command: $verbose, error: proc do |output| ErrorHandler.handle_package_error(output) end) end end return @patched_package_application_path # Return path to the patched PackageApplication end |
.should_apply_watchkit1_fix? ⇒ Boolean
Should only be applied if watchkit app is not a watchkit2 app
35 36 37 |
# File 'lib/gym/xcodebuild_fixes/watchkit_fix.rb', line 35 def should_apply_watchkit1_fix? watchkit? && !Gym::XcodebuildFixes.watchkit2? end |
.swift_library_fix ⇒ Object
Determine whether it is a Swift project and, eventually, include all required libraries to copy from Xcode’s toolchain directory. Since there’s no “xcodebuild” target to do just that, it is done post-build when exporting an archived build.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/gym/xcodebuild_fixes/swift_fix.rb', line 12 def swift_library_fix require 'fileutils' return if check_for_swift(PackageCommandGenerator) UI.verbose "Packaging up the Swift Framework as the current app is a Swift app" ipa_swift_frameworks = Dir["#{PackageCommandGenerator.appfile_path}/Frameworks/libswift*"] Dir.mktmpdir do |tmpdir| # Copy all necessary Swift libraries to a temporary "SwiftSupport" directory so that we can # easily add it to the .ipa later. swift_support = File.join(tmpdir, "SwiftSupport") Dir.mkdir(swift_support) ipa_swift_frameworks.each do |path| framework = File.basename(path) begin toolchain_dir = toolchain_dir_name_for_toolchain(Gym.config[:toolchain]) from = File.join(Xcode.xcode_path, "Toolchains/#{toolchain_dir}/usr/lib/swift/iphoneos/#{framework}") to = File.join(swift_support, framework) UI.verbose("Copying Swift framework from '#{from}'") FileUtils.copy_file(from, to) rescue => ex UI.error("Error copying over framework file. Please try running gym without the legacy build API enabled") UI.error("For more information visit https://github.com/fastlane/fastlane/issues/5863") UI.error("Missing file #{path} inside #{Xcode.xcode_path}") if Gym.config[:toolchain].nil? UI.important("If you're using Swift 2.3, but already updated to Xcode 8") UI.important("try adding the following parameter to your gym call:") UI.success("gym(use_legacy_build_api: true, toolchain: :swift_2_3)") UI.("or") UI.success("gym --use_legacy_build_api --toolchain swift_2_3") end UI.user_error!(ex) end end # Add "SwiftSupport" to the .ipa archive Dir.chdir(tmpdir) do command_parts = ["zip --recurse-paths '#{PackageCommandGenerator.ipa_path}' SwiftSupport"] command_parts << "> /dev/null" unless $verbose FastlaneCore::CommandExecutor.execute(command: command_parts, print_all: false, print_command: !Gym.config[:silent], error: proc do |output| ErrorHandler.handle_package_error(output) end) end end end |
.toolchain_dir_name_for_toolchain(toolchain) ⇒ Object
70 71 72 73 74 75 |
# File 'lib/gym/xcodebuild_fixes/swift_fix.rb', line 70 def toolchain_dir_name_for_toolchain(toolchain) return "Swift_2.3.xctoolchain" if toolchain == "com.apple.dt.toolchain.Swift_2_3" UI.error("No specific folder was found for toolchain #{toolchain}. Using the default toolchain location.") if toolchain return "XcodeDefault.xctoolchain" end |
.watchkit2? ⇒ Boolean
Does this application have a WatchKit target
28 29 30 31 32 |
# File 'lib/gym/xcodebuild_fixes/watchkit2_fix.rb', line 28 def watchkit2? Dir["#{PackageCommandGenerator.appfile_path}/**/*.plist"].any? do |plist_path| `/usr/libexec/PlistBuddy -c 'Print DTSDKName' '#{plist_path}' 2>&1`.match(/^\s*watchos2\.\d+\s*$/) end end |
.watchkit2_fix ⇒ Object
Determine whether this app has WatchKit2 support and manually package up the WatchKit2 framework
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/gym/xcodebuild_fixes/watchkit2_fix.rb', line 5 def watchkit2_fix return unless watchkit2? UI.verbose "Adding WatchKit2 support" Dir.mktmpdir do |tmpdir| # Make watchkit support directory watchkit_support = File.join(tmpdir, "WatchKitSupport2") Dir.mkdir(watchkit_support) # Copy WK from Xcode into WatchKitSupport2 FileUtils.copy_file("#{Xcode.xcode_path}/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/Library/Application Support/WatchKit/WK", File.join(watchkit_support, "WK")) # Add "WatchKitSupport2" to the .ipa archive Dir.chdir(tmpdir) do abort unless system %(zip --recurse-paths "#{PackageCommandGenerator.ipa_path}" "WatchKitSupport2" > /dev/null) end UI.verbose "Successfully added WatchKit2 support" end end |
.watchkit? ⇒ Boolean
Does this application have a WatchKit target
28 29 30 31 32 |
# File 'lib/gym/xcodebuild_fixes/watchkit_fix.rb', line 28 def watchkit? Dir["#{PackageCommandGenerator.appfile_path}/**/*.plist"].any? do |plist_path| `/usr/libexec/PlistBuddy -c 'Print WKWatchKitApp' '#{plist_path}' 2>&1`.strip == 'true' end end |
.watchkit_fix ⇒ Object
Determine whether this app has WatchKit support and manually package up the WatchKit framework
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/gym/xcodebuild_fixes/watchkit_fix.rb', line 5 def watchkit_fix return unless should_apply_watchkit1_fix? UI.verbose "Adding WatchKit support" Dir.mktmpdir do |tmpdir| # Make watchkit support directory watchkit_support = File.join(tmpdir, "WatchKitSupport") Dir.mkdir(watchkit_support) # Copy WK from Xcode into WatchKitSupport FileUtils.copy_file("#{Xcode.xcode_path}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/Library/Application Support/WatchKit/WK", File.join(watchkit_support, "WK")) # Add "WatchKitSupport" to the .ipa archive Dir.chdir(tmpdir) do abort unless system %(zip --recurse-paths "#{PackageCommandGenerator.ipa_path}" "WatchKitSupport" > /dev/null) end UI.verbose "Successfully added WatchKit support" end end |
.wrap_xcodebuild ⇒ Object
Wrap xcodebuild to work-around ipatool dependecy to system ruby
47 48 49 50 |
# File 'lib/gym/xcodebuild_fixes/package_application_fix.rb', line 47 def wrap_xcodebuild require 'fileutils' @wrapped_xcodebuild_path ||= File.join(Gym::ROOT, "lib/assets/wrap_xcodebuild/xcbuild-safe.sh") end |
.zip_entries_matching(zipfile, file_pattern) ⇒ Object
return the entries (files or directories) in the zip matching the pattern
88 89 90 91 92 93 94 95 96 |
# File 'lib/gym/xcodebuild_fixes/swift_fix.rb', line 88 def zip_entries_matching(zipfile, file_pattern) files = [] Zip::File.open(zipfile) do |zip_file| zip_file.each do |entry| files << entry.name if entry.name.force_encoding("utf-8").match file_pattern end end files end |