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

Add validation against unsupported package targets (binary targets, system modules, conditional dependencies) #32

Merged
merged 2 commits into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion Sources/CreateXCFramework/Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,23 @@ struct Command: ParsableCommand {

// MARK: - Execution

// swiftlint:disable:next function_body_length
func run() throws {

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

// validate that package to make sure we can generate it
let validation = package.validationErrors()
if validation.isEmpty == false {
for error in validation {
print((error.isFatal ? "Error:" : "Warning:"), error.errorDescription!)
}
if validation.contains(where: { $0.isFatal }) {
Darwin.exit(1)
}
}

// generate the Xcode project file
let generator = ProjectGenerator(package: package)

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


// start building
let builder = XcodeBuilder(project: project, projectPath: generator.projectPath, package: package, options: self.options)

Expand Down
80 changes: 66 additions & 14 deletions Sources/CreateXCFramework/PackageInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ struct PackageInfo {
let diagnostics = DiagnosticsEngine()

let options: Command.Options
let package: Package
// let package: Package
let graph: PackageGraph
let manifest: Manifest
let toolchain: Toolchain
let workspace: Workspace


// MARJ: - Initialisation
// MARK: - Initialisation

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

self.package = try PackageBuilder.loadPackage (
packagePath: root,
swiftCompiler: self.toolchain.swiftCompiler,
xcTestMinimumDeploymentTargets: [:],
diagnostics: self.diagnostics
)

self.graph = self.workspace.loadPackageGraph(root: root, diagnostics: self.diagnostics)

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


// MARK: - Validation

func validationErrors () -> [PackageValidationError] {
var errors = [PackageValidationError]()

// check the graph for binary targets
let binary = self.graph.allTargets.filter { $0.type == .binary }
if binary.isEmpty == false {
errors.append(.containsBinaryTargets(binary.map(\.name)))
}

// check for system modules
let system = self.graph.allTargets.filter { $0.type == .systemModule }
if system.isEmpty == false {
errors.append(.containsSystemModules(system.map(\.name)))
}

// and for conditional dependencies
let conditionals = self.graph.allTargets.filter { $0.dependencies.contains { $0.conditions.isEmpty == false } }
if conditionals.isEmpty == false {
errors.append(.containsConditionalDependencies(conditionals.map(\.name)))
}

return errors
}


// MARK: - Product/Target Names

func validProductNames (project: Xcode.Project) throws -> [String] {
Expand All @@ -100,7 +120,7 @@ struct PackageInfo {
if self.options.products.isEmpty == false {
productNames = self.options.products
} else {
productNames = package.manifest.libraryProductNames
productNames = self.manifest.libraryProductNames
}

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

let allLibraryProductNames = self.package.manifest.libraryProductNames
let allLibraryProductNames = self.manifest.libraryProductNames
let nonRootPackageTargets = xcodeTargetNames.filter { allLibraryProductNames.contains($0) == false }

throw ValidationError (
"""
Invalid product/target name(s):
\(invalidProducts.joined(separator: "\n "))

Available \(self.package.name) products:
Available \(self.manifest.name) products:
\(allLibraryProductNames.sorted().joined(separator: "\n "))

Additional available targets:
Expand All @@ -136,13 +156,13 @@ struct PackageInfo {
}

func printAllProducts (project: Xcode.Project) {
let allLibraryProductNames = self.package.manifest.libraryProductNames
let allLibraryProductNames = self.manifest.libraryProductNames
let xcodeTargetNames = project.frameworkTargets.map { $0.name }
let nonRootPackageTargets = xcodeTargetNames.filter { allLibraryProductNames.contains($0) == false }

print (
"""
\nAvailable \(self.package.name) products:
\nAvailable \(self.manifest.name) products:
\(allLibraryProductNames.sorted().joined(separator: "\n "))

Additional available targets:
Expand Down Expand Up @@ -182,6 +202,7 @@ struct PackageInfo {
private var absoluteRootDirectory: AbsolutePath {
AbsolutePath(self.rootDirectory.path)
}

}


Expand All @@ -206,3 +227,34 @@ extension SupportedPlatform: Equatable, Comparable {
return lhs.platform.name < rhs.platform.name
}
}

// MARK: - Validation Errors

enum PackageValidationError: LocalizedError {
case containsBinaryTargets([String])
case containsSystemModules([String])
case containsConditionalDependencies([String])

var isFatal: Bool {
switch self {
case .containsBinaryTargets, .containsSystemModules:
return true
case .containsConditionalDependencies:
return false
}
}

var errorDescription: String? {
switch self {
case let .containsBinaryTargets(targets):
return "Xcode project generation is not supported by Swift Package Manager for packages that contain binary targets."
+ "These binary targets were detected: \(targets.joined(separator: ", "))"
case let .containsSystemModules(targets):
return "Xcode project generation is not supported by Swift Package Manager for packages that reference system modules."
+ "These system modules were referenced: \(targets.joined(separator: ", "))"
case let .containsConditionalDependencies(targets):
return "Xcode project generation does not support conditional target dependencies, so the generated project may not build successfully."
+ "These targets contain conditional dependencies: \(targets.joined(separator: ", "))"
}
}
}
2 changes: 1 addition & 1 deletion Sources/CreateXCFramework/ProjectGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct ProjectGenerator {

var projectPath: AbsolutePath {
let dir = AbsolutePath(self.package.projectBuildDirectory.path)
return buildXcodeprojPath(outputDir: dir, projectName: self.package.package.name)
return buildXcodeprojPath(outputDir: dir, projectName: self.package.manifest.name)
}


Expand Down