Skip to content

Commit 42903af

Browse files
authored
Don't emit "unhandled files" warnings for files that are handled by build commands from package plugins (#3971)
In the long run we will want to make the built-in rules and plugins all go through the same funnel. This is a more tactical change than that, which defers emitting warnings until after running the plugins. rdar://74663980
1 parent af43c15 commit 42903af

File tree

5 files changed

+67
-33
lines changed

5 files changed

+67
-33
lines changed

Sources/Build/BuildOperation.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,36 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
209209
let prebuildCommandResults = try graph.reachableTargets.reduce(into: [:], { partial, target in
210210
partial[target] = try buildToolPluginInvocationResults[target].map { try self.runPrebuildCommands(for: $0) }
211211
})
212+
213+
// Emit warnings about any unhandled files in authored packages. We do this after applying build tool plugins, once we know what files they handled.
214+
for package in graph.rootPackages where package.manifest.toolsVersion >= .v5_3 {
215+
for target in package.targets {
216+
// Get the set of unhandled files in targets.
217+
var unhandledFiles = Set(target.underlyingTarget.others)
218+
if unhandledFiles.isEmpty { continue }
219+
220+
// Subtract out any that were inputs to any commands generated by plugins.
221+
if let result = buildToolPluginInvocationResults[target] {
222+
let handledFiles = result.flatMap{ $0.buildCommands.flatMap{ $0.inputFiles } }
223+
unhandledFiles.subtract(handledFiles)
224+
}
225+
if unhandledFiles.isEmpty { continue }
226+
227+
// Emit a diagnostic if any remain. This is kept the same as the previous message for now, but this could be improved.
228+
let diagnosticsEmitter = self.observabilityScope.makeDiagnosticsEmitter {
229+
var metadata = ObservabilityMetadata()
230+
metadata.packageIdentity = package.identity
231+
metadata.packageKind = package.manifest.packageKind
232+
metadata.targetName = target.name
233+
return metadata
234+
}
235+
var warning = "found \(unhandledFiles.count) file(s) which are unhandled; explicitly declare them as resources or exclude from the target\n"
236+
for file in unhandledFiles {
237+
warning += " " + file.pathString + "\n"
238+
}
239+
diagnosticsEmitter.emit(warning: warning)
240+
}
241+
}
212242

213243
// Create the build plan based, on the graph and any information from plugins.
214244
let plan = try BuildPlan(

Sources/PackageLoading/TargetSourcesBuilder.swift

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -162,26 +162,11 @@ public struct TargetSourcesBuilder {
162162
pathToRule[path] = self.computeRule(for: path)
163163
}
164164

165-
// Emit an error if we found files without a matching rule in
166-
// tools version >= v5_3. This will be activated once resources
167-
// support is complete.
168-
var others: [AbsolutePath] = []
169-
if toolsVersion >= .v5_3 {
170-
let filesWithNoRules = pathToRule.filter { $0.value.rule == .none }
171-
if !filesWithNoRules.isEmpty, self.packageKind.emitAuthorWarnings {
172-
var warning = "found \(filesWithNoRules.count) file(s) which are unhandled; explicitly declare them as resources or exclude from the target\n"
173-
for (file, _) in filesWithNoRules {
174-
warning += " " + file.pathString + "\n"
175-
}
176-
self.observabilityScope.emit(warning: warning)
177-
}
178-
others.append(contentsOf: filesWithNoRules.keys)
179-
}
180-
181165
let headers = pathToRule.lazy.filter { $0.value.rule == .header }.map { $0.key }.sorted()
182166
let compilePaths = pathToRule.lazy.filter { $0.value.rule == .compile }.map { $0.key }
183167
let sources = Sources(paths: Array(compilePaths), root: targetPath)
184168
let resources: [Resource] = pathToRule.compactMap { resource(for: $0.key, with: $0.value) }
169+
let others = pathToRule.filter { $0.value.rule == .none }.map { $0.key }
185170

186171
diagnoseConflictingResources(in: resources)
187172
diagnoseCopyConflictsWithLocalizationDirectories(in: resources)

Sources/PackageModel/Target.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ public final class ClangTarget: Target {
434434
type: Kind,
435435
sources: Sources,
436436
resources: [Resource] = [],
437+
others: [AbsolutePath] = [],
437438
dependencies: [Target.Dependency] = [],
438439
buildSettings: BuildSettings.AssignmentTable = .init()
439440
) {
@@ -452,6 +453,7 @@ public final class ClangTarget: Target {
452453
type: type,
453454
sources: sources,
454455
resources: resources,
456+
others: others,
455457
dependencies: dependencies,
456458
buildSettings: buildSettings,
457459
pluginUsages: []

Tests/CommandsTests/PackageToolTests.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,16 @@ final class PackageToolTests: CommandsTestCase {
10701070
public func Foo() { }
10711071
"""
10721072
}
1073+
try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "MyLibrary", "library.foo")) {
1074+
$0 <<< """
1075+
a file with a filename suffix handled by the plugin
1076+
"""
1077+
}
1078+
try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "MyLibrary", "library.bar")) {
1079+
$0 <<< """
1080+
a file with a filename suffix not handled by the plugin
1081+
"""
1082+
}
10731083
try localFileSystem.writeFileContents(packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift")) {
10741084
$0 <<< """
10751085
import PackagePlugin
@@ -1080,23 +1090,30 @@ final class PackageToolTests: CommandsTestCase {
10801090
context: PluginContext,
10811091
target: Target
10821092
) throws -> [Command] {
1093+
// Check that the package display name is what we expect.
10831094
guard context.package.displayName == "MyPackage" else {
1084-
throw Error.inconsistentDisplayName(context.package.displayName)
1095+
throw "expected display name to be ‘MyPackage’ but found ‘\\(context.package.displayName)"
10851096
}
1086-
return []
1097+
// Create and return a build command that uses all the `.foo` files in the target as inputs, so they get counted as having been handled.
1098+
let fooFiles = (target as? SourceModuleTarget)?.sourceFiles.compactMap{ $0.path.extension == "foo" ? $0.path : nil } ?? []
1099+
return [ .buildCommand(displayName: "A command", executable: "/bin/echo", arguments: ["Hello"], inputFiles: fooFiles) ]
10871100
}
10881101
1089-
enum Error : Swift.Error {
1090-
case inconsistentDisplayName(String)
1091-
}
10921102
}
1103+
extension String : Error {}
10931104
"""
10941105
}
10951106

10961107
// Invoke it, and check the results.
10971108
let result = try SwiftPMProduct.SwiftBuild.executeProcess([], packagePath: packageDir)
10981109
XCTAssertEqual(result.exitStatus, .terminated(code: 0))
10991110
XCTAssert(try result.utf8Output().contains("Build complete!"))
1111+
1112+
// We expect a warning about `library.bar` but not about `library.foo`.
1113+
let stderrOutput = try result.utf8stderrOutput()
1114+
XCTAssertMatch(stderrOutput, .contains("found 1 file(s) which are unhandled"))
1115+
XCTAssertNoMatch(stderrOutput, .contains("Sources/MyLibrary/library.foo"))
1116+
XCTAssertMatch(stderrOutput, .contains("Sources/MyLibrary/library.bar"))
11001117
}
11011118
}
11021119

Tests/PackageLoadingTests/TargetSourcesBuilderTests.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -855,13 +855,13 @@ class TargetSourcesBuilderTests: XCTestCase {
855855
fileSystem: fs,
856856
observabilityScope: observability.topScope
857857
)
858-
_ = try builder.run()
858+
let outputs = try builder.run()
859+
XCTAssertEqual(outputs.sources.paths, [AbsolutePath("/File.swift")])
860+
XCTAssertEqual(outputs.resources, [])
861+
XCTAssertEqual(outputs.others, [AbsolutePath("/Foo.xcdatamodel")])
859862

860-
testDiagnostics(observability.diagnostics) { result in
861-
let diagnostic = result.check(diagnostic: "found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target\n /Foo.xcdatamodel\n", severity: .warning)
862-
XCTAssertEqual(diagnostic?.metadata?.packageIdentity, builder.packageIdentity)
863-
XCTAssertEqual(diagnostic?.metadata?.targetName, target.name)
864-
}
863+
XCTAssertFalse(observability.hasWarningDiagnostics)
864+
XCTAssertFalse(observability.hasErrorDiagnostics)
865865
}
866866

867867
func testUnhandledResources() throws {
@@ -895,13 +895,13 @@ class TargetSourcesBuilderTests: XCTestCase {
895895
fileSystem: fs,
896896
observabilityScope: observability.topScope
897897
)
898-
_ = try builder.run()
898+
let outputs = try builder.run()
899+
XCTAssertEqual(outputs.sources.paths, [AbsolutePath("/File.swift")])
900+
XCTAssertEqual(outputs.resources, [])
901+
XCTAssertEqual(outputs.others, [AbsolutePath("/foo.bar")])
899902

900-
testDiagnostics(observability.diagnostics) { result in
901-
let diagnostic = result.check(diagnostic: "found 1 file(s) which are unhandled; explicitly declare them as resources or exclude from the target\n /foo.bar\n", severity: .warning)
902-
XCTAssertEqual(diagnostic?.metadata?.packageIdentity, builder.packageIdentity)
903-
XCTAssertEqual(diagnostic?.metadata?.targetName, target.name)
904-
}
903+
XCTAssertFalse(observability.hasWarningDiagnostics)
904+
XCTAssertFalse(observability.hasErrorDiagnostics)
905905
}
906906

907907
// should not emit for "remote" packages

0 commit comments

Comments
 (0)