Skip to content

Commit 40f2b4b

Browse files
committed
Implement SE-208 System Library Targets
<rdar://problem/39418939> [SR-7434]: Implement SE-208 Package Manager System Library Targets
1 parent 04a8d81 commit 40f2b4b

File tree

11 files changed

+350
-40
lines changed

11 files changed

+350
-40
lines changed

Sources/PackageDescription4/Package.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,22 @@ extension Package {
243243

244244
extension Target {
245245
func toJSON() -> JSON {
246-
return .dictionary([
246+
var dict: [String: JSON] = [
247247
"name": .string(name),
248-
"isTest": .bool(isTest),
248+
"type": .string(type.rawValue),
249249
"publicHeadersPath": publicHeadersPath.map(JSON.string) ?? JSON.null,
250250
"dependencies": .array(dependencies.map({ $0.toJSON() })),
251251
"path": path.map(JSON.string) ?? JSON.null,
252252
"exclude": .array(exclude.map(JSON.string)),
253253
"sources": sources.map({ JSON.array($0.map(JSON.string)) }) ?? JSON.null,
254-
])
254+
]
255+
if let pkgConfig = self.pkgConfig {
256+
dict["pkgConfig"] = .string(pkgConfig)
257+
}
258+
if let providers = self.providers {
259+
dict["providers"] = .array(providers.map({ $0.toJSON() }))
260+
}
261+
return .dictionary(dict)
255262
}
256263
}
257264

Sources/PackageDescription4/Target.swift

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
/// The description for an individual target.
1212
public final class Target {
1313

14+
/// The type of this target.
15+
public enum TargetType: String {
16+
case regular
17+
case test
18+
case system
19+
}
20+
1421
/// Represents a target's dependency on another entity.
1522
public enum Dependency {
1623

@@ -51,7 +58,9 @@ public final class Target {
5158
public var exclude: [String]
5259

5360
/// If this is a test target.
54-
public var isTest: Bool
61+
public var isTest: Bool {
62+
return type == .test
63+
}
5564

5665
/// Dependencies on other entities inside or outside the package.
5766
public var dependencies: [Dependency]
@@ -61,6 +70,17 @@ public final class Target {
6170
/// If a value is not provided, the directory will be set to "include".
6271
public var publicHeadersPath: String?
6372

73+
/// The type of target.
74+
public let type: TargetType
75+
76+
/// `pkgconfig` name to use for system library target. If present, swiftpm will try to
77+
/// search for <name>.pc file to get the additional flags needed for the
78+
/// system target.
79+
public let pkgConfig: String?
80+
81+
/// Providers array for the System library target.
82+
public let providers: [SystemPackageProvider]?
83+
6484
/// Construct a target.
6585
init(
6686
name: String,
@@ -69,15 +89,25 @@ public final class Target {
6989
exclude: [String],
7090
sources: [String]?,
7191
publicHeadersPath: String?,
72-
isTest: Bool
92+
type: TargetType,
93+
pkgConfig: String? = nil,
94+
providers: [SystemPackageProvider]? = nil
7395
) {
7496
self.name = name
7597
self.dependencies = dependencies
7698
self.path = path
7799
self.publicHeadersPath = publicHeadersPath
78100
self.sources = sources
79101
self.exclude = exclude
80-
self.isTest = isTest
102+
self.type = type
103+
self.pkgConfig = pkgConfig
104+
self.providers = providers
105+
106+
switch type {
107+
case .regular, .test:
108+
precondition(pkgConfig == nil && providers == nil)
109+
case .system: break
110+
}
81111
}
82112

83113
public static func target(
@@ -95,7 +125,7 @@ public final class Target {
95125
exclude: exclude,
96126
sources: sources,
97127
publicHeadersPath: publicHeadersPath,
98-
isTest: false)
128+
type: .regular)
99129
}
100130

101131
public static func testTarget(
@@ -112,8 +142,28 @@ public final class Target {
112142
exclude: exclude,
113143
sources: sources,
114144
publicHeadersPath: nil,
115-
isTest: true)
145+
type: .test)
146+
}
147+
148+
#if !PACKAGE_DESCRIPTION_4
149+
public static func systemLibrary(
150+
name: String,
151+
path: String? = nil,
152+
pkgConfig: String? = nil,
153+
providers: [SystemPackageProvider]? = nil
154+
) -> Target {
155+
return Target(
156+
name: name,
157+
dependencies: [],
158+
path: path,
159+
exclude: [],
160+
sources: nil,
161+
publicHeadersPath: nil,
162+
type: .system,
163+
pkgConfig: pkgConfig,
164+
providers: providers)
116165
}
166+
#endif
117167
}
118168

119169
extension Target.Dependency {

Sources/PackageGraph/PackageGraphLoader.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,15 @@ private func createResolvedPackages(
252252
// The diagnostics location for this package.
253253
let diagnosticLocation = { PackageLocation.Local(name: package.name, packagePath: package.path) }
254254

255-
// Get all the system module dependencies in this package.
256-
let systemModulesDeps = packageBuilder.dependencies
255+
// Get all implicit system library dependencies in this package.
256+
let implicitSystemTargetDeps = packageBuilder.dependencies
257257
.flatMap({ $0.targets })
258-
.filter({ $0.target.type == .systemModule })
258+
.filter({
259+
if case let systemLibrary as SystemLibraryTarget = $0.target {
260+
return systemLibrary.isImplicit
261+
}
262+
return false
263+
})
259264

260265
// Get all the products from dependencies of this package.
261266
let productDependencies = packageBuilder.dependencies
@@ -273,7 +278,7 @@ private func createResolvedPackages(
273278
allTargetNames.insert(targetName)
274279

275280
// Directly add all the system module dependencies.
276-
targetBuilder.dependencies += systemModulesDeps
281+
targetBuilder.dependencies += implicitSystemTargetDeps
277282

278283
// Establish product dependencies based on the type of manifest.
279284
switch package.manifest.package {

Sources/PackageLoading/Diagnostics.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,41 @@ public enum PackageBuilderDiagnostics {
7676

7777
public let product: Product
7878
}
79+
80+
struct SystemPackageDeprecatedDiagnostic: DiagnosticData {
81+
static let id = DiagnosticID(
82+
type: SystemPackageDeprecatedDiagnostic.self,
83+
name: "org.swift.diags.pkg-builder.sys-pkg-deprecated",
84+
defaultBehavior: .warning,
85+
description: {
86+
$0 <<< "system packages are deprecated;"
87+
$0 <<< "use system library targets instead"
88+
}
89+
)
90+
}
91+
92+
struct SystemPackageDeclaresTargetsDiagnostic: DiagnosticData {
93+
static let id = DiagnosticID(
94+
type: SystemPackageDeclaresTargetsDiagnostic.self,
95+
name: "org.swift.diags.pkg-builder.sys-pkg-decl-targets",
96+
defaultBehavior: .warning,
97+
description: {
98+
$0 <<< "Ignoring declared target(s)" <<< { "'\($0.targets.joined(separator: ", "))'" } <<< "in the system package"
99+
}
100+
)
101+
102+
let targets: [String]
103+
}
104+
105+
struct SystemPackageProductValidationDiagnostic: DiagnosticData {
106+
static let id = DiagnosticID(
107+
type: SystemPackageProductValidationDiagnostic.self,
108+
name: "org.swift.diags.pkg-builder.sys-pkg-product-validatoin",
109+
description: {
110+
$0 <<< "system library product" <<< { $0.product } <<< "shouldn't have a type and contain only one target"
111+
}
112+
)
113+
114+
let product: String
115+
}
79116
}

Sources/PackageLoading/PackageBuilder.swift

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -408,12 +408,32 @@ public final class PackageBuilder {
408408
// Check for a modulemap file, which indicates a system target.
409409
let moduleMapPath = packagePath.appending(component: moduleMapFilename)
410410
if fileSystem.isFile(moduleMapPath) {
411+
412+
// Warn about any declared targets.
413+
let targets = manifest.package.targets
414+
if !targets.isEmpty {
415+
diagnostics.emit(
416+
data: PackageBuilderDiagnostics.SystemPackageDeclaresTargetsDiagnostic(targets: targets.map({ $0.name })),
417+
location: PackageLocation.Local(name: manifest.name, packagePath: packagePath)
418+
)
419+
}
420+
421+
// Emit deprecation notice.
422+
switch manifest.manifestVersion {
423+
case .v3, .v4: break
424+
case .v4_2:
425+
diagnostics.emit(
426+
data: PackageBuilderDiagnostics.SystemPackageDeprecatedDiagnostic(),
427+
location: PackageLocation.Local(name: manifest.name, packagePath: packagePath)
428+
)
429+
}
430+
411431
// Package contains a modulemap at the top level, so we assuming
412432
// it's a system library target.
413433
return [
414434
SystemLibraryTarget(
415435
name: manifest.name,
416-
path: packagePath,
436+
path: packagePath, isImplicit: true,
417437
pkgConfig: manifest.package.pkgConfig,
418438
providers: manifest.package.providers)
419439
]
@@ -493,7 +513,7 @@ public final class PackageBuilder {
493513
let potentialTargets: [PotentialModule]
494514
potentialTargets = try manifest.package.targets.map({ target in
495515
let path = try findPath(for: target)
496-
return PotentialModule(name: target.name, path: path, isTest: target.isTest)
516+
return PotentialModule(name: target.name, path: path, type: target.type)
497517
})
498518
return try createModules(potentialTargets)
499519
}
@@ -543,9 +563,9 @@ public final class PackageBuilder {
543563
if potentialModulePaths.isEmpty {
544564
// There are no directories that look like targets, so try to create a target for the source directory
545565
// itself (with the name coming from the name in the manifest).
546-
potentialModules = [PotentialModule(name: manifest.name, path: srcDir, isTest: false)]
566+
potentialModules = [PotentialModule(name: manifest.name, path: srcDir, type: .regular)]
547567
} else {
548-
potentialModules = potentialModulePaths.map({ PotentialModule(name: $0.basename, path: $0, isTest: false) })
568+
potentialModules = potentialModulePaths.map({ PotentialModule(name: $0.basename, path: $0, type: .regular) })
549569
}
550570
return try createModules(potentialModules + potentialTestModules())
551571
}
@@ -691,6 +711,19 @@ public final class PackageBuilder {
691711
productDeps: [(name: String, package: String?)]
692712
) throws -> Target? {
693713

714+
// Create system library target.
715+
if potentialModule.type == .system {
716+
let moduleMapPath = potentialModule.path.appending(component: moduleMapFilename)
717+
guard fileSystem.isFile(moduleMapPath) else {
718+
return nil
719+
}
720+
return SystemLibraryTarget(
721+
name: potentialModule.name,
722+
path: potentialModule.path, isImplicit: false,
723+
pkgConfig: manifestTarget?.pkgConfig,
724+
providers: manifestTarget?.providers)
725+
}
726+
694727
// Compute the path to public headers directory.
695728
let publicHeaderComponent = manifestTarget?.publicHeadersPath ?? ClangTarget.defaultPublicHeadersComponent
696729
let publicHeadersPath = potentialModule.path.appending(RelativePath(publicHeaderComponent))
@@ -882,7 +915,7 @@ public final class PackageBuilder {
882915

883916
return testsDirContents
884917
.filter(shouldConsiderDirectory)
885-
.map({ PotentialModule(name: $0.basename, path: $0, isTest: true) })
918+
.map({ PotentialModule(name: $0.basename, path: $0, type: .test) })
886919
}
887920

888921
/// Find the linux main file for the package.
@@ -1054,6 +1087,19 @@ public final class PackageBuilder {
10541087
case nil: type = .library(.automatic)
10551088
}
10561089
let targets = try modulesFrom(targetNames: p.targets, product: p.name)
1090+
1091+
// Peform special validations if this product is exporting
1092+
// a system library target.
1093+
if targets.contains(where: { $0 is SystemLibraryTarget }) {
1094+
if type != .library(.automatic) || targets.count != 1 {
1095+
diagnostics.emit(
1096+
data: PackageBuilderDiagnostics.SystemPackageProductValidationDiagnostic(product: p.name),
1097+
location: PackageLocation.Local(name: manifest.name, packagePath: packagePath)
1098+
)
1099+
continue
1100+
}
1101+
}
1102+
10571103
append(Product(name: p.name, type: type, targets: targets))
10581104
default:
10591105
fatalError("Unreachable")
@@ -1076,12 +1122,17 @@ private struct PotentialModule: Hashable {
10761122
let path: AbsolutePath
10771123

10781124
/// If this should be a test target.
1079-
let isTest: Bool
1125+
var isTest: Bool {
1126+
return type == .test
1127+
}
1128+
1129+
/// The target type.
1130+
let type: PackageDescription4.Target.TargetType
10801131

10811132
/// The base prefix for the test target, used to associate with the target it tests.
10821133
public var basename: String {
10831134
guard isTest else {
1084-
fatalError("\(type(of: self)) should be a test target to access basename.")
1135+
fatalError("\(Swift.type(of: self)) should be a test target to access basename.")
10851136
}
10861137
precondition(name.hasSuffix(Target.testModuleNameSuffix))
10871138
let endIndex = name.index(name.endIndex, offsetBy: -Target.testModuleNameSuffix.count)

0 commit comments

Comments
 (0)