Skip to content

Commit 98499b4

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

File tree

6 files changed

+141
-50
lines changed

6 files changed

+141
-50
lines changed

Sources/Build/BuildPlan.swift

Lines changed: 71 additions & 14 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+
// FIXME: Clang targets should support generated resources.
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 {
@@ -542,16 +553,31 @@ 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 target.underlyingTarget.bundleName != nil {
562+
return buildParameters.bundlePath(for: target)
563+
} else if !pluginDerivedResources.isEmpty {
564+
// FIXME: Use proper bundle name here.
565+
return buildParameters.bundlePath(named: "foo_\(target.name)")
566+
} else {
567+
return nil
568+
}
548569
}
549570

550571
/// The list of all source files in the target, including the derived ones.
551572
public var sources: [AbsolutePath] {
552573
target.sources.paths + derivedSources.paths + pluginDerivedSources.paths
553574
}
554575

576+
/// The list of all resource files in the target, including the derived ones.
577+
public var resources: [Resource] {
578+
target.underlyingTarget.resources + pluginDerivedResources
579+
}
580+
555581
/// The objects in this target.
556582
public var objects: [AbsolutePath] {
557583
let relativePaths = target.sources.relativePaths + derivedSources.relativePaths + pluginDerivedSources.relativePaths
@@ -633,6 +659,9 @@ public final class SwiftTargetBuildDescription {
633659
/// The results of running any prebuild commands for this target.
634660
public let prebuildCommandResults: [PrebuildCommandResult]
635661

662+
/// ObservabilityScope with which to emit diagnostics
663+
private let observabilityScope: ObservabilityScope
664+
636665
/// Create a new target description with target and build parameters.
637666
init(
638667
target: ResolvedTarget,
@@ -642,7 +671,8 @@ public final class SwiftTargetBuildDescription {
642671
prebuildCommandResults: [PrebuildCommandResult] = [],
643672
isTestTarget: Bool? = nil,
644673
isTestDiscoveryTarget: Bool = false,
645-
fileSystem: FileSystem
674+
fileSystem: FileSystem,
675+
observabilityScope: ObservabilityScope
646676
) throws {
647677
guard target.underlyingTarget is SwiftTarget else {
648678
throw InternalError("underlying target type mismatch \(target)")
@@ -657,24 +687,46 @@ public final class SwiftTargetBuildDescription {
657687
self.tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build")
658688
self.derivedSources = Sources(paths: [], root: tempsPath.appending(component: "DerivedSources"))
659689
self.pluginDerivedSources = Sources(paths: [], root: buildParameters.dataPath)
690+
self.pluginDerivedResources = []
660691
self.buildToolPluginInvocationResults = buildToolPluginInvocationResults
661692
self.prebuildCommandResults = prebuildCommandResults
693+
self.observabilityScope = observabilityScope
694+
695+
func handlePluginDerivedFile(path absPath: AbsolutePath) {
696+
// FIXME: Carry `additionalFileRules` until here.
697+
let rule = TargetSourcesBuilder.computeRule(for: absPath, toolsVersion: toolsVersion, additionalFileRules: [], observabilityScope: observabilityScope)
698+
print("Determined \(rule) for generated file at \(absPath)")
699+
switch rule {
700+
case .compile:
701+
if absPath.extension == "swift" {
702+
let relPath = absPath.relative(to: self.pluginDerivedSources.root)
703+
self.pluginDerivedSources.relativePaths.append(relPath)
704+
} else {
705+
// FIXME: Error for unsupported source file type.
706+
}
707+
case .copy, .processResource:
708+
if let resource = TargetSourcesBuilder.resource(for: absPath, with: rule, defaultLocalization: target.defaultLocalization, targetName: target.name, targetPath: target.underlyingTarget.path, observabilityScope: observabilityScope) {
709+
self.pluginDerivedResources.append(resource)
710+
} else {
711+
// FIXME: Error for not obtaining a `Resource` object for a file.
712+
}
713+
case .header, .ignored, .modulemap, .none:
714+
break // Ignored for now.
715+
}
716+
}
662717

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

672725
// Add any derived source files that were discovered from output directories of prebuild commands.
673726
for result in self.prebuildCommandResults {
674-
// TODO: What should we do if we find non-Swift sources here?
727+
// FIXME: `derivedSourceFiles` should probably be renamed here.
675728
for path in result.derivedSourceFiles {
676-
let relPath = path.relative(to: self.pluginDerivedSources.root)
677-
self.pluginDerivedSources.relativePaths.append(relPath)
729+
handlePluginDerivedFile(path: path)
678730
}
679731
}
680732

@@ -1585,7 +1637,8 @@ public class BuildPlan {
15851637
toolsVersion: toolsVersion,
15861638
buildParameters: buildParameters,
15871639
isTestTarget: true,
1588-
fileSystem: fileSystem
1640+
fileSystem: fileSystem,
1641+
observabilityScope: observabilityScope
15891642
)
15901643

15911644
result.append((testProduct, desc))
@@ -1621,7 +1674,8 @@ public class BuildPlan {
16211674
buildParameters: buildParameters,
16221675
isTestTarget: true,
16231676
isTestDiscoveryTarget: true,
1624-
fileSystem: fileSystem
1677+
fileSystem: fileSystem,
1678+
observabilityScope: observabilityScope
16251679
)
16261680

16271681
result.append((testProduct, target))
@@ -1703,7 +1757,8 @@ public class BuildPlan {
17031757
buildParameters: buildParameters,
17041758
buildToolPluginInvocationResults: buildToolPluginInvocationResults[target] ?? [],
17051759
prebuildCommandResults: prebuildCommandResults[target] ?? [],
1706-
fileSystem: fileSystem)
1760+
fileSystem: fileSystem,
1761+
observabilityScope: observabilityScope)
17071762
)
17081763
case is ClangTarget:
17091764
targetMap[target] = try .clang(ClangTargetBuildDescription(
@@ -2238,9 +2293,11 @@ private extension Basics.Diagnostic {
22382293
extension BuildParameters {
22392294
/// Returns a target's bundle path inside the build directory.
22402295
fileprivate func bundlePath(for target: ResolvedTarget) -> AbsolutePath? {
2241-
target.underlyingTarget.bundleName
2242-
.map{ $0 + triple.nsbundleExtension }
2243-
.map(buildPath.appending(component:))
2296+
target.underlyingTarget.bundleName.map(bundlePath(named:))
2297+
}
2298+
2299+
fileprivate func bundlePath(named: String) -> AbsolutePath {
2300+
return buildPath.appending(component: named + triple.nsbundleExtension)
22442301
}
22452302
}
22462303

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/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(),

Sources/PackageLoading/TargetSourcesBuilder.swift

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ public struct TargetSourcesBuilder {
7575
self.target = target
7676
self.defaultLocalization = defaultLocalization
7777
self.targetPath = path
78-
// In version 5.4 and earlier, SwiftPM did not support `additionalFileRules` and always implicitly included XCBuild file types.
79-
let actualAdditionalRules = (toolsVersion <= ToolsVersion.v5_4 ? FileRuleDescription.xcbuildFileTypes : additionalFileRules)
80-
self.rules = FileRuleDescription.builtinRules + actualAdditionalRules
78+
self.rules = Self.rules(additionalFileRules: additionalFileRules, toolsVersion: toolsVersion)
8179
self.toolsVersion = toolsVersion
8280
let excludedPaths = target.exclude.map { AbsolutePath($0, relativeTo: path) }
8381
self.excludedPaths = Set(excludedPaths)
@@ -125,6 +123,12 @@ public struct TargetSourcesBuilder {
125123
#endif
126124
}
127125

126+
private static func rules(additionalFileRules: [FileRuleDescription], toolsVersion: ToolsVersion) -> [FileRuleDescription] {
127+
// In version 5.4 and earlier, SwiftPM did not support `additionalFileRules` and always implicitly included XCBuild file types.
128+
let actualAdditionalRules = (toolsVersion <= ToolsVersion.v5_4 ? FileRuleDescription.xcbuildFileTypes : additionalFileRules)
129+
return FileRuleDescription.builtinRules + actualAdditionalRules
130+
}
131+
128132
@discardableResult
129133
private func validTargetPath(at: AbsolutePath) -> Error? {
130134
// Check if paths that are enumerated in targets: [] exist
@@ -185,26 +189,32 @@ public struct TargetSourcesBuilder {
185189
}
186190

187191
/// Compute the rule for the given path.
188-
private func computeRule(for path: AbsolutePath) -> FileRuleDescription.Rule {
192+
public static func computeRule(for path: AbsolutePath, toolsVersion: ToolsVersion, additionalFileRules: [FileRuleDescription], observabilityScope: ObservabilityScope) -> FileRuleDescription.Rule {
193+
let rules = Self.rules(additionalFileRules: additionalFileRules, toolsVersion: toolsVersion)
194+
// For now, we are not passing in any declared resources or sources here and instead handle any generated files automatically at the callsite. Eventually, we will want the ability to declare opinions for generated files in the manifest as well.
195+
return Self.computeRule(for: path, toolsVersion: toolsVersion, rules: rules, declaredResources: [], declaredSources: nil, observabilityScope: observabilityScope)
196+
}
197+
198+
private static func computeRule(for path: AbsolutePath, toolsVersion: ToolsVersion, rules: [FileRuleDescription], declaredResources: [(path: AbsolutePath, rule: TargetDescription.Resource.Rule)], declaredSources: [AbsolutePath]?, observabilityScope: ObservabilityScope) -> FileRuleDescription.Rule {
189199
var matchedRule: FileRuleDescription.Rule = .none
190200

191201
// First match any resources explicitly declared in the manifest file.
192-
for declaredResource in target.resources {
193-
let resourcePath = AbsolutePath(declaredResource.path, relativeTo: self.targetPath)
202+
for declaredResource in declaredResources {
203+
let resourcePath = declaredResource.path
194204
if path.isDescendantOfOrEqual(to: resourcePath) {
195205
if matchedRule != .none {
196-
self.observabilityScope.emit(error: "duplicate resource rule '\(declaredResource.rule)' found for file at '\(path)'")
206+
observabilityScope.emit(error: "duplicate resource rule '\(declaredResource.rule)' found for file at '\(path)'")
197207
}
198208
matchedRule = .init(declaredResource.rule)
199209
}
200210
}
201211

202212
// Match any sources explicitly declared in the manifest file.
203-
if let declaredSources = self.declaredSources {
213+
if let declaredSources = declaredSources {
204214
for sourcePath in declaredSources {
205215
if path.isDescendantOfOrEqual(to: sourcePath) {
206216
if matchedRule != .none {
207-
self.observabilityScope.emit(error: "duplicate rule found for file at '\(path)'")
217+
observabilityScope.emit(error: "duplicate rule found for file at '\(path)'")
208218
}
209219

210220
// Check for header files as they're allowed to be mixed with sources.
@@ -231,10 +241,10 @@ public struct TargetSourcesBuilder {
231241
let effectiveRules: [FileRuleDescription] = {
232242
// Don't automatically match compile rules if target's sources are
233243
// explicitly declared in the package manifest.
234-
if target.sources != nil {
235-
return self.rules.filter { $0.rule != .compile }
244+
if declaredSources != nil {
245+
return rules.filter { $0.rule != .compile }
236246
}
237-
return self.rules
247+
return rules
238248
}()
239249

240250
if let needle = effectiveRules.first(where: { $0.match(path: path, toolsVersion: toolsVersion) }) {
@@ -247,8 +257,13 @@ public struct TargetSourcesBuilder {
247257
return matchedRule
248258
}
249259

260+
private func computeRule(for path: AbsolutePath) -> FileRuleDescription.Rule {
261+
let declaredResources = target.resources.map { (path: AbsolutePath($0.path, relativeTo: self.targetPath), rule: $0.rule) }
262+
return Self.computeRule(for: path, toolsVersion: toolsVersion, rules: rules, declaredResources: declaredResources, declaredSources: declaredSources, observabilityScope: observabilityScope)
263+
}
264+
250265
/// Returns the `Resource` file associated with a file and a particular rule, if there is one.
251-
private func resource(for path: AbsolutePath, with rule: FileRuleDescription.Rule) -> Resource? {
266+
public static func resource(for path: AbsolutePath, with rule: FileRuleDescription.Rule, defaultLocalization: String?, targetName: String, targetPath: AbsolutePath, observabilityScope: ObservabilityScope) -> Resource? {
252267
switch rule {
253268
case .compile, .header, .none, .modulemap, .ignored:
254269
return nil
@@ -275,7 +290,7 @@ public struct TargetSourcesBuilder {
275290
// If a resource is both inside a localization directory and has an explicit localization, it's ambiguous.
276291
guard implicitLocalization == nil || explicitLocalization == nil else {
277292
let relativePath = path.relative(to: targetPath)
278-
self.observabilityScope.emit(.localizationAmbiguity(path: relativePath, targetName: target.name))
293+
observabilityScope.emit(.localizationAmbiguity(path: relativePath, targetName: targetName))
279294
return nil
280295
}
281296

@@ -285,6 +300,10 @@ public struct TargetSourcesBuilder {
285300
}
286301
}
287302

303+
private func resource(for path: AbsolutePath, with rule: FileRuleDescription.Rule) -> Resource? {
304+
return Self.resource(for: path, with: rule, defaultLocalization: defaultLocalization, targetName: target.name, targetPath: targetPath, observabilityScope: observabilityScope)
305+
}
306+
288307
private func diagnoseConflictingResources(in resources: [Resource]) {
289308
let duplicateResources = resources.spm_findDuplicateElements(by: \.destination)
290309
for resources in duplicateResources {

0 commit comments

Comments
 (0)