Skip to content
This repository was archived by the owner on Apr 11, 2024. It is now read-only.

Commit 41c2108

Browse files
authored
Merge pull request #32 from unsignedapps/tech/generation-validation
Add validation against unsupported package targets (binary targets, system modules, conditional dependencies)
2 parents 0df8e21 + 8074ec0 commit 41c2108

File tree

3 files changed

+79
-16
lines changed

3 files changed

+79
-16
lines changed

Sources/CreateXCFramework/Command.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,23 @@ struct Command: ParsableCommand {
3939

4040
// MARK: - Execution
4141

42+
// swiftlint:disable:next function_body_length
4243
func run() throws {
4344

4445
// load all/validate of the package info
4546
let package = try PackageInfo(options: self.options)
4647

48+
// validate that package to make sure we can generate it
49+
let validation = package.validationErrors()
50+
if validation.isEmpty == false {
51+
for error in validation {
52+
print((error.isFatal ? "Error:" : "Warning:"), error.errorDescription!)
53+
}
54+
if validation.contains(where: { $0.isFatal }) {
55+
Darwin.exit(1)
56+
}
57+
}
58+
4759
// generate the Xcode project file
4860
let generator = ProjectGenerator(package: package)
4961

@@ -70,7 +82,6 @@ struct Command: ParsableCommand {
7082
// save the project
7183
try project.save(to: generator.projectPath)
7284

73-
7485
// start building
7586
let builder = XcodeBuilder(project: project, projectPath: generator.projectPath, package: package, options: self.options)
7687

Sources/CreateXCFramework/PackageInfo.swift

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,14 @@ struct PackageInfo {
5252
let diagnostics = DiagnosticsEngine()
5353

5454
let options: Command.Options
55-
let package: Package
55+
// let package: Package
5656
let graph: PackageGraph
5757
let manifest: Manifest
5858
let toolchain: Toolchain
5959
let workspace: Workspace
6060

6161

62-
// MARJ: - Initialisation
62+
// MARK: - Initialisation
6363

6464
init (options: Command.Options) throws {
6565
self.options = options
@@ -74,13 +74,6 @@ struct PackageInfo {
7474
let loader = ManifestLoader(manifestResources: resources)
7575
self.workspace = Workspace.create(forRootPackage: root, manifestLoader: loader)
7676

77-
self.package = try PackageBuilder.loadPackage (
78-
packagePath: root,
79-
swiftCompiler: self.toolchain.swiftCompiler,
80-
xcTestMinimumDeploymentTargets: [:],
81-
diagnostics: self.diagnostics
82-
)
83-
8477
self.graph = self.workspace.loadPackageGraph(root: root, diagnostics: self.diagnostics)
8578

8679
self.manifest = try ManifestLoader.loadManifest (
@@ -91,6 +84,33 @@ struct PackageInfo {
9184
}
9285

9386

87+
// MARK: - Validation
88+
89+
func validationErrors () -> [PackageValidationError] {
90+
var errors = [PackageValidationError]()
91+
92+
// check the graph for binary targets
93+
let binary = self.graph.allTargets.filter { $0.type == .binary }
94+
if binary.isEmpty == false {
95+
errors.append(.containsBinaryTargets(binary.map(\.name)))
96+
}
97+
98+
// check for system modules
99+
let system = self.graph.allTargets.filter { $0.type == .systemModule }
100+
if system.isEmpty == false {
101+
errors.append(.containsSystemModules(system.map(\.name)))
102+
}
103+
104+
// and for conditional dependencies
105+
let conditionals = self.graph.allTargets.filter { $0.dependencies.contains { $0.conditions.isEmpty == false } }
106+
if conditionals.isEmpty == false {
107+
errors.append(.containsConditionalDependencies(conditionals.map(\.name)))
108+
}
109+
110+
return errors
111+
}
112+
113+
94114
// MARK: - Product/Target Names
95115

96116
func validProductNames (project: Xcode.Project) throws -> [String] {
@@ -100,7 +120,7 @@ struct PackageInfo {
100120
if self.options.products.isEmpty == false {
101121
productNames = self.options.products
102122
} else {
103-
productNames = package.manifest.libraryProductNames
123+
productNames = self.manifest.libraryProductNames
104124
}
105125

106126
// validation
@@ -115,15 +135,15 @@ struct PackageInfo {
115135
let invalidProducts = productNames.filter { xcodeTargetNames.contains($0) == false }
116136
guard invalidProducts.isEmpty == true else {
117137

118-
let allLibraryProductNames = self.package.manifest.libraryProductNames
138+
let allLibraryProductNames = self.manifest.libraryProductNames
119139
let nonRootPackageTargets = xcodeTargetNames.filter { allLibraryProductNames.contains($0) == false }
120140

121141
throw ValidationError (
122142
"""
123143
Invalid product/target name(s):
124144
\(invalidProducts.joined(separator: "\n "))
125145
126-
Available \(self.package.name) products:
146+
Available \(self.manifest.name) products:
127147
\(allLibraryProductNames.sorted().joined(separator: "\n "))
128148
129149
Additional available targets:
@@ -136,13 +156,13 @@ struct PackageInfo {
136156
}
137157

138158
func printAllProducts (project: Xcode.Project) {
139-
let allLibraryProductNames = self.package.manifest.libraryProductNames
159+
let allLibraryProductNames = self.manifest.libraryProductNames
140160
let xcodeTargetNames = project.frameworkTargets.map { $0.name }
141161
let nonRootPackageTargets = xcodeTargetNames.filter { allLibraryProductNames.contains($0) == false }
142162

143163
print (
144164
"""
145-
\nAvailable \(self.package.name) products:
165+
\nAvailable \(self.manifest.name) products:
146166
\(allLibraryProductNames.sorted().joined(separator: "\n "))
147167
148168
Additional available targets:
@@ -182,6 +202,7 @@ struct PackageInfo {
182202
private var absoluteRootDirectory: AbsolutePath {
183203
AbsolutePath(self.rootDirectory.path)
184204
}
205+
185206
}
186207

187208

@@ -206,3 +227,34 @@ extension SupportedPlatform: Equatable, Comparable {
206227
return lhs.platform.name < rhs.platform.name
207228
}
208229
}
230+
231+
// MARK: - Validation Errors
232+
233+
enum PackageValidationError: LocalizedError {
234+
case containsBinaryTargets([String])
235+
case containsSystemModules([String])
236+
case containsConditionalDependencies([String])
237+
238+
var isFatal: Bool {
239+
switch self {
240+
case .containsBinaryTargets, .containsSystemModules:
241+
return true
242+
case .containsConditionalDependencies:
243+
return false
244+
}
245+
}
246+
247+
var errorDescription: String? {
248+
switch self {
249+
case let .containsBinaryTargets(targets):
250+
return "Xcode project generation is not supported by Swift Package Manager for packages that contain binary targets."
251+
+ "These binary targets were detected: \(targets.joined(separator: ", "))"
252+
case let .containsSystemModules(targets):
253+
return "Xcode project generation is not supported by Swift Package Manager for packages that reference system modules."
254+
+ "These system modules were referenced: \(targets.joined(separator: ", "))"
255+
case let .containsConditionalDependencies(targets):
256+
return "Xcode project generation does not support conditional target dependencies, so the generated project may not build successfully."
257+
+ "These targets contain conditional dependencies: \(targets.joined(separator: ", "))"
258+
}
259+
}
260+
}

Sources/CreateXCFramework/ProjectGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct ProjectGenerator {
2323

2424
var projectPath: AbsolutePath {
2525
let dir = AbsolutePath(self.package.projectBuildDirectory.path)
26-
return buildXcodeprojPath(outputDir: dir, projectName: self.package.package.name)
26+
return buildXcodeprojPath(outputDir: dir, projectName: self.package.manifest.name)
2727
}
2828

2929

0 commit comments

Comments
 (0)