Skip to content

Commit 90abb3c

Browse files
committed
[Build] Add lang std flag per source file
We were adding language standard flag per target which is wrong because a Clang target can contain mixed C and C++ files. - <rdar://problem/35782488> [SR-6430]: [SwiftPM] Build error passing invalid C standard to a C source file
1 parent 4f7a99f commit 90abb3c

File tree

6 files changed

+45
-26
lines changed

6 files changed

+45
-26
lines changed

Sources/Build/BuildPlan.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,6 @@ public final class ClangTargetDescription {
193193
#endif
194194
args += ["-fblocks"]
195195
args += ["-fmodules", "-fmodule-name=" + target.c99name]
196-
if let languageStandard = clangTarget.languageStandard {
197-
args += ["-std=\(languageStandard)"]
198-
}
199196
args += ["-I", clangTarget.includeDir.asString]
200197
args += additionalFlags
201198
args += moduleCacheArgs

Sources/Build/llbuild.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,25 @@ public struct LLBuildManifestGenerator {
201201

202202
/// Create a llbuild target for a Clang target description.
203203
private func createClangCompileTarget(_ target: ClangTargetDescription) -> Target {
204+
205+
let standards = [
206+
(target.clangTarget.cxxLanguageStandard, SupportedLanguageExtension.cppExtensions),
207+
(target.clangTarget.cLanguageStandard, SupportedLanguageExtension.cExtensions),
208+
]
209+
204210
let commands: [Command] = target.compilePaths().map({ path in
205211
var args = target.basicArguments()
206212
args += ["-MD", "-MT", "dependencies", "-MF", path.deps.asString]
213+
214+
// Add language standard flag if needed.
215+
if let ext = path.source.extension {
216+
for (standard, validExtensions) in standards {
217+
if let languageStandard = standard, validExtensions.contains(ext) {
218+
args += ["-std=\(languageStandard)"]
219+
}
220+
}
221+
}
222+
207223
args += ["-c", path.source.asString, "-o", path.object.asString]
208224
let clang = ClangTool(
209225
desc: "Compile \(target.target.name) \(path.filename.asString)",

Sources/PackageLoading/PackageBuilder.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -791,14 +791,10 @@ public final class PackageBuilder {
791791

792792
let sources = Sources(paths: cSources, root: potentialModule.path)
793793

794-
// Select the right language standard.
795-
let isCXX = sources.containsCXXFiles
796-
let languageStandard = isCXX ? manifest.package.cxxLanguageStandard?.rawValue : manifest.package.cLanguageStandard?.rawValue
797-
798794
return ClangTarget(
799795
name: potentialModule.name,
800-
isCXX: isCXX,
801-
languageStandard: languageStandard,
796+
cLanguageStandard: manifest.package.cLanguageStandard?.rawValue,
797+
cxxLanguageStandard: manifest.package.cxxLanguageStandard?.rawValue,
802798
includeDir: publicHeadersPath,
803799
isTest: potentialModule.isTest,
804800
sources: sources,

Sources/PackageModel/Module.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,24 +140,27 @@ public class ClangTarget: Target {
140140
/// True if this is a C++ target.
141141
public let isCXX: Bool
142142

143-
/// The C or C++ language standard flag.
144-
public let languageStandard: String?
143+
/// The C language standard flag.
144+
public let cLanguageStandard: String?
145+
146+
/// The C++ language standard flag.
147+
public let cxxLanguageStandard: String?
145148

146149
public init(
147150
name: String,
148-
isCXX: Bool,
149-
languageStandard: String?,
151+
cLanguageStandard: String?,
152+
cxxLanguageStandard: String?,
150153
includeDir: AbsolutePath,
151154
isTest: Bool = false,
152155
sources: Sources,
153156
dependencies: [Target] = [],
154157
productDependencies: [(name: String, package: String?)] = []
155158
) {
156159
assert(includeDir.contains(sources.root), "\(includeDir) should be contained in the source root \(sources.root)")
157-
assert(sources.containsCXXFiles == isCXX)
158160
let type: Kind = isTest ? .test : sources.computeModuleType()
159-
self.isCXX = isCXX
160-
self.languageStandard = languageStandard
161+
self.isCXX = sources.containsCXXFiles
162+
self.cLanguageStandard = cLanguageStandard
163+
self.cxxLanguageStandard = cxxLanguageStandard
161164
self.includeDir = includeDir
162165
super.init(
163166
name: name,

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ final class BuildPlanTests: XCTestCase {
259259
let fs = InMemoryFileSystem(emptyFiles:
260260
"/Pkg/Sources/exe/main.cpp",
261261
"/Pkg/Sources/lib/lib.c",
262+
"/Pkg/Sources/lib/libx.cpp",
262263
"/Pkg/Sources/lib/include/lib.h"
263264
)
264265
let pkg = PackageDescription4.Package(
@@ -272,33 +273,39 @@ final class BuildPlanTests: XCTestCase {
272273
)
273274
let diagnostics = DiagnosticsEngine()
274275
let graph = loadMockPackageGraph4(["/Pkg": pkg], root: "/Pkg", diagnostics: diagnostics, in: fs)
275-
let result = BuildPlanResult(plan: try BuildPlan(buildParameters: mockBuildParameters(), graph: graph, fileSystem: fs))
276+
let plan = try BuildPlan(buildParameters: mockBuildParameters(), graph: graph, fileSystem: fs)
277+
let result = BuildPlanResult(plan: plan)
276278

277279
result.checkProductsCount(1)
278280
result.checkTargetsCount(2)
279281

280-
let lib = try result.target(for: "lib").clangTarget()
281-
XCTAssertTrue(lib.basicArguments().contains("-std=gnu99"))
282-
283-
let exe = try result.target(for: "exe").clangTarget()
284-
XCTAssertTrue(exe.basicArguments().contains("-std=c++1z"))
285-
286282
#if os(macOS)
287283
XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [
288284
"/fake/path/to/swiftc", "-lc++", "-g", "-L", "/path/to/build/debug", "-o",
289285
"/path/to/build/debug/exe", "-module-name", "exe", "-emit-executable",
290286
"/path/to/build/debug/exe.build/main.cpp.o",
291-
"/path/to/build/debug/lib.build/lib.c.o"
287+
"/path/to/build/debug/lib.build/lib.c.o",
288+
"/path/to/build/debug/lib.build/libx.cpp.o"
292289
])
293290
#else
294291
XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [
295292
"/fake/path/to/swiftc", "-lstdc++", "-g", "-L", "/path/to/build/debug", "-o",
296293
"/path/to/build/debug/exe", "-module-name", "exe", "-emit-executable",
297294
"-Xlinker", "-rpath=$ORIGIN",
298295
"/path/to/build/debug/exe.build/main.cpp.o",
299-
"/path/to/build/debug/lib.build/lib.c.o"
296+
"/path/to/build/debug/lib.build/lib.c.o",
297+
"/path/to/build/debug/lib.build/libx.cpp.o"
300298
])
301299
#endif
300+
301+
mktmpdir { path in
302+
let yaml = path.appending(component: "debug.yaml")
303+
let llbuild = LLBuildManifestGenerator(plan)
304+
try llbuild.generateManifest(at: yaml)
305+
let contents = try localFileSystem.readFileContents(yaml).asString!
306+
XCTAssertTrue(contents.contains("-std=gnu99\",\"-c\",\"/Pkg/Sources/lib/lib.c"))
307+
XCTAssertTrue(contents.contains("-std=c++1z\",\"-c\",\"/Pkg/Sources/lib/libx.cpp"))
308+
}
302309
}
303310

304311
func testSwiftCMixed() throws {

Tests/PackageLoadingTests/ModuleMapGenerationTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class ModuleMapGeneration: XCTestCase {
146146

147147
func ModuleMapTester(_ name: String, includeDir: String = "include", in fileSystem: FileSystem, _ body: (ModuleMapResult) -> Void) {
148148
let includeDir = AbsolutePath.root.appending(component: includeDir)
149-
let target = ClangTarget(name: name, isCXX: false, languageStandard: nil, includeDir: includeDir, isTest: false, sources: Sources(paths: [], root: .root))
149+
let target = ClangTarget(name: name, cLanguageStandard: nil, cxxLanguageStandard: nil, includeDir: includeDir, isTest: false, sources: Sources(paths: [], root: .root))
150150
let warningStream = BufferedOutputByteStream()
151151
var generator = ModuleMapGenerator(for: target, fileSystem: fileSystem, warningStream: warningStream)
152152
var diagnostics = Set<String>()

0 commit comments

Comments
 (0)