Skip to content

Commit 4650e37

Browse files
committed
Handle generated resources from plugins
rdar://89693335
1 parent e78566f commit 4650e37

File tree

9 files changed

+170
-79
lines changed

9 files changed

+170
-79
lines changed

Sources/Build/BuildOperation.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import Basics
1414
import LLBuildManifest
1515
import PackageGraph
16+
import PackageLoading
1617
import PackageModel
1718
import SPMBuildCore
1819
import SPMLLBuild
@@ -78,10 +79,14 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
7879
(try? getBuildDescription())?.builtTestProducts ?? []
7980
}
8081

82+
/// File rules to determine resource handling behavior.
83+
private let additionalFileRules: [FileRuleDescription]
84+
8185
public init(
8286
buildParameters: BuildParameters,
8387
cacheBuildManifest: Bool,
8488
packageGraphLoader: @escaping () throws -> PackageGraph,
89+
additionalFileRules: [FileRuleDescription],
8590
pluginScriptRunner: PluginScriptRunner,
8691
pluginWorkDirectory: AbsolutePath,
8792
disableSandboxForPluginCommands: Bool = false,
@@ -97,6 +102,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
97102
self.buildParameters = buildParameters
98103
self.cacheBuildManifest = cacheBuildManifest
99104
self.packageGraphLoader = packageGraphLoader
105+
self.additionalFileRules = additionalFileRules
100106
self.pluginScriptRunner = pluginScriptRunner
101107
self.pluginWorkDirectory = pluginWorkDirectory
102108
self.disableSandboxForPluginCommands = disableSandboxForPluginCommands
@@ -351,6 +357,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
351357
let plan = try BuildPlan(
352358
buildParameters: buildParameters,
353359
graph: graph,
360+
additionalFileRules: additionalFileRules,
354361
buildToolPluginInvocationResults: buildToolPluginInvocationResults,
355362
prebuildCommandResults: prebuildCommandResults,
356363
fileSystem: self.fileSystem,
@@ -443,7 +450,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
443450
return try pluginResults.map { pluginResult in
444451
// As we go we will collect a list of prebuild output directories whose contents should be input to the build,
445452
// and a list of the files in those directories after running the commands.
446-
var derivedSourceFiles: [AbsolutePath] = []
453+
var derivedFiles: [AbsolutePath] = []
447454
var prebuildOutputDirs: [AbsolutePath] = []
448455
for command in pluginResult.prebuildCommands {
449456
self.observabilityScope.emit(info: "Running" + (command.configuration.displayName ?? command.configuration.executable.basename))
@@ -463,15 +470,15 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
463470
// Add any files found in the output directory declared for the prebuild command after the command ends.
464471
let outputFilesDir = command.outputFilesDirectory
465472
if let swiftFiles = try? self.fileSystem.getDirectoryContents(outputFilesDir).sorted() {
466-
derivedSourceFiles.append(contentsOf: swiftFiles.map{ outputFilesDir.appending(component: $0) })
473+
derivedFiles.append(contentsOf: swiftFiles.map{ outputFilesDir.appending(component: $0) })
467474
}
468475

469476
// Add the output directory to the list of directories whose structure should affect the build plan.
470477
prebuildOutputDirs.append(outputFilesDir)
471478
}
472479

473480
// Add the results of running any prebuild commands for this invocation.
474-
return PrebuildCommandResult(derivedSourceFiles: derivedSourceFiles, outputDirectories: prebuildOutputDirs)
481+
return PrebuildCommandResult(derivedFiles: derivedFiles, outputDirectories: prebuildOutputDirs)
475482
}
476483
}
477484

Sources/Build/BuildPlan.swift

Lines changed: 85 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,17 @@ public enum TargetBuildDescription {
174174
}
175175
}
176176

177+
/// The resources in this target.
178+
var resources: [Resource] {
179+
switch self {
180+
case .swift(let target):
181+
return target.resources
182+
case .clang(let target):
183+
// TODO: Clang targets should support generated resources in the future.
184+
return target.target.underlyingTarget.resources
185+
}
186+
}
187+
177188
/// Path to the bundle generated for this module (if any).
178189
var bundlePath: AbsolutePath? {
179190
switch self {
@@ -239,7 +250,7 @@ public final class ClangTargetBuildDescription {
239250

240251
/// Path to the bundle generated for this module (if any).
241252
var bundlePath: AbsolutePath? {
242-
buildParameters.bundlePath(for: target)
253+
target.underlyingTarget.bundleName.map(buildParameters.bundlePath(named:))
243254
}
244255

245256
/// The modulemap file for this target, if any.
@@ -542,16 +553,28 @@ public final class SwiftTargetBuildDescription {
542553
/// These are the source files derived from plugins.
543554
private var pluginDerivedSources: Sources
544555

556+
/// These are the resource files derived from plugins.
557+
private var pluginDerivedResources: [Resource]
558+
545559
/// Path to the bundle generated for this module (if any).
546560
var bundlePath: AbsolutePath? {
547-
buildParameters.bundlePath(for: target)
561+
if let bundleName = target.underlyingTarget.potentialBundleName, !resources.isEmpty {
562+
return buildParameters.bundlePath(named: bundleName)
563+
} else {
564+
return nil
565+
}
548566
}
549567

550568
/// The list of all source files in the target, including the derived ones.
551569
public var sources: [AbsolutePath] {
552570
target.sources.paths + derivedSources.paths + pluginDerivedSources.paths
553571
}
554572

573+
/// The list of all resource files in the target, including the derived ones.
574+
public var resources: [Resource] {
575+
target.underlyingTarget.resources + pluginDerivedResources
576+
}
577+
555578
/// The objects in this target.
556579
public var objects: [AbsolutePath] {
557580
let relativePaths = target.sources.relativePaths + derivedSources.relativePaths + pluginDerivedSources.relativePaths
@@ -633,16 +656,21 @@ public final class SwiftTargetBuildDescription {
633656
/// The results of running any prebuild commands for this target.
634657
public let prebuildCommandResults: [PrebuildCommandResult]
635658

659+
/// ObservabilityScope with which to emit diagnostics
660+
private let observabilityScope: ObservabilityScope
661+
636662
/// Create a new target description with target and build parameters.
637663
init(
638664
target: ResolvedTarget,
639665
toolsVersion: ToolsVersion,
666+
additionalFileRules: [FileRuleDescription] = [],
640667
buildParameters: BuildParameters,
641668
buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] = [],
642669
prebuildCommandResults: [PrebuildCommandResult] = [],
643670
isTestTarget: Bool? = nil,
644671
isTestDiscoveryTarget: Bool = false,
645-
fileSystem: FileSystem
672+
fileSystem: FileSystem,
673+
observabilityScope: ObservabilityScope
646674
) throws {
647675
guard target.underlyingTarget is SwiftTarget else {
648676
throw InternalError("underlying target type mismatch \(target)")
@@ -657,24 +685,60 @@ public final class SwiftTargetBuildDescription {
657685
self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build")
658686
self.derivedSources = Sources(paths: [], root: tempsPath.appending(component: "DerivedSources"))
659687
self.pluginDerivedSources = Sources(paths: [], root: buildParameters.dataPath)
688+
self.pluginDerivedResources = []
660689
self.buildToolPluginInvocationResults = buildToolPluginInvocationResults
661690
self.prebuildCommandResults = prebuildCommandResults
691+
self.observabilityScope = observabilityScope
692+
693+
func handlePluginDerivedFile(path absPath: AbsolutePath) {
694+
// 5.6 handled treated all generated files as sources.
695+
if toolsVersion <= .v5_6 {
696+
let relPath = absPath.relative(to: self.pluginDerivedSources.root)
697+
self.pluginDerivedSources.relativePaths.append(relPath)
698+
return
699+
}
700+
701+
var rule = TargetSourcesBuilder.computeRule(for: absPath, toolsVersion: toolsVersion, additionalFileRules: additionalFileRules, observabilityScope: observabilityScope)
702+
703+
// If we did not find a rule for a generated file, we treat it as to be processed for now. Eventually, we should handle generated files the same as other files and require explicit handling in the manifest for unknown types.
704+
if rule == .none {
705+
rule = .processResource(localization: .none)
706+
}
707+
708+
switch rule {
709+
case .compile:
710+
if absPath.extension == "swift" {
711+
let relPath = absPath.relative(to: self.pluginDerivedSources.root)
712+
self.pluginDerivedSources.relativePaths.append(relPath)
713+
} else {
714+
observabilityScope.emit(error: "Generated plugin source files can only use Swift: \(absPath)")
715+
}
716+
case .copy, .processResource:
717+
if let resource = TargetSourcesBuilder.resource(for: absPath, with: rule, defaultLocalization: target.defaultLocalization, targetName: target.name, targetPath: target.underlyingTarget.path, observabilityScope: observabilityScope) {
718+
self.pluginDerivedResources.append(resource)
719+
} else {
720+
// If this is reached, `TargetSourcesBuilder` already emitted a diagnostic, so we can ignore this case here.
721+
}
722+
case .header:
723+
observabilityScope.emit(error: "Headers generated by plugins are not supported at this time: \(absPath)")
724+
case .modulemap:
725+
observabilityScope.emit(error: "Module maps generated by plugins are not supported at this time: \(absPath)")
726+
case .ignored, .none:
727+
break
728+
}
729+
}
662730

663731
// Add any derived source files that were declared for any commands from plugin invocations.
664732
for command in buildToolPluginInvocationResults.reduce([], { $0 + $1.buildCommands }) {
665-
// TODO: What should we do if we find non-Swift sources here?
666733
for absPath in command.outputFiles {
667-
let relPath = absPath.relative(to: self.pluginDerivedSources.root)
668-
self.pluginDerivedSources.relativePaths.append(relPath)
734+
handlePluginDerivedFile(path: absPath)
669735
}
670736
}
671737

672738
// Add any derived source files that were discovered from output directories of prebuild commands.
673739
for result in self.prebuildCommandResults {
674-
// TODO: What should we do if we find non-Swift sources here?
675-
for path in result.derivedSourceFiles {
676-
let relPath = path.relative(to: self.pluginDerivedSources.root)
677-
self.pluginDerivedSources.relativePaths.append(relPath)
740+
for path in result.derivedFiles {
741+
handlePluginDerivedFile(path: path)
678742
}
679743
}
680744

@@ -1585,7 +1649,8 @@ public class BuildPlan {
15851649
toolsVersion: toolsVersion,
15861650
buildParameters: buildParameters,
15871651
isTestTarget: true,
1588-
fileSystem: fileSystem
1652+
fileSystem: fileSystem,
1653+
observabilityScope: observabilityScope
15891654
)
15901655

15911656
result.append((testProduct, desc))
@@ -1621,7 +1686,8 @@ public class BuildPlan {
16211686
buildParameters: buildParameters,
16221687
isTestTarget: true,
16231688
isTestDiscoveryTarget: true,
1624-
fileSystem: fileSystem
1689+
fileSystem: fileSystem,
1690+
observabilityScope: observabilityScope
16251691
)
16261692

16271693
result.append((testProduct, target))
@@ -1635,28 +1701,11 @@ public class BuildPlan {
16351701
return result
16361702
}
16371703

1638-
@available(*, deprecated, message: "use observability system instead")
1639-
public convenience init(
1640-
buildParameters: BuildParameters,
1641-
graph: PackageGraph,
1642-
buildToolPluginInvocationResults: [ResolvedTarget: [BuildToolPluginInvocationResult]] = [:],
1643-
prebuildCommandResults: [ResolvedTarget: [PrebuildCommandResult]] = [:],
1644-
diagnostics: DiagnosticsEngine,
1645-
fileSystem: FileSystem
1646-
) throws {
1647-
let observabilitySystem = ObservabilitySystem(diagnosticEngine: diagnostics)
1648-
try self.init(
1649-
buildParameters: buildParameters,
1650-
graph: graph,
1651-
fileSystem: fileSystem,
1652-
observabilityScope: observabilitySystem.topScope
1653-
)
1654-
}
1655-
16561704
/// Create a build plan with build parameters and a package graph.
16571705
public init(
16581706
buildParameters: BuildParameters,
16591707
graph: PackageGraph,
1708+
additionalFileRules: [FileRuleDescription] = [],
16601709
buildToolPluginInvocationResults: [ResolvedTarget: [BuildToolPluginInvocationResult]] = [:],
16611710
prebuildCommandResults: [ResolvedTarget: [PrebuildCommandResult]] = [:],
16621711
fileSystem: FileSystem,
@@ -1700,10 +1749,12 @@ public class BuildPlan {
17001749
targetMap[target] = try .swift(SwiftTargetBuildDescription(
17011750
target: target,
17021751
toolsVersion: toolsVersion,
1752+
additionalFileRules: additionalFileRules,
17031753
buildParameters: buildParameters,
17041754
buildToolPluginInvocationResults: buildToolPluginInvocationResults[target] ?? [],
17051755
prebuildCommandResults: prebuildCommandResults[target] ?? [],
1706-
fileSystem: fileSystem)
1756+
fileSystem: fileSystem,
1757+
observabilityScope: observabilityScope)
17071758
)
17081759
case is ClangTarget:
17091760
targetMap[target] = try .clang(ClangTargetBuildDescription(
@@ -2236,11 +2287,9 @@ private extension Basics.Diagnostic {
22362287
}
22372288

22382289
extension BuildParameters {
2239-
/// Returns a target's bundle path inside the build directory.
2240-
fileprivate func bundlePath(for target: ResolvedTarget) -> AbsolutePath? {
2241-
target.underlyingTarget.bundleName
2242-
.map{ $0 + triple.nsbundleExtension }
2243-
.map(buildPath.appending(component:))
2290+
/// Returns a named bundle's path inside the build directory.
2291+
fileprivate func bundlePath(named name: String) -> AbsolutePath {
2292+
return buildPath.appending(component: name + triple.nsbundleExtension)
22442293
}
22452294
}
22462295

Sources/Build/LLBuildManifestBuilder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ extension LLBuildManifestBuilder {
171171
let infoPlistDestination = RelativePath("Info.plist")
172172

173173
// Create a copy command for each resource file.
174-
for resource in target.target.underlyingTarget.resources {
174+
for resource in target.resources {
175175
let destination = bundlePath.appending(resource.destination)
176176
let (_, output) = addCopyCommand(from: resource.path, to: destination)
177177
outputs.append(output)

Sources/Commands/SwiftTool.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ public class SwiftTool {
752752
buildParameters: customBuildParameters ?? self.buildParameters(),
753753
cacheBuildManifest: cacheBuildManifest && self.canUseCachedBuildManifest(),
754754
packageGraphLoader: customPackageGraphLoader ?? graphLoader,
755+
additionalFileRules: FileRuleDescription.swiftpmFileTypes,
755756
pluginScriptRunner: self.getPluginScriptRunner(),
756757
pluginWorkDirectory: try self.getActiveWorkspace().location.pluginWorkingDirectory,
757758
disableSandboxForPluginCommands: self.options.security.shouldDisableSandbox,

Sources/PackageLoading/PackageBuilder.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ public final class PackageBuilder {
767767

768768
// FIXME: use identity instead?
769769
// The name of the bundle, if one is being generated.
770-
let bundleName = resources.isEmpty ? nil : self.manifest.displayName + "_" + potentialModule.name
770+
let potentialBundleName = self.manifest.displayName + "_" + potentialModule.name
771771

772772
if sources.relativePaths.isEmpty && resources.isEmpty {
773773
return nil
@@ -808,8 +808,9 @@ public final class PackageBuilder {
808808
if sources.hasSwiftSources {
809809
return SwiftTarget(
810810
name: potentialModule.name,
811-
bundleName: bundleName,
811+
potentialBundleName: potentialBundleName,
812812
type: targetType,
813+
path: potentialModule.path,
813814
sources: sources,
814815
resources: resources,
815816
ignored: ignored,
@@ -836,13 +837,14 @@ public final class PackageBuilder {
836837

837838
return try ClangTarget(
838839
name: potentialModule.name,
839-
bundleName: bundleName,
840+
potentialBundleName: potentialBundleName,
840841
cLanguageStandard: manifest.cLanguageStandard,
841842
cxxLanguageStandard: manifest.cxxLanguageStandard,
842843
includeDir: publicHeadersPath,
843844
moduleMapType: moduleMapType,
844845
headers: headers,
845846
type: targetType,
847+
path: potentialModule.path,
846848
sources: sources,
847849
resources: resources,
848850
ignored: ignored,
@@ -1362,6 +1364,7 @@ extension PackageBuilder {
13621364
return SwiftTarget(
13631365
name: name,
13641366
type: .snippet,
1367+
path: .root,
13651368
sources: sources,
13661369
dependencies: dependencies,
13671370
swiftVersion: try swiftVersion(),

0 commit comments

Comments
 (0)