Skip to content

Commit b56f100

Browse files
committed
Allow sources anywhere in ./Sources when only one target is present
After getting some feedback about the added `path` argument for executable packages generated with `swift package init` (swiftlang#6144), this change allows a target's sources to occupy the entire sources directory when there is only one target in the package. All package types can benefit from this. When there is more than one target in a package, the existing requirements for target sources still apply. This change should be compatible with existing layouts as well. If there is only a single target in a package, then sources can of course continue to exist in `./Sources/<target>`. Amend the `executable` template's generated manifest to not include the `path` argument anymore. rdar://106829666
1 parent 57d829a commit b56f100

File tree

3 files changed

+67
-20
lines changed

3 files changed

+67
-20
lines changed

Sources/PackageLoading/PackageBuilder.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,12 @@ public final class PackageBuilder {
526526
return path
527527
}
528528

529+
// If there is only one target defined, it may be allowed to occupy the
530+
// entire predefined target directory.
531+
if self.manifest.targets.count == 1 {
532+
return predefinedTargetDirectory.path
533+
}
534+
529535
// Otherwise, if the path "exists" then the case in manifest differs from the case on the file system.
530536
if fileSystem.isDirectory(path) {
531537
self.observabilityScope.emit(.targetNameHasIncorrectCase(target: target.name))

Sources/Workspace/InitPackage.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,7 @@ public final class InitPackage {
257257
if packageType == .executable {
258258
param += """
259259
.executableTarget(
260-
name: "\(pkgname)",
261-
path: "Sources"),
260+
name: "\(pkgname)"),
262261
]
263262
"""
264263
} else if packageType == .tool {

Tests/PackageLoadingTests/PackageBuilderTests.swift

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,9 +1000,30 @@ class PackageBuilderTests: XCTestCase {
10001000

10011001
func testManifestTargetDeclErrors() throws {
10021002
do {
1003-
// Reference a target which doesn't exist.
1003+
// Single target: Sources are expected in ./Sources.
10041004
let fs = InMemoryFileSystem(emptyFiles:
1005-
"/Foo.swift")
1005+
"/Sources/Foo.swift")
1006+
1007+
let manifest = Manifest.createRootManifest(
1008+
displayName: "pkg",
1009+
targets: [
1010+
try TargetDescription(name: "Random"),
1011+
]
1012+
)
1013+
PackageBuilderTester(manifest, in: fs) { package, diagnostics in
1014+
package.checkModule("Random") { result in
1015+
XCTAssertEqual("/Sources", result.target.path)
1016+
}
1017+
}
1018+
}
1019+
1020+
do {
1021+
// Single target: Sources are expected in ./Sources.
1022+
// In this case, there is a stray source file at the top-level, and no sources
1023+
// under ./Sources, so the target Random has no sources.
1024+
// This results in a *warning* that there are no sources for the target.
1025+
let fs = InMemoryFileSystem(emptyFiles:
1026+
"/Stray.swift")
10061027

10071028
let manifest = Manifest.createRootManifest(
10081029
displayName: "pkg",
@@ -1011,7 +1032,43 @@ class PackageBuilderTests: XCTestCase {
10111032
]
10121033
)
10131034
PackageBuilderTester(manifest, in: fs) { _, diagnostics in
1014-
diagnostics.check(diagnostic: .contains("Source files for target Random should be located under 'Sources/Random'"), severity: .error)
1035+
diagnostics.check(diagnostic: .contains("Source files for target Random should be located under /Sources"), severity: .warning)
1036+
}
1037+
}
1038+
1039+
do {
1040+
// Single target: Sources are expected in ./Sources. In this case,
1041+
// there is a stray source file at the top-level which is ignored.
1042+
let fs = InMemoryFileSystem(emptyFiles:
1043+
"/Stray.swift",
1044+
"/Sources/Random.swift")
1045+
1046+
let manifest = Manifest.createRootManifest(
1047+
displayName: "pkg",
1048+
targets: [
1049+
try TargetDescription(name: "Random"),
1050+
]
1051+
)
1052+
PackageBuilderTester(manifest, in: fs) { package, diagnostics in
1053+
package.checkModule("Random")
1054+
}
1055+
}
1056+
1057+
do {
1058+
// Multiple targets: Sources are expected in their respective subdirectories
1059+
// under Sources
1060+
let fs = InMemoryFileSystem(emptyFiles:
1061+
"/Foo.swift")
1062+
1063+
let manifest = Manifest.createRootManifest(
1064+
displayName: "pkg",
1065+
targets: [
1066+
try TargetDescription(name: "TargetA"),
1067+
try TargetDescription(name: "TargetB"),
1068+
]
1069+
)
1070+
PackageBuilderTester(manifest, in: fs) { _, diagnostics in
1071+
diagnostics.check(diagnostic: .contains("Source files for target TargetA should be located under 'Sources/TargetA'" ), severity: .error)
10151072
}
10161073
}
10171074

@@ -1060,21 +1117,6 @@ class PackageBuilderTests: XCTestCase {
10601117
}
10611118
}
10621119

1063-
do {
1064-
let fs = InMemoryFileSystem(emptyFiles:
1065-
"/Source/pkg/Foo.swift")
1066-
// Reference invalid target.
1067-
let manifest = Manifest.createRootManifest(
1068-
displayName: "pkg",
1069-
targets: [
1070-
try TargetDescription(name: "foo"),
1071-
]
1072-
)
1073-
PackageBuilderTester(manifest, in: fs) { _, diagnotics in
1074-
diagnotics.check(diagnostic: .contains("Source files for target foo should be located under 'Sources/foo'"), severity: .error)
1075-
}
1076-
}
1077-
10781120
do {
10791121
let fs = InMemoryFileSystem()
10801122
// Binary target.

0 commit comments

Comments
 (0)