Compare commits

..

34 Commits

Author SHA1 Message Date
Bartosz Polaczyk 352e72f44c Merge pull request #199 from polac24/add-graceful-missing-common-sha
Add support for graceful handling missing common sha
2023-04-20 10:04:08 -04:00
Bartosz Polaczyk 1c67b79a7a Apply suggestions from code review
Co-authored-by: Aleksander Grzyb <aleksander.grzyb@gmail.com>
2023-04-19 21:44:17 -04:00
Bartosz Polaczyk c7de203741 Fix linting 2023-04-18 19:51:10 -07:00
Bartosz Polaczyk b7e18916e6 Add support for graceful handling missing common sha 2023-04-18 19:41:53 -07:00
Bartosz Polaczyk dfb4039404 Merge pull request #198 from polac24/bartosz/20230413-override-exclusions
Set libtool Build Setting for excluded sdks
2023-04-14 20:21:37 -07:00
Bartosz Polaczyk b28613a2ef Fix formatting 2023-04-13 18:47:54 -07:00
Bartosz Polaczyk 1535b762bc Cleanup unnecessary unless 2023-04-13 18:33:10 -07:00
Bartosz Polaczyk e6816846c3 Add original LIBTOOL to excluded SDKs 2023-04-13 18:27:27 -07:00
Bartosz Polaczyk b89d98f411 Reset LIBTOOL to libtool for excluded sdks 2023-04-12 17:43:22 -07:00
Bartosz Polaczyk a0d3d1b0b9 Reset SWIFT_EXEC to swiftc for excluded sdks 2023-04-11 22:33:20 -07:00
Bartosz Polaczyk f432917505 Merge pull request #197 from polac24/fix-exclude-sdk
Reset cflags and swiftflags on each cocoapods integration
2023-04-11 18:49:32 -07:00
Bartosz Polaczyk 5d297a4fb2 Revert incorrect OTHER_SWIFT_FLAGS format 2023-04-10 22:04:45 -07:00
Bartosz Polaczyk b0d5f1660e Reset cflags and swiftflags on each cocoapods integration 2023-04-10 19:30:32 -07:00
Bartosz Polaczyk baea2de79a Merge pull request #196 from polac24/exclude-sdks
Add support for excluded sdks
2023-04-10 19:20:09 -07:00
Bartosz Polaczyk d2803f4ad5 Fix linter 2023-04-09 11:40:46 -07:00
Bartosz Polaczyk ab017367b2 Add disabled support in Marking 2023-04-09 11:35:50 -07:00
Bartosz Polaczyk 30cb648641 Add disabled support in XCPostbuild 2023-04-09 11:06:50 -07:00
Bartosz Polaczyk 3330ca45f8 Cleanup README.md 2023-04-09 09:33:42 -07:00
Bartosz Polaczyk d3193b15a8 Fix hook 2023-04-09 09:30:42 -07:00
Bartosz Polaczyk 7b14f2c9ff Revert e2e cleanups 2023-04-09 09:24:24 -07:00
Bartosz Polaczyk 4933b454a5 Reset build settings in cocoapods 2023-04-09 09:23:49 -07:00
Bartosz Polaczyk 79ffdce295 Fix standalone validation 2023-04-08 20:03:30 -07:00
Bartosz Polaczyk 376e6a17c5 Fix linting 2023-04-08 19:47:42 -07:00
Bartosz Polaczyk a4d1849821 Fix linting 2023-04-08 19:36:35 -07:00
Bartosz Polaczyk 325fb07080 Add explicit switch 2023-04-08 19:27:58 -07:00
Bartosz Polaczyk a2afe62751 Fix configs 2023-04-08 17:51:09 -07:00
Bartosz Polaczyk bbaa374e12 Fix configs jsons 2023-04-08 17:46:41 -07:00
Bartosz Polaczyk 39a259ff49 Fix hook 2023-04-08 17:39:12 -07:00
Bartosz Polaczyk a2b9cbf332 Add cocoapods E2E tests 2023-04-08 17:29:35 -07:00
Bartosz Polaczyk 5710594bc4 Bump version to 0.0.16 2023-04-08 04:37:35 -07:00
Bartosz Polaczyk 7d123792b8 Add cocoapods support 2023-04-08 04:37:14 -07:00
Bartosz Polaczyk 5856dbec77 Add skippedSDKs 2023-04-08 04:19:28 -07:00
Bartosz Polaczyk 600310f44b Merge pull request #194 from polac24/space-aws
Encode path in AWS V4 signing requests
2023-04-03 07:24:02 -07:00
Bartosz Polaczyk aae2b3289c Use addingPercentEncoding for path escaping 2023-03-31 18:33:39 -07:00
34 changed files with 518 additions and 65 deletions
+2
View File
@@ -152,6 +152,7 @@ xcremotecache/xcprepare integrate --input <yourProject.xcodeproj> --mode consume
| `--lldb-init` | LLDBInit mode. Appends to .lldbinit a command required for debugging. Supported values: 'none' (do not append to .lldbinit), 'user' (append to ~/.lldbinit) | `user` | ⬜️ |
| `--fake-src-root` | An arbitrary source location shared between producers and consumers. Should be unique for a project. | `/xxxxxxxxxx` | ⬜️ |
| `--output` | Save the project with integrated XCRemoteCache to a separate location. | N/A | ⬜️ |
| `--sdks-exclude` | comma separated list of sdks to not integrate XCRemoteCache (e.g. "watchos*, watchsimulator*"). (Experimental) | `""` | ⬜️ |
</details>
@@ -357,6 +358,7 @@ Note: This step is not required if at least one of these is true:
| `disable_vfs_overlay` | A feature flag to disable virtual file system overlay support (temporary) | `false` | ⬜️ |
| `custom_rewrite_envs` | A list of extra ENVs that should be used as placeholders in the dependency list. ENV rewrite process is optimistic - does nothing if an ENV is not defined in the pre/postbuild process. | `[]` | ⬜️ |
| `irrelevant_dependencies_paths` | Regexes of files that should not be included in a list of dependencies. Warning! Add entries here with caution - excluding dependencies that are relevant might lead to a target overcaching. The regex can match either partially or fully the filepath, e.g. `\\.modulemap$` will exclude all `.modulemap` files. | `[]` | ⬜️ |
| `gracefully_handle_missing_common_sha` | If true, do not fail `prepare` if cannot find the most recent common commits with the primary branch. That might be useful on CI, where a shallow clone is used and cloning depth is not big enough to fetch a commit from a primary branch | `false` | ⬜️ |
## Backend cache server
@@ -87,6 +87,8 @@ public struct PostbuildContext {
let irrelevantDependenciesPaths: [String]
/// Location of public headers. Not always available (e.g. static libraries)
var publicHeadersFolderPath: URL?
/// XCRemoteCache is explicitly disabled
let disabled: Bool
}
extension PostbuildContext {
@@ -146,5 +148,6 @@ extension PostbuildContext {
// generated and it is up to a project configuration to place it in a common location (e.g. static library)
publicHeadersFolderPath = builtProductsDir.appendingPathComponent(publicHeadersPath)
}
disabled = try env.readEnv(key: "XCRC_DISABLED") ?? false
}
}
@@ -274,7 +274,12 @@ public class XCPostbuild {
)
// Trigger build completion
if try modeController.isEnabled() {
if context.disabled {
infoLog("XCRC fully disabled for \(context.targetName), \(context.platform), \(context.configuration)")
// Cutoff the process is disabled, but generate an "empty" list of dependencies
try? modeController.disable()
return
} else if try modeController.isEnabled() {
// Decorate .swiftmodule in the product dir with fingerprint(s) overrides from a cache artifact
try postbuildAction.performBuildCompletion()
} else if context.mode == .consumer {
@@ -20,6 +20,7 @@
import Foundation
enum PrebuildResult: Equatable {
case disabled
case incompatible
case compatible(localDependencies: [URL])
}
@@ -57,6 +58,9 @@ class Prebuild {
// swiftlint:disable:next function_body_length
public func perform() throws -> PrebuildResult {
guard !context.disabled else {
return .disabled
}
guard case .available(let commit) = context.remoteCommit else {
return .incompatible
}
@@ -46,6 +46,8 @@ public struct PrebuildContext {
/// location of the json file that define virtual files system overlay
/// (mappings of the virtual location file -> local file path)
let overlayHeadersPath: URL
/// XCRemoteCache is explicitly disabled
let disabled: Bool
}
extension PrebuildContext {
@@ -69,5 +71,6 @@ extension PrebuildContext {
thinnedTargets = thinFocusedTargetsString?.split(separator: ",").map(String.init)
/// Note: The file has yaml extension, even it is in the json format
overlayHeadersPath = targetTempDir.appendingPathComponent("all-product-headers.yaml")
disabled = try env.readEnv(key: "XCRC_DISABLED") ?? false
}
}
@@ -202,6 +202,9 @@ public class XCPrebuild {
case .compatible(localDependencies: let dependencies):
// TODO: pass `allowedInputFiles` observed in the build time
try modeController.enable(allowedInputFiles: dependencies, dependencies: dependencies)
case .disabled:
infoLog("XCRemoteCache is explicitly disabled")
try modeController.disable()
}
} catch {
disableRemoteCache(
@@ -34,24 +34,33 @@ class XcodeProjBuildSettingsIntegrateAppender: BuildSettingsIntegrateAppender {
private let mode: Mode
private let repoRoot: URL
private let fakeSrcRoot: URL
private let sdksExclude: [String]
init(mode: Mode, repoRoot: URL, fakeSrcRoot: URL) {
init(mode: Mode, repoRoot: URL, fakeSrcRoot: URL, sdksExclude: [String]) {
self.mode = mode
self.repoRoot = repoRoot
self.fakeSrcRoot = fakeSrcRoot
self.sdksExclude = sdksExclude
}
func appendToBuildSettings(buildSettings: BuildSettings, wrappers: XCRCBinariesPaths) -> BuildSettings {
var result = buildSettings
result["SWIFT_EXEC"] = wrappers.swiftc.path
result["SWIFT_USE_INTEGRATED_DRIVER"] = "NO"
setBuildSetting(buildSettings: &result, key: "SWIFT_EXEC", value: wrappers.swiftc.path )
setBuildSetting(buildSettings: &result, key: "SWIFT_USE_INTEGRATED_DRIVER", value: "NO" )
// When generating artifacts, no need to shell-out all compilation commands to our wrappers
if case .consumer = mode {
result["CC"] = wrappers.cc.path
result["LD"] = wrappers.ld.path
result["LIBTOOL"] = wrappers.libtool.path
result["LIPO"] = wrappers.lipo.path
result["LDPLUSPLUS"] = wrappers.ldplusplus.path
setBuildSetting(buildSettings: &result, key: "CC", value: wrappers.cc.path )
setBuildSetting(buildSettings: &result, key: "LD", value: wrappers.ld.path )
// Setting LIBTOOL to '' breaks SwiftDriver intengration so resetting it to the original value
// 'libtool' for all excluded configurations
setBuildSetting(
buildSettings: &result,
key: "LIBTOOL",
value: wrappers.libtool.path,
excludedValue: "libtool"
)
setBuildSetting(buildSettings: &result, key: "LIPO", value: wrappers.lipo.path )
setBuildSetting(buildSettings: &result, key: "LDPLUSPLUS", value: wrappers.ldplusplus.path )
}
let existingSwiftFlags = result["OTHER_SWIFT_FLAGS"] as? String
@@ -63,14 +72,36 @@ class XcodeProjBuildSettingsIntegrateAppender: BuildSettingsIntegrateAppender {
swiftFlags.assignFlag(key: "debug-prefix-map", value: "\(repoRoot.path)=$(XCRC_FAKE_SRCROOT)")
clangFlags.assignFlag(key: "debug-prefix-map", value: "\(repoRoot.path)=$(XCRC_FAKE_SRCROOT)")
result["OTHER_SWIFT_FLAGS"] = swiftFlags.settingValue
result["OTHER_CFLAGS"] = clangFlags.settingValue
setBuildSetting(buildSettings: &result, key: "OTHER_SWIFT_FLAGS", value: swiftFlags.settingValue )
setBuildSetting(buildSettings: &result, key: "OTHER_CFLAGS", value: clangFlags.settingValue )
result["XCRC_FAKE_SRCROOT"] = "\(fakeSrcRoot.path)"
result["XCRC_PLATFORM_PREFERRED_ARCH"] =
setBuildSetting(buildSettings: &result, key: "XCRC_FAKE_SRCROOT", value: "\(fakeSrcRoot.path)" )
setBuildSetting(buildSettings: &result, key: "XCRC_PLATFORM_PREFERRED_ARCH", value:
"""
$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(PLATFORM_PREFERRED_ARCH):dir:standardizepath:file:default=arm64)
"""
)
explicitlyDisableSDKs(buildSettings: &result)
return result
}
private func setBuildSetting(buildSettings: inout BuildSettings, key: String, value: String?, excludedValue: String = "") {
buildSettings[key] = value
guard value != nil else {
// no need to exclude as the value will
return
}
// Erase all overrides for a given sdk so a default toolchain is used
for skippedSDK in sdksExclude {
buildSettings["\(key)[sdk=\(skippedSDK)]"] = excludedValue
}
}
// For all exlcuded SDKs passes XCRC_DISABLED=TRUE, which will cut-off early the pre_build phase
private func explicitlyDisableSDKs(buildSettings: inout BuildSettings) {
for skippedSDK in sdksExclude {
buildSettings["XCRC_DISABLED[sdk=\(skippedSDK)]"] = "YES"
}
}
}
@@ -34,6 +34,7 @@ public class XCIntegrate {
private let consumerEligiblePlatforms: String
private let lldbMode: LLDBInitMode
private let fakeSrcRoot: String
private let sdksExclude: String
private let output: String?
public init(
@@ -48,6 +49,7 @@ public class XCIntegrate {
consumerEligiblePlatforms: String,
lldbMode: LLDBInitMode,
fakeSrcRoot: String,
sdksExclude: String,
output: String?
) {
projectPath = input
@@ -61,6 +63,7 @@ public class XCIntegrate {
self.consumerEligiblePlatforms = consumerEligiblePlatforms
self.lldbMode = lldbMode
self.fakeSrcRoot = fakeSrcRoot
self.sdksExclude = sdksExclude
self.output = output
}
@@ -98,7 +101,8 @@ public class XCIntegrate {
let buildSettingsAppender = XcodeProjBuildSettingsIntegrateAppender(
mode: context.mode,
repoRoot: context.repoRoot,
fakeSrcRoot: context.fakeSrcRoot
fakeSrcRoot: context.fakeSrcRoot,
sdksExclude: sdksExclude.integrateArrayArguments
)
let lldbPatcher: LLDBInitPatcher
switch lldbMode {
@@ -77,7 +77,18 @@ class Prepare: PrepareLogic {
guard fileAccessor.fileExists(atPath: PhaseCacheModeController.xcodeSelectLink.path) else {
throw PrepareError.missingXcodeSelectDirectory
}
let commonSha = try gitClient.getCommonPrimarySha()
let commonSha: String
do {
commonSha = try gitClient.getCommonPrimarySha()
} catch let GitClientError.noCommonShaWithPrimaryRepo(remoteName, error) {
guard context.gracefullyHandleMissingCommonSha else {
throw GitClientError.noCommonShaWithPrimaryRepo(remoteName: remoteName, error: error)
}
infoLog("Cannot find a common sha with the primary branch: \(error). Gracefully disabling remote cache")
try disable()
return .failed
}
if context.offline {
// Optimistically take first common sha
@@ -52,6 +52,8 @@ public struct PrepareContext {
let cacheHealthPathProbeCount: Int
/// clang wrapper output file
let xcccCommand: URL
/// gracefully disable remote cache for missing common sha with the primary branch
let gracefullyHandleMissingCommonSha: Bool
}
extension PrepareContext {
@@ -77,5 +79,6 @@ extension PrepareContext {
cacheAddresses = try config.cacheAddresses.map(URL.build)
cacheHealthPath = config.cacheHealthPath
cacheHealthPathProbeCount = config.cacheHealthPathProbeCount
gracefullyHandleMissingCommonSha = config.gracefullyHandleMissingCommonSha
}
}
@@ -31,10 +31,12 @@ public struct PrepareMarkContext {
let recommendedCacheAddress: URL
/// All remote servers to mark
let cacheAddresses: [URL]
/// XCRemoteCache is explicitly disabled
let disabled: Bool
}
extension PrepareMarkContext {
init(_ config: XCRemoteCacheConfig) throws {
init(_ config: XCRemoteCacheConfig, env: [String: String]) throws {
let sourceRoot = URL(fileURLWithPath: config.sourceRoot, isDirectory: true)
repoRoot = URL(fileURLWithPath: config.repoRoot, relativeTo: sourceRoot)
guard let address = URL(string: config.recommendedCacheAddress) else {
@@ -43,5 +45,6 @@ extension PrepareMarkContext {
}
recommendedCacheAddress = address
cacheAddresses = try config.cacheAddresses.map(URL.build)
disabled = try env.readEnv(key: "XCRC_DISABLED") ?? false
}
}
@@ -47,12 +47,17 @@ public class XCPrepareMark {
let xcodeVersion: String
do {
config = try XCRemoteCacheConfigReader(env: env, fileReader: fileManager).readConfiguration()
context = try PrepareMarkContext(config)
context = try PrepareMarkContext(config, env: env)
xcodeVersion = try xcode ?? XcodeProbeImpl(shell: shellGetStdout).read().buildVersion
} catch {
exit(1, "FATAL: Prepare initialization failed with error: \(error)")
}
guard !context.disabled else {
infoLog("XCRemoteCache explicitly disabled for marking.")
return
}
do {
let sessionFactory = DefaultURLSessionFactory(config: config)
var awsV4Signature: AWSV4Signature?
@@ -148,6 +148,9 @@ public struct XCRemoteCacheConfig: Encodable {
/// Note: The regex can match either partially or fully the filepath, e.g. `\\.modulemap$` will exclude
/// all `.modulemap` files
var irrelevantDependenciesPaths: [String] = []
/// If true, do not fail `prepare` if cannot find the most recent common commits with the primary branch
/// That might useful on CI, where a shallow clone is used
var gracefullyHandleMissingCommonSha: Bool = false
}
extension XCRemoteCacheConfig {
@@ -206,6 +209,8 @@ extension XCRemoteCacheConfig {
merge.disableVFSOverlay = scheme.disableVFSOverlay ?? disableVFSOverlay
merge.customRewriteEnvs = scheme.customRewriteEnvs ?? customRewriteEnvs
merge.irrelevantDependenciesPaths = scheme.irrelevantDependenciesPaths ?? irrelevantDependenciesPaths
merge.gracefullyHandleMissingCommonSha =
scheme.gracefullyHandleMissingCommonSha ?? gracefullyHandleMissingCommonSha
return merge
}
@@ -273,6 +278,7 @@ struct ConfigFileScheme: Decodable {
let disableVFSOverlay: Bool?
let customRewriteEnvs: [String]?
let irrelevantDependenciesPaths: [String]?
let gracefullyHandleMissingCommonSha: Bool?
// Yams library doesn't support encoding strategy, see https://github.com/jpsim/Yams/issues/84
enum CodingKeys: String, CodingKey {
@@ -323,6 +329,7 @@ struct ConfigFileScheme: Decodable {
case disableVFSOverlay = "disable_vfs_overlay"
case customRewriteEnvs = "custom_rewrite_envs"
case irrelevantDependenciesPaths = "irrelevant_dependencies_paths"
case gracefullyHandleMissingCommonSha = "gracefully_handle_missing_common_sha"
}
}
@@ -34,7 +34,15 @@ struct CanonicalRequest {
if url.path.isEmpty {
path = "/"
} else {
path = url.path
if let escapedPath = url.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
path = escapedPath
} else {
path = "/"
printWarning("""
Escaping the path \(url.path) failed and a placeholder is used instead. \
Make sure the path doesn't contain invalid characters.
""")
}
}
return
"\(httpMethod)\n" +
@@ -55,4 +55,11 @@ extension Dictionary where Key == String, Value == String {
}
return value == "YES"
}
func readEnv(key: String) throws -> Bool? {
guard let value = self[key] else {
return nil
}
return value == "YES"
}
}
+6
View File
@@ -229,6 +229,11 @@ struct XCPrepareMain: ParsableCommand {
)
var fakeSrcRoot: String
@Option(name: .customLong("sdks-exclude"), default: "", help: """
comma separated list of sdks to not integrate XCRemoteCache (e.g. "watchos*, watchsimulator*")
""", transform: nonEmptyString)
var sdksExclude: String
func run() throws {
XCIntegrate(
@@ -243,6 +248,7 @@ struct XCPrepareMain: ParsableCommand {
consumerEligiblePlatforms: consumerEligiblePlatforms,
lldbMode: lldbInit,
fakeSrcRoot: fakeSrcRoot,
sdksExclude: sdksExclude,
output: output
).main()
}
@@ -159,4 +159,31 @@ class PostbuildContextTests: FileXCTestCase {
XCTAssertNil(context.publicHeadersFolderPath)
}
func testDisabledEnvIsFalseByDefault() throws {
var envs = Self.SampleEnvs
envs.removeValue(forKey: "XCRC_DISABLED")
let context = try PostbuildContext(config, env: envs)
XCTAssertFalse(context.disabled)
}
func testDisabledIsTrueForYesEnv() throws {
var envs = Self.SampleEnvs
envs["XCRC_DISABLED"] = "YES"
let context = try PostbuildContext(config, env: envs)
XCTAssertTrue(context.disabled)
}
func testDisabledIsFalseForNonYesEnv() throws {
var envs = Self.SampleEnvs
envs["XCRC_DISABLED"] = "NO"
let context = try PostbuildContext(config, env: envs)
XCTAssertFalse(context.disabled)
}
}
@@ -56,7 +56,8 @@ class PostbuildTests: FileXCTestCase {
modeMarkerPath: "",
overlayHeadersPath: "",
irrelevantDependenciesPaths: [],
publicHeadersFolderPath: nil
publicHeadersFolderPath: nil,
disabled: false
)
private var network = RemoteNetworkClientImpl(
NetworkClientFake(fileManager: .default),
@@ -63,7 +63,8 @@ class PrebuildTests: FileXCTestCase {
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: "",
overlayHeadersPath: ""
overlayHeadersPath: "",
disabled: false
)
contextCached = PrebuildContext(
targetTempDir: sampleURL,
@@ -76,7 +77,8 @@ class PrebuildTests: FileXCTestCase {
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: "",
overlayHeadersPath: ""
overlayHeadersPath: "",
disabled: false
)
organizer = ArtifactOrganizerFake(artifactRoot: artifactsRoot, unzippedExtension: "unzip")
globalCacheSwitcher = InMemoryGlobalCacheSwitcher()
@@ -241,7 +243,8 @@ class PrebuildTests: FileXCTestCase {
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: "",
overlayHeadersPath: ""
overlayHeadersPath: "",
disabled: false
)
let prebuild = Prebuild(
@@ -272,7 +275,8 @@ class PrebuildTests: FileXCTestCase {
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: "",
overlayHeadersPath: ""
overlayHeadersPath: "",
disabled: false
)
metaContent = try generateMeta(fingerprint: generator.generate(), filekey: "1")
let downloadedArtifactPackage = artifactsRoot.appendingPathComponent("1")
@@ -335,7 +339,8 @@ class PrebuildTests: FileXCTestCase {
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: false,
targetName: "",
overlayHeadersPath: ""
overlayHeadersPath: "",
disabled: false
)
try globalCacheSwitcher.enable(sha: "1")
let prebuild = Prebuild(
@@ -353,4 +358,34 @@ class PrebuildTests: FileXCTestCase {
XCTAssertEqual(globalCacheSwitcher.state, .enabled(sha: "1"))
}
func testReturnsDisabledIfXCRCExplicitlyDisabled() throws {
contextNonCached = PrebuildContext(
targetTempDir: sampleURL,
productsDir: sampleURL,
moduleName: nil,
remoteCommit: .unavailable,
remoteCommitLocation: sampleURL,
recommendedCacheAddress: sampleURL,
forceCached: false,
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: "",
overlayHeadersPath: "",
disabled: true
)
let prebuild = Prebuild(
context: contextNonCached,
networkClient: remoteNetwork,
remapper: remapper,
fingerprintAccumulator: generator,
artifactsOrganizer: organizer,
globalCacheSwitcher: globalCacheSwitcher,
metaReader: metaReader,
artifactConsumerPrebuildPlugins: []
)
XCTAssertEqual(try prebuild.perform(), .disabled)
}
}
@@ -45,7 +45,12 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase {
func testProducerSettingFakeSrcRoot() throws {
let mode: Mode = .producer
let fakeRootURL: URL = "/xxxxxxxxxxP"
let appender = XcodeProjBuildSettingsIntegrateAppender(mode: mode, repoRoot: rootURL, fakeSrcRoot: fakeRootURL)
let appender = XcodeProjBuildSettingsIntegrateAppender(
mode: mode,
repoRoot: rootURL,
fakeSrcRoot: fakeRootURL,
sdksExclude: []
)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let resultURL = try XCTUnwrap(result["XCRC_FAKE_SRCROOT"] as? String)
@@ -55,7 +60,12 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase {
func testConsumerSettingFakeSrcRoot() throws {
let mode: Mode = .consumer
let fakeRootURL: URL = "/xxxxxxxxxxC"
let appender = XcodeProjBuildSettingsIntegrateAppender(mode: mode, repoRoot: rootURL, fakeSrcRoot: fakeRootURL)
let appender = XcodeProjBuildSettingsIntegrateAppender(
mode: mode,
repoRoot: rootURL,
fakeSrcRoot: fakeRootURL,
sdksExclude: []
)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let resultURL: String = try XCTUnwrap(result["XCRC_FAKE_SRCROOT"] as? String)
@@ -65,10 +75,75 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase {
func testConsumerSettingLdPlusPlus() throws {
let mode: Mode = .consumer
let fakeRootURL: URL = "/xxxxxxxxxxC"
let appender = XcodeProjBuildSettingsIntegrateAppender(mode: mode, repoRoot: rootURL, fakeSrcRoot: fakeRootURL)
let appender = XcodeProjBuildSettingsIntegrateAppender(
mode: mode,
repoRoot: rootURL,
fakeSrcRoot: fakeRootURL,
sdksExclude: []
)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let ldPlusPlus: String = try XCTUnwrap(result["LDPLUSPLUS"] as? String)
XCTAssertEqual(ldPlusPlus, binaries.ldplusplus.path)
}
func testSinglesdksExcludeIsAppended() throws {
let mode: Mode = .consumer
let appender = XcodeProjBuildSettingsIntegrateAppender(
mode: mode,
repoRoot: rootURL,
fakeSrcRoot: "/",
sdksExclude: ["watchOS*"]
)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let ldPlusPlusWatchOS: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchOS*]"] as? String)
XCTAssertEqual(ldPlusPlusWatchOS, "")
}
func testLibtoolIsSetForExcludedSdks() throws {
let mode: Mode = .consumer
let appender = XcodeProjBuildSettingsIntegrateAppender(
mode: mode,
repoRoot: rootURL,
fakeSrcRoot: "/",
sdksExclude: ["watchOS*"]
)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let libtoolWatchOS: String = try XCTUnwrap(result["LIBTOOL[sdk=watchOS*]"] as? String)
XCTAssertEqual(libtoolWatchOS, "libtool")
}
func testMultiplesdksExcludeAreAppended() throws {
let mode: Mode = .consumer
let appender = XcodeProjBuildSettingsIntegrateAppender(
mode: mode,
repoRoot: rootURL,
fakeSrcRoot: "/",
sdksExclude: ["watchOS*", "watchsimulator*"]
)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let ldPlusPlusWatchOS: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchOS*]"] as? String)
let ldPlusPlusWatchSimulator: String = try XCTUnwrap(result["LDPLUSPLUS[sdk=watchsimulator*]"] as? String)
XCTAssertEqual(ldPlusPlusWatchOS, "")
XCTAssertEqual(ldPlusPlusWatchSimulator, "")
}
func testAddsDisabledFlagForExcludedSDKs() throws {
let mode: Mode = .consumer
let appender = XcodeProjBuildSettingsIntegrateAppender(
mode: mode,
repoRoot: rootURL,
fakeSrcRoot: "/",
sdksExclude: ["watchOS*", "watchsimulator*"]
)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let disabledWatchOS: String = try XCTUnwrap(result["XCRC_DISABLED[sdk=watchOS*]"] as? String)
let disabledWatchSimulator: String = try XCTUnwrap(result["XCRC_DISABLED[sdk=watchsimulator*]"] as? String)
XCTAssertEqual(disabledWatchOS, "YES")
XCTAssertEqual(disabledWatchSimulator, "YES")
}
}
@@ -34,7 +34,7 @@ class PrepareMarkContextTests: XCTestCase {
let repoPath = "/AbsolutePath"
config.repoRoot = repoPath
let context = try PrepareMarkContext(config)
let context = try PrepareMarkContext(config, env: [:])
XCTAssertEqual(context.repoRoot.path, repoPath)
}
@@ -43,8 +43,20 @@ class PrepareMarkContextTests: XCTestCase {
let repoPath = "."
config.repoRoot = repoPath
let context = try PrepareMarkContext(config)
let context = try PrepareMarkContext(config, env: [:])
XCTAssertEqual(context.repoRoot.path, "/Root")
}
func testDisabledIsFalseByDefault() throws {
let context = try PrepareMarkContext(config, env: [:])
XCTAssertFalse(context.disabled)
}
func testDisabledIsTrueForYes() throws {
let context = try PrepareMarkContext(config, env: ["XCRC_DISABLED": "YES"])
XCTAssertTrue(context.disabled)
}
}
@@ -89,4 +89,66 @@ class PrepareTests: XCTestCase {
XCTAssertEqual(result, .preparedFor(sha: .init(sha: "2", age: 0), recommendedCacheAddress: remoteURL))
}
func testFailsForMissingCommonShaWhenConfiguredToGrefullyDisable() throws {
git = GitClientFake(shaHistory: [], primaryBranchIndex: 0)
config.gracefullyHandleMissingCommonSha = true
let prepare = Prepare(
context: try PrepareContext(config, offline: false),
gitClient: git,
networkClients: [],
ccBuilder: CCWrapperBuilderFake(),
fileAccessor: FileManager.default,
globalCacheSwitcher: globalCacheSwitcher,
cacheInvalidator: CacheInvalidatorFake()
)
let result = try prepare.prepare()
XCTAssertEqual(result, .failed)
}
func testDisablesGlobalCacheForMissingCommonShaWhenConfiguredToGrefullyDisable() throws {
git = GitClientFake(shaHistory: [], primaryBranchIndex: 0)
config.gracefullyHandleMissingCommonSha = true
try globalCacheSwitcher.enable(sha: "starting_state")
let prepare = Prepare(
context: try PrepareContext(config, offline: false),
gitClient: git,
networkClients: [],
ccBuilder: CCWrapperBuilderFake(),
fileAccessor: FileManager.default,
globalCacheSwitcher: globalCacheSwitcher,
cacheInvalidator: CacheInvalidatorFake()
)
_ = try prepare.prepare()
XCTAssertEqual(globalCacheSwitcher.state, .disabled)
}
func testThrowsForMissingCommonSha() throws {
git = GitClientFake(shaHistory: [], primaryBranchIndex: 0)
config.gracefullyHandleMissingCommonSha = false
let prepare = Prepare(
context: try PrepareContext(config, offline: false),
gitClient: git,
networkClients: [],
ccBuilder: CCWrapperBuilderFake(),
fileAccessor: FileManager.default,
globalCacheSwitcher: globalCacheSwitcher,
cacheInvalidator: CacheInvalidatorFake()
)
XCTAssertThrowsError(try prepare.prepare()) { error in
switch error {
case GitClientError.noCommonShaWithPrimaryRepo: break
default:
XCTFail("Not expected error")
}
}
}
}
@@ -37,6 +37,27 @@ class CanonicalRequestTest: XCTestCase {
}
func testCanonicalRequest() {
request.url = URL(
string: "https://region.amazonaws.com/bucket/with%20space?param=value&hej=hej&abd=cde&test=-_.~"
)!
let canonicalRequest = CanonicalRequest(
request: request
)
XCTAssertEqual(
canonicalRequest.value,
"GET\n" +
"/bucket/with%20space\n" +
"abd=cde&hej=hej&param=value&test=-_.~\n" +
"a-header2key:A-Header2Value\n" +
"b-header3key:B-Header3Value\n" +
"c-header4key:C Header 4 Value\n" +
"x-header1key:X-Header1Value\n\n" +
"a-header2key;b-header3key;c-header4key;x-header1key\n" +
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
)
}
func testCanonicalRequestWithEmptySpaceInPath() {
let canonicalRequest = CanonicalRequest(
request: request
)
@@ -34,7 +34,10 @@ class GitClientFake: GitClient {
}
func getCommonPrimarySha() throws -> String {
shaHistory[primaryBranchIndex].sha
guard shaHistory.count > primaryBranchIndex else {
throw GitClientError.noCommonShaWithPrimaryRepo(remoteName: "testing", error: "SampleError")
}
return shaHistory[primaryBranchIndex].sha
}
func getShaDate(sha: String) throws -> Date {
+3 -2
View File
@@ -28,7 +28,7 @@ plugin 'cocoapods-xcremotecache'
2. Configure XCRemoteCache at the top of your `Podfile` definition:
```ruby
xcremotecache({
'cache_addresses' => ['http://localhost:8080/cache/pods'],
'cache_addresses' => ['http://localhost:8080/cache/pods'],
'primary_repo' => 'https://your.primary.repo.git',
'mode' => 'consumer'
})
@@ -48,13 +48,14 @@ An object that is passed to the `xcremotecache` can contain all properties suppo
| `exclude_build_configurations` | Comma-separated list of configurations that shouldn't use XCRemoteCache | `[]`| ⬜️ |
| `final_target` | A target name that is build at the end of the build chain. Relevant only for a 'producer' mode to mark a given sha as ready to use from cache | `Debug` | ⬜️ |
| `check_build_configuration` | A build configuration for which the remote cache availability is performed. Relevant only for a 'consumer' mode | `Debug` | ⬜️ |
| `check_platform` | A platform for which the remote cache availability is performed. Relevant only for a 'consumer' mode | `iphonesimulator` | ⬜️
| `check_platform` | A platform for which the remote cache availability is performed. Relevant only for a 'consumer' mode | `iphonesimulator` | ⬜️
| `modify_lldb_init` | Controls if the pod integration should modify `~/.lldbinit` | `true` | ⬜️ |
| `xccc_file` | The path where should be placed the `xccc` binary (in the pod installation phase) | `{podfile_dir}/.rc/xccc` | ⬜️ |
| `remote_commit_file` | The path of the file with the remote commit sha (in the pod installation phase) | `{podfile_dir}/.rc/arc.rc`| ⬜️ |
| `prettify_meta_files` | A Boolean value that opts-in pretty JSON formatting for meta files | `false` | ⬜️ |
| `fake_src_root` | An arbitrary source location shared between producers and consumers. Should be unique for a project. | `/xxxxxxxxxx` | ⬜️ |
| `disable_certificate_verification` | A Boolean value that opts-in SSL certificate validation is disabled | `false` | ⬜️ |
| `exclude_sdks_configurations` | array of sdks to not integrate XCRemoteCache (e.g. "watchos*, watchsimulator*") (Experimental) | `[]`| ⬜️ |
## Uninstalling
@@ -40,6 +40,7 @@ module CocoapodsXCRemoteCacheModifier
'check_platform',
'modify_lldb_init',
'fake_src_root',
'exclude_sdks_configurations'
]
class XCRemoteCache
@@ -64,7 +65,8 @@ module CocoapodsXCRemoteCacheModifier
'prettify_meta_files' => false,
'fake_src_root' => "/#{'x' * 10 }",
'disable_certificate_verification' => false,
'custom_rewrite_envs' => []
'custom_rewrite_envs' => [],
'exclude_sdks_configurations' => []
}
@@configuration.merge! default_values.select { |k, v| !@@configuration.key?(k) }
# Always include XCRC_COOCAPODS_ROOT_KEY in custom_rewrite_envs
@@ -111,7 +113,18 @@ module CocoapodsXCRemoteCacheModifier
# @param mode [String] mode name ('consumer', 'producer', 'producer-fast' etc.)
# @param exclude_build_configurations [String[]] list of targets that should have disabled remote cache
# @param final_target [String] name of target that should trigger marking
def self.enable_xcremotecache(target, repo_distance, xc_location, xc_cc_path, mode, exclude_build_configurations, final_target, fake_src_root)
# @param exclude_sdks_configurations [String[]] list of sdks that should have disabled remote cache
def self.enable_xcremotecache(
target,
repo_distance,
xc_location,
xc_cc_path,
mode,
exclude_build_configurations,
final_target,
fake_src_root,
exclude_sdks_configurations
)
srcroot_relative_xc_location = parent_dir(xc_location, repo_distance)
# location of the entrite CocoaPods project, relative to SRCROOT
srcroot_relative_project_location = parent_dir('', repo_distance)
@@ -120,23 +133,27 @@ module CocoapodsXCRemoteCacheModifier
# apply only for relevant Configurations
next if exclude_build_configurations.include?(config.name)
if mode == 'consumer'
config.build_settings['CC'] = ["$SRCROOT/#{parent_dir(xc_cc_path, repo_distance)}"]
reset_build_setting(config.build_settings, 'CC', "$SRCROOT/#{parent_dir(xc_cc_path, repo_distance)}", exclude_sdks_configurations)
elsif mode == 'producer' || mode == 'producer-fast'
config.build_settings.delete('CC') if config.build_settings.key?('CC')
end
config.build_settings['SWIFT_EXEC'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xcswiftc"]
config.build_settings['LIBTOOL'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xclibtool"]
config.build_settings['LD'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xcld"]
config.build_settings['LDPLUSPLUS'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xcldplusplus"]
config.build_settings['LIPO'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xclipo"]
config.build_settings['SWIFT_USE_INTEGRATED_DRIVER'] = ['NO']
reset_build_setting(config.build_settings, 'SWIFT_EXEC', "$SRCROOT/#{srcroot_relative_xc_location}/xcswiftc", exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'LIBTOOL', "$SRCROOT/#{srcroot_relative_xc_location}/xclibtool", exclude_sdks_configurations)
# Setting LIBTOOL to '' breaks SwiftDriver intengration so resetting it to the original value 'libtool' for all excluded configurations
add_build_setting_for_sdks(config.build_settings, 'LIBTOOL', 'libtool', exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'LD', "$SRCROOT/#{srcroot_relative_xc_location}/xcld", exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'LDPLUSPLUS', "$SRCROOT/#{srcroot_relative_xc_location}/xcldplusplus", exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'LIPO', "$SRCROOT/#{srcroot_relative_xc_location}/xclipo", exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'SWIFT_USE_INTEGRATED_DRIVER', 'NO', exclude_sdks_configurations)
config.build_settings['XCREMOTE_CACHE_FAKE_SRCROOT'] = fake_src_root
config.build_settings['XCRC_PLATFORM_PREFERRED_ARCH'] = ["$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(PLATFORM_PREFERRED_ARCH):dir:standardizepath:file:default=arm64)"]
config.build_settings[XCRC_COOCAPODS_ROOT_KEY] = ["$SRCROOT/#{srcroot_relative_project_location}"]
reset_build_setting(config.build_settings, 'XCREMOTE_CACHE_FAKE_SRCROOT', fake_src_root, exclude_sdks_configurations)
reset_build_setting(config.build_settings, 'XCRC_PLATFORM_PREFERRED_ARCH', "$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(PLATFORM_PREFERRED_ARCH):dir:standardizepath:file:default=arm64)", exclude_sdks_configurations)
reset_build_setting(config.build_settings, XCRC_COOCAPODS_ROOT_KEY, "$SRCROOT/#{srcroot_relative_project_location}", exclude_sdks_configurations)
debug_prefix_map_replacement = '$(SRCROOT' + ':dir:standardizepath' * repo_distance + ')'
add_cflags!(config.build_settings, '-fdebug-prefix-map', "#{debug_prefix_map_replacement}=$(XCREMOTE_CACHE_FAKE_SRCROOT)")
add_swiftflags!(config.build_settings, '-debug-prefix-map', "#{debug_prefix_map_replacement}=$(XCREMOTE_CACHE_FAKE_SRCROOT)")
add_cflags!(config.build_settings, '-fdebug-prefix-map', "#{debug_prefix_map_replacement}=$(XCREMOTE_CACHE_FAKE_SRCROOT)", exclude_sdks_configurations)
add_swiftflags!(config.build_settings, '-debug-prefix-map', "#{debug_prefix_map_replacement}=$(XCREMOTE_CACHE_FAKE_SRCROOT)", exclude_sdks_configurations)
delete_build_setting(config.build_settings, 'XCRC_DISABLED')
add_build_setting_for_sdks(config.build_settings, 'XCRC_DISABLED', 'YES', exclude_sdks_configurations)
end
# Prebuild
@@ -283,9 +300,8 @@ module CocoapodsXCRemoteCacheModifier
end
end
def self.add_cflags!(options, key, value)
return if options.fetch('OTHER_CFLAGS',[]).include?(value)
options['OTHER_CFLAGS'] = remove_cflags!(options, key) << "#{key}=#{value}"
def self.add_cflags!(options, key, value, exclude_sdks_configurations)
reset_build_setting(options, 'OTHER_CFLAGS', remove_cflags!(options, key) << "#{key}=#{value}", exclude_sdks_configurations)
end
def self.remove_cflags!(options, key)
@@ -295,9 +311,8 @@ module CocoapodsXCRemoteCacheModifier
options['OTHER_CFLAGS']
end
def self.add_swiftflags!(options, key, value)
return if options.fetch('OTHER_SWIFT_FLAGS','').include?(value)
options['OTHER_SWIFT_FLAGS'] = remove_swiftflags!(options, key) + " #{key} #{value}"
def self.add_swiftflags!(options, key, value, exclude_sdks_configurations)
reset_build_setting(options, 'OTHER_SWIFT_FLAGS', remove_swiftflags!(options, key) + " #{key} #{value}", exclude_sdks_configurations)
end
def self.remove_swiftflags!(options, key)
@@ -305,6 +320,34 @@ module CocoapodsXCRemoteCacheModifier
options['OTHER_SWIFT_FLAGS']
end
def self.add_build_setting(build_settings, key, value, exclude_sdks_configurations)
build_settings[key] = value
for exclude_sdks_configuration in exclude_sdks_configurations
build_settings["#{key}[sdk=#{exclude_sdks_configuration}]"] = [""]
end
end
# Deletes all previous build settings for a key, and sets a new value to all configurations
# but the sdks in exclude_sdks_configurations
def self.reset_build_setting(build_settings, key, value, exclude_sdks_configurations)
delete_build_setting(build_settings, key)
add_build_setting(build_settings, key, value, exclude_sdks_configurations)
end
# Delete all build setting for a key, including settings like "[skd=*,arch=*]"
def self.delete_build_setting(build_settings, key)
for build_setting_key in build_settings.keys
build_settings.delete(build_setting_key) if build_setting_key == key || build_setting_key.start_with?("#{key}[")
end
end
# Sets value for a key only for a subset of sdk configurations
def self.add_build_setting_for_sdks(build_settings, key, value, sdk_configurations)
for sdk_configuration in sdk_configurations
build_settings["#{key}[sdk=#{sdk_configuration}]"] = value
end
end
# Uninstall the XCRemoteCache
def self.disable_xcremotecache(user_project, pods_project = nil)
user_project.targets.each do |target|
@@ -454,6 +497,7 @@ module CocoapodsXCRemoteCacheModifier
check_build_configuration = @@configuration['check_build_configuration']
check_platform = @@configuration['check_platform']
fake_src_root = @@configuration['fake_src_root']
exclude_sdks_configurations = @@configuration['exclude_sdks_configurations'] || []
xccc_location_absolute = "#{user_proj_directory}/#{xccc_location}"
xcrc_location_absolute = "#{user_proj_directory}/#{xcrc_location}"
@@ -477,7 +521,7 @@ module CocoapodsXCRemoteCacheModifier
next if target.name.start_with?("Pods-")
next if target.name.end_with?("Tests")
next if exclude_targets.include?(target.name)
enable_xcremotecache(target, 1, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root)
enable_xcremotecache(target, 1, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations)
end
# Create .rcinfo into `Pods` directory as that .xcodeproj reads configuration from .xcodeproj location
@@ -490,7 +534,7 @@ module CocoapodsXCRemoteCacheModifier
next if target.source_build_phase.files_references.empty?
next if target.name.end_with?("Tests")
next if exclude_targets.include?(target.name)
enable_xcremotecache(target, 1, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root)
enable_xcremotecache(target, 1, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations)
end
generated_project.save()
end
@@ -531,7 +575,7 @@ module CocoapodsXCRemoteCacheModifier
# Attach XCRC to the app targets
user_project.targets.each do |target|
next if exclude_targets.include?(target.name)
enable_xcremotecache(target, 0, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root)
enable_xcremotecache(target, 0, xcrc_location, xccc_location, mode, exclude_build_configurations, final_target,fake_src_root, exclude_sdks_configurations)
end
# Set Target sourcemap
@@ -13,5 +13,5 @@
# limitations under the License.
module CocoapodsXcremotecache
VERSION = "0.0.15"
VERSION = "0.0.16"
end
+9
View File
@@ -0,0 +1,9 @@
### How to add a CocoaPods scenario
In order to run different test suites in cocoapods integration, you can create multiple `.Podfile` files as a "starting point" of the E2E test.
#### Files organization
1. File `{SUITE_NAME}.Podfile` contains a base Podfile that should be excercised
1. [Optional] File `{SUITE_NAME}.Podfile.config` contains a hash of extra xcremote-cache-plugin parameters that should be added to the `xcremotecache()` configuration in a podfile
1. [Optional] File `{SUITE_NAME}.Podfile.expectations` overrides a set of default validation steps that should be checked after consumer's build. Supported steps are: `hits`, `misses` or `hit_rate` (e.g. `100` for 100$). By default, 100% `hit_rate` and 0 `misses` are used - if you want to skip a validation step for a given suite, you can set it to `null` in a dictionary
+8
View File
@@ -0,0 +1,8 @@
plugin 'cocoapods-xcremotecache'
target 'XCRemoteCacheSample' do
use_frameworks!
pod 'Firebase/Analytics'
pod 'ReactiveSwift'
end
@@ -0,0 +1,3 @@
{
"exclude_sdks_configurations": ["iphoneos*", "iphonesimulator*"]
}
@@ -0,0 +1,5 @@
{
"hits": 0,
"misses": 0,
"hit_rate": null
}
+8
View File
@@ -0,0 +1,8 @@
plugin 'cocoapods-xcremotecache'
target 'XCRemoteCacheSample' do
use_frameworks!
pod 'Firebase/Analytics'
pod 'ReactiveSwift'
end
@@ -0,0 +1,3 @@
{
"exclude_sdks_configurations": ["watchos*", "watchsimulator*"]
}
+40 -9
View File
@@ -1,4 +1,5 @@
require 'json'
require "ostruct"
desc 'Support for E2E tests: building XCRemoteCache-enabled xcodeproj using xcodebuild'
namespace :e2e do
@@ -21,7 +22,11 @@ namespace :e2e do
'mode' => 'consumer',
'final_target' => 'XCRemoteCacheSample',
'artifact_maximum_age' => 0
}
}.freeze
DEFAULT_EXPECTATIONS = {
'misses' => 0,
'hit_rate' => 100
}.freeze
Stats = Struct.new(:hits, :misses, :hit_rate)
@@ -76,7 +81,7 @@ namespace :e2e do
system("#{XCRC_BINARIES}/xcprepare integrate --input StandaloneApp.xcodeproj --mode consumer")
build_project(nil, "StandaloneApp.xcodeproj", 'WatchExtension', 'watch', 'watchOS', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"})
build_project(nil, "StandaloneApp.xcodeproj", 'StandaloneApp', 'iphone', 'iOS', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"})
valide_hit_rate
valide_hit_rate(OpenStruct.new(DEFAULT_EXPECTATIONS))
puts 'Building standalone consumer with local change...'
# Extra: validate local compilation of the Standalone ObjC code
@@ -198,22 +203,33 @@ namespace :e2e do
misses = stats_json_string.fetch('miss_count', 0)
hits = stats_json_string.fetch('hit_count', 0)
all_targets = misses + hits
raise "Failure: No XCRemoteCache targets invoked" if all_targets == 0
hit_rate = hits * 100 / all_targets
hit_rate = all_targets == 0 ? nil : hits * 100 / all_targets
Stats.new(hits, misses, hit_rate)
end
# validate 100% hit rate
def self.valide_hit_rate
def self.valide_hit_rate(expectations)
status = read_stats()
all_targets = status.misses + status.hits
raise "Failure: Hit rate is only #{status.hit_rate}% (#{all_targets})" if status.misses > 0
unless expectations.misses.nil?
raise "Failure: Unexpected misses: #{status.misses} (#{all_targets}). Expected #{expectations.misses}" if status.misses != expectations.misses
end
unless expectations.hit_rate.nil?
raise "Failure: Hit rate is #{status.hit_rate}% (#{all_targets}). Expected #{expectations.hit_rate}%" if status.hit_rate != expectations.hit_rate
end
unless expectations.hits.nil?
raise "Failure: Hits count is #{status.hit_rate}% (#{all_targets}). Expected #{expectations.hits}" if status.hits != expectations.hits
end
puts("Hit rate: #{status.hit_rate}% (#{status.hits}/#{all_targets})")
end
def self.run_cocoapods_scenario(template_path)
producer_configuration = cocoapods_configuration_string({'mode' => 'producer'})
consumer_configuration = cocoapods_configuration_string()
# Optional file, which adds extra cocoapods configs to a template
template_config_path = "#{template_path}.config"
extra_config = File.exist?(template_config_path) ? JSON.load(File.read(template_config_path)) : {}
producer_configuration = cocoapods_configuration_string({'mode' => 'producer'}.merge(extra_config))
consumer_configuration = cocoapods_configuration_string(extra_config)
expectations = build_expectations(template_path)
puts("****** Scenario: #{template_path}")
@@ -233,7 +249,7 @@ namespace :e2e do
puts('Building consumer ...')
Dir.chdir(E2E_COCOAPODS_SAMPLE_DIR) do
build_project_cocoapods('iphone', 'iOS', {'derivedDataPath' => "#{DERIVED_DATA_PATH}_consumer"})
valide_hit_rate
valide_hit_rate(expectations)
end
end
@@ -241,4 +257,19 @@ namespace :e2e do
clean_git
system("ln -s $(pwd)/releases #{dir}/#{XCRC_BINARIES}")
end
# Returns a hash of all expectations that should be validated for a template
# The implementation assumes 100% hitrate and extra expecations can be provided in an optional file
# #{template_path}.expectations
def self.build_expectations(template_path)
expectations = DEFAULT_EXPECTATIONS.dup
return expectations if template_path.nil?
template_config_path = "#{template_path}.expectations"
if File.exist?(template_config_path)
extra_config = File.exist?(template_config_path) ? JSON.load(File.read(template_config_path)) : {}
expectations.merge!(extra_config)
end
OpenStruct.new(expectations)
end
end