Skip to content

Commit b67ac83

Browse files
authored
Merge pull request #2815 from abertelrud/eng/warn-about-clang-targets-with-nonmodular-header-layout
Add a warning for cases in which SwiftPM generates a module map for unsupported header layouts
2 parents 42eb57e + fc0a871 commit b67ac83

File tree

9 files changed

+54
-20
lines changed

9 files changed

+54
-20
lines changed

Documentation/Usage.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -724,9 +724,10 @@ automatically generate a modulemap for each C language library target for these
724724
3 cases:
725725

726726
* If `include/Foo/Foo.h` exists and `Foo` is the only directory under the
727-
include directory, then `include/Foo/Foo.h` becomes the umbrella header.
727+
include directory, and the include directory contains no header files, then
728+
`include/Foo/Foo.h` becomes the umbrella header.
728729

729-
* If `include/Foo.h` exists and `include` contains no other subdirectory then
730+
* If `include/Foo.h` exists and `include` contains no other subdirectory, then
730731
`include/Foo.h` becomes the umbrella header.
731732

732733
* Otherwise, if the `include` directory only contains header files and no other

Fixtures/CFamilyTargets/ModuleMapGenerationCases/Package.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@ let package = Package(
66
targets: [
77
.target(
88
name: "Baz",
9-
dependencies: ["FlatInclude", "UmbrellaHeader", "UmbellaModuleNameInclude", "UmbrellaHeaderFlat"]),
9+
dependencies: ["FlatInclude", "NonModuleDirectoryInclude", "UmbrellaHeader", "UmbrellaDirectoryInclude", "UmbrellaHeaderFlat"]),
1010
.target(
1111
name: "FlatInclude",
1212
dependencies: []),
1313
.target(
1414
name: "NoIncludeDir",
1515
dependencies: []),
1616
.target(
17-
name: "UmbellaModuleNameInclude",
17+
name: "NonModuleDirectoryInclude",
18+
dependencies: []),
19+
.target(
20+
name: "UmbrellaDirectoryInclude",
1821
dependencies: []),
1922
.target(
2023
name: "UmbrellaHeader",

Fixtures/CFamilyTargets/ModuleMapGenerationCases/Sources/Baz/main.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import UmbellaModuleNameInclude
1+
import UmbrellaDirectoryInclude
22
import FlatInclude
33
import UmbrellaHeader
44
import UmbrellaHeaderFlat
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "NonModuleDirectoryInclude/Maz.h"
2+
3+
int maz() {
4+
int a = 6;
5+
int b = a;
6+
a = b;
7+
return a;
8+
}

Fixtures/CFamilyTargets/ModuleMapGenerationCases/Sources/UmbellaModuleNameInclude/Jaz.c renamed to Fixtures/CFamilyTargets/ModuleMapGenerationCases/Sources/UmbrellaDirectoryInclude/Jaz.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "UmbellaModuleNameInclude/Paz.h"
1+
#include "Paz.h"
22

33
int jaz() {
44
int a = 6;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
int jaz();

Sources/PackageLoading/ModuleMapGenerator.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
4+
Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -44,11 +44,11 @@ extension ClangTarget: ModuleMapProtocol {
4444
///
4545
/// Modulemap is generated under the following rules provided it is not already present in include directory:
4646
///
47-
/// * "include/foo/foo.h" exists and `foo` is the only directory under include directory.
47+
/// * "include/foo/foo.h" exists and `foo` is the only directory under the "include" directory, and the "include" directory contains no header files:
4848
/// Generates: `umbrella header "/path/to/include/foo/foo.h"`
49-
/// * "include/foo.h" exists and include contains no other directory.
49+
/// * "include/foo.h" exists and "include" contains no other subdirectory:
5050
/// Generates: `umbrella header "/path/to/include/foo.h"`
51-
/// * Otherwise in all other cases.
51+
/// * Otherwise, if the "include" directory only contains header files and no other subdirectory:
5252
/// Generates: `umbrella "path/to/include"`
5353
public struct ModuleMapGenerator {
5454

@@ -81,8 +81,7 @@ public struct ModuleMapGenerator {
8181
}
8282
}
8383

84-
/// Create the synthesized modulemap, if necessary.
85-
/// Note: modulemap is not generated for test targets.
84+
/// Generates a modulemap based on the layout of the target's public headers. This is only valid for library targets.
8685
public mutating func generateModuleMap(inDir wd: AbsolutePath) throws {
8786
assert(target.type == .library)
8887

@@ -105,8 +104,10 @@ public struct ModuleMapGenerator {
105104
let files = walked.filter({ fileSystem.isFile($0) && $0.suffix == ".h" })
106105
let dirs = walked.filter({ fileSystem.isDirectory($0) })
107106

107+
// If 'include/ModuleName.h' exists, then use it as the umbrella header (this is case 2 at https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#creating-c-language-targets).
108108
let umbrellaHeaderFlat = includeDir.appending(component: target.c99name + ".h")
109109
if fileSystem.isFile(umbrellaHeaderFlat) {
110+
// In this case, 'include' is expected to contain no subdirectories.
110111
guard dirs.isEmpty else {
111112
throw ModuleMapError.unsupportedIncludeLayoutForModule(
112113
target.name,
@@ -117,8 +118,10 @@ public struct ModuleMapGenerator {
117118
}
118119
diagnoseInvalidUmbrellaHeader(includeDir)
119120

121+
// If 'include/ModuleName/ModuleName.h' exists, then use it as the umbrella header (this is case 1 at Documentation/Usage.md#creating-c-language-targets).
120122
let umbrellaHeader = includeDir.appending(components: target.c99name, target.c99name + ".h")
121123
if fileSystem.isFile(umbrellaHeader) {
124+
// In this case, 'include' is expected to contain no subdirectories other than 'ModuleName', and no header files.
122125
guard dirs.count == 1 else {
123126
throw ModuleMapError.unsupportedIncludeLayoutForModule(
124127
target.name,
@@ -134,6 +137,17 @@ public struct ModuleMapGenerator {
134137
}
135138
diagnoseInvalidUmbrellaHeader(includeDir.appending(component: target.c99name))
136139

140+
// Otherwise, if 'include' contains only header files and no subdirectories, use it as the umbrella directory (this is case 3 at https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#creating-c-language-targets).
141+
if files.count == walked.count {
142+
try createModuleMap(inDir: wd, type: .directory(includeDir))
143+
return
144+
}
145+
146+
// Otherwise, the target's public headers are considered to be incompatible with modules. Other C targets can still import them, but Swift won't be able to see them. This is documented as an error, but because SwiftPM has previously allowed it (creating module maps that then cause errors when used), we instead emit a warning and for now, continue to emit what SwiftPM has historically emitted (an umbrella directory include).
147+
warningStream <<< "warning: the include directory of target '\(target.name)' has "
148+
warningStream <<< "a layout that is incompatible with modules; consider adding a "
149+
warningStream <<< "custom module map to the target"
150+
warningStream.flush()
137151
try createModuleMap(inDir: wd, type: .directory(includeDir))
138152
}
139153

Tests/PackageLoadingTests/ModuleMapGenerationTests.swift

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
4+
Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See http://swift.org/LICENSE.txt for license information
@@ -77,13 +77,6 @@ class ModuleMapGeneration: XCTestCase {
7777
"/Foo.c")
7878
checkExpected()
7979

80-
// FIXME: Should this be allowed?
81-
fs = InMemoryFileSystem(emptyFiles:
82-
"/include/Baz/Foo.h",
83-
"/include/Bar/Bar.h",
84-
"/Foo.c")
85-
checkExpected()
86-
8780
fs = InMemoryFileSystem(emptyFiles:
8881
"/include/Baz.h",
8982
"/include/Bar.h",
@@ -111,6 +104,20 @@ class ModuleMapGeneration: XCTestCase {
111104
result.check(value: expected.bytes)
112105
result.checkDiagnostics("warning: /include/F-o-o.h should be renamed to /include/F_o_o.h to be used as an umbrella header")
113106
}
107+
108+
fs = InMemoryFileSystem(emptyFiles:
109+
"/include/Baz/Foo.h",
110+
"/include/Bar/Bar.h",
111+
"/Foo.c")
112+
let expected2 = BufferedOutputByteStream()
113+
expected2 <<< "module Foo {\n"
114+
expected2 <<< " umbrella \"/include\"\n"
115+
expected2 <<< " export *\n"
116+
expected2 <<< "}\n"
117+
ModuleMapTester("Foo", in: fs) { result in
118+
result.check(value: expected2.bytes)
119+
result.checkDiagnostics("warning: the include directory of target \'Foo\' has a layout that is incompatible with modules; consider adding a custom module map to the target")
120+
}
114121
}
115122

116123
func testUnsupportedLayouts() throws {

0 commit comments

Comments
 (0)