Skip to content

Commit ae1cb55

Browse files
authored
Check for the include directory for a library Clang target (#3583)
* Throw an error for a library clang target if include dir is not found * Added toolchain version check / Added a test * cleanup * cleanup diagnostic msg
1 parent ffe154f commit ae1cb55

File tree

8 files changed

+83
-6
lines changed

8 files changed

+83
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// swift-tools-version:5.5
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "Cfactorial",
8+
products: [
9+
// Products define the executables and libraries a package produces, and make them visible to other packages.
10+
.library(
11+
name: "Cfactorial",
12+
targets: ["Cfactorial"]),
13+
],
14+
dependencies: [
15+
// Dependencies declare other packages that this package depends on.
16+
// .package(url: /* package url */, from: "1.0.0"),
17+
],
18+
targets: [
19+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
20+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
21+
.target(
22+
name: "Cfactorial",
23+
dependencies: [],
24+
path: "Sources/factorial"),
25+
]
26+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//#include "include/factorial.h"
2+
#include "factorial.h"
3+
long factorial(int n) {
4+
if (n == 0 || n == 1) return 1;
5+
return n * factorial(n-1);
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef factorial_h
2+
#define factorial_h
3+
4+
#include <stdio.h>
5+
6+
long factorial(int n);
7+
8+
#endif /* factorial_h */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// swift-tools-version:5.5
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "Client",
8+
dependencies: [
9+
// Dependencies declare other packages that this package depends on.
10+
// .package(url: /* package url */, from: "1.0.0"),
11+
.package(path: "Cfactorial")
12+
],
13+
targets: [
14+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
15+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
16+
.executableTarget(
17+
name: "Client",
18+
dependencies: ["Cfactorial"]),
19+
]
20+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Cfactorial
2+
3+
print(factorial(5))
4+
print("Hello, world!")

Sources/PackageLoading/PackageBuilder.swift

+6-4
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ extension ModuleError: CustomStringConvertible {
9696
(cycle.path + cycle.cycle).joined(separator: " -> ") +
9797
" -> " + cycle.cycle[0]
9898
case .invalidPublicHeadersDirectory(let name):
99-
return "public headers directory path for '\(name)' is invalid or not contained in the target"
99+
return "public headers (\"include\") directory path for '\(name)' is invalid or not contained in the target"
100100
case .overlappingSources(let target, let sources):
101101
return "target '\(target)' has sources overlapping sources: " +
102102
sources.map({ $0.description }).joined(separator: ", ")
@@ -914,13 +914,15 @@ public final class PackageBuilder {
914914
// It's not a Swift target, so it's a Clang target (those are the only two types of source target currently supported).
915915

916916
// First determine the type of module map that will be appropriate for the target based on its header layout.
917-
// FIXME: We should really be checking the target type to see whether it is one that can vend headers, not just check for the existence of the public headers path. But right now we have now way of distinguishing between, for example, a library and an executable. The semantics here should be to only try to detect the header layout of targets that can vend public headers.
918917
let moduleMapType: ModuleMapType
918+
919919
if fileSystem.exists(publicHeadersPath) {
920920
let moduleMapGenerator = ModuleMapGenerator(targetName: potentialModule.name, moduleName: potentialModule.name.spm_mangledToC99ExtendedIdentifier(), publicHeadersDir: publicHeadersPath, fileSystem: fileSystem)
921921
moduleMapType = moduleMapGenerator.determineModuleMapType(diagnostics: diagnostics)
922-
}
923-
else {
922+
} else if targetType == .library, manifest.toolsVersion >= .v5_5 {
923+
// If this clang target is a library, it must contain "include" directory.
924+
throw ModuleError.invalidPublicHeadersDirectory(potentialModule.name)
925+
} else {
924926
moduleMapType = .none
925927
}
926928

Tests/FunctionalTests/CFamilyTargetTests.swift

+11
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ class CFamilyTargetTestCase: XCTestCase {
6464
XCTAssertDirectoryContainsFile(dir: debugPath, filename: "UmbrellaHeader.c.o")
6565
}
6666
}
67+
68+
func testNoIncludeDirCheck() {
69+
fixture(name: "CFamilyTargets/CLibraryNoIncludeDir") { prefix in
70+
XCTAssertThrowsError(try executeSwiftBuild(prefix), "This build should throw an error") { err in
71+
// The err.localizedDescription doesn't capture the detailed error string so interpolate
72+
let errStr = "\(err)"
73+
let missingIncludeDirStr = "\(ModuleError.invalidPublicHeadersDirectory("Cfactorial"))"
74+
XCTAssert(errStr.contains(missingIncludeDirStr))
75+
}
76+
}
77+
}
6778

6879
func testCanForwardExtraFlagsToClang() {
6980
// Try building a fixture which needs extra flags to be able to build.

Tests/PackageLoadingTests/PackageBuilderTests.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,7 @@ class PackageBuilderTests: XCTestCase {
11041104
)
11051105

11061106
PackageBuilderTester(manifest, in: fs) { _, diagnostics in
1107-
diagnostics.check(diagnostic: "public headers directory path for 'Foo' is invalid or not contained in the target", behavior: .error)
1107+
diagnostics.check(diagnostic: "public headers (\"include\") directory path for 'Foo' is invalid or not contained in the target", behavior: .error)
11081108
}
11091109

11101110
manifest = Manifest.createV4Manifest(
@@ -1114,7 +1114,7 @@ class PackageBuilderTests: XCTestCase {
11141114
]
11151115
)
11161116
PackageBuilderTester(manifest, in: fs) { _, diagnostics in
1117-
diagnostics.check(diagnostic: "public headers directory path for 'Bar' is invalid or not contained in the target", behavior: .error)
1117+
diagnostics.check(diagnostic: "public headers (\"include\") directory path for 'Bar' is invalid or not contained in the target", behavior: .error)
11181118
}
11191119
}
11201120

0 commit comments

Comments
 (0)