Skip to content

Commit 2ea136b

Browse files
authored
Add PackageDescription.Context API (#3794)
* Add Package.Context This struct contains context information available to a package manifest at runtime. Initially, it contains the packageRoot : URL property. * Serialize a Package.Context, and pass to manifest on command line * Rename PackageContext -> Context, add ContextModel ContextModel is the value type that gets serialized and wrapped by PackageDescription.Context. ContextModel copied into PackageDescription by symbolic link PackageModel.PackageContext. Add .environment property to PackageDescription.Context. * Implement ManifestLoaderDelegate, capture path to parsed manifest * Test PackageDescription.Context * Add .testPackageContextDirectory() This checks that the directory contents is accessible. * Switch from usleep() to Thread.sleep() usleep() isn't portable. * Add initial documentation comments * Change all methods to be static No need for a singleton instance. * Update unit test to use Context.packageDirectory * Fix bootstrap build Add Context.swift, ContextModel.swift to CMakeLists.txt * Make unit test more robust Return comma separated list of Temporary*.swift
1 parent e0e1810 commit 2ea136b

File tree

10 files changed

+147
-6
lines changed

10 files changed

+147
-6
lines changed

Sources/PackageDescription/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
add_library(PackageDescription
1010
BuildSettings.swift
11+
Context.swift
12+
ContextModel.swift
1113
LanguageStandardSettings.swift
1214
PackageDescription.swift
1315
PackageDescriptionSerialization.swift
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2018 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
/// The context a Swift package is running in. This encapsulates states that are known at build-time.
12+
/// For example where in the file system the current package resides.
13+
@available(_PackageDescription, introduced: 5.6)
14+
public struct Context {
15+
private static let model = ContextModel.decode()
16+
17+
/// The directory containing Package.swift.
18+
public static var packageDirectory : String {
19+
model.packageDirectory
20+
}
21+
22+
/// Snapshot of the system environment variables.
23+
public static var environment : [String : String] {
24+
model.environment
25+
}
26+
27+
private init() {
28+
}
29+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../PackageModel/ContextModel.swift

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,11 @@ public final class ManifestLoader: ManifestLoaderProtocol {
819819
#else
820820
cmd += ["-fileno", "\(fileno(jsonOutputFileDesc))"]
821821
#endif
822+
823+
let packageDirectory = manifestPath.parentDirectory.pathString
824+
let contextModel = ContextModel(packageDirectory: packageDirectory)
825+
cmd += ["-context", contextModel.encoded]
826+
822827
// If enabled, run command in a sandbox.
823828
// This provides some safety against arbitrary code execution when parsing manifest files.
824829
// We only allow the permissions which are absolutely necessary.

Sources/PackageModel/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_library(PackageModel
1010
BuildConfiguration.swift
1111
BuildEnvironment.swift
1212
BuildSettings.swift
13+
ContextModel.swift
1314
Diagnostics.swift
1415
Manifest.swift
1516
Manifest/PackageConditionDescription.swift
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import class Foundation.JSONDecoder
12+
import class Foundation.JSONEncoder
13+
import class Foundation.ProcessInfo
14+
15+
public struct ContextModel {
16+
public let packageDirectory : String
17+
18+
public init(packageDirectory : String) {
19+
self.packageDirectory = packageDirectory
20+
}
21+
22+
public var environment : [String : String] {
23+
ProcessInfo.processInfo.environment
24+
}
25+
}
26+
27+
extension ContextModel : Codable {
28+
private enum CodingKeys: CodingKey {
29+
case packageDirectory
30+
}
31+
32+
public init(from decoder: Decoder) throws {
33+
let container = try decoder.container(keyedBy: CodingKeys.self)
34+
self.packageDirectory = try container.decode(String.self, forKey: .packageDirectory)
35+
}
36+
37+
public func encode(to encoder: Encoder) throws {
38+
var container = encoder.container(keyedBy: CodingKeys.self)
39+
try container.encode(packageDirectory, forKey: .packageDirectory)
40+
}
41+
42+
public var encoded : String {
43+
let encoder = JSONEncoder()
44+
let data = try! encoder.encode(self)
45+
return String(data: data, encoding: .utf8)!
46+
}
47+
48+
public static func decode() -> ContextModel {
49+
// TODO: Look at ProcessInfo.processInfo
50+
var args = Array(ProcessInfo.processInfo.arguments[1...]).makeIterator()
51+
while let arg = args.next() {
52+
if arg == "-context", let json = args.next() {
53+
let decoder = JSONDecoder()
54+
let data = json.data(using: .utf8)!
55+
return (try! decoder.decode(ContextModel.self, from: data))
56+
}
57+
}
58+
fatalError("Could not decode ContextModel parameter.")
59+
}
60+
}

Tests/BasicsTests/ConcurrencyHelpersTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class ConcurrencyHelpersTest: XCTestCase {
2626
let cache = ThreadSafeKeyValueStore<Int, Int>()
2727
for index in 0 ..< 1000 {
2828
self.queue.async(group: sync) {
29-
usleep(UInt32.random(in: 100 ... 300))
29+
Thread.sleep(forTimeInterval: Double.random(in: 100 ... 300) * 1.0e-6)
3030
let value = Int.random(in: Int.min ..< Int.max)
3131
lock.withLock {
3232
expected[index] = value
@@ -61,7 +61,7 @@ final class ConcurrencyHelpersTest: XCTestCase {
6161
let cache = ThreadSafeArrayStore<Int>()
6262
for _ in 0 ..< 1000 {
6363
self.queue.async(group: sync) {
64-
usleep(UInt32.random(in: 100 ... 300))
64+
Thread.sleep(forTimeInterval: Double.random(in: 100 ... 300) * 1.0e-6)
6565
let value = Int.random(in: Int.min ..< Int.max)
6666
lock.withLock {
6767
expected.append(value)
@@ -93,7 +93,7 @@ final class ConcurrencyHelpersTest: XCTestCase {
9393
let cache = ThreadSafeBox<Int>()
9494
for index in 0 ..< 1000 {
9595
self.queue.async(group: sync) {
96-
usleep(UInt32.random(in: 100 ... 300))
96+
Thread.sleep(forTimeInterval: Double.random(in: 100 ... 300) * 1.0e-6)
9797
serial.async(group: sync) {
9898
lock.withLock {
9999
if winner == nil {

Tests/PackageCollectionsTests/TrieTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ class TrieTests: XCTestCase {
203203

204204
for i in 0 ..< docCount {
205205
queue.async(group: sync) {
206-
usleep(UInt32.random(in: 100 ... 300))
206+
Thread.sleep(forTimeInterval: Double.random(in: 100 ... 300) * 1.0e-6)
207207

208208
trie.remove { $0 == i }
209209
trie.insert(word: "word-\(i)", foundIn: i)

Tests/PackageLoadingTests/PDLoadingTests.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@ import TSCBasic
1616
import TSCUtility
1717
import XCTest
1818

19-
class PackageDescriptionLoadingTests: XCTestCase {
20-
let manifestLoader = ManifestLoader(toolchain: ToolchainConfiguration.default)
19+
class PackageDescriptionLoadingTests: XCTestCase, ManifestLoaderDelegate {
20+
lazy var manifestLoader = ManifestLoader(toolchain: ToolchainConfiguration.default, delegate: self)
21+
var parsedManifest : AbsolutePath?
22+
23+
public func willLoad(manifest: AbsolutePath) {
24+
}
25+
26+
public func willParse(manifest: AbsolutePath) {
27+
parsedManifest = manifest
28+
}
2129

2230
var toolsVersion: ToolsVersion {
2331
fatalError("implement in subclass")

Tests/PackageLoadingTests/PD_5_6_LoadingTests.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,39 @@ class PackageDescription5_6LoadingTests: PackageDescriptionLoadingTests {
153153
}
154154

155155
}
156+
157+
/// Tests use of Context.current.packageDirectory
158+
func testPackageContextName() throws {
159+
let stream = BufferedOutputByteStream()
160+
stream <<< """
161+
import PackageDescription
162+
let package = Package(name: Context.packageDirectory)
163+
"""
164+
165+
loadManifest(stream.bytes) { manifest in
166+
let name = parsedManifest?.parentDirectory.pathString ?? ""
167+
XCTAssertEqual(manifest.name, name)
168+
}
169+
}
170+
171+
/// Tests access to the package's directory contents.
172+
func testPackageContextDirectory() throws {
173+
let stream = BufferedOutputByteStream()
174+
stream <<< """
175+
import PackageDescription
176+
import Foundation
177+
178+
let fileManager = FileManager.default
179+
let contents = (try? fileManager.contentsOfDirectory(atPath: Context.packageDirectory)) ?? []
180+
let swiftFiles = contents.filter { $0.hasPrefix("TemporaryFile") && $0.hasSuffix(".swift") }
181+
182+
let package = Package(name: swiftFiles.joined(separator: ","))
183+
"""
184+
185+
loadManifest(stream.bytes) { manifest in
186+
let name = parsedManifest?.components.last ?? ""
187+
let swiftFiles = manifest.name.split(separator: ",").map(String.init)
188+
XCTAssertNotNil(swiftFiles.firstIndex(of: name))
189+
}
190+
}
156191
}

0 commit comments

Comments
 (0)