Skip to content

Commit 2222764

Browse files
committed
Initial support for declaring package extension targets and defining their capability.
This is guarded by a feature flag until the proposal is accepted and any requested changes have been implemented. Until then, extensions can be enabled by setting SWIFTPM_ENABLE_EXTENSION_TARGETS=1 in the environment. Package extension targets don't yet do anything in this commit — that will come in future commits. But they can be declared in manifests, and they are present in the project model. They show up in generated manifest sources and in package descriptions. This commit includes a test fixture that will also be expanded on and used in future unit tests.
1 parent 11eac26 commit 2222764

File tree

26 files changed

+679
-28
lines changed

26 files changed

+679
-28
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// swift-tools-version: 999.0
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "MySourceGenExtension",
6+
products: [
7+
// The product that vends MySourceGenExt to client packages.
8+
// .extension(
9+
// name: "MySourceGenExt",
10+
// target: "MySourceGenExt"
11+
// )
12+
],
13+
targets: [
14+
// The target that implements the extension and generates commands to invoke MySourceGenTool.
15+
.extension(
16+
name: "MySourceGenExt",
17+
capability: .prebuild(),
18+
dependencies: [
19+
"MySourceGenTool"
20+
]
21+
),
22+
// The command line tool that generates source files.
23+
.executableTarget(
24+
name: "MySourceGenTool",
25+
dependencies: [
26+
"MySourceGenToolLib"
27+
]
28+
),
29+
// A library used by MySourceGenTool (not the client).
30+
.executableTarget(
31+
name: "MySourceGenToolLib"
32+
),
33+
// A runtime library that the client needs to link against.
34+
.target(
35+
name: "MySourceGenRuntimeLib"
36+
),
37+
// A local tool that uses the extension.
38+
.executableTarget(
39+
name: "MyLocalTool",
40+
dependencies: [
41+
"MySourceGenExt"
42+
]
43+
),
44+
// Unit tests for the extension.
45+
.testTarget(
46+
name: "MySourceGenExtTests",
47+
dependencies: [
48+
"MySourceGenExt",
49+
"MySourceGenRuntimeLib"
50+
]
51+
)
52+
]
53+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print("Exec: \\(name)")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import PackageExtension
2+
3+
print("Hello MySourceGenExt")
4+
print(targetBuildContext)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public func GetLibraryName() -> String {
2+
return "MySourceGenRuntimeLib"
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print("Hello MySourceGenTool")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public func GetLibraryName() -> String {
2+
return "MySourceGenToolLib"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import XCTest
2+
import class Foundation.Bundle
3+
4+
final class SwiftyProtobufTests: XCTestCase {
5+
func testExample() throws {
6+
// This is an example of a functional test case.
7+
// Use XCTAssert and related functions to verify your tests produce the correct
8+
// results.
9+
10+
// Some of the APIs that we use below are available in macOS 10.13 and above.
11+
guard #available(macOS 10.13, *) else {
12+
return
13+
}
14+
15+
// Mac Catalyst won't have `Process`, but it is supported for executables.
16+
#if !targetEnvironment(macCatalyst)
17+
18+
let fooBinary = productsDirectory.appendingPathComponent("MySourceGenTool")
19+
20+
let process = Process()
21+
process.executableURL = fooBinary
22+
23+
let pipe = Pipe()
24+
process.standardOutput = pipe
25+
26+
try process.run()
27+
process.waitUntilExit()
28+
29+
let data = pipe.fileHandleForReading.readDataToEndOfFile()
30+
let output = String(data: data, encoding: .utf8)
31+
32+
XCTAssertEqual(output, "Hello, world!\n")
33+
#endif
34+
}
35+
36+
/// Returns path to the built products directory.
37+
var productsDirectory: URL {
38+
#if os(macOS)
39+
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
40+
return bundle.bundleURL.deletingLastPathComponent()
41+
}
42+
fatalError("couldn't find the products directory")
43+
#else
44+
return Bundle.main.bundleURL
45+
#endif
46+
}
47+
}

Sources/Build/BuildPlan.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright 2015 - 2019 Apple Inc. and the Swift project authors
4+
Copyright 2015 - 2021 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -702,7 +702,7 @@ public final class SwiftTargetBuildDescription {
702702
case .library, .test:
703703
result.append("-parse-as-library")
704704

705-
case .executable, .systemModule, .binary:
705+
case .executable, .systemModule, .binary, .extension:
706706
do { }
707707
}
708708

@@ -1360,7 +1360,7 @@ public class BuildPlan {
13601360
buildParameters: buildParameters,
13611361
fileSystem: fileSystem,
13621362
diagnostics: diagnostics))
1363-
case is SystemLibraryTarget, is BinaryTarget:
1363+
case is SystemLibraryTarget, is BinaryTarget, is ExtensionTarget:
13641364
break
13651365
default:
13661366
fatalError("unhandled \(target.underlyingTarget)")
@@ -1595,6 +1595,8 @@ public class BuildPlan {
15951595
if let library = xcFrameworkLibrary(for: binaryTarget) {
15961596
libraryBinaryPaths.insert(library.binaryPath)
15971597
}
1598+
case .extension:
1599+
continue
15981600
}
15991601

16001602
case .product(let product, _):

Sources/Commands/Describe.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
4+
Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -132,12 +132,29 @@ fileprivate struct DescribedPackage: Encodable {
132132
}
133133
}
134134

135+
/// Represents a extension capability for the sole purpose of generating a description.
136+
struct DescribedExtensionCapability: Encodable {
137+
let type: String
138+
139+
init(from capability: ExtensionCapability, in package: Package) {
140+
switch capability {
141+
case .prebuild:
142+
self.type = "prebuild"
143+
case .buildTool:
144+
self.type = "buildTool"
145+
case .postbuild:
146+
self.type = "postbuild"
147+
}
148+
}
149+
}
150+
135151
/// Represents a target for the sole purpose of generating a description.
136152
struct DescribedTarget: Encodable {
137153
let name: String
138154
let type: String
139155
let c99name: String?
140156
let moduleType: String?
157+
let extensionCapability: DescribedExtensionCapability?
141158
let path: String
142159
let sources: [String]
143160
let resources: [PackageModel.Resource]?
@@ -149,6 +166,7 @@ fileprivate struct DescribedPackage: Encodable {
149166
self.type = target.type.rawValue
150167
self.c99name = target.c99name
151168
self.moduleType = String(describing: Swift.type(of: target))
169+
self.extensionCapability = (target as? ExtensionTarget).map{ DescribedExtensionCapability(from: $0.capability, in: package) }
152170
self.path = target.sources.root.relative(to: package.path).pathString
153171
self.sources = target.sources.relativePaths.map{ $0.pathString }
154172
self.resources = target.resources.isEmpty ? nil : target.resources

Sources/PackageDescription/PackageDescription.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2018 Apple Inc. and the Swift project authors
4+
Copyright (c) 2018 - 2021 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -592,6 +592,30 @@ extension SystemPackageProvider: Encodable {
592592
}
593593
}
594594

595+
extension Target.ExtensionCapability: Encodable {
596+
private enum CodingKeys: CodingKey {
597+
case type
598+
}
599+
600+
private enum Capability: String, Encodable {
601+
case prebuild
602+
case buildTool
603+
case postbuild
604+
}
605+
606+
public func encode(to encoder: Encoder) throws {
607+
var container = encoder.container(keyedBy: CodingKeys.self)
608+
switch self {
609+
case ._prebuild:
610+
try container.encode(Capability.prebuild, forKey: .type)
611+
case ._buildTool:
612+
try container.encode(Capability.buildTool, forKey: .type)
613+
case ._postbuild:
614+
try container.encode(Capability.postbuild, forKey: .type)
615+
}
616+
}
617+
}
618+
595619
extension Target.Dependency: Encodable {
596620
private enum CodingKeys: CodingKey {
597621
case type
@@ -650,6 +674,7 @@ func manifestToJSON(_ package: Package) -> String {
650674
}
651675

652676
let encoder = JSONEncoder()
677+
encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
653678
let data = try! encoder.encode(Output(package: package, errors: errors))
654679
return String(data: data, encoding: .utf8)!
655680
}

0 commit comments

Comments
 (0)