Skip to content

Commit 842c9e8

Browse files
committed
[PackageLoading] Pick up resource files in a target
This adds ability to automatically load sources and resources files in a target based on rules. <rdar://problem/56480468>
1 parent f945f25 commit 842c9e8

File tree

3 files changed

+531
-107
lines changed

3 files changed

+531
-107
lines changed

Sources/PackageLoading/PackageBuilder.swift

Lines changed: 36 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ public final class PackageBuilder {
620620
moduleDependencies: [Target],
621621
productDeps: [(name: String, package: String?)]
622622
) throws -> Target? {
623+
guard let manifestTarget = manifestTarget else { return nil }
623624

624625
// Create system library target.
625626
if potentialModule.type == .system {
@@ -632,8 +633,8 @@ public final class PackageBuilder {
632633
name: potentialModule.name,
633634
platforms: self.platforms(),
634635
path: potentialModule.path, isImplicit: false,
635-
pkgConfig: manifestTarget?.pkgConfig,
636-
providers: manifestTarget?.providers
636+
pkgConfig: manifestTarget.pkgConfig,
637+
providers: manifestTarget.providers
637638
)
638639
}
639640

@@ -643,118 +644,51 @@ public final class PackageBuilder {
643644
diagnostics.emit(.duplicateTargetDependency(dependency: $0, target: potentialModule.name))
644645
}
645646

647+
// Create the build setting assignment table for this target.
648+
let buildSettings = try self.buildSettings(for: manifestTarget, targetRoot: potentialModule.path)
649+
646650
// Compute the path to public headers directory.
647-
let publicHeaderComponent = manifestTarget?.publicHeadersPath ?? ClangTarget.defaultPublicHeadersComponent
651+
let publicHeaderComponent = manifestTarget.publicHeadersPath ?? ClangTarget.defaultPublicHeadersComponent
648652
let publicHeadersPath = potentialModule.path.appending(try RelativePath(validating: publicHeaderComponent))
649653
guard publicHeadersPath.contains(potentialModule.path) else {
650654
throw ModuleError.invalidPublicHeadersDirectory(potentialModule.name)
651655
}
652656

653-
// Compute the excluded paths in the target.
654-
let targetExcludedPaths: Set<AbsolutePath>
655-
if let excludedSubPaths = manifestTarget?.exclude {
656-
let excludedPaths = excludedSubPaths.map({ potentialModule.path.appending(RelativePath($0)) })
657-
targetExcludedPaths = Set(excludedPaths)
658-
} else {
659-
targetExcludedPaths = []
660-
}
661-
662-
// Contains the set of sources for this target.
663-
var walked = Set<AbsolutePath>()
664-
665-
// Contains the paths we need to recursively iterate.
666-
var pathsToWalk = [AbsolutePath]()
667-
668-
// If there are sources defined in the target use that.
669-
if let definedSources = manifestTarget?.sources {
670-
for definedSource in definedSources {
671-
let definedSourcePath = potentialModule.path.appending(RelativePath(definedSource))
672-
if fileSystem.isDirectory(definedSourcePath) {
673-
// If this is a directory, add it to the list of paths to walk.
674-
pathsToWalk.append(definedSourcePath)
675-
} else if fileSystem.isFile(definedSourcePath) {
676-
// Otherwise, this is a sourcefile.
677-
walked.insert(definedSourcePath)
678-
} else {
679-
// FIXME: Should we emit warning about this declared thing or silently ignore?
680-
}
681-
}
682-
} else {
683-
// Use the top level target path as the path to be walked.
684-
pathsToWalk.append(potentialModule.path)
685-
}
686-
687-
// Walk each path and form our set of possible source files.
688-
for pathToWalk in pathsToWalk {
689-
let contents = try walk(pathToWalk, fileSystem: fileSystem, recursing: { path in
690-
// Exclude the public header directory.
691-
if path == publicHeadersPath { return false }
692-
693-
// Exclude if it in the excluded paths of the target.
694-
if targetExcludedPaths.contains(path) { return false }
695-
696-
// Exclude the directories that should never be walked.
697-
let base = path.basename
698-
if base.hasSuffix(".xcodeproj") || base.hasSuffix(".playground") || base.hasPrefix(".") {
699-
return false
700-
}
701-
702-
return true
703-
}).map({$0})
704-
walked.formUnion(contents)
705-
}
706-
707-
// Make sure there is no modulemap mixed with the sources.
708-
if let path = walked.first(where: { $0.basename == moduleMapFilename }) {
709-
throw ModuleError.invalidLayout(.modulemapInSources(path))
710-
}
711-
// Select any source files for the C-based languages and for Swift.
712-
let sources = walked.filter(isValidSource).filter({ !targetExcludedPaths.contains($0) })
713-
let clangSources = sources.filter({ SupportedLanguageExtension.clangTargetExtensions(toolsVersion: self.manifest.toolsVersion).contains($0.extension!)})
714-
let swiftSources = sources.filter({ SupportedLanguageExtension.swiftExtensions.contains($0.extension!) })
715-
assert(sources.count == clangSources.count + swiftSources.count)
716-
717-
// Create the build setting assignment table for this target.
718-
let buildSettings = try self.buildSettings(for: manifestTarget, targetRoot: potentialModule.path)
719-
let resources = self.computeResources(for: manifestTarget, targetRoot: potentialModule.path)
657+
let sourcesBuilder = TargetSourcesBuilder(
658+
packageName: manifest.name,
659+
packagePath: packagePath,
660+
target: manifestTarget,
661+
path: potentialModule.path,
662+
extraExcludes: [publicHeadersPath],
663+
toolsVersion: manifest.toolsVersion,
664+
fs: fileSystem,
665+
diags: diagnostics
666+
)
667+
let (sources, resources) = try sourcesBuilder.run()
720668

721669
// The name of the bundle, if one is being generated.
722-
var bundleName: String?
670+
let bundleName = resources.isEmpty ? nil : manifest.name + "_" + potentialModule.name
723671

724-
// FIXME: This needs to depend on if we have *any* resources, not just explicitly
725-
// declared ones.
726-
if !resources.isEmpty {
727-
bundleName = manifest.name + "_" + potentialModule.name
672+
if sources.relativePaths.isEmpty && resources.isEmpty {
673+
return nil
728674
}
675+
try validateSourcesOverlapping(forTarget: potentialModule.name, sources: sources.paths)
729676

730677
// Create and return the right kind of target depending on what kind of sources we found.
731-
if clangSources.isEmpty {
732-
guard !swiftSources.isEmpty else { return nil }
733-
let swiftSources = Array(swiftSources)
734-
try validateSourcesOverlapping(forTarget: potentialModule.name, sources: swiftSources)
735-
736-
737-
// No C sources, so we expect to have Swift sources, and we create a Swift target.
678+
if sources.hasSwiftSources {
738679
return SwiftTarget(
739680
name: potentialModule.name,
740681
bundleName: bundleName,
741682
platforms: self.platforms(),
742683
isTest: potentialModule.isTest,
743-
sources: Sources(paths: swiftSources, root: potentialModule.path),
684+
sources: sources,
744685
resources: resources,
745686
dependencies: moduleDependencies,
746687
productDependencies: productDeps,
747688
swiftVersion: try swiftVersion(),
748689
buildSettings: buildSettings
749690
)
750691
} else {
751-
// No Swift sources, so we expect to have C sources, and we create a C target.
752-
guard swiftSources.isEmpty else { throw Target.Error.mixedSources(potentialModule.path) }
753-
let cSources = Array(clangSources)
754-
try validateSourcesOverlapping(forTarget: potentialModule.name, sources: cSources)
755-
756-
let sources = Sources(paths: cSources, root: potentialModule.path)
757-
758692
return ClangTarget(
759693
name: potentialModule.name,
760694
bundleName: bundleName,
@@ -772,23 +706,6 @@ public final class PackageBuilder {
772706
}
773707
}
774708

775-
/// Compute the resources in the target.
776-
// FIXME: This is prelimary logic just to get things started.
777-
func computeResources(
778-
for target: TargetDescription?,
779-
targetRoot: AbsolutePath
780-
) -> [Resource] {
781-
guard let target = target else { return [] }
782-
var result: [Resource] = []
783-
784-
for resource in target.resources {
785-
let path = targetRoot.appending(RelativePath(resource.path))
786-
result += [Resource(rule: resource.rule, path: path)]
787-
}
788-
789-
return result
790-
}
791-
792709
/// Creates build setting assignment table for the given target.
793710
func buildSettings(for target: TargetDescription?, targetRoot: AbsolutePath) throws -> BuildSettings.AssignmentTable {
794711
var table = BuildSettings.AssignmentTable()
@@ -1197,3 +1114,15 @@ private extension Manifest {
11971114
return Set(names)
11981115
}
11991116
}
1117+
1118+
extension Sources {
1119+
var hasSwiftSources: Bool {
1120+
paths.first?.extension == "swift"
1121+
}
1122+
1123+
var containsMixedLanguage: Bool {
1124+
let swiftSources = relativePaths.filter{ $0.extension == "swift" }
1125+
if swiftSources.isEmpty { return false }
1126+
return swiftSources.count != relativePaths.count
1127+
}
1128+
}

0 commit comments

Comments
 (0)