diff --git a/Package.swift b/Package.swift index f3a670af..ef5b54b8 100644 --- a/Package.swift +++ b/Package.swift @@ -33,9 +33,13 @@ let package = Package( dependencies: ["XCRemoteCache"] ), .target( - name: "xclibtool", + name: "xclibtoolSupport", dependencies: ["XCRemoteCache"] ), + .target( + name: "xclibtool", + dependencies: ["XCRemoteCache", "xclibtoolSupport"] + ), .target( name: "xcpostbuild", dependencies: ["XCRemoteCache"] @@ -65,5 +69,9 @@ let package = Package( dependencies: ["XCRemoteCache"], resources: [.copy("TestData")] ), + .testTarget( + name: "xclibtoolSupportTests", + dependencies: ["xclibtoolSupport"] + ), ] ) diff --git a/Sources/XCRemoteCache/Commands/Libtool/XCLibtool.swift b/Sources/XCRemoteCache/Commands/Libtool/XCLibtool.swift index 39e241ab..70c5004a 100644 --- a/Sources/XCRemoteCache/Commands/Libtool/XCLibtool.swift +++ b/Sources/XCRemoteCache/Commands/Libtool/XCLibtool.swift @@ -20,7 +20,7 @@ import Foundation /// Represents a mode that libtool was called -public enum XCLibtoolMode { +public enum XCLibtoolMode: Equatable { /// Creating a static library (ar format) from a set of .o input files case createLibrary(output: String, filelist: String, dependencyInfo: String) /// Creating a universal library (multiple-architectures) from a set of input .a static libraries diff --git a/Sources/xclibtool/XCLibtoolMain.swift b/Sources/xclibtool/XCLibtoolMain.swift index 65eec739..f4bdc788 100644 --- a/Sources/xclibtool/XCLibtoolMain.swift +++ b/Sources/xclibtool/XCLibtoolMain.swift @@ -18,53 +18,24 @@ // under the License. import Foundation +import xclibtoolSupport import XCRemoteCache +public enum XCLibtoolMainError: Error { + case missingOutput + case unsupportedMode +} + /// Wrapper for a `libtool` program that copies the build executable (e.g. .a) from a cached-downloaded location /// Fallbacks to a standard `libtool` when the Ramote cache is not applicable (e.g. modified sources) public class XCLibtoolMain { + public init() { } + public func main() { let args = ProcessInfo().arguments - var output: String? - // all input arguments library '.a'. Used to create an universal binary - var inputLibraries: [String] = [] - var filelist: String? - var dependencyInfo: String? - var i = 0 - while i < args.count { - switch args[i] { - case "-o": - output = args[i + 1] - i += 1 - case "-filelist": - filelist = args[i + 1] - i += 1 - case "-dependency_info": - dependencyInfo = args[i + 1] - i += 1 - case let input where input.hasSuffix(".a"): - inputLibraries.append(input) - default: - break - } - i += 1 - } - guard let outputInput = output else { - exit(1, "Missing 'output' argument. Args: \(args)") - } - let mode: XCLibtoolMode - if let filelistInput = filelist, let dependencyInfoInput = dependencyInfo { - // libtool is creating a library - mode = .createLibrary(output: outputInput, filelist: filelistInput, dependencyInfo: dependencyInfoInput) - } else if !inputLibraries.isEmpty { - // multiple input libraries suggest creating an universal binary - mode = .createUniversalBinary(output: outputInput, inputs: inputLibraries) - } else { - // unknown mode - exit(1, "Unsupported mode. Args: \(args)") - } do { + let mode = try XCLibtoolHelper.buildMode(args: args) try XCLibtool(mode).run() } catch { exit(1, "Failed with: \(error). Args: \(args)") diff --git a/Sources/xclibtoolSupport/XCLibtoolHelper.swift b/Sources/xclibtoolSupport/XCLibtoolHelper.swift new file mode 100644 index 00000000..8b0c11f3 --- /dev/null +++ b/Sources/xclibtoolSupport/XCLibtoolHelper.swift @@ -0,0 +1,72 @@ +// Copyright (c) 2023 Spotify AB. +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import Foundation +import XCRemoteCache + +public enum XCLibtoolHelperError: Error { + case missingOutput + case unsupportedMode +} + +public class XCLibtoolHelper { + public static func buildMode(args: [String]) throws -> XCLibtoolMode { + var output: String? + // all input arguments library '.a'. Used to create an universal binary + var inputLibraries: [String] = [] + var filelist: String? + var dependencyInfo: String? + var i = 0 + while i < args.count { + switch args[i] { + case "-o": + output = args[i + 1] + i += 1 + case "-filelist": + filelist = args[i + 1] + i += 1 + case "-dependency_info": + dependencyInfo = args[i + 1] + i += 1 + case let input where input.hasSuffix(".a"): + // Support for + inputLibraries.append(input) + default: + break + } + i += 1 + } + guard let outputInput = output else { + throw XCLibtoolHelperError.missingOutput + } + + let mode: XCLibtoolMode + if let filelistInput = filelist, let dependencyInfoInput = dependencyInfo { + // libtool is creating a library + mode = .createLibrary(output: outputInput, filelist: filelistInput, dependencyInfo: dependencyInfoInput) + } else if !inputLibraries.isEmpty { + // multiple input libraries suggest creating an universal binary + mode = .createUniversalBinary(output: outputInput, inputs: inputLibraries) + } else { + // unknown mode + throw XCLibtoolHelperError.unsupportedMode + } + return mode + } +} diff --git a/Tests/xclibtoolSupportTests/XCLibtoolHelperTests.swift b/Tests/xclibtoolSupportTests/XCLibtoolHelperTests.swift new file mode 100644 index 00000000..836f7b96 --- /dev/null +++ b/Tests/xclibtoolSupportTests/XCLibtoolHelperTests.swift @@ -0,0 +1,66 @@ +// Copyright (c) 2023 Spotify AB. +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +@testable import xclibtoolSupport +import XCTest + +class XCLibtoolHelperTests: XCTestCase { +// func testStaticFrameworkUniversalBinary() throws { +// let mode = try XCLibtoolHelper.buildMode( +// args: ["-o", "/universal/static", "/arch1/static", "arch2/static"] +// ) +// +// XCTAssertEqual(mode, .createUniversalBinary( +// output: "/universal/static", +// inputs: ["/arch1/static", "arch2/static"] +// )) +// } +// +// func testStaticLibraryUniversalBinary() throws { +// let mode = try XCLibtoolHelper.buildMode( +// args: ["-o", "/universal/static.a", "/arch1/static.a", "arch2/static.a"] +// ) +// +// XCTAssertEqual(mode, .createUniversalBinary( +// output: "/universal/static.a", +// inputs: ["/arch1/static.a", "arch2/static.a"] +// )) +// } +// +// func testUnknownExtensionInputThrowsUnsupportedMode() throws { +// XCTAssertThrowsError(try XCLibtoolHelper.buildMode( +// args: ["-o", "/universal/static.a", "/arch1/static.unknown"])) { error in +// switch error { +// case XCLibtoolHelperError.unsupportedMode: break +// default: +// XCTFail("Not expected error") +// } +// } +// } +// +// func testMissingOutputThrowsMissingOutput() throws { +// XCTAssertThrowsError(try XCLibtoolHelper.buildMode(args: ["/arch1/static"])) { error in +// switch error { +// case XCLibtoolHelperError.missingOutput: break +// default: +// XCTFail("Not expected error") +// } +// } +// } +} diff --git a/e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/project.pbxproj b/e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/project.pbxproj index b894e30d..346464aa 100644 --- a/e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/project.pbxproj +++ b/e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 36201A2A2843B3D3002FF70F /* MixedTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36201A292843B3D3002FF70F /* MixedTarget.swift */; }; 36201A362843B435002FF70F /* libMixedTarget.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 36201A272843B3D3002FF70F /* libMixedTarget.a */; }; 36201A392843BDDC002FF70F /* StandaloneObjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 36201A382843BDDC002FF70F /* StandaloneObjc.m */; }; + 4EE6CF4929B6C1A000AEE1B4 /* StaticFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EE6CF4829B6C1A000AEE1B4 /* StaticFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4EE6CF5329B6C1AF00AEE1B4 /* StaticFrameworkFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE6CF5229B6C1AF00AEE1B4 /* StaticFrameworkFile.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -38,6 +40,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4EE6CF4E29B6C1A000AEE1B4 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -55,6 +67,12 @@ 36201A302843B414002FF70F /* SomeObjC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SomeObjC.h; sourceTree = ""; }; 36201A372843BDDC002FF70F /* StandaloneObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StandaloneObjc.h; sourceTree = ""; }; 36201A382843BDDC002FF70F /* StandaloneObjc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StandaloneObjc.m; sourceTree = ""; }; + 4E628CA229B8066500AF2DB0 /* SandaloneWatchAppExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SandaloneWatchAppExtension.swift; sourceTree = ""; }; + 4E628CA429B8066500AF2DB0 /* SandaloneWatchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SandaloneWatchApp.swift; sourceTree = ""; }; + 4E628CA629B8066500AF2DB0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4EE6CF4629B6C1A000AEE1B4 /* StaticFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = StaticFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4EE6CF4829B6C1A000AEE1B4 /* StaticFramework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StaticFramework.h; sourceTree = ""; }; + 4EE6CF5229B6C1AF00AEE1B4 /* StaticFrameworkFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StaticFrameworkFile.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -73,6 +91,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4EE6CF4329B6C1A000AEE1B4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -81,6 +106,8 @@ children = ( 36201A0E2843B3C3002FF70F /* StandaloneApp */, 36201A282843B3D3002FF70F /* MixedTarget */, + 4EE6CF4729B6C1A000AEE1B4 /* StaticFramework */, + 4E628CA129B8066500AF2DB0 /* SandaloneWatchApp */, 36201A0D2843B3C3002FF70F /* Products */, 36201A352843B435002FF70F /* Frameworks */, ); @@ -91,6 +118,7 @@ children = ( 36201A0C2843B3C3002FF70F /* StandaloneApp.app */, 36201A272843B3D3002FF70F /* libMixedTarget.a */, + 4EE6CF4629B6C1A000AEE1B4 /* StaticFramework.framework */, ); name = Products; sourceTree = ""; @@ -128,8 +156,38 @@ name = Frameworks; sourceTree = ""; }; + 4E628CA129B8066500AF2DB0 /* SandaloneWatchApp */ = { + isa = PBXGroup; + children = ( + 4E628CA229B8066500AF2DB0 /* SandaloneWatchAppExtension.swift */, + 4E628CA429B8066500AF2DB0 /* SandaloneWatchApp.swift */, + 4E628CA629B8066500AF2DB0 /* Info.plist */, + ); + path = SandaloneWatchApp; + sourceTree = ""; + }; + 4EE6CF4729B6C1A000AEE1B4 /* StaticFramework */ = { + isa = PBXGroup; + children = ( + 4EE6CF5229B6C1AF00AEE1B4 /* StaticFrameworkFile.swift */, + 4EE6CF4829B6C1A000AEE1B4 /* StaticFramework.h */, + ); + path = StaticFramework; + sourceTree = ""; + }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 4EE6CF4129B6C1A000AEE1B4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4EE6CF4929B6C1A000AEE1B4 /* StaticFramework.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ 36201A0B2843B3C3002FF70F /* StandaloneApp */ = { isa = PBXNativeTarget; @@ -138,6 +196,7 @@ 36201A082843B3C3002FF70F /* Sources */, 36201A092843B3C3002FF70F /* Frameworks */, 36201A0A2843B3C3002FF70F /* Resources */, + 4EE6CF4E29B6C1A000AEE1B4 /* Embed Frameworks */, ); buildRules = ( ); @@ -167,6 +226,24 @@ productReference = 36201A272843B3D3002FF70F /* libMixedTarget.a */; productType = "com.apple.product-type.library.static"; }; + 4EE6CF4529B6C1A000AEE1B4 /* StaticFramework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4EE6CF5129B6C1A000AEE1B4 /* Build configuration list for PBXNativeTarget "StaticFramework" */; + buildPhases = ( + 4EE6CF4129B6C1A000AEE1B4 /* Headers */, + 4EE6CF4229B6C1A000AEE1B4 /* Sources */, + 4EE6CF4329B6C1A000AEE1B4 /* Frameworks */, + 4EE6CF4429B6C1A000AEE1B4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = StaticFramework; + productName = StaticFramework; + productReference = 4EE6CF4629B6C1A000AEE1B4 /* StaticFramework.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -174,7 +251,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1320; + LastSwiftUpdateCheck = 1420; LastUpgradeCheck = 1320; TargetAttributes = { 36201A0B2843B3C3002FF70F = { @@ -185,6 +262,10 @@ CreatedOnToolsVersion = 13.2.1; LastSwiftMigration = 1320; }; + 4EE6CF4529B6C1A000AEE1B4 = { + CreatedOnToolsVersion = 14.2; + LastSwiftMigration = 1420; + }; }; }; buildConfigurationList = 36201A072843B3C3002FF70F /* Build configuration list for PBXProject "StandaloneApp" */; @@ -202,6 +283,7 @@ targets = ( 36201A0B2843B3C3002FF70F /* StandaloneApp */, 36201A262843B3D3002FF70F /* MixedTarget */, + 4EE6CF4529B6C1A000AEE1B4 /* StaticFramework */, ); }; /* End PBXProject section */ @@ -217,6 +299,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4EE6CF4429B6C1A000AEE1B4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -264,6 +353,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4EE6CF4229B6C1A000AEE1B4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4EE6CF5329B6C1AF00AEE1B4 /* StaticFrameworkFile.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -413,6 +510,7 @@ 36201A212843B3C7002FF70F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; @@ -443,6 +541,7 @@ 36201A222843B3C7002FF70F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; @@ -508,6 +607,79 @@ }; name = Release; }; + 4EE6CF4F29B6C1A000AEE1B4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MARKETING_VERSION = 1.0; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.squareup.StaticFramework; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 4.0; + }; + name = Debug; + }; + 4EE6CF5029B6C1A000AEE1B4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MARKETING_VERSION = 1.0; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.squareup.StaticFramework; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 4.0; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -538,6 +710,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 4EE6CF5129B6C1A000AEE1B4 /* Build configuration list for PBXNativeTarget "StaticFramework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4EE6CF4F29B6C1A000AEE1B4 /* Debug */, + 4EE6CF5029B6C1A000AEE1B4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 36201A042843B3C3002FF70F /* Project object */; diff --git a/e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/xcshareddata/xcschemes/StaticFramework.xcscheme b/e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/xcshareddata/xcschemes/StaticFramework.xcscheme new file mode 100644 index 00000000..b645eca9 --- /dev/null +++ b/e2eTests/StandaloneSampleApp/StandaloneApp.xcodeproj/xcshareddata/xcschemes/StaticFramework.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/e2eTests/StandaloneSampleApp/StaticFramework/StaticFramework.h b/e2eTests/StandaloneSampleApp/StaticFramework/StaticFramework.h new file mode 100644 index 00000000..d67d10a3 --- /dev/null +++ b/e2eTests/StandaloneSampleApp/StaticFramework/StaticFramework.h @@ -0,0 +1,11 @@ +#import + +//! Project version number for StaticFramework. +FOUNDATION_EXPORT double StaticFrameworkVersionNumber; + +//! Project version string for StaticFramework. +FOUNDATION_EXPORT const unsigned char StaticFrameworkVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/e2eTests/StandaloneSampleApp/StaticFramework/StaticFrameworkFile.swift b/e2eTests/StandaloneSampleApp/StaticFramework/StaticFrameworkFile.swift new file mode 100644 index 00000000..4d3fe516 --- /dev/null +++ b/e2eTests/StandaloneSampleApp/StaticFramework/StaticFrameworkFile.swift @@ -0,0 +1,5 @@ +import Foundation + +public class SomeClass { + public init() {} +} diff --git a/tasks/e2e.rb b/tasks/e2e.rb index 7700b441..7bcde1aa 100644 --- a/tasks/e2e.rb +++ b/tasks/e2e.rb @@ -15,7 +15,7 @@ NGINX_ROOT_DIR = '/tmp/cache' XCRC_BINARIES = 'XCRC' SHARED_COCOAPODS_CONFIG = { - 'cache_addresses' => ['http://localhost:8080/cache/pods'], + 'cache_addresses' => ['http://localhost:8080/cache/pods'], 'primary_repo' => GIT_REMOTE_ADDRESS, 'primary_branch' => GIT_BRANCH, 'mode' => 'consumer', @@ -58,6 +58,7 @@ system("pwd") system("#{XCRC_BINARIES}/xcprepare integrate --input StandaloneApp.xcodeproj --mode producer --final-producer-target StandaloneApp") # Build the project to fill in the cache + build_project(nil, "StandaloneApp.xcodeproj", 'StaticFramework', 'watch', 'watchOS') build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp') system("#{XCRC_BINARIES}/xcprepare stats --reset --format json") end @@ -73,13 +74,15 @@ prepare_for_standalone(consumer_srcroot) Dir.chdir(consumer_srcroot) do system("#{XCRC_BINARIES}/xcprepare integrate --input StandaloneApp.xcodeproj --mode consumer") - build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"}) + build_project(nil, "StandaloneApp.xcodeproj", 'StaticFramework', 'watch', 'watchOS', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"}) + build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', 'iphone', 'iOS', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"}) valide_hit_rate puts 'Building standalone consumer with local change...' # Extra: validate local compilation of the Standalone ObjC code system("echo '' >> StandaloneApp/StandaloneObjc.m") - build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_local"}) + build_project(nil, "StandaloneApp.xcodeproj", 'StaticFramework', 'watch', 'watchOS', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_local"}) + build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', 'iphone', 'iOS', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer_local"}) end # Revert all side effects @@ -103,7 +106,7 @@ def self.start_nginx at_exit { puts('resetting ngingx'); system('nginx -s stop') } end - # Create a new branch out of a current commit and + # Create a new branch out of a current commit and # add remote that points to itself def self.configure_git system("git checkout -B #{GIT_BRANCH}") @@ -144,7 +147,7 @@ def self.clean def self.cocoapods_configuration_string(extra_configs = {}) configuration_lines = ['xcremotecache({'] all_properties = SHARED_COCOAPODS_CONFIG.merge(extra_configs) - config_lines = all_properties.map {|key, value| " \"#{key}\" => #{value.inspect},"} + config_lines = all_properties.map {|key, value| " \"#{key}\" => #{value.inspect},"} configuration_lines.push(*config_lines) configuration_lines << '})' configuration_lines.join("\n") @@ -159,20 +162,20 @@ def self.dump_podfile(config, source) end end - def self.build_project(workspace, project, scheme, extra_args = {}) + def self.build_project(workspace, project, scheme, sdk = 'iphone', platform = 'iOS', extra_args = {}) xcodebuild_args = { 'workspace' => workspace, 'project' => project, 'scheme' => scheme, 'configuration' => 'Debug', - 'sdk' => 'iphonesimulator', - 'destination' => 'generic/platform=iOS Simulator', + 'sdk' => "#{sdk}simulator", + 'destination' => "generic/platform=#{platform} Simulator", 'derivedDataPath' => DERIVED_DATA_PATH, }.merge(extra_args).compact xcodebuild_vars = { - 'EXCLUDED_ARCHS' => 'arm64 i386' + 'EXCLUDED_ARCHS' => 'arm64' } - args = ['xcodebuild'] + args = ['set -o pipefail;', 'xcodebuild'] args.push(*xcodebuild_args.map {|k,v| "-#{k} '#{v}'"}) args.push(*xcodebuild_vars.map {|k,v| "#{k}='#{v}'"}) args.push('clean build') @@ -185,12 +188,12 @@ def self.build_project(workspace, project, scheme, extra_args = {}) end end - def self.build_project_cocoapods(extra_args = {}) + def self.build_project_cocoapods(sdk = 'iphone', platform = 'iOS', extra_args = {}) system('pod install') - build_project('XCRemoteCacheSample.xcworkspace', nil, 'XCRemoteCacheSample', extra_args) + build_project('XCRemoteCacheSample.xcworkspace', nil, 'XCRemoteCacheSample', sdk, platform, extra_args) end - def self.read_stats + def self.read_stats stats_json_string = JSON.parse(`#{XCRC_BINARIES}/xcprepare stats --format json`) misses = stats_json_string.fetch('miss_count', 0) hits = stats_json_string.fetch('hit_count', 0) @@ -213,7 +216,7 @@ def self.run_cocoapods_scenario(template_path) consumer_configuration = cocoapods_configuration_string() puts("****** Scenario: #{template_path}") - + # Run producer build pre_producer_setup dump_podfile(producer_configuration, template_path) @@ -229,7 +232,7 @@ def self.run_cocoapods_scenario(template_path) dump_podfile(consumer_configuration, template_path) puts('Building consumer ...') Dir.chdir(E2E_COCOAPODS_SAMPLE_DIR) do - build_project_cocoapods({'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"}) + build_project_cocoapods('iphone', 'iOS', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"}) valide_hit_rate end end