Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some warnings with ProcessEnvironmentBlock #7364

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion Package.swift
Expand Up @@ -351,7 +351,7 @@ let package = Package(
/** Support for building using Xcode's build system */
name: "XCBuildSupport",
dependencies: ["DriverSupport", "SPMBuildCore", "PackageGraph"],
exclude: ["CMakeLists.txt", "CODEOWNERS"]
exclude: ["CODEOWNERS"]
),
.target(
/** High level functionality */
Expand Down
1 change: 1 addition & 0 deletions Sources/Basics/CMakeLists.txt
Expand Up @@ -34,6 +34,7 @@ add_library(Basics
FileSystem/TemporaryFile.swift
FileSystem/TSCAdapters.swift
FileSystem/VFSOverlay.swift
Git.swift
SourceControlURL.swift
HTTPClient/HTTPClient.swift
HTTPClient/HTTPClientConfiguration.swift
Expand Down
2 changes: 1 addition & 1 deletion Sources/Basics/Concurrency/ConcurrencyHelpers.swift
Expand Up @@ -20,7 +20,7 @@ import func TSCBasic.tsc_await

public enum Concurrency {
public static var maxOperations: Int {
ProcessEnv.vars["SWIFTPM_MAX_CONCURRENT_OPERATIONS"].flatMap(Int.init) ?? ProcessInfo.processInfo
ProcessEnv.block["SWIFTPM_MAX_CONCURRENT_OPERATIONS"].flatMap(Int.init) ?? ProcessInfo.processInfo
.activeProcessorCount
}
}
Expand Down
30 changes: 14 additions & 16 deletions Sources/Basics/EnvironmentVariables.swift
Expand Up @@ -11,35 +11,38 @@
//===----------------------------------------------------------------------===//

import class Foundation.ProcessInfo
import typealias TSCBasic.ProcessEnvironmentBlock
import struct TSCBasic.ProcessEnvironmentKey
import enum TSCBasic.ProcessEnv

public typealias EnvironmentVariables = [String: String]
public typealias EnvironmentVariables = ProcessEnvironmentBlock
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to worry about this accidentally causing build issues?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we expose it anywhere outside of SwiftPM to any clients, at least not on purpose. IIUC corresponding TSC APIs were widely adopted instead. As soon as package support is merged in my other PR, I'll try using package visibility here to prevent any unintended adoption of this typealias in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use SPI in the meantime, would also make it easy to remember all the places we want package 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And can now use package.


extension EnvironmentVariables {
extension ProcessEnvironmentBlock {
public static func empty() -> EnvironmentVariables {
[:]
}

public static func process() -> EnvironmentVariables {
ProcessInfo.processInfo.environment
ProcessEnv.block
}

public mutating func prependPath(_ key: String, value: String) {
public mutating func prependPath(_ key: ProcessEnvironmentKey, value: String) {
var values = value.isEmpty ? [] : [value]
if let existing = self[key], !existing.isEmpty {
values.append(existing)
}
self.setPath(key, values)
}

public mutating func appendPath(_ key: String, value: String) {
public mutating func appendPath(_ key: ProcessEnvironmentKey, value: String) {
var values = value.isEmpty ? [] : [value]
if let existing = self[key], !existing.isEmpty {
values.insert(existing, at: 0)
}
self.setPath(key, values)
}

private mutating func setPath(_ key: String, _ values: [String]) {
private mutating func setPath(_ key: ProcessEnvironmentKey, _ values: [String]) {
#if os(Windows)
let delimiter = ";"
#else
Expand All @@ -49,22 +52,17 @@ extension EnvironmentVariables {
}

/// `PATH` variable in the process's environment (`Path` under Windows).
public var path: String? {
#if os(Windows)
let pathArg = "Path"
#else
let pathArg = "PATH"
#endif
return self[pathArg]
package var path: String? {
ProcessEnv.path
}
}

// filter env variable that should not be included in a cache as they change
// often and should not be considered in business logic
// rdar://107029374
extension EnvironmentVariables {
extension ProcessEnvironmentBlock {
// internal for testing
static let nonCachableKeys: Set<String> = [
static let nonCachableKeys: Set<ProcessEnvironmentKey> = [
"TERM",
"TERM_PROGRAM",
"TERM_PROGRAM_VERSION",
Expand All @@ -82,7 +80,7 @@ extension EnvironmentVariables {
"SSH_AUTH_SOCK",
]

public var cachable: EnvironmentVariables {
package var cachable: EnvironmentVariables {
return self.filter { !Self.nonCachableKeys.contains($0.key) }
}
}
65 changes: 65 additions & 0 deletions Sources/Basics/Git.swift
@@ -0,0 +1,65 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import class Foundation.ProcessInfo
import TSCBasic

package enum Git {
/// A shell command to run for Git. Can be either a name or a path.
/// - Note: modification is not thread safe, designed for testing only
package static var tool: String = "git\(executableFileSuffix)"

/// Returns true if the git reference name is well formed.
package static func checkRefFormat(ref: String) -> Bool {
do {
let result = try Process.popen(args: tool, "check-ref-format", "--allow-onelevel", ref)
return result.exitStatus == .terminated(code: 0)
} catch {
return false
}
}

private static var _gitEnvironment = ProcessEnv.block

/// Returns the environment variables for launching the git subprocess.
///
/// This contains the current environment with custom overrides for using
/// git from swift build.
/// - Note: modification is not thread safe, designed for testing only
package static var environmentBlock: ProcessEnvironmentBlock {
get {
var env = Self._gitEnvironment

// These variables are inserted into the environment when shelling out
// to git if not already present.
let underrideVariables: ProcessEnvironmentBlock = [
// Disable terminal prompts in git. This will make git error out and return
// when it needs a user/pass etc instead of hanging the terminal (SR-3981).
"GIT_TERMINAL_PROMPT": "0",

// The above is env variable is not enough. However, ssh_config's batch
// mode is made for this purpose. see: https://linux.die.net/man/5/ssh_config
"GIT_SSH_COMMAND": "ssh -oBatchMode=yes",
]

for (key, value) in underrideVariables {
// Skip this key is already present in the env.
if env.keys.contains(key) { continue }

env[key] = value
}

return env
}
set {
Self._gitEnvironment = newValue
}
}
}
6 changes: 3 additions & 3 deletions Sources/Basics/ImportScanning.swift
Expand Up @@ -26,12 +26,12 @@ public protocol ImportScanner {
func scanImports(_ filePathToScan: AbsolutePath) async throws -> [String]
}

public struct SwiftcImportScanner: ImportScanner {
package struct SwiftcImportScanner: ImportScanner {
private let swiftCompilerEnvironment: EnvironmentVariables
private let swiftCompilerFlags: [String]
private let swiftCompilerPath: AbsolutePath

public init(
package init(
swiftCompilerEnvironment: EnvironmentVariables,
swiftCompilerFlags: [String],
swiftCompilerPath: AbsolutePath
Expand All @@ -46,7 +46,7 @@ public struct SwiftcImportScanner: ImportScanner {
filePathToScan.pathString,
"-scan-dependencies", "-Xfrontend", "-import-prescan"] + self.swiftCompilerFlags

let result = try await TSCBasic.Process.popen(arguments: cmd, environment: self.swiftCompilerEnvironment)
let result = try await TSCBasic.Process.popen(arguments: cmd, environmentBlock: self.swiftCompilerEnvironment)

let stdout = try result.utf8Output()
return try JSONDecoder.makeWithDefaults().decode(Imports.self, from: stdout).imports
Expand Down
28 changes: 14 additions & 14 deletions Sources/Build/BuildDescription/ClangTargetBuildDescription.swift
Expand Up @@ -22,17 +22,17 @@ import struct SPMBuildCore.PrebuildCommandResult
import enum TSCBasic.ProcessEnv

/// Target description for a Clang target i.e. C language family target.
public final class ClangTargetBuildDescription {
package final class ClangTargetBuildDescription {
/// The target described by this target.
public let target: ResolvedTarget
package let target: ResolvedTarget

/// The underlying clang target.
public let clangTarget: ClangTarget
package let clangTarget: ClangTarget

/// The tools version of the package that declared the target. This can
/// can be used to conditionalize semantically significant changes in how
/// a target is built.
public let toolsVersion: ToolsVersion
package let toolsVersion: ToolsVersion

/// The build parameters.
let buildParameters: BuildParameters
Expand All @@ -43,7 +43,7 @@ public final class ClangTargetBuildDescription {
}

/// The list of all resource files in the target, including the derived ones.
public var resources: [Resource] {
package var resources: [Resource] {
self.target.underlying.resources + self.pluginDerivedResources
}

Expand All @@ -61,7 +61,7 @@ public final class ClangTargetBuildDescription {
}

/// The modulemap file for this target, if any.
public private(set) var moduleMap: AbsolutePath?
package private(set) var moduleMap: AbsolutePath?

/// Path to the temporary directory for this target.
var tempsPath: AbsolutePath
Expand All @@ -78,13 +78,13 @@ public final class ClangTargetBuildDescription {
private var pluginDerivedResources: [Resource]

/// Path to the resource accessor header file, if generated.
public private(set) var resourceAccessorHeaderFile: AbsolutePath?
package private(set) var resourceAccessorHeaderFile: AbsolutePath?

/// Path to the resource Info.plist file, if generated.
public private(set) var resourceBundleInfoPlistPath: AbsolutePath?
package private(set) var resourceBundleInfoPlistPath: AbsolutePath?

/// The objects in this target.
public var objects: [AbsolutePath] {
package var objects: [AbsolutePath] {
get throws {
try compilePaths().map(\.object)
}
Expand All @@ -100,12 +100,12 @@ public final class ClangTargetBuildDescription {
private let fileSystem: FileSystem

/// If this target is a test target.
public var isTestTarget: Bool {
package var isTestTarget: Bool {
target.type == .test
}

/// The results of applying any build tool plugins to this target.
public let buildToolPluginInvocationResults: [BuildToolPluginInvocationResult]
package let buildToolPluginInvocationResults: [BuildToolPluginInvocationResult]

/// Create a new target description with target and build parameters.
init(
Expand Down Expand Up @@ -182,7 +182,7 @@ public final class ClangTargetBuildDescription {
}

/// An array of tuples containing filename, source, object and dependency path for each of the source in this target.
public func compilePaths()
package func compilePaths()
throws -> [(filename: RelativePath, source: AbsolutePath, object: AbsolutePath, deps: AbsolutePath)]
{
let sources = [
Expand All @@ -206,7 +206,7 @@ public final class ClangTargetBuildDescription {
/// NOTE: The parameter to specify whether to get C++ semantics is currently optional, but this is only for revlock
/// avoidance with clients. Callers should always specify what they want based either the user's indication or on a
/// default value (possibly based on the filename suffix).
public func basicArguments(
package func basicArguments(
isCXX isCXXOverride: Bool? = .none,
isC: Bool = false
) throws -> [String] {
Expand Down Expand Up @@ -308,7 +308,7 @@ public final class ClangTargetBuildDescription {
return args
}

public func emitCommandLine(for filePath: AbsolutePath) throws -> [String] {
package func emitCommandLine(for filePath: AbsolutePath) throws -> [String] {
let standards = [
(clangTarget.cxxLanguageStandard, SupportedLanguageExtension.cppExtensions),
(clangTarget.cLanguageStandard, SupportedLanguageExtension.cExtensions),
Expand Down
12 changes: 6 additions & 6 deletions Sources/Build/BuildDescription/PluginDescription.swift
Expand Up @@ -21,24 +21,24 @@ import protocol Basics.FileSystem
/// But because the package graph and build plan are not loaded for incremental
/// builds, this information is included in the BuildDescription, and the plugin
/// targets are compiled directly.
public final class PluginDescription: Codable {
package final class PluginDescription: Codable {
/// The identity of the package in which the plugin is defined.
public let package: PackageIdentity
package let package: PackageIdentity

/// The name of the plugin target in that package (this is also the name of
/// the plugin).
public let targetName: String
package let targetName: String

/// The names of any plugin products in that package that vend the plugin
/// to other packages.
public let productNames: [String]
package let productNames: [String]

/// The tools version of the package that declared the target. This affects
/// the API that is available in the PackagePlugin module.
public let toolsVersion: ToolsVersion
package let toolsVersion: ToolsVersion

/// Swift source files that comprise the plugin.
public let sources: Sources
package let sources: Sources

/// Initialize a new plugin target description. The target is expected to be
/// a `PluginTarget`.
Expand Down
18 changes: 9 additions & 9 deletions Sources/Build/BuildDescription/ProductBuildDescription.swift
Expand Up @@ -21,25 +21,25 @@ import SPMBuildCore
import struct TSCBasic.SortedArray

/// The build description for a product.
public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription {
package final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription {
/// The reference to the product.
public let package: ResolvedPackage
package let package: ResolvedPackage

/// The reference to the product.
public let product: ResolvedProduct
package let product: ResolvedProduct

/// The tools version of the package that declared the product. This can
/// can be used to conditionalize semantically significant changes in how
/// a target is built.
public let toolsVersion: ToolsVersion
package let toolsVersion: ToolsVersion

/// The build parameters.
public let buildParameters: BuildParameters
package let buildParameters: BuildParameters

/// All object files to link into this product.
///
// Computed during build planning.
public internal(set) var objects = SortedArray<AbsolutePath>()
package internal(set) var objects = SortedArray<AbsolutePath>()

/// The dynamic libraries this product needs to link with.
// Computed during build planning.
Expand Down Expand Up @@ -128,7 +128,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription
}

/// The arguments to the librarian to create a static library.
public func archiveArguments() throws -> [String] {
package func archiveArguments() throws -> [String] {
let librarian = self.buildParameters.toolchain.librarianPath.pathString
let triple = self.buildParameters.triple
if triple.isWindows(), librarian.hasSuffix("link") || librarian.hasSuffix("link.exe") {
Expand All @@ -141,7 +141,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription
}

/// The arguments to link and create this product.
public func linkArguments() throws -> [String] {
package func linkArguments() throws -> [String] {
var args = [buildParameters.toolchain.swiftCompilerPath.pathString]
args += self.buildParameters.sanitizers.linkSwiftFlags()
args += self.additionalFlags
Expand Down Expand Up @@ -390,7 +390,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription
}

extension SortedArray where Element == AbsolutePath {
public static func +=<S: Sequence>(lhs: inout SortedArray, rhs: S) where S.Iterator.Element == AbsolutePath {
package static func +=<S: Sequence>(lhs: inout SortedArray, rhs: S) where S.Iterator.Element == AbsolutePath {
lhs.insert(contentsOf: rhs)
}
}
Expand Down