Compare commits

...

44 Commits

Author SHA1 Message Date
Bartosz Polaczyk 62b637bdaa Merge pull request #113 from polac24/cocoapods-bump-008
Bump cocoapods plugin version 0.0.8
2022-04-04 09:27:19 +02:00
Bartosz Polaczyk 0bca5e5bc4 Bump cocoapods plugin version 0.0.8 2022-04-04 08:14:06 +02:00
Bartosz Polaczyk eb0d4b17b7 Merge pull request #95 from polac24/20220226-pch-suport
Switch to local PCH headers compilation
2022-04-01 08:41:39 +02:00
Bartosz Polaczyk d9ea5bfd49 Apply suggestions from code review
Co-authored-by: Aleksander Grzyb <aleksander.grzyb@gmail.com>
2022-04-01 08:24:07 +02:00
Bartosz Polaczyk ee8bf69814 Add comments 2022-03-31 22:09:55 +02:00
Bartosz Polaczyk 2c0f78056f Merge branch 'master' into 20220226-pch-suport 2022-03-31 22:03:10 +02:00
Bartosz Polaczyk b6b17bdc8a Merge pull request #111 from devMEremenko/integrate-aws-security-token
Integrate AWS Temporary Access Keys
2022-03-31 20:15:30 +02:00
Maxim Eremenko a387359930 Removed trailing_whitespace 2022-03-31 20:44:13 +03:00
Maxim Eremenko 456233b90e Update tests 2022-03-30 23:46:15 +03:00
Maxim Eremenko d6f6e2e9bc Integrate AWS Temporary Security Token 2022-03-30 23:21:22 +03:00
Bartosz Polaczyk a35bd0b394 Merge pull request #110 from samuelsainz/bugfix/mark-script_build-config-param
Adding quotes to the config param in the Mark script
2022-03-25 23:20:02 +01:00
Samuel Sainz def131f2a0 Fix config param in Mark script considering build configs with a space in the name 2022-03-25 17:40:17 -03:00
Bartosz Polaczyk c052ed8ed6 Merge pull request #105 from cezarsignori/csignori/xcrc_fake_src_root
Set provided fakeSrcRoot on Xcode proj build settings as XCRC_FAKE_SRCROOT
2022-03-22 17:00:59 +01:00
Cezar Signori d4f9486b92 Set provided fakeSrcRoot on Xcode proj build settings as XCRC_FAKE_SRCROOT 2022-03-22 16:26:16 +01:00
Bartosz Polaczyk 8241914543 Merge pull request #106 from alecgarza96/master
Fixes issue #49
2022-03-21 20:08:19 +01:00
alecgarza96 b5ff16484f removed trailing whitespace 2022-03-21 11:31:09 -05:00
alecgarza96 2b9dde9aec added support for both ".S" and ".s" extensions 2022-03-21 11:16:40 -05:00
alecgarza96 136e7a99ff added .s file extension for assembly support 2022-03-10 14:40:58 -06:00
Bartosz Polaczyk c626d51f97 Merge pull request #99 from polac24/20220304-document-debugging
[Docs] Local development steps
2022-03-07 17:29:49 +01:00
Bartosz Polaczyk e8ddc9297d [Docs] Local development steps 2022-03-04 18:47:29 +01:00
Bartosz Polaczyk 3b614c6172 Merge pull request #97 from polac24/20220228-fix-swiftlint
Fix swiftlint errors
2022-03-02 08:47:44 +01:00
Bartosz Polaczyk 87a214104e Add headers linting 2022-03-01 22:58:00 +01:00
Bartosz Polaczyk 599e1e229d Fix linter errors 2022-03-01 22:44:21 +01:00
Bartosz Polaczyk 423da7cc4a Enable trailing_dot_in_comments 2022-03-01 22:44:03 +01:00
Bartosz Polaczyk 4b082e9dd2 Run CI linting in strict mode 2022-03-01 22:22:45 +01:00
Vadim Smal 36803d6b5d Merge pull request #96 from CognitiveDisson/update-dependencies
Update dependencies
2022-03-01 08:54:36 +00:00
Bartosz Polaczyk 5809bc963c Fix serious errors 2022-02-28 23:14:52 +01:00
Bartosz Polaczyk cb6626cfbc Add exclude to E2E Pods 2022-02-28 22:57:45 +01:00
Bartosz Polaczyk 3e18711e09 Apply autocorrect 2022-02-28 22:56:34 +01:00
Bartosz Polaczyk ccda424791 Formatting 2022-02-28 22:55:04 +01:00
Vadim Smal 90d784cc8d Update dependencies 2022-02-28 16:05:59 +00:00
Bartosz Polaczyk 6715824195 Update Sources/XCRemoteCache/Commands/Prepare/CCWrapperBuilder.swift 2022-02-28 08:29:35 +01:00
Bartosz Polaczyk c4e08d4288 Remove limitation 2022-02-26 19:11:54 +01:00
Bartosz Polaczyk a2067f8dfe Add unit test 2022-02-26 18:37:56 +01:00
Bartosz Polaczyk 253e9597bd Force compilation success in xccc generation tests 2022-02-26 18:37:56 +01:00
Bartosz Polaczyk 4a93f944fd Fallback when compiling pch header 2022-02-26 16:03:54 +01:00
Bartosz Polaczyk f839b4064b Merge pull request #93 from polac24/20220223-add-troubleshootings
[Docs] Add troubleshooting tips
2022-02-24 20:48:29 +01:00
Bartosz Polaczyk e71837b8b2 Merge pull request #94 from polac24/20220223-missing-overlay-log
Print overlay error messages to os_log only
2022-02-24 20:48:12 +01:00
Bartosz Polaczyk b45792646b Apply suggestions from code review
Co-authored-by: Aleksander Grzyb <aleksander.grzyb@gmail.com>
2022-02-24 08:46:57 +01:00
Bartosz Polaczyk 1966562eef Print overlay error messages to logs only 2022-02-23 21:58:03 +01:00
Bartosz Polaczyk 883b207c5b Fix formatting 2022-02-23 21:43:40 +01:00
Bartosz Polaczyk ca137d0ce4 Add troubleshooting steps 2022-02-23 21:24:07 +01:00
Bartosz Polaczyk b9a633c86f Merge pull request #91 from polac24/20220222-disable-urlcache
Disable local URLSession cache
2022-02-22 17:33:59 +01:00
Bartosz Polaczyk 55a87eb4e9 Disable local URLSession cache 2022-02-22 14:37:06 +01:00
58 changed files with 351 additions and 93 deletions
+2
View File
@@ -9,6 +9,8 @@ jobs:
- uses: actions/checkout@v1
- name: SwiftLint
uses: norio-nomura/action-swiftlint@3.1.0
with:
args: --strict
macOS:
runs-on: macOS-latest
+21 -2
View File
@@ -5,7 +5,6 @@ disabled_rules:
- superfluous_disable_command # Disabled since we disable some rules pre-emptively to avoid issues in the future
- todo # Temporarily disabled. We have too many right now hiding real issues :(
- nesting # Does not make sense anymore since Swift 4 uses nested `CodingKeys` enums for example
- trailing_dot_in_comments # Triggers warnings for generated file headers
opt_in_rules:
- anyobject_protocol
@@ -64,6 +63,7 @@ excluded:
- docs/
- fastlane/
- DerivedData/
- e2eTests/XCRemoteCacheSample/Pods
attributes:
always_on_same_line:
@@ -88,6 +88,25 @@ file_header:
\/\/ Created by .*? on .*\.
\/\/ Copyright © \d{4} .*\. All rights reserved\.
\/\/
required_pattern: |
\/\/ Copyright \(c\) \d{4} 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\.
force_cast: warning
force_try: warning
implicit_getter: warning
@@ -124,6 +143,6 @@ custom_rules:
severity: warning
trailing_dot_in_comments:
name: "Trailing dot in comments"
regex: '^[ ]*///?[^\n]*\.\n'
regex: '^(?!\/\/\ Copyright\ \(c\)\ \d{4}\ Spotify AB\.|\/\/\ under\ the\ License\.)[ ]*///?[^\n]*\.\n'
message: "There shouldn't be trailing dot in comments"
severity: warning
+10 -10
View File
@@ -3,16 +3,16 @@
"pins": [
{
"package": "AEXML",
"repositoryURL": "https://github.com/tadija/AEXML",
"repositoryURL": "https://github.com/tadija/AEXML.git",
"state": {
"branch": null,
"revision": "8623e73b193386909566a9ca20203e33a09af142",
"version": "4.5.0"
"revision": "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3",
"version": "4.6.1"
}
},
{
"package": "PathKit",
"repositoryURL": "https://github.com/kylef/PathKit",
"repositoryURL": "https://github.com/kylef/PathKit.git",
"state": {
"branch": null,
"revision": "73f8e9dca9b7a3078cb79128217dc8f2e585a511",
@@ -42,8 +42,8 @@
"repositoryURL": "https://github.com/tuist/XcodeProj.git",
"state": {
"branch": null,
"revision": "0b18c3e7a10c241323397a80cb445051f4494971",
"version": "8.0.0"
"revision": "c75c3acc25460195cfd203a04dde165395bf00e0",
"version": "8.7.1"
}
},
{
@@ -51,8 +51,8 @@
"repositoryURL": "https://github.com/jpsim/Yams.git",
"state": {
"branch": null,
"revision": "53741ba55ecca5c7149d8c9f810913ec80845c69",
"version": "3.0.0"
"revision": "00c403debcd0a007b854bb35e598466207a2d58c",
"version": "5.0.0"
}
},
{
@@ -60,8 +60,8 @@
"repositoryURL": "https://github.com/marmelroy/Zip.git",
"state": {
"branch": null,
"revision": "80b1c3005ee25b4c7ce46c4029ac3347e8d5e37e",
"version": "2.0.0"
"revision": "67fa55813b9e7b3b9acee9c0ae501def28746d76",
"version": "2.1.2"
}
}
]
+4 -3
View File
@@ -1,4 +1,5 @@
// swift-tools-version:5.3
// swiftlint:disable:previous file_header
// The swift-tools-version declares the minimum version of Swift required to build this package
import PackageDescription
@@ -12,10 +13,10 @@ let package = Package(
.executable(name: "xcprebuild", targets: ["xcprebuild"]),
],
dependencies: [
.package(url: "https://github.com/marmelroy/Zip.git", from: "2.0.0"),
.package(url: "https://github.com/jpsim/Yams.git", from: "3.0.0"),
.package(url: "https://github.com/marmelroy/Zip.git", from: "2.1.2"),
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1"),
.package(url: "https://github.com/tuist/XcodeProj.git", from: "8.0.0"),
.package(url: "https://github.com/tuist/XcodeProj.git", from: "8.7.1"),
],
targets: [
.target(
+4
View File
@@ -308,6 +308,7 @@ _Note that for the `producer` mode, the prebuild build phase and `xccc`, `xcld`,
| `prettify_meta_files` | A Boolean value that opts-in pretty JSON formatting for meta files | `false` | ⬜️ |
| `aws_secret_key` | Secret key for AWS V4 Signature Authorization. If this is set to a non-empty String - an Authentication Header will be added based on this and the other `aws_*` parameters.| `""` | ⬜️ |
| `aws_access_key` | Access key for AWS V4 Signature Authorization. | `""` | ⬜️ |
| `aws_security_token` | Temporary security token provided by the AWS Security Token Service. | `nil` | ⬜️ |
| `aws_region` | Region for AWS V4 Signature Authorization. E.g. `eu`. | `""` | ⬜️ |
| `aws_service` | Service for AWS V4 Signature Authorization. E.g. `storage`. | `""` | ⬜️ |
| `out_of_band_mappings` | A dictionary of files path remapping that should be applied to make it absolute path agnostic on a list of dependencies. Useful if a project refers files out of repo root, either compilation files or precompiled dependencies. Keys represent generic replacement and values are substrings that should be replaced. Example: for mapping `["COOL_LIBRARY": "/CoolLibrary"]` `/CoolLibrary/main.swift`will be represented as `$(COOL_LIBRARY)/main.swift`). Warning: remapping order is not-deterministic so avoid remappings with multiple matchings. | `[:]` | ⬜️ |
@@ -351,6 +352,8 @@ XCRemoteCache supports Amazon S3 and Google Cloud Storage buckets to be used as
To set it up use the configuration parameters `aws_secret_key`, `aws_access_key`, `aws_region`, and `aws_service` in the `.rcinfo` file. Specify the URL to the bucket in cache-addresses field in the same file.
XCRemoteCache also supports [AWS Temporary Access Keys](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#temporary-access-keys). Use additional `aws_security_token` parameter combined with `aws_secret_key`, `aws_access_key` to set it up. [This page](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html) describes how to receive a security token.
Example
```yaml
...
@@ -404,6 +407,7 @@ Note: This setup is not recommended and may not be supported in future XCRemoteC
* Recommended: multi-targets Xcode project
* Recommended: do not use fast-forward PR strategy (use merge or squash instead)
* Recommended: avoid `DWARF with dSYM File` "Debug Information Format" build setting. Use `DWARF` instead
* Recommended: avoid having a symbolic link in the source root (e.g. placing a project in `/tmp`)
## Limitations
@@ -38,7 +38,7 @@ class ThinningCreatorPlugin: ArtifactCreatorPlugin {
/// Default Initializer
/// - Parameter targetTempDir: Location of current target-specific temp dir (TARGET_TEMP_DIR)
/// - Parameter modeMarkerPath: path of maker file that informs if a given target can reuse remote artifacts.
/// - Parameter modeMarkerPath: path of maker file that informs if a given target can reuse remote artifacts
/// - Parameter dirScanner: scanner to access disk and read files and directories hierarchy
init(targetTempDir: URL, modeMarkerPath: String, dirScanner: DirScanner) {
self.targetTempDir = targetTempDir
@@ -94,7 +94,7 @@ class ThinningCreatorPlugin: ArtifactCreatorPlugin {
// ProducerFast mode:
// If a target reused already existing artifact, it still has `$(TARGET_TEMP_DIR)/rc.enabled` marker file
// and the reused zip is placed in:
// `$(TARGET_TEMP_DIR)/xccache/{{fileKey}}.zip` location.
// `$(TARGET_TEMP_DIR)/xccache/{{fileKey}}.zip` location
let targetEnabledMarker = tempDir.appendingPathComponent(modeMarkerPath)
let targetReusedArtifactRootDir = tempDir.appendingPathComponent("xccache")
@@ -25,7 +25,7 @@ class ThinningConsumerPlugin {
deinit {
// initialised but never run plugin suggests that standard target fallbacks to the local development
// and DerivedData still misses build artifacts.
// and DerivedData still misses build artifacts
guard wasRun else {
let errorMessage = """
\(type(of: self)) plugin has never been run, thinning cannot be supported. Verify you \
@@ -76,14 +76,16 @@ public struct PostbuildContext {
let derivedSourcesDir: URL
/// List of all targets to downloaded from the thinning aggregation target
var thinnedTargets: [String]
/// Action type: build, indexbuild etc.
/// Action type: build, indexbuild etc
var action: BuildActionType
let modeMarkerPath: String
/// location of the json file that define virtual files system overlay (mappings of the virtual location file -> local file path)
/// location of the json file that define virtual files system overlay
/// (mappings of the virtual location file -> local file path)
let overlayHeadersPath: URL
}
extension PostbuildContext {
// swiftlint:disable:next function_body_length
init(_ config: XCRemoteCacheConfig, env: [String: String]) throws {
mode = config.mode
let targetNameValue: String = try env.readEnv(key: "TARGET_NAME")
@@ -19,6 +19,7 @@
import Foundation
// swiftlint:disable type_body_length
/// Checks current mode from a configuration and based on that:
/// * triggers build completion
/// * triggers uploading artifacts to the server for a 'producer' mode
@@ -113,6 +114,7 @@ public class XCPostbuild {
awsV4Signature = AWSV4Signature(
secretKey: config.AWSSecretKey,
accessKey: config.AWSAccessKey,
securityToken: config.AWSSecurityToken,
region: config.AWSRegion,
service: config.AWSService,
date: Date(timeIntervalSinceNow: 0)
@@ -150,7 +152,8 @@ public class XCPostbuild {
if !config.disableVFSOverlay {
// As the PostbuildContext assumes file location and filename (`all-product-headers.yaml`)
// do not fail in case of a missing headers overlay file. In the future, all overlay files could be
// captured from the swiftc invocation similarly is stored in the `history.compile` for the consumer mode.
// captured from the swiftc invocation similarly is stored in the `history.compile`
// for the consumer mode
let overlayReader = JsonOverlayReader(
context.overlayHeadersPath,
mode: .bestEffort,
@@ -319,3 +322,4 @@ public class XCPostbuild {
}
}
}
// swiftlint:enable type_body_length
@@ -63,7 +63,9 @@ class Prebuild {
do {
let metaData = try networkClient.fetch(.meta(commit: commit))
let meta = try metaReader.read(data: metaData)
let localDependencies = try remapper.replace(genericPaths: meta.dependencies).map(URL.init(fileURLWithPath:))
let localDependencies = try remapper.replace(
genericPaths: meta.dependencies
).map(URL.init(fileURLWithPath:))
let localFingerprint = try generateFingerprint(for: localDependencies)
if localFingerprint.raw != meta.rawFingerprint {
if context.forceCached {
@@ -43,7 +43,8 @@ public struct PrebuildContext {
let targetName: String
/// List of all targets to downloaded from the thinning aggregation target
var thinnedTargets: [String]?
/// location of the json file that define virtual files system overlay (mappings of the virtual location file -> local file path)
/// location of the json file that define virtual files system overlay
/// (mappings of the virtual location file -> local file path)
let overlayHeadersPath: URL
}
@@ -96,6 +96,7 @@ public class XCPrebuild {
awsV4Signature = AWSV4Signature(
secretKey: config.AWSSecretKey,
accessKey: config.AWSAccessKey,
securityToken: config.AWSSecurityToken,
region: config.AWSRegion,
service: config.AWSService,
date: Date(timeIntervalSinceNow: 0)
@@ -124,7 +125,7 @@ public class XCPrebuild {
var remappers: [DependenciesRemapper] = []
if !config.disableVFSOverlay {
// As PrebuildContext assumes file location and its filename (`all-product-headers.yaml`)
// do not fail in case of a missing headers overlay file.
// do not fail in case of a missing headers overlay file
let overlayReader = JsonOverlayReader(
context.overlayHeadersPath,
mode: .bestEffort,
@@ -72,7 +72,17 @@ class TemplateBasedCCWrapperBuilder: CCWrapperBuilder {
)
infoLog("ClangWrapperBuilder compiles file at \(compilationFile).")
// -O3: optimize for faster execution
let args = [clangCommand, "-arch", "arm64", "-arch", "x86_64", "-O3", compilationFile.path, "-o", destination.path]
let args = [
clangCommand,
"-arch",
"arm64",
"-arch",
"x86_64",
"-O3",
compilationFile.path,
"-o",
destination.path,
]
let compilationOutput = try shell("xcrun", args, URL(fileURLWithPath: "").path, nil)
infoLog("Clang compilation output: \(compilationOutput)")
}
@@ -385,6 +395,8 @@ class TemplateBasedCCWrapperBuilder: CCWrapperBuilder {
const char *dependency_arg_name = "-MF";
const char *output_arg_name = "-o";
const char *serialize_diagnostics_arg_name = "--serialize-diagnostics";
const char *language_mode_arg_name = "-x";
const char *precompile_header_arg_value = "objective-c-header";
const char *clang_cmd = "\(clangCommand)";
const char *markerFile = "\(markerFilename)";
const char *compilationHistoryFile = "\(compilationHistoryFilename)";
@@ -399,6 +411,7 @@ class TemplateBasedCCWrapperBuilder: CCWrapperBuilder {
const char *output_file= NULL;
const char *input_file = NULL;
const char *diagnostics_file = NULL;
const char *language_mode = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], dependency_arg_name) == 0 && i < (argc - 1) ) {
@@ -419,6 +432,12 @@ class TemplateBasedCCWrapperBuilder: CCWrapperBuilder {
i += 1;
clang_args[i] = argv[i];
diagnostics_file = argv[i];
} if (strcmp(argv[i], language_mode_arg_name) == 0 && i < (argc - 1) ) {
// called with "-x path" pattern and not the last argument
clang_args[i] = argv[i];
i += 1;
clang_args[i] = argv[i];
language_mode = argv[i];
} else if (
isSuffixed(argv[i],".m") ||
isSuffixed(argv[i],".mm") ||
@@ -426,10 +445,13 @@ class TemplateBasedCCWrapperBuilder: CCWrapperBuilder {
isSuffixed(argv[i],".cc") ||
isSuffixed(argv[i],".cpp") ||
isSuffixed(argv[i],".c++") ||
isSuffixed(argv[i],".cxx")
isSuffixed(argv[i],".cxx") ||
isSuffixed(argv[i],".S") ||
isSuffixed(argv[i],".s")
) {
// a full list of extensions is taken from https://clang.llvm.org/docs/ClangFormatStyleOptions.html
// support for .m,.mm,.c,.cc,.cpp,.c++,.cxx input files
// .s and .S are assembly files
clang_args[i] = argv[i];
input_file = argv[i];
} else {
@@ -438,6 +460,14 @@ class TemplateBasedCCWrapperBuilder: CCWrapperBuilder {
}
}
// null-terminating the args array needed for local compilation fallback
clang_args[argc] = NULL;
// Verify mode. Even a target is cached, pch mode is not supported. Fallback to the local compilation
if (language_mode != NULL && strcmp(language_mode, precompile_header_arg_value) == 0) {
return execvp(clang_cmd, (char *const*) clang_args);
}
// Verify all input arguments
if (dependency_file == NULL) {
fprintf(stderr, "error: missing %s input\\n", dependency_arg_name);
@@ -508,8 +538,6 @@ class TemplateBasedCCWrapperBuilder: CCWrapperBuilder {
}
}
// null-terminating the args array
clang_args[argc] = NULL;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
/// execvp takes $PATH to consideration
@@ -33,10 +33,12 @@ protocol BuildSettingsIntegrateAppender {
class XcodeProjBuildSettingsIntegrateAppender: BuildSettingsIntegrateAppender {
private let mode: Mode
private let repoRoot: URL
private let fakeSrcRoot: URL
init(mode: Mode, repoRoot: URL) {
init(mode: Mode, repoRoot: URL, fakeSrcRoot: URL) {
self.mode = mode
self.repoRoot = repoRoot
self.fakeSrcRoot = fakeSrcRoot
}
func appendToBuildSettings(buildSettings: BuildSettings, wrappers: XCRCBinariesPaths) -> BuildSettings {
@@ -61,8 +63,11 @@ class XcodeProjBuildSettingsIntegrateAppender: BuildSettingsIntegrateAppender {
result["OTHER_SWIFT_FLAGS"] = swiftFlags.settingValue
result["OTHER_CFLAGS"] = clangFlags.settingValue
result["XCRC_FAKE_SRCROOT"] = "/\(String(repeating: "x", count: 10))"
result["XCRC_PLATFORM_PREFERRED_ARCH"] = "$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(PLATFORM_PREFERRED_ARCH):dir:standardizepath:file:default=arm64)"
result["XCRC_FAKE_SRCROOT"] = "\(fakeSrcRoot.path)"
result["XCRC_PLATFORM_PREFERRED_ARCH"] =
"""
$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(PLATFORM_PREFERRED_ARCH):dir:standardizepath:file:default=arm64)
"""
return result
}
}
@@ -35,7 +35,7 @@ struct IncludeExcludeOracle: IncludeOracle {
func shouldInclude(identifier: OracleIdentifierType) -> Bool {
// exclude array has precedence.
// exclude array has precedence
if excludes.contains(identifier) {
return false
}
@@ -97,7 +97,8 @@ public class XCIntegrate {
)
let buildSettingsAppender = XcodeProjBuildSettingsIntegrateAppender(
mode: context.mode,
repoRoot: context.repoRoot
repoRoot: context.repoRoot,
fakeSrcRoot: context.fakeSrcRoot
)
let lldbPatcher: LLDBInitPatcher
switch lldbMode {
@@ -78,6 +78,7 @@ public class XCPrepare {
awsV4Signature = AWSV4Signature(
secretKey: config.AWSSecretKey,
accessKey: config.AWSAccessKey,
securityToken: config.AWSSecurityToken,
region: config.AWSRegion,
service: config.AWSService,
date: Date(timeIntervalSinceNow: 0)
@@ -60,6 +60,7 @@ public class XCPrepareMark {
awsV4Signature = AWSV4Signature(
secretKey: config.AWSSecretKey,
accessKey: config.AWSAccessKey,
securityToken: config.AWSSecurityToken,
region: config.AWSRegion,
service: config.AWSService,
date: Date(timeIntervalSinceNow: 0)
@@ -110,8 +110,8 @@ class SwiftcOrchestrator {
try invocationStorage.store(args: invocationArgs)
}
} catch {
// The critical section is protected by a lock. Some other process already called compilation history.
// We only need to call our current step then.
// The critical section is protected by a lock. Some other process already called compilation history
// We only need to call our current step then
fallbackToDefault(command: swiftcCommand)
}
case .consumer:
@@ -20,7 +20,7 @@
import Foundation
enum DiskSwiftcProductsGeneratorError: Error {
/// When a generator was asked to generate unknown swiftmodule extension file.
/// When a generator was asked to generate unknown swiftmodule extension file
/// Probably a programmer error: asking to generate excessive extensions, not listed in
/// `SwiftmoduleFileExtension.SwiftmoduleExtensions`
case unknownSwiftmoduleFile
@@ -95,10 +95,10 @@ public struct XCRemoteCacheConfig: Encodable {
/// Disable cache for http requests to fecth metadata and download artifacts
var disableHttpCache: Bool = false
/// Path, relative to $TARGET_TEMP_DIR which gathers all compilation commands that should be e
/// xecuted if a target switches to local compilation.
/// xecuted if a target switches to local compilation
/// Example: A new `.swift` file invalidates remote arXcodeProjIntegrate.swifttifact and triggers local compilation
/// When that happens, all previously skipped clang build steps
/// need to be eventually called locally - this file lists all these commands.
/// need to be eventually called locally - this file lists all these commands
var compilationHistoryFile: String = "history.compile"
/// Timeout for remote response data interval (in seconds). If an interval between data chunks is
/// longer than a timeout, a request fails
@@ -118,23 +118,25 @@ public struct XCRemoteCacheConfig: Encodable {
var AWSSecretKey: String = ""
/// Access key for AWS V4 Signature
var AWSAccessKey: String = ""
/// Temporary security token provided by the AWS Security Token Service
var AWSSecurityToken: String?
/// Region for AWS V4 Signature (e.g. `eu`)
var AWSRegion: String = ""
/// Service for AWS V4 Signature (e.g. `storage`)
var AWSService: String = ""
/// A dictionary of files path remapping that should be applied to make it absolute path agnostic on a list of dependencies.
/// Useful if a project refers files out of repo root, either compilation files or precompiled dependencies.
/// Keys represent generic replacement and values are substrings that should be replaced.
/// A dictionary of files path remapping that should be applied to make it absolute path agnostic on a list of
/// dependencies. Useful if a project refers files out of repo root, either compilation files or precompiled
/// dependencies. Keys represent generic replacement and values are substrings that should be replaced
/// Example: for mapping `["COOL_LIBRARY": "/CoolLibrary"]`
/// `/CoolLibrary/main.swift`will be represented as `$(COOL_LIBRARY)/main.swift`).
/// Warning: remapping order is not-deterministic so avoid remappings with multiple matchings.
/// `/CoolLibrary/main.swift`will be represented as `$(COOL_LIBRARY)/main.swift`)
/// Warning: remapping order is not-deterministic so avoid remappings with multiple matchings
var outOfBandMappings: [String: String] = [:]
/// If true, SSL certificate validation is disabled
var disableCertificateVerification: Bool = false
/// A feature flag to disable virtual file system overlay support (temporary)
var disableVFSOverlay: Bool = false
/// 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.
/// 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
var customRewriteEnvs: [String] = []
}
@@ -184,6 +186,7 @@ extension XCRemoteCacheConfig {
merge.prettifyMetaFiles = scheme.prettifyMetaFiles ?? prettifyMetaFiles
merge.AWSAccessKey = scheme.AWSAccessKey ?? AWSAccessKey
merge.AWSSecretKey = scheme.AWSSecretKey ?? AWSSecretKey
merge.AWSSecurityToken = scheme.AWSSecurityToken ?? AWSSecurityToken
merge.AWSRegion = scheme.AWSRegion ?? AWSRegion
merge.AWSService = scheme.AWSService ?? AWSService
merge.outOfBandMappings = scheme.outOfBandMappings ?? outOfBandMappings
@@ -247,6 +250,7 @@ struct ConfigFileScheme: Decodable {
let prettifyMetaFiles: Bool?
let AWSSecretKey: String?
let AWSAccessKey: String?
let AWSSecurityToken: String?
let AWSRegion: String?
let AWSService: String?
let outOfBandMappings: [String: String]?
@@ -293,6 +297,7 @@ struct ConfigFileScheme: Decodable {
case prettifyMetaFiles = "prettify_meta_files"
case AWSSecretKey = "aws_secret_key"
case AWSAccessKey = "aws_access_key"
case AWSSecurityToken = "aws_security_token"
case AWSRegion = "aws_region"
case AWSService = "aws_service"
case outOfBandMappings = "out_of_band_mappings"
@@ -72,7 +72,7 @@ public class FileDependenciesReader: DependenciesReader {
return Set(splitDependencyFileList(value))
case let s where s.hasSuffix(".o") || s.hasSuffix(".bc"):
// 'swiftc' output formatting
// take dependencies from any .o or .bc file.
// take dependencies from any .o or .bc file
// Note: For WMO, all .{o|bc} files have the same dependencies
return Set(splitDependencyFileList(value))
default:
@@ -42,8 +42,8 @@ class OverlayDependenciesRemapper: DependenciesRemapper {
private func mapPath(
_ path: String,
source: KeyPath<OverlayMapping,URL>,
destination: KeyPath<OverlayMapping,URL>
source: KeyPath<OverlayMapping, URL>,
destination: KeyPath<OverlayMapping, URL>
) throws -> String {
guard let mapping = try getMappings().first(where: { $0[keyPath: source].path == path }) else {
// TODO: support partial mappings, where a directory path can be replaced with some other directory
@@ -91,7 +91,7 @@ class JsonOverlayReader: OverlayReader {
case .strict:
throw JsonOverlayReaderError.missingSourceFile(json)
case .bestEffort:
printWarning("overlay mapping file \(json) doesn't exist. Skipping overlay for the best-effort mode.")
debugLog("overlay mapping file \(json) doesn't exist. Skipping overlay for the best-effort mode.")
return []
}
}
@@ -100,7 +100,7 @@ class JsonOverlayReader: OverlayReader {
let mappings: [OverlayMapping] = try overlay.roots.reduce([]) { prev, root in
switch root.type {
case .directory:
//iterate all contents
// iterate all contents
let dir = URL(fileURLWithPath: root.name)
let mappings: [OverlayMapping] = try root.contents.map { content in
switch content.type {
@@ -124,7 +124,7 @@ class JsonOverlayReader: OverlayReader {
case .strict:
throw error
case .bestEffort:
printWarning("Overlay reader has failed with an error \(error). Best-effort mode - skipping an overlay.")
errorLog("Overlay reader has failed with an error \(error). Best-effort mode - skipping an overlay.")
return []
}
}
@@ -30,10 +30,10 @@ class PathDependenciesRemapperFactory {
envs: [String: String],
customMappings: [String: String]
) throws -> StringDependenciesRemapper {
let mappingMap = try envs.merging(customMappings) { envValue, outOfBandMapping in
let mappingMap = try envs.merging(customMappings) { _, _ in
throw PathDependenciesRemapperFactoryError.mappingKeyDuplication
}
let mappingOrderKeys = orderKeys + customMappings.keys
let mappingOrderKeys = orderKeys + customMappings.keys
let mappings: [StringDependenciesRemapper.Mapping] = mappingOrderKeys.compactMap { key in
guard let localURL: URL = mappingMap.readEnv(key: key) else {
debugLog("\(key) ENV to map a dependency is not defined")
@@ -42,7 +42,7 @@ class TargetDependenciesReader: DependenciesReader {
let allURLs = try dirScanner.items(at: directory)
let mergedDependencies = try allURLs.reduce(Set<String>()) { (prev: Set<String>, file) in
// include only these .d files that either have corresponding .o file (incremental) or end
// with '-master' (whole-module).
// with '-master' (whole-module)
// Otherwise .d is probably just a leftover from previous builds
let correspondingOutputURL = file.deletingPathExtension().appendingPathExtension("o")
let isDependencyFile = file.pathExtension == "d"
@@ -31,7 +31,7 @@ class EnvironmentFingerprintGenerator {
"DYLIB_COMPATIBILITY_VERSION",
"DYLIB_CURRENT_VERSION",
"PRODUCT_MODULE_NAME",
"ARCHS"
"ARCHS",
]
private let version: String
private let customFingerprintEnvs: [String]
@@ -20,7 +20,7 @@
import Foundation
protocol MetaWriter {
func write<T>(_ meta: T, locationDir : URL) throws -> URL where T : Meta
func write<T>(_ meta: T, locationDir: URL) throws -> URL where T: Meta
}
class JsonMetaWriter: MetaWriter {
@@ -36,7 +36,7 @@ class JsonMetaWriter: MetaWriter {
self.metaEncoder = encoder
}
func write<T>(_ meta: T, locationDir : URL) throws -> URL where T : Meta {
func write<T>(_ meta: T, locationDir: URL) throws -> URL where T: Meta {
let metaURL = locationDir.appendingPathComponent(meta.fileKey).appendingPathExtension("json")
let metaData = try metaEncoder.encode(meta)
try fileWriter.write(toPath: metaURL.path, contents: metaData)
@@ -23,17 +23,21 @@ struct AWSV4Signature {
let secretKey: String
let accessKey: String
let securityToken: String?
let region: String
let service: String
let date: Date
func addSignatureHeaderTo(request: inout URLRequest) {
request.setValue(request.url?.host, forHTTPHeaderField: "host")
request.setValue(StringToSign.ISO8601BasicFormatter.string(from: date), forHTTPHeaderField: "x-amz-date")
request.setValue((request.httpBody ?? Data()).sha256(), forHTTPHeaderField: "x-amz-content-sha256")
if let securityToken = securityToken {
request.setValue(securityToken, forHTTPHeaderField: "x-amz-security-token")
}
let canonicalRequest = CanonicalRequest(request: request)
let stringToSign = StringToSign(
region: region,
@@ -25,7 +25,7 @@ final class IgnoringCertificatesTrustManager: NSObject, URLSessionDelegate {
completionHandler(.performDefaultHandling, nil)
return
}
let urlCredential = URLCredential(trust: serverTrust)
completionHandler(.useCredential, urlCredential)
}
@@ -36,6 +36,8 @@ class DefaultURLSessionFactory: URLSessionFactory {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = config.requestCustomHeaders
configuration.timeoutIntervalForRequest = config.timeoutResponseDataChunksInterval
configuration.urlCache?.memoryCapacity = 0
configuration.urlCache?.diskCapacity = 0
switch config.disableCertificateVerification {
case true:
return URLSession(
@@ -76,8 +76,8 @@ class ExclusiveFile: ExclusiveFileAccessor {
guard flock(fd, LOCK_EX) == 0 else {
throw FileAccessorError.lockingFailure
}
// While having a lock, make sure the file still exists.
// It might delete it while we were waiting for a lock.
// While having a lock, make sure the file still exists
// It might delete it while we were waiting for a lock
guard access(fileURL.path, F_OK) == 0 else {
throw FileAccessorError.lockingFailure
}
@@ -131,7 +131,7 @@ class ThinningCreatorPluginTests: FileXCTestCase {
XCTAssertEqual(extraKeys, [
"thinning_Generated": "000",
"thinning_Reused": "999"
"thinning_Reused": "999",
])
}
}
@@ -42,7 +42,7 @@ class PostbuildContextTests: FileXCTestCase {
"DWARF_DSYM_FILE_NAME": "DWARF_DSYM_FILE_NAME",
"BUILT_PRODUCTS_DIR": "BUILT_PRODUCTS_DIR",
"DERIVED_SOURCES_DIR": "DERIVED_SOURCES_DIR",
"CURRENT_VARIANT": "normal"
"CURRENT_VARIANT": "normal",
]
override func setUpWithError() throws {
@@ -20,6 +20,7 @@
@testable import XCRemoteCache
import XCTest
// swiftlint:disable file_length
// swiftlint:disable:next type_body_length
class PostbuildTests: FileXCTestCase {
private var postbuildContext = PostbuildContext(
@@ -639,4 +640,3 @@ class PostbuildTests: FileXCTestCase {
XCTAssertEqual(downloadedMeta, expectedMeta)
}
}
// swiftlint:disable:next file_length
@@ -0,0 +1,62 @@
// Copyright (c) 2021 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 XCRemoteCache
import XCTest
class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase {
private let rootURL: URL = "/root"
private let binariesDir: URL = "/binaries"
private var buildSettings: BuildSettings!
private var binaries: XCRCBinariesPaths!
override func setUp() {
super.setUp()
buildSettings = BuildSettings()
binaries = XCRCBinariesPaths(
prepare: binariesDir.appendingPathComponent("xcprepare"),
cc: binariesDir.appendingPathComponent("xccc"),
swiftc: binariesDir.appendingPathComponent("xcswiftc"),
libtool: binariesDir.appendingPathComponent("xclibtool"),
ld: binariesDir.appendingPathComponent("xcld"),
prebuild: binariesDir.appendingPathComponent("xcprebuild"),
postbuild: binariesDir.appendingPathComponent("xcpostbuild")
)
}
func testProducerSettingFakeSrcRoot() throws {
let mode: Mode = .producer
let fakeRootURL: URL = "/xxxxxxxxxxP"
let appender = XcodeProjBuildSettingsIntegrateAppender(mode: mode, repoRoot: rootURL, fakeSrcRoot: fakeRootURL)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let resultURL = try XCTUnwrap(result["XCRC_FAKE_SRCROOT"] as? String)
XCTAssertEqual(resultURL, fakeRootURL.path)
}
func testConsumerSettingFakeSrcRoot() throws {
let mode: Mode = .consumer
let fakeRootURL: URL = "/xxxxxxxxxxC"
let appender = XcodeProjBuildSettingsIntegrateAppender(mode: mode, repoRoot: rootURL, fakeSrcRoot: fakeRootURL)
let result = appender.appendToBuildSettings(buildSettings: buildSettings, wrappers: binaries)
let resultURL: String = try XCTUnwrap(result["XCRC_FAKE_SRCROOT"] as? String)
XCTAssertEqual(resultURL, fakeRootURL.path)
}
}
@@ -47,7 +47,8 @@ class TemplateBasedCCWrapperBuilderTests: FileXCTestCase {
let app = appDir.appendingPathComponent("xccc")
try? fileManager.removeItem(at: appDir)
try? FileManager.default.createDirectory(at: appDir, withIntermediateDirectories: true, attributes: nil)
try? builder.compile(to: app, commitSha: commitSha)
// swiftlint:disable:next force_try
try! builder.compile(to: app, commitSha: commitSha)
return app
}()
@@ -332,11 +333,31 @@ class TemplateBasedCCWrapperBuilderTests: FileXCTestCase {
XCTAssertNotEqual(newFileOutputData, Data())
}
func testPCHCompilationFallbacks() throws {
// Marker is empty to mimic the new file scenario
let pchFile = directory.appendingPathComponent("input.pch")
createValidPCHFile(pchFile)
arguments = ["-x", "objective-c-header", "-MF", dependencyFile.path, "-o", outputFile.path, pchFile.path]
try shellExec(Self.xccc.path, args: arguments, inDir: directory.path)
XCTAssertTrue(fileManager.fileExists(atPath: outputFile.path))
}
/// Creates a simple C code in the location
private func createValidCFile(_ location: URL) {
fileManager.createFile(atPath: location.path, contents: "int main(){}".data(using: .utf8), attributes: nil)
}
/// Creates a simple PCH code in the location
private func createValidPCHFile(_ location: URL) {
fileManager.createFile(
atPath: location.path,
contents: "#import <Availability.h>".data(using: .utf8),
attributes: nil
)
}
/// Creates a C code that requires extra CUSTOM_STR clang macro to compile
private func createInvalidCFile(_ location: URL) {
fileManager.createFile(
@@ -81,8 +81,18 @@ class DependenciesRemapperCompositeTests: XCTestCase {
func testRemapsMultipleMatchingMappers() throws {
let remapper = DependenciesRemapperComposite([
StringDependenciesRemapper(mappings: [StringDependenciesRemapper.Mapping(generic: "$(ROOT)", local: "/root")]),
StringDependenciesRemapper(mappings: [StringDependenciesRemapper.Mapping(generic: "$(SPECIFIC)", local: "$(ROOT)/specific")])
StringDependenciesRemapper(mappings: [
StringDependenciesRemapper.Mapping(
generic: "$(ROOT)",
local: "/root"
),
]),
StringDependenciesRemapper(mappings: [
StringDependenciesRemapper.Mapping(
generic: "$(SPECIFIC)",
local: "$(ROOT)/specific"
),
]),
])
let localPaths = ["/root/specific/file"]
@@ -93,8 +103,18 @@ class DependenciesRemapperCompositeTests: XCTestCase {
func testRemapsBackToLocalWithRevertedRemappersOrder() throws {
let remapper = DependenciesRemapperComposite([
StringDependenciesRemapper(mappings: [StringDependenciesRemapper.Mapping(generic: "$(ROOT)", local: "/root")]),
StringDependenciesRemapper(mappings: [StringDependenciesRemapper.Mapping(generic: "$(SPECIFIC)", local: "$(ROOT)/specific")])
StringDependenciesRemapper(mappings: [
StringDependenciesRemapper.Mapping(
generic: "$(ROOT)",
local: "/root"
),
]),
StringDependenciesRemapper(mappings: [
StringDependenciesRemapper.Mapping(
generic: "$(SPECIFIC)",
local: "$(ROOT)/specific"
),
]),
])
let genericPaths = ["$(SPECIFIC)/file"]
@@ -105,8 +125,18 @@ class DependenciesRemapperCompositeTests: XCTestCase {
func testRemappingTwoMappingsBackAndForthIsIdentical() throws {
let remapper = DependenciesRemapperComposite([
StringDependenciesRemapper(mappings: [StringDependenciesRemapper.Mapping(generic: "$(ROOT)", local: "/root")]),
StringDependenciesRemapper(mappings: [StringDependenciesRemapper.Mapping(generic: "$(SPECIFIC)", local: "$(ROOT)/specific")])
StringDependenciesRemapper(mappings: [
StringDependenciesRemapper.Mapping(
generic: "$(ROOT)",
local: "/root"
),
]),
StringDependenciesRemapper(mappings: [
StringDependenciesRemapper.Mapping(
generic: "$(SPECIFIC)",
local: "$(ROOT)/specific"
),
]),
])
let localPaths = ["/root/specific/file"]
@@ -133,10 +133,14 @@ class DependencyProcessorImplTests: FileXCTestCase {
bundle: "/Bundle"
)
let intermediateFileSymlink = createSymlink(filename: someFilename, sourceDir: symlink, destinationDir: intermediateDirReal)
let intermediateFileSymlink = createSymlink(
filename: someFilename,
sourceDir: symlink,
destinationDir: intermediateDirReal
)
let dependencies = processor.process([
intermediateFileSymlink
intermediateFileSymlink,
])
XCTAssertEqual(dependencies, [])
@@ -157,10 +161,14 @@ class DependencyProcessorImplTests: FileXCTestCase {
bundle: "/Bundle"
)
let sourceFileSymlink = createSymlink(filename: someFilename, sourceDir: symlink, destinationDir: sourceDirReal)
let sourceFileSymlink = createSymlink(
filename: someFilename,
sourceDir: symlink,
destinationDir: sourceDirReal
)
let dependencies = processor.process([
sourceFileSymlink
sourceFileSymlink,
])
XCTAssertEqual(dependencies, [.init(url: sourceFileSymlink, type: .source)])
@@ -173,8 +181,10 @@ class DependencyProcessorImplTests: FileXCTestCase {
fileprivate func createSymlink(filename: String, sourceDir: URL, destinationDir: URL) -> URL {
let fileMng = FileManager.default
XCTAssertNoThrow(try fileMng.spt_forceSymbolicLink(at: sourceDir,
withDestinationURL: destinationDir))
XCTAssertNoThrow(try fileMng.spt_forceSymbolicLink(
at: sourceDir,
withDestinationURL: destinationDir
))
XCTAssertNoThrow(try fileMng.spt_createEmptyFile(destinationDir.appendingPathComponent(filename)))
return sourceDir.appendingPathComponent(filename)
@@ -68,4 +68,3 @@ class OverlayDependenciesRemapperTests: XCTestCase {
XCTAssertEqual(secondDependencies, ["/file.h"])
}
}
@@ -29,8 +29,14 @@ class JsonOverlayReaderTests: FileXCTestCase {
let mappings = try reader.provideMappings()
let expectedMappings = [
OverlayMapping(virtual: "/DerivedDataProducts/Target1.framework/Headers/Target1.h", local: "/Path/Target1/Target1.h"),
OverlayMapping(virtual: "/DerivedDataProducts/Target2.framework/Modules/module.modulemap", local: "/DerivedDataIntermediate/Target2.build/module.modulemap")
OverlayMapping(
virtual: "/DerivedDataProducts/Target1.framework/Headers/Target1.h",
local: "/Path/Target1/Target1.h"
),
OverlayMapping(
virtual: "/DerivedDataProducts/Target2.framework/Modules/module.modulemap",
local: "/DerivedDataIntermediate/Target2.build/module.modulemap"
),
]
XCTAssertEqual(Set(mappings), Set(expectedMappings))
}
@@ -79,6 +85,10 @@ class JsonOverlayReaderTests: FileXCTestCase {
}
private func pathForTestData(name: String) throws -> URL {
return try XCTUnwrap(Bundle.module.url(forResource: name, withExtension: "json", subdirectory: JsonOverlayReaderTests.resourcesSubdirectory))
return try XCTUnwrap(Bundle.module.url(
forResource: name,
withExtension: "json",
subdirectory: JsonOverlayReaderTests.resourcesSubdirectory
))
}
}
@@ -24,6 +24,7 @@ class PathDependenciesRemapperFactoryTests: XCTestCase {
private var factory: PathDependenciesRemapperFactory!
override func setUp() {
super.setUp()
factory = PathDependenciesRemapperFactory()
}
@@ -33,7 +33,7 @@ class EnvironmentFingerprintGeneratorTests: XCTestCase {
"DYLIB_COMPATIBILITY_VERSION": "2",
"DYLIB_CURRENT_VERSION": "3",
"PRODUCT_MODULE_NAME": "4",
"ARCHS": "AR"
"ARCHS": "AR",
]
/// Corresponds to EnvironmentFingerprintGenerator.version
private static let currentVersion = "5"
@@ -35,10 +35,12 @@ class AWSV4SignatureTest: XCTestCase {
let key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
let accessKey = "AKIDEXAMPLE"
let securityToken = "IQoJb3JpZ2luX2VjENv//////////wEaCXVzLWVhc3Q+bsHwqnovXtl/1JVe61XHMnAw3AIXwOAOxqMvhw=="
AWSV4Signature(
secretKey: key,
accessKey: accessKey,
securityToken: securityToken,
region: "us-east-1",
service: "iam",
date: Date(timeIntervalSince1970: 1_440_938_160)
@@ -49,8 +51,8 @@ class AWSV4SignatureTest: XCTestCase {
func testAuthHeaderContainsCorrectSignature() throws {
XCTAssertEqual(
"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, " +
"SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, " +
"Signature=dd479fa8a80364edf2119ec24bebde66712ee9c9cb2b0d92eb3ab9ccdc0c3947",
"SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, " +
"Signature=d26ab974bba1b248f041ea1120064e1fa672d6f06cac2cff42b38acea87b76e5",
request.allHTTPHeaderFields?["Authorization"]
)
}
@@ -203,6 +203,7 @@ class NetworkClientImplTests: XCTestCase {
let signature = AWSV4Signature(
secretKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
accessKey: "AKIDEXAMPLE",
securityToken: "IQoJb3JpZ2luX2VjENv//////////wEaCXVzLWVhc3Q+bsHwqnovXtl/1JVe61XHMnAw3AIXwOAOxqMvhw==",
region: "us-east-1",
service: "iam",
date: Date(timeIntervalSince1970: 1_440_938_160)
@@ -213,8 +214,8 @@ class NetworkClientImplTests: XCTestCase {
XCTAssertEqual(
"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, " +
"SignedHeaders=host;x-amz-content-sha256;x-amz-date, " +
"Signature=acdb223475463b6ce711b063f199387a7a12bd638e4dccaaeb5efcbf159b6454",
"SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token, " +
"Signature=e5578464567fb97fd26e871702e4ec4ff7d61cb87eb72a40d22b80e12da30c34",
try requests[0].allHTTPHeaderFields?["Authorization"].unwrap()
)
}
@@ -22,7 +22,7 @@ import Foundation
class OverlayReaderFake: OverlayReader {
var mappings: [OverlayMapping]
init(mappings: [OverlayMapping]) {
self.mappings = mappings
}
@@ -180,7 +180,7 @@ module CocoapodsXCRemoteCacheModifier
end
end
mark_script = existing_mark_script || target.new_shell_script_build_phase("[XCRC] Mark")
mark_script.shell_script = "\"$SCRIPT_INPUT_FILE_0\" mark --configuration $CONFIGURATION --platform $PLATFORM_NAME"
mark_script.shell_script = "\"$SCRIPT_INPUT_FILE_0\" mark --configuration \"$CONFIGURATION\" --platform $PLATFORM_NAME"
mark_script.input_paths = ["$SRCROOT/#{srcroot_relative_xc_location}/xcprepare"]
else
# Delete existing mark build phase (to support switching between modes or changing the final target)
@@ -13,5 +13,5 @@
# limitations under the License.
module CocoapodsXcremotecache
VERSION = "0.0.7"
VERSION = "0.0.8"
end
+28
View File
@@ -23,6 +23,34 @@ If you prefer to edit in Xcode, run `swift package generate-xcodeproj`. Do **not
The generated Xcode project contains schemes for each output application (like `xcswiftc`, `xcprebuild` etc.) so to build a single app, just select the appropriate scheme and build (⌘+B). If you want to build all applications at once, select `Aggregator` scheme that automatically builds all apps. `Aggregator` target in `Package.swift` is defined only for development convenience, it shouldn't be ever used as a dependency.
#### Debugging the app in a real project
Debugging XCRemoteCache in a real project is simple:
* Open XCRemoteCache's `Package.swift` in Xcode and select a scheme that corresponds to the process you want to debug, e.g. `xcpostbuild`
![Select scheme](./img/debug-scheme.png)
* Build the app with Product->Build (or ⌘+B)
* Open the scheme's settings (⌘+⇧+<) and for the "Run" action, select "Wait for the executable to be launched"
![Configure scheme](./img/debug-scheme-wait.png)
* Find the produced binary in your DerivedData. The default location would be `~/Library/Developer/Xcode/DerivedData/XCRemoteCache-{hash}/Build/Products/Debug/xcpostbuild`
* In the project you want to debug, pick the target you want to inspect and expand the build phase that calls the process
* Replace the Input Files path so it points the locally built binary placed in DerivedData
![Update phase](./img/debug-phase-update.png)
* Now, you can run the **XCRemoteCache** project (not the project you want to debug). Because Xcode will not initiate a new process, it will just wait until someone else triggers it.
![LLDB waiting](./img/debug-wait.png)
* Finally, build the project to want to debug and expect your XCRemoteCache breakpoints to hit
![Breakpoint hit](./img/debug-breakpoint.png)
> Tip: If your breakpoints don't hit, try adding a dummy `sleep(1)` in the process entry point (e.g. at the top of `XCPostbuild.main` function)
#### Running tests in Xcode
All unit tests are placed in `XCRemoteCacheTests`. To run them from Xcode, just pick any application scheme and run tests (⌘+U).
+15
View File
@@ -50,3 +50,18 @@ log show --predicate 'sender == "xcprepare"' --style compact --info --debug -las
log show --predicate 'sender BEGINSWITH "xc"' --style compact --info --debug -last 10m
```
</details>
### Troubleshooting cache misses
Here is a non-exhaustive list of steps that may help with troubleshooting poor cache hit rate.
1. ***Producer&Consumer:*** Review XCRemoteCache [Requirements](../#Requirements) and [Limitations](../#limitations)
1. ***Producer&Consumer:*** Make sure a producer build uses the same architecture(s) as a consumer. You can inspect `ARCHS` Build Setting in Xcode's Script Phase output logs. Navigate to the report navigator (⌘+9) and expand XCRemoteCache's `prebuild` step output using the "collapsed menu icon" (aka hamburger menu)
1. ***Producer:*** Verify that all Xcode targets have a Build Phase called `postbuild`
1. ***Producer:*** If you are using optional XCRemoteCache auto-marking feature (`--final-producer-target` or `final_target`) verify an extra Build Phase called `mark` is added to the specified target
1. ***Producer:*** After a full build, review logs according to [docs](#how-can-i-find-xcremotecache-logs)
1. ***Consumer:*** Verify that all Xcode targets have extra XCRemoteCache Build Phase called `prebuild` and `postbuild`
1. ***Consumer:*** After a full build, review according to [docs](#how-can-i-find-xcremotecache-logs). Find a ***first:*** target that reports a cache miss with a message like `Prebuild step failed with error: ...`. If a target reports faces a cache miss, it may have a knock-on effect where a lot of its consumers (dependant targets) need to be built locally too
1. ***Consumer:*** ***After a full build, review all meta files placed in `~/Library/Caches/XCRemoteCache/{your_host_path}/meta/*.json` and make sure no absolute paths are used in its `dependencies`. All paths should start a placeholder, like `$(SRCROOT)` or `$(BUILD_DIR)`***
1. ***Consumer:*** If you are integrating XCRemoteCache and rebuild artifacts for the same sha, previously downloaded artifacts placed in a local cache may still be used on a consumer side. You can either manually delete your local cache at `~/Library/Caches/XCRemoteCache/` before any consumer build or disable a local cache with `artifact_maximum_age: 0` property in `.rcinfo`
1. ***Consumer:*** To find an actual cache hit, before building in Xcode reset statistics with `xcprepare stats --reset` and once it is done, call `xcprepare stats` to find a cache hit rate
Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -23,7 +23,6 @@ import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
@@ -38,4 +37,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}
@@ -41,4 +41,3 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func sceneDidEnterBackground(_ scene: UIScene) {
}
}
@@ -27,4 +27,3 @@ class ViewController: UIViewController {
}