Add integration

This reverts commit b2d47760cf.
This commit is contained in:
Bartosz Polaczyk
2022-02-04 21:48:43 +01:00
parent b2d47760cf
commit cf0b27d03c
9 changed files with 198 additions and 8 deletions
@@ -79,6 +79,8 @@ public struct PostbuildContext {
/// 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)
let overlayHeadersPath: URL
}
extension PostbuildContext {
@@ -127,5 +129,7 @@ extension PostbuildContext {
thinnedTargets = thinFocusedTargetsString.split(separator: ",").map(String.init)
action = (try? BuildActionType(rawValue: env.readEnv(key: "ACTION"))) ?? .unknown
modeMarkerPath = config.modeMarkerPath
/// The files has yaml extension, even it is in the json format
overlayHeadersPath = targetTempDir.appendingPathComponent("all-product-headers.yaml")
}
}
@@ -66,7 +66,7 @@ public class XCPostbuild {
// Initialize dependencies
let primaryGitBranch = GitBranch(repoLocation: config.primaryRepo, branch: config.primaryBranch)
let gitClient = GitClientImpl(repoRoot: config.repoRoot, primary: primaryGitBranch, shell: shellGetStdout)
let pathRemapper = try StringDependenciesRemapperFactory().build(
let envsRemapper = try StringDependenciesRemapperFactory().build(
orderKeys: DependenciesMapping.rewrittenEnvs,
envs: env,
customMappings: config.outOfBandMappings
@@ -145,6 +145,18 @@ public class XCPostbuild {
fileDependeciesReaderFactory: fileReaderFactory,
dirScanner: fileManager
)
// As the PostbuildContext assumes file format location and filename (`all-product-headers.yaml`)
// do not fail in case of a missing headers overlay file. In the future, all overlay files should be
// captured from the swiftc invocation similarly is stored in the `history.compile` for the consumer mode.
let overlayReader = JsonOverlayReader(
context.overlayHeadersPath,
mode: .bestEffort,
fileReader: fileManager
)
let overlayRemapper = try OverlayDependenciesRemapper(
overlayReader: overlayReader
)
let pathRemapper = DependenciesRemapperComposite([overlayRemapper, envsRemapper])
let dependencyProcessor = DependencyProcessorImpl(
xcode: context.xcodeDir,
product: context.productsDir,
@@ -43,6 +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)
let overlayHeadersPath: URL
}
extension PrebuildContext {
@@ -64,5 +66,7 @@ extension PrebuildContext {
self.targetName = targetName
let thinFocusedTargetsString: String? = env.readEnv(key: "SPT_XCREMOTE_CACHE_THINNED_TARGETS")
thinnedTargets = thinFocusedTargetsString?.split(separator: ",").map(String.init)
/// The files has yaml extension, even it is in the json format
overlayHeadersPath = targetTempDir.appendingPathComponent("all-product-headers.yaml")
}
}
@@ -115,11 +115,23 @@ public class XCPrebuild {
)
let client: NetworkClient = config.disableHttpCache ? networkClient : cacheNetworkClient
let remoteNetworkClient = RemoteNetworkClientImpl(client, urlBuilder)
let pathRemapper = try StringDependenciesRemapperFactory().build(
let envsRemapper = try StringDependenciesRemapperFactory().build(
orderKeys: DependenciesMapping.rewrittenEnvs,
envs: env,
customMappings: config.outOfBandMappings
)
// As the PostbuildContext assumes file format location and filename (`all-product-headers.yaml`)
// do not fail in case of a missing headers overlay file. In the future, all overlay files should be
// captured from the swiftc invocation similarly is stored in the `history.compile` for the consumer mode.
let overlayReader = JsonOverlayReader(
context.overlayHeadersPath,
mode: .bestEffort,
fileReader: fileManager
)
let overlayRemapper = try OverlayDependenciesRemapper(
overlayReader: overlayReader
)
let pathRemapper = DependenciesRemapperComposite([overlayRemapper, envsRemapper])
let filesFingerprintGenerator = FingerprintAccumulatorImpl(
algorithm: MD5Algorithm(),
fileManager: fileManager
@@ -0,0 +1,55 @@
// 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.
import Foundation
/// File paths remapper according the virtual file system mappings
/// Warning: this class is not thread safe
class OverlayDependenciesRemapper: DependenciesRemapper {
private var mappings: [OverlayMapping]
init(overlayReader: OverlayReader) throws {
mappings = try overlayReader.provideMappings()
}
private func mapPath(
_ path: String,
source: KeyPath<OverlayMapping,URL>,
destination: KeyPath<OverlayMapping,URL>
) -> String {
guard let mapping = mappings.first(where: { $0[keyPath: source].path == path }) else {
// TODO: support partial mappings, where a directory path can be replaced with some other directory
// no direct mapping found
return path
}
return mapping[keyPath: destination].path
}
func replace(genericPaths: [String]) -> [String] {
Set(genericPaths.map {
mapPath($0, source: \.virtual, destination: \.local)
}).sorted()
}
func replace(localPaths: [String]) -> [String] {
Set(localPaths.map {
mapPath($0, source: \.local, destination: \.virtual)
}).sorted()
}
}
@@ -51,7 +51,8 @@ class PostbuildTests: FileXCTestCase {
derivedSourcesDir: "",
thinnedTargets: [],
action: .build,
modeMarkerPath: ""
modeMarkerPath: "",
overlayHeadersPath: ""
)
private var network = RemoteNetworkClientImpl(
NetworkClientFake(fileManager: .default),
@@ -62,7 +62,8 @@ class PrebuildTests: FileXCTestCase {
forceCached: false,
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: ""
targetName: "",
overlayHeadersPath: ""
)
contextCached = PrebuildContext(
targetTempDir: sampleURL,
@@ -74,7 +75,8 @@ class PrebuildTests: FileXCTestCase {
forceCached: true,
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: ""
targetName: "",
overlayHeadersPath: ""
)
organizer = ArtifactOrganizerFake(artifactRoot: artifactsRoot, unzippedExtension: "unzip")
globalCacheSwitcher = InMemoryGlobalCacheSwitcher()
@@ -238,7 +240,8 @@ class PrebuildTests: FileXCTestCase {
forceCached: false,
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: ""
targetName: "",
overlayHeadersPath: ""
)
let prebuild = Prebuild(
@@ -268,7 +271,8 @@ class PrebuildTests: FileXCTestCase {
forceCached: false,
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: true,
targetName: ""
targetName: "",
overlayHeadersPath: ""
)
metaContent = try generateMeta(fingerprint: generator.generate(), filekey: "1")
let downloadedArtifactPackage = artifactsRoot.appendingPathComponent("1")
@@ -330,7 +334,8 @@ class PrebuildTests: FileXCTestCase {
forceCached: false,
compilationHistoryFile: compilationHistory,
turnOffRemoteCacheOnFirstTimeout: false,
targetName: ""
targetName: "",
overlayHeadersPath: ""
)
try globalCacheSwitcher.enable(sha: "1")
let prebuild = Prebuild(
@@ -0,0 +1,65 @@
// 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 OverlayDependenciesRemapperTests: XCTestCase {
private let overlayReader = OverlayReaderFake(
mappings: [.init(virtual: "/file.h", local: "/Intermediate/Some/file.h")]
)
func testMappingFromLocalToGeneric() throws {
let reader = try OverlayDependenciesRemapper(
overlayReader: overlayReader
)
let dependencies = reader.replace(localPaths: ["/Intermediate/Some/file.h"])
XCTAssertEqual(dependencies, ["/file.h"])
}
func testMappingFromGenericToLocal() throws {
let reader = try OverlayDependenciesRemapper(
overlayReader: overlayReader
)
let dependencies = reader.replace(genericPaths: ["/file.h"])
XCTAssertEqual(dependencies, ["/Intermediate/Some/file.h"])
}
func testGenericDependenciesAreMerged() throws {
let reader = try OverlayDependenciesRemapper(
overlayReader: overlayReader
)
let dependencies = reader.replace(localPaths: ["/Intermediate/Some/file.h", "/file.h"])
XCTAssertEqual(dependencies, ["/file.h"])
}
func testLocalDependenciesAreMerged() throws {
let reader = try OverlayDependenciesRemapper(
overlayReader: overlayReader
)
let dependencies = reader.replace(genericPaths: ["/Intermediate/Some/file.h", "/file.h"])
XCTAssertEqual(dependencies, ["/Intermediate/Some/file.h"])
}
}
@@ -0,0 +1,32 @@
// 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.
import Foundation
@testable import XCRemoteCache
class OverlayReaderFake: OverlayReader {
private let mappings: [OverlayMapping]
init(mappings: [OverlayMapping]) {
self.mappings = mappings
}
func provideMappings() throws -> [OverlayMapping] {
return mappings
}
}