Skip to content

Commit 566238b

Browse files
committed
[Perf] Cache process env
Cache ProcessEnv.vars because ProcessInfo.processInfo.environment is costly and we don't really mutate the env. <rdar://problem/53828756>
1 parent 1defcb0 commit 566238b

File tree

12 files changed

+37
-30
lines changed

12 files changed

+37
-30
lines changed

Sources/Basic/Process.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public final class Process: ObjectIdentifierProtocol {
159159
public let verbose: Bool
160160

161161
/// The current environment.
162+
@available(*, deprecated, message: "use ProcessEnv.vars instead")
162163
static public var env: [String: String] {
163164
return ProcessInfo.processInfo.environment
164165
}
@@ -233,7 +234,7 @@ public final class Process: ObjectIdentifierProtocol {
233234
/// continue running even if the parent is killed or interrupted. Default value is true.
234235
public init(
235236
arguments: [String],
236-
environment: [String: String] = env,
237+
environment: [String: String] = ProcessEnv.vars,
237238
outputRedirection: OutputRedirection = .collect,
238239
verbose: Bool = Process.verbose,
239240
startNewProcessGroup: Bool = true
@@ -256,7 +257,7 @@ public final class Process: ObjectIdentifierProtocol {
256257
}
257258
// FIXME: This can be cached.
258259
let envSearchPaths = getEnvSearchPaths(
259-
pathString: Process.env["PATH"],
260+
pathString: ProcessEnv.vars["PATH"],
260261
currentWorkingDirectory: localFileSystem.currentWorkingDirectory
261262
)
262263
// Lookup and cache the executable path.
@@ -545,14 +546,14 @@ extension Process {
545546
/// will be inherited.
546547
/// - Returns: The process result.
547548
@discardableResult
548-
static public func popen(arguments: [String], environment: [String: String] = env) throws -> ProcessResult {
549+
static public func popen(arguments: [String], environment: [String: String] = ProcessEnv.vars) throws -> ProcessResult {
549550
let process = Process(arguments: arguments, environment: environment, outputRedirection: .collect)
550551
try process.launch()
551552
return try process.waitUntilExit()
552553
}
553554

554555
@discardableResult
555-
static public func popen(args: String..., environment: [String: String] = env) throws -> ProcessResult {
556+
static public func popen(args: String..., environment: [String: String] = ProcessEnv.vars) throws -> ProcessResult {
556557
return try Process.popen(arguments: args, environment: environment)
557558
}
558559

@@ -564,7 +565,7 @@ extension Process {
564565
/// will be inherited.
565566
/// - Returns: The process output (stdout + stderr).
566567
@discardableResult
567-
static public func checkNonZeroExit(arguments: [String], environment: [String: String] = env) throws -> String {
568+
static public func checkNonZeroExit(arguments: [String], environment: [String: String] = ProcessEnv.vars) throws -> String {
568569
let process = Process(arguments: arguments, environment: environment, outputRedirection: .collect)
569570
try process.launch()
570571
let result = try process.waitUntilExit()
@@ -576,11 +577,11 @@ extension Process {
576577
}
577578

578579
@discardableResult
579-
static public func checkNonZeroExit(args: String..., environment: [String: String] = env) throws -> String {
580+
static public func checkNonZeroExit(args: String..., environment: [String: String] = ProcessEnv.vars) throws -> String {
580581
return try checkNonZeroExit(arguments: args, environment: environment)
581582
}
582583

583-
public convenience init(args: String..., environment: [String: String] = env, outputRedirection: OutputRedirection = .collect) {
584+
public convenience init(args: String..., environment: [String: String] = ProcessEnv.vars, outputRedirection: OutputRedirection = .collect) {
584585
self.init(arguments: args, environment: environment, outputRedirection: outputRedirection)
585586
}
586587
}

Sources/Basic/ProcessEnv.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ import SPMLibc
1515
public enum ProcessEnv {
1616

1717
/// Returns a dictionary containing the current environment.
18-
public static var vars: [String: String] {
19-
return ProcessInfo.processInfo.environment
18+
public static var vars: [String: String] { _vars }
19+
private static var _vars = ProcessInfo.processInfo.environment
20+
21+
/// Invalidate the cached env.
22+
public static func invalidateEnv() {
23+
_vars = ProcessInfo.processInfo.environment
2024
}
2125

2226
/// Set the given key and value in the process's environment.
@@ -34,6 +38,7 @@ public enum ProcessEnv {
3438
throw SystemError.setenv(errno, key)
3539
}
3640
#endif
41+
invalidateEnv()
3742
}
3843

3944
/// Unset the give key in the process's environment.
@@ -49,6 +54,7 @@ public enum ProcessEnv {
4954
throw SystemError.unsetenv(errno, key)
5055
}
5156
#endif
57+
invalidateEnv()
5258
}
5359

5460
/// The current working directory of the process.

Sources/Basic/TemporaryFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public func determineTempDirectory(_ dir: AbsolutePath? = nil) throws -> Absolut
5555
/// Returns temporary directory location by searching relevant env variables.
5656
/// Evaluates once per execution.
5757
private var cachedTempDirectory: AbsolutePath = {
58-
return AbsolutePath(Process.env["TMPDIR"] ?? Process.env["TEMP"] ?? Process.env["TMP"] ?? "/tmp/")
58+
return AbsolutePath(ProcessEnv.vars["TMPDIR"] ?? ProcessEnv.vars["TEMP"] ?? ProcessEnv.vars["TMP"] ?? "/tmp/")
5959
}()
6060

6161
/// This class is basically a wrapper over posix's mkstemps() function to creates disposable files.

Sources/Basic/TerminalController.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public final class TerminalController {
9494

9595
/// Computes the terminal type of the stream.
9696
public static func terminalType(_ stream: LocalFileOutputByteStream) -> TerminalType {
97-
if Process.env["TERM"] == "dumb" {
97+
if ProcessEnv.vars["TERM"] == "dumb" {
9898
return .dumb
9999
}
100100
let isTTY = isatty(fileno(stream.filePointer)) != 0
@@ -107,7 +107,7 @@ public final class TerminalController {
107107
/// - Returns: Current width of terminal if it was determinable.
108108
public static func terminalWidth() -> Int? {
109109
// Try to get from environment.
110-
if let columns = Process.env["COLUMNS"], let width = Int(columns) {
110+
if let columns = ProcessEnv.vars["COLUMNS"], let width = Int(columns) {
111111
return width
112112
}
113113

Sources/Build/BuildPlan.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public struct BuildParameters {
3030
fileprivate var moduleCache: AbsolutePath {
3131
// FIXME: We use this hack to let swiftpm's functional test use shared
3232
// cache so it doesn't become painfully slow.
33-
if let path = Process.env["SWIFTPM_TESTS_MODULECACHE"] {
33+
if let path = ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"] {
3434
return AbsolutePath(path)
3535
}
3636
return buildPath.appending(component: "ModuleCache")
@@ -324,7 +324,7 @@ public final class ClangTargetBuildDescription {
324324
//
325325
// This feature is not widely available in OSS clang. So, we only enable
326326
// index store for Apple's clang or if explicitly asked to.
327-
if Process.env.keys.contains("SWIFTPM_ENABLE_CLANG_INDEX_STORE") {
327+
if ProcessEnv.vars.keys.contains("SWIFTPM_ENABLE_CLANG_INDEX_STORE") {
328328
args += buildParameters.indexStoreArguments
329329
} else if buildParameters.triple.isDarwin(), (try? buildParameters.toolchain._isClangCompilerVendorApple()) == true {
330330
args += buildParameters.indexStoreArguments
@@ -525,7 +525,7 @@ public final class SwiftTargetBuildDescription {
525525

526526
// Add arguments to colorize output if stdout is tty
527527
if buildParameters.isTTY {
528-
if Process.env["SWIFTPM_USE_NEW_COLOR_DIAGNOSTICS"] != nil {
528+
if ProcessEnv.vars["SWIFTPM_USE_NEW_COLOR_DIAGNOSTICS"] != nil {
529529
args += ["-color-diagnostics"]
530530
} else {
531531
args += ["-Xfrontend", "-color-diagnostics"]

Sources/Commands/SwiftTestTool.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ final class ParallelTestRunner {
725725
self.numJobs = numJobs
726726
self.diagnostics = diagnostics
727727

728-
if Process.env["SWIFTPM_TEST_RUNNER_PROGRESS_BAR"] == "lit" {
728+
if ProcessEnv.vars["SWIFTPM_TEST_RUNNER_PROGRESS_BAR"] == "lit" {
729729
progressAnimation = PercentProgressAnimation(stream: stdoutStream, header: "Testing:")
730730
} else {
731731
progressAnimation = NinjaProgressAnimation(stream: stdoutStream)
@@ -965,7 +965,7 @@ fileprivate func constructTestEnvironment(
965965
options: ToolOptions,
966966
buildParameters: BuildParameters
967967
) throws -> [String: String] {
968-
var env = Process.env
968+
var env = ProcessEnv.vars
969969

970970
// Add the code coverage related variables.
971971
if options.shouldEnableCodeCoverage {

Sources/Commands/SwiftTool.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ public class SwiftTool<Options: ToolOptions> {
417417

418418
func configFilePath() throws -> AbsolutePath {
419419
// Look for the override in the environment.
420-
if let envPath = Process.env["SWIFTPM_MIRROR_CONFIG"] {
420+
if let envPath = ProcessEnv.vars["SWIFTPM_MIRROR_CONFIG"] {
421421
return try AbsolutePath(validating: envPath)
422422
}
423423

@@ -753,8 +753,8 @@ private func findPackageRoot() -> AbsolutePath? {
753753
/// Returns the build path from the environment, if present.
754754
private func getEnvBuildPath(workingDir: AbsolutePath) -> AbsolutePath? {
755755
// Don't rely on build path from env for SwiftPM's own tests.
756-
guard Process.env["SWIFTPM_TESTS_MODULECACHE"] == nil else { return nil }
757-
guard let env = Process.env["SWIFTPM_BUILD_DIR"] else { return nil }
756+
guard ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"] == nil else { return nil }
757+
guard let env = ProcessEnv.vars["SWIFTPM_BUILD_DIR"] else { return nil }
758758
return AbsolutePath(env, relativeTo: workingDir)
759759
}
760760

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ public final class ManifestLoader: ManifestLoaderProtocol {
404404

405405
// FIXME: Workaround for the module cache bug that's been haunting Swift CI
406406
// <rdar://problem/48443680>
407-
let moduleCachePath = Process.env["SWIFTPM_MODULECACHE_OVERRIDE"] ?? Process.env["SWIFTPM_TESTS_MODULECACHE"]
407+
let moduleCachePath = ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"]
408408

409409
var cmd = [String]()
410410
#if os(macOS)

Sources/SPMUtility/PkgConfig.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public struct PkgConfig {
166166
}
167167

168168
private static var envSearchPaths: [AbsolutePath] {
169-
if let configPath = Process.env["PKG_CONFIG_PATH"] {
169+
if let configPath = ProcessEnv.vars["PKG_CONFIG_PATH"] {
170170
return configPath.split(separator: ":").map({ AbsolutePath(String($0)) })
171171
}
172172
return []

Sources/TestSupport/misc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ public func loadPackageGraph(
239239
/// from different threads, the environment will neither be setup nor restored
240240
/// correctly.
241241
public func withCustomEnv(_ env: [String: String], body: () throws -> Void) throws {
242-
let state = Array(env.keys).map({ ($0, Process.env[$0]) })
242+
let state = Array(env.keys).map({ ($0, ProcessEnv.vars[$0]) })
243243
let restore = {
244244
for (key, value) in state {
245245
if let value = value {

Sources/Workspace/Destination.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public struct Destination {
8181
public static func hostDestination(
8282
_ binDir: AbsolutePath? = nil,
8383
originalWorkingDirectory: AbsolutePath? = localFileSystem.currentWorkingDirectory,
84-
environment: [String:String] = Process.env
84+
environment: [String:String] = ProcessEnv.vars
8585
) throws -> Destination {
8686
// Select the correct binDir.
8787
var customBinDir: AbsolutePath?
@@ -94,7 +94,7 @@ public struct Destination {
9494
#if os(macOS)
9595
// Get the SDK.
9696
let sdkPath: AbsolutePath
97-
if let value = lookupExecutablePath(filename: Process.env["SDKROOT"]) {
97+
if let value = lookupExecutablePath(filename: ProcessEnv.vars["SDKROOT"]) {
9898
sdkPath = value
9999
} else {
100100
// No value in env, so search for it.
@@ -131,7 +131,7 @@ public struct Destination {
131131
}
132132

133133
/// Returns macosx sdk platform framework path.
134-
public static func sdkPlatformFrameworkPath(environment: [String:String] = Process.env) -> AbsolutePath? {
134+
public static func sdkPlatformFrameworkPath(environment: [String:String] = ProcessEnv.vars) -> AbsolutePath? {
135135
if let path = _sdkPlatformFrameworkPath {
136136
return path
137137
}

Sources/Workspace/UserToolchain.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public final class UserToolchain: Toolchain {
112112
}
113113

114114
private static func lookup(variable: String, searchPaths: [AbsolutePath]) -> AbsolutePath? {
115-
return lookupExecutablePath(filename: Process.env[variable], searchPaths: searchPaths)
115+
return lookupExecutablePath(filename: ProcessEnv.vars[variable], searchPaths: searchPaths)
116116
}
117117

118118
/// Environment to use when looking up tools.
@@ -180,13 +180,13 @@ public final class UserToolchain: Toolchain {
180180
return toolPath
181181
}
182182

183-
public init(destination: Destination, environment: [String: String] = Process.env) throws {
183+
public init(destination: Destination, environment: [String: String] = ProcessEnv.vars) throws {
184184
self.destination = destination
185185
self.processEnvironment = environment
186186

187187
// Get the search paths from PATH.
188188
let searchPaths = getEnvSearchPaths(
189-
pathString: Process.env["PATH"], currentWorkingDirectory: localFileSystem.currentWorkingDirectory)
189+
pathString: ProcessEnv.vars["PATH"], currentWorkingDirectory: localFileSystem.currentWorkingDirectory)
190190

191191
self.envSearchPaths = searchPaths
192192

@@ -218,7 +218,7 @@ public final class UserToolchain: Toolchain {
218218
var pdLibDir = UserManifestResources.libDir(forBinDir: binDir)
219219

220220
// Look for an override in the env.
221-
if let pdLibDirEnvStr = Process.env["SWIFTPM_PD_LIBS"] {
221+
if let pdLibDirEnvStr = ProcessEnv.vars["SWIFTPM_PD_LIBS"] {
222222
// We pick the first path which exists in a colon seperated list.
223223
let paths = pdLibDirEnvStr.split(separator: ":").map(String.init)
224224
for pathString in paths {

0 commit comments

Comments
 (0)