Skip to content

Commit 60d2fda

Browse files
authored
[5.5] Allow a custom manifest loader to provide custom environment variables when compiling the manifest (#3667)
* Get rid of a forced unwrap and extra lookup of each key when iterating a sorted dictionary. * [5.5] Add the ability to control the environment passed to the manifest and plugin compilation It defaults to `ProcessEnv.vars` so that there is no change in semantics unless the client code customizes the environment.
1 parent 519aa4a commit 60d2fda

File tree

3 files changed

+80
-5
lines changed

3 files changed

+80
-5
lines changed

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ public protocol ManifestResourceProvider {
4242
/// The bin directory.
4343
var binDir: AbsolutePath? { get }
4444

45-
/// Extra flags to pass the Swift compiler.
45+
/// Extra flags to pass the Swift compiler (if not specified, no additional arguments are passed).
4646
var swiftCompilerFlags: [String] { get }
47+
48+
/// Custom environment to pass to the Swift compiler (if not specified, the inherited environment is passed).
49+
var swiftCompilerEnvironment: [String: String] { get }
4750

4851
/// XCTest Location
4952
var xctestLocation: AbsolutePath? { get }
@@ -63,6 +66,10 @@ public extension ManifestResourceProvider {
6366
var swiftCompilerFlags: [String] {
6467
return []
6568
}
69+
70+
var swiftCompilerEnvironment: [String: String] {
71+
return ProcessEnv.vars
72+
}
6673

6774
var xctestLocation: AbsolutePath? {
6875
return nil
@@ -666,8 +673,8 @@ public final class ManifestLoader: ManifestLoaderProtocol {
666673
stream <<< packageIdentity
667674
stream <<< manifestContents
668675
stream <<< toolsVersion.description
669-
for key in env.keys.sorted(by: >) {
670-
stream <<< key <<< env[key]! // forced unwrap safe
676+
for (key, value) in env.sorted(by: { $0.key > $1.key }) {
677+
stream <<< key <<< value
671678
}
672679
stream <<< swiftpmVersion
673680
return stream.bytes.sha256Checksum
@@ -807,9 +814,11 @@ public final class ManifestLoader: ManifestLoaderProtocol {
807814
#endif
808815
let compiledManifestFile = tmpDir.appending(component: "\(packageIdentity)-manifest\(executableSuffix)")
809816
cmd += ["-o", compiledManifestFile.pathString]
817+
818+
let compilerEnv = resources.swiftCompilerEnvironment
810819

811820
// Compile the manifest.
812-
let compilerResult = try Process.popen(arguments: cmd)
821+
let compilerResult = try Process.popen(arguments: cmd, environment: compilerEnv)
813822
let compilerOutput = try (compilerResult.utf8Output() + compilerResult.utf8stderrOutput()).spm_chuzzle()
814823
manifestParseResult.compilerOutput = compilerOutput
815824

Sources/Workspace/DefaultPluginScriptRunner.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,10 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner {
114114
command += sources.paths.map { $0.pathString }
115115
let compiledExec = cacheDir.appending(component: "compiled-plugin")
116116
command += ["-o", compiledExec.pathString]
117+
118+
let compilerEnv = resources.swiftCompilerEnvironment
117119

118-
let result = try Process.popen(arguments: command)
120+
let result = try Process.popen(arguments: command, environment: compilerEnv)
119121
let output = try (result.utf8Output() + result.utf8stderrOutput()).spm_chuzzle() ?? ""
120122
if result.exitStatus != .terminated(code: 0) {
121123
// TODO: Make this a proper error.

Tests/PackageLoadingTests/PD5_0LoadingTests.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,4 +547,68 @@ class PackageDescription5_0LoadingTests: PackageDescriptionLoadingTests {
547547
XCTAssertEqual(manifest.dependencies, [])
548548
}
549549
}
550+
551+
func testManifestLoaderEnvironment() throws {
552+
try testWithTemporaryDirectory { path in
553+
let fs = localFileSystem
554+
555+
struct CustomManifestResources: ManifestResourceProvider {
556+
var swiftCompiler: AbsolutePath
557+
var libDir: AbsolutePath
558+
var binDir: AbsolutePath?
559+
var sdkRoot: AbsolutePath?
560+
var swiftCompilerEnvironment: [String: String]
561+
}
562+
563+
let packagePath = path.appending(component: "pkg")
564+
let manifestPath = packagePath.appending(component: "Package.swift")
565+
try fs.writeFileContents(manifestPath) { stream in
566+
stream <<< """
567+
// swift-tools-version:5
568+
import PackageDescription
569+
570+
let package = Package(
571+
name: "Trivial",
572+
targets: [
573+
.target(
574+
name: "foo",
575+
dependencies: []),
576+
]
577+
)
578+
"""
579+
}
580+
581+
let moduleTraceFilePath = path.appending(component: "swift-module-trace")
582+
var customManifestCompilerEnv = ProcessEnv.vars
583+
customManifestCompilerEnv["SWIFT_LOADED_MODULE_TRACE_FILE"] = moduleTraceFilePath.pathString
584+
let customResources = CustomManifestResources(
585+
swiftCompiler: Resources.default.swiftCompiler,
586+
libDir: Resources.default.libDir,
587+
binDir: Resources.default.binDir,
588+
sdkRoot: Resources.default.sdkRoot,
589+
swiftCompilerEnvironment: customManifestCompilerEnv)
590+
let manifestLoader = ManifestLoader(
591+
manifestResources: customResources,
592+
serializedDiagnostics: true,
593+
isManifestSandboxEnabled: false,
594+
cacheDir: nil)
595+
596+
let diagnostics = DiagnosticsEngine()
597+
let manifest = try manifestLoader.load(
598+
at: manifestPath.parentDirectory,
599+
packageKind: .local,
600+
packageLocation: manifestPath.pathString,
601+
toolsVersion: .v5,
602+
fileSystem: fs,
603+
diagnostics: diagnostics
604+
)
605+
606+
XCTAssertTrue(diagnostics.diagnostics.isEmpty)
607+
XCTAssertEqual(manifest.name, "Trivial")
608+
609+
if let moduleTraceJSON = try? localFileSystem.readFileContents(moduleTraceFilePath).validDescription {
610+
XCTAssert(moduleTraceJSON.contains("PackageDescription"))
611+
}
612+
}
613+
}
550614
}

0 commit comments

Comments
 (0)