Skip to content

[cxx-interop] Make test discovery compatible with C++ interop #7165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,25 @@ public final class SwiftTargetBuildDescription {
args += ["-color-diagnostics"]
}

// If this is a generated test discovery target, it might import a test
// target that is built with C++ interop enabled. In that case, the test
// discovery target must enable C++ interop as well
switch testTargetRole {
case .discovery:
for dependency in try self.target.recursiveTargetDependencies() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is somewhat ugly, but it should go away once we have a better story for mixing C-interop and C++-interop in a single project.

let dependencyScope = self.buildParameters.createScope(for: dependency)
let dependencySwiftFlags = dependencyScope.evaluate(.OTHER_SWIFT_FLAGS)
if let interopModeFlag = dependencySwiftFlags.first(where: { $0.hasPrefix("-cxx-interoperability-mode=") }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we doing these kind of checks elsewhere in the code? should it be encapsulated behind something like dependencySwiftFlags.cxxInteropMode returning an optional?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to this question

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't happening elsewhere in the code, no. We basically don't want to implicitly enable C++ interop unless is it really unavoidable, like it is in the case of a test discovery target, because parsing Objective-C headers in Objective-C++ language mode sometimes changes semantics/ABI.

I only see one other usage of extraSwiftFlags.first(where: ...) in UserToolchain.swift and one other usage of .evaluate(.OTHER_SWIFT_FLAGS). I can extract a common method for either, let me know if you think it's worth it.

Copy link
Contributor

@MaxDesiatov MaxDesiatov Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generalizing extraSwiftFlags.first(where:) (preferably its replacement that isn't stringly-typed) makes sense to me, the other seems fine as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I just realized that the other usage of extraSwiftFlags.first operates on [String], not on flags/scopes. So I don't really see a way to simplify this.

args += [interopModeFlag]
break
}
}
if let cxxStandard = self.package.manifest.cxxLanguageStandard {
args += ["-Xcc", "-std=\(cxxStandard)"]
}
default: break
}

// Add arguments from declared build settings.
args += try self.buildSettingsFlags()

Expand Down
17 changes: 16 additions & 1 deletion Tests/BuildTests/BuildPlanTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3556,6 +3556,7 @@ final class BuildPlanTests: XCTestCase {
"/A/Sources/cbar/barcpp.cpp",
"/A/Sources/cbar/bar.c",
"/A/Sources/cbar/include/bar.h",
"/A/Tests/MySwiftTests/test.swift",

"/B/Sources/t1/dep.swift",
"/B/Sources/t2/dep.swift",
Expand All @@ -3566,6 +3567,7 @@ final class BuildPlanTests: XCTestCase {
displayName: "A",
path: "/A",
toolsVersion: .v5,
cxxLanguageStandard: "c++17",
dependencies: [
.localSourceControl(path: "/B", requirement: .upToNextMajor(from: "1.0.0")),
],
Expand Down Expand Up @@ -3606,6 +3608,12 @@ final class BuildPlanTests: XCTestCase {
.init(tool: .linker, kind: .unsafeFlags(["-Ilfoo", "-L", "lbar"])),
]
),
try TargetDescription(
name: "MySwiftTests", type: .test,
settings: [
.init(tool: .swift, kind: .interoperabilityMode(.Cxx)),
]
),
]
)

Expand Down Expand Up @@ -3682,6 +3690,7 @@ final class BuildPlanTests: XCTestCase {
"-Isfoo",
"-L", "sbar",
"-cxx-interoperability-mode=default",
"-Xcc", "-std=c++17",
"-enable-upcoming-feature", "BestFeature",
"-g",
"-Xcc", "-g",
Expand All @@ -3695,6 +3704,9 @@ final class BuildPlanTests: XCTestCase {

let linkExe = try result.buildProduct(for: "exe").linkArguments()
XCTAssertMatch(linkExe, [.anySequence, "-lsqlite3", "-llibz", "-Ilfoo", "-L", "lbar", "-g", .end])

let testDiscovery = try result.target(for: "APackageDiscoveredTests").swiftTarget().compileArguments()
XCTAssertMatch(testDiscovery, [.anySequence, "-cxx-interoperability-mode=default", "-Xcc", "-std=c++17"])
}

// omit frame pointers explicitly set to true
Expand Down Expand Up @@ -3739,6 +3751,7 @@ final class BuildPlanTests: XCTestCase {
"-Isfoo",
"-L", "sbar",
"-cxx-interoperability-mode=default",
"-Xcc", "-std=c++17",
"-enable-upcoming-feature",
"BestFeature",
"-g",
Expand Down Expand Up @@ -3794,6 +3807,7 @@ final class BuildPlanTests: XCTestCase {
"-Isfoo",
"-L", "sbar",
"-cxx-interoperability-mode=default",
"-Xcc", "-std=c++17",
"-enable-upcoming-feature",
"BestFeature",
"-g",
Expand Down Expand Up @@ -3836,6 +3850,7 @@ final class BuildPlanTests: XCTestCase {
"-Isfoo",
"-L", "sbar",
"-cxx-interoperability-mode=default",
"-Xcc", "-std=c++17",
"-enable-upcoming-feature", "BestFeature",
"-enable-upcoming-feature", "WorstFeature",
"-g",
Expand All @@ -3845,7 +3860,7 @@ final class BuildPlanTests: XCTestCase {
)

let exe = try result.target(for: "exe").swiftTarget().compileArguments()
XCTAssertMatch(exe, [.anySequence, "-DFOO", "-cxx-interoperability-mode=default", "-g", "-Xcc", "-g", .end])
XCTAssertMatch(exe, [.anySequence, "-DFOO", "-cxx-interoperability-mode=default", "-Xcc", "-std=c++17", "-g", "-Xcc", "-g", .end])

let linkExe = try result.buildProduct(for: "exe").linkArguments()
XCTAssertMatch(
Expand Down