Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix swift-api-digester search paths #7664

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
16 changes: 16 additions & 0 deletions Sources/Basics/Collections/IdentifiableSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ public struct IdentifiableSet<Element: Identifiable>: Collection {
public func contains(id: Element.ID) -> Bool {
self.storage.keys.contains(id)
}

public func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> Self {
package func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> Self {

var copy = Self()
for element in self {
if try isIncluded(element) {
copy.insert(element)
}
}
return copy
}
}

extension OrderedDictionary where Value: Identifiable, Key == Value.ID {
Expand All @@ -118,3 +128,9 @@ extension IdentifiableSet: Hashable {
}
}
}

extension IdentifiableSet: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Element...) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public init(arrayLiteral elements: Element...) {
package init(arrayLiteral elements: Element...) {

self.init(elements)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,18 @@ public final class ClangTargetBuildDescription {
return args
}

/// Determines the arguments needed to run `swift-api-digester` for emitting
/// an API baseline for this module.
package func apiDigesterEmitBaselineArguments() throws -> [String] {
throw InternalError("Unimplemented \(#function)")
}

/// Determines the arguments needed to run `swift-api-digester` for
/// comparing to an API baseline for this module.
package func apiDigesterCompareBaselineArguments() throws -> [String] {
throw InternalError("Unimplemented \(#function)")
}

/// Builds up basic compilation arguments for a source file in this target; these arguments may be different for C++
/// vs non-C++.
/// NOTE: The parameter to specify whether to get C++ semantics is currently optional, but this is only for revlock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,44 @@ public final class SwiftTargetBuildDescription {
return args
}

/// Determines the arguments needed to run `swift-api-digester` for emitting
/// an API baseline for this module.
package func apiDigesterEmitBaselineArguments() throws -> [String] {
var args = [String]()
args += try self.apiDigesterCommonArguments()
return args
}

/// Determines the arguments needed to run `swift-api-digester` for
/// comparing to an API baseline for this module.
package func apiDigesterCompareBaselineArguments() throws -> [String] {
var args = [String]()
args += try self.apiDigesterCommonArguments()
return args
}
Comment on lines +646 to +658
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear what these two methods are for if they just append the result of apiDigesterCommonArguments to an empty array?


public func apiDigesterCommonArguments() throws -> [String] {
var args = [String]()
args += self.buildParameters.toolchain.extraFlags.swiftCompilerFlags

// FIXME: remove additionalFlags
// Add search paths determined during planning
args += self.additionalFlags
args += ["-I", self.modulesPath.pathString]

// FIXME: Only include valid args
// `swift-api-digester` args doesn't support -L args.
for index in args.indices.dropLast().reversed() {
if args[index] == "-L" {
// Remove the flag
args.remove(at: index)
// Remove the argument
args.remove(at: index)
}
}
return args
}

// FIXME: this function should operation on a strongly typed buildSetting
// Move logic from PackageBuilder here.
/// Determines the arguments needed for cxx interop for this module.
Expand Down
18 changes: 18 additions & 0 deletions Sources/Build/BuildDescription/TargetBuildDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,22 @@ public enum TargetBuildDescription {
case .clang(let target): try target.symbolGraphExtractArguments()
}
}

/// Determines the arguments needed to run `swift-api-digester` for emitting
/// an API baseline for this module.
package func apiDigesterEmitBaselineArguments() throws -> [String] {
switch self {
case .swift(let target): try target.apiDigesterEmitBaselineArguments()
case .clang(let target): try target.apiDigesterEmitBaselineArguments()
}
}

/// Determines the arguments needed to run `swift-api-digester` for
/// comparing to an API baseline for this module.
package func apiDigesterCompareBaselineArguments() throws -> [String] {
switch self {
case .swift(let target): try target.apiDigesterCompareBaselineArguments()
case .clang(let target): try target.apiDigesterCompareBaselineArguments()
}
}
}
65 changes: 18 additions & 47 deletions Sources/Build/BuildPlan/BuildPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -523,53 +523,6 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
// handle that situation.
}

public func createAPIToolCommonArgs(includeLibrarySearchPaths: Bool) throws -> [String] {
// API tool runs on products, hence using `self.productsBuildParameters`, not `self.toolsBuildParameters`
let buildPath = self.destinationBuildParameters.buildPath.pathString
var arguments = ["-I", buildPath]

// swift-symbolgraph-extract does not support parsing `-use-ld=lld` and
// will silently error failing the operation. Filter out this flag
// similar to how we filter out the library search path unless
// explicitly requested.
var extraSwiftCFlags = self.destinationBuildParameters.toolchain.extraFlags.swiftCompilerFlags
.filter { !$0.starts(with: "-use-ld=") }
if !includeLibrarySearchPaths {
for index in extraSwiftCFlags.indices.dropLast().reversed() {
if extraSwiftCFlags[index] == "-L" {
// Remove the flag
extraSwiftCFlags.remove(at: index)
// Remove the argument
extraSwiftCFlags.remove(at: index)
}
}
}
arguments += extraSwiftCFlags

// Add search paths to the directories containing module maps and Swift modules.
for target in self.targets {
switch target {
case .swift(let targetDescription):
arguments += ["-I", targetDescription.moduleOutputPath.parentDirectory.pathString]
case .clang(let targetDescription):
if let includeDir = targetDescription.moduleMap?.parentDirectory {
arguments += ["-I", includeDir.pathString]
}
}
}

// Add search paths from the system library targets.
for target in self.graph.reachableTargets {
if let systemLib = target.underlying as? SystemLibraryTarget {
try arguments.append(contentsOf: self.pkgConfig(for: systemLib).cFlags)
// Add the path to the module map.
arguments += ["-I", systemLib.moduleMapPath.parentDirectory.pathString]
}
}

return arguments
}

/// Creates arguments required to launch the Swift REPL that will allow
/// importing the modules in the package graph.
public func createREPLArguments() throws -> [String] {
Expand Down Expand Up @@ -656,6 +609,24 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
}
return try description.symbolGraphExtractArguments()
}

/// Determines the arguments needed to run `swift-api-digester` for emitting
/// an API baseline for a particular module.
public func apiDigesterEmitBaselineArguments(for module: ResolvedModule) throws -> [String] {
guard let description = self.targetMap[module.id] else {
throw InternalError("Expected description for module \(module)")
}
return try description.apiDigesterEmitBaselineArguments()
}

/// Determines the arguments needed to run `swift-api-digester` for
/// comparing to an API baseline for a particular module.
public func apiDigesterCompareBaselineArguments(for module: ResolvedModule) throws -> [String] {
guard let description = self.targetMap[module.id] else {
throw InternalError("Expected description for module \(module)")
}
return try description.apiDigesterCompareBaselineArguments()
}
Comment on lines +613 to +629
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto, it looks like these two functions are equivalent

}

extension Basics.Diagnostic {
Expand Down
30 changes: 17 additions & 13 deletions Sources/Commands/PackageCommands/APIDiff.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import PackageModel
import SourceControl

struct DeprecatedAPIDiff: ParsableCommand {
static let configuration = CommandConfiguration(commandName: "experimental-api-diff",
abstract: "Deprecated - use `swift package diagnose-api-breaking-changes` instead",
shouldDisplay: false)
static let configuration = CommandConfiguration(
commandName: "experimental-api-diff",
abstract: "Deprecated - use `swift package diagnose-api-breaking-changes` instead",
shouldDisplay: false)

@Argument(parsing: .captureForPassthrough)
var args: [String] = []
Expand Down Expand Up @@ -109,18 +110,18 @@ struct APIDiff: SwiftCommand {
at: overrideBaselineDir,
force: regenerateBaseline,
logLevel: swiftCommandState.logLevel,
swiftCommandState: swiftCommandState
swiftCommandState: swiftCommandState
)

let results = ThreadSafeArrayStore<SwiftAPIDigester.ComparisonResult>()
let group = DispatchGroup()
let semaphore = DispatchSemaphore(value: Int(try buildSystem.buildPlan.destinationBuildParameters.workers))
var skippedModules: Set<String> = []
var skippedModules = IdentifiableSet<ResolvedModule>()

for module in modulesToDiff {
let moduleBaselinePath = baselineDir.appending("\(module).json")
let moduleBaselinePath = baselineDir.appending("\(module.c99name).json")
guard swiftCommandState.fileSystem.exists(moduleBaselinePath) else {
print("\nSkipping \(module) because it does not exist in the baseline")
print("\nSkipping \(module.c99name) because it does not exist in the baseline")
skippedModules.insert(module)
continue
}
Expand All @@ -146,7 +147,7 @@ struct APIDiff: SwiftCommand {

let failedModules = modulesToDiff
.subtracting(skippedModules)
.subtracting(results.map(\.moduleName))
.subtracting(results.map(\.module))
for failedModule in failedModules {
swiftCommandState.observabilityScope.emit(error: "failed to read API digester output for \(failedModule)")
}
Expand All @@ -160,8 +161,8 @@ struct APIDiff: SwiftCommand {
}
}

private func determineModulesToDiff(packageGraph: ModulesGraph, observabilityScope: ObservabilityScope) throws -> Set<String> {
var modulesToDiff: Set<String> = []
private func determineModulesToDiff(packageGraph: ModulesGraph, observabilityScope: ObservabilityScope) throws -> IdentifiableSet<ResolvedModule> {
var modulesToDiff = IdentifiableSet<ResolvedModule>()
if products.isEmpty && targets.isEmpty {
modulesToDiff.formUnion(packageGraph.apiDigesterModules)
} else {
Expand All @@ -177,7 +178,10 @@ struct APIDiff: SwiftCommand {
observabilityScope.emit(error: "'\(productName)' is not a library product")
continue
}
modulesToDiff.formUnion(product.targets.filter { $0.underlying is SwiftTarget }.map(\.c99name))
let swiftModules = product
.targets
.filter { $0.underlying is SwiftTarget }
modulesToDiff.formUnion(swiftModules)
}
for targetName in targets {
guard let target = packageGraph
Expand All @@ -195,7 +199,7 @@ struct APIDiff: SwiftCommand {
observabilityScope.emit(error: "'\(targetName)' is not a Swift language target")
continue
}
modulesToDiff.insert(target.c99name)
modulesToDiff.insert(target)
}
guard !observabilityScope.errorsReported else {
throw ExitCode.failure
Expand Down Expand Up @@ -232,7 +236,7 @@ struct APIDiff: SwiftCommand {
}
}

let moduleName = comparisonResult.moduleName
let moduleName = comparisonResult.module.c99name
if comparisonResult.apiBreakingChanges.isEmpty {
print("\nNo breaking changes detected in \(moduleName)")
} else {
Expand Down
Loading