@@ -1075,6 +1075,112 @@ import SWBMacro
1075
1075
}
1076
1076
}
1077
1077
1078
+ @Test
1079
+ func clangCompileTaskConstructionWithCXX20ModulesDisabled( ) async throws {
1080
+ let core = try await getCore ( )
1081
+ let clangSpec = try core. specRegistry. getSpec ( " com.apple.compilers.llvm.clang.1_0 " ) as CommandLineToolSpec
1082
+
1083
+ // Create the dummy table. We include all the defaults for the tool specification.
1084
+ var table = MacroValueAssignmentTable ( namespace: core. specRegistry. internalMacroNamespace)
1085
+ for option in clangSpec. flattenedOrderedBuildOptions {
1086
+ guard let value = option. defaultValue else { continue }
1087
+ table. push ( option. macro, value)
1088
+ }
1089
+ // We also add some other settings that are more contextual in nature.
1090
+ table. push ( BuiltinMacros . CURRENT_ARCH, literal: " x86_64 " )
1091
+ table. push ( BuiltinMacros . arch, literal: " x86_64 " )
1092
+ table. push ( BuiltinMacros . LLVM_TARGET_TRIPLE_VENDOR, literal: " apple " )
1093
+ table. push ( BuiltinMacros . LLVM_TARGET_TRIPLE_OS_VERSION, BuiltinMacros . namespace. parseString ( " $(SWIFT_PLATFORM_TARGET_PREFIX)$($(DEPLOYMENT_TARGET_SETTING_NAME)) " ) )
1094
+ table. push ( BuiltinMacros . SWIFT_PLATFORM_TARGET_PREFIX, literal: " macos " )
1095
+ table. push ( BuiltinMacros . DEPLOYMENT_TARGET_SETTING_NAME, literal: " MACOSX_DEPLOYMENT_TARGET " )
1096
+ table. push ( BuiltinMacros . MACOSX_DEPLOYMENT_TARGET, literal: " 13.0 " )
1097
+ table. push ( BuiltinMacros . CURRENT_VARIANT, literal: " normal " )
1098
+ table. push ( BuiltinMacros . PRODUCT_NAME, literal: " Product " )
1099
+ table. push ( BuiltinMacros . TEMP_DIR, literal: Path . root. join ( " tmp " ) . str)
1100
+ table. push ( BuiltinMacros . OBJECT_FILE_DIR, literal: Path . root. join ( " tmp/output/obj " ) . str)
1101
+ table. push ( BuiltinMacros . PER_ARCH_OBJECT_FILE_DIR, BuiltinMacros . namespace. parseString ( " $(OBJECT_FILE_DIR)-$(CURRENT_VARIANT)/$(CURRENT_ARCH) " ) )
1102
+ table. push ( BuiltinMacros . BUILT_PRODUCTS_DIR, literal: Path . root. join ( " tmp/output/sym " ) . str)
1103
+ table. push ( BuiltinMacros . ENABLE_DEFAULT_SEARCH_PATHS, literal: true )
1104
+ table. push ( BuiltinMacros . CLANG_ENABLE_MODULES, literal: false )
1105
+
1106
+ // Turn off response files, so we can directly check arguments
1107
+ table. push ( BuiltinMacros . CLANG_USE_RESPONSE_FILE, literal: false )
1108
+
1109
+ /// Utility method to create a task and pass it to a checker block.
1110
+ func createTask( with table: MacroValueAssignmentTable , cxxLangMode: String , spec: String , _ checkTask: ( ( inout PlannedTaskBuilder ) -> Void ) ,
1111
+ sourceLocation: SourceLocation = #_sourceLocation) async throws {
1112
+ var configTable = table
1113
+ configTable. push ( BuiltinMacros . CLANG_CXX_LANGUAGE_STANDARD, literal: cxxLangMode)
1114
+ // Create the delegate, scope, file type, etc.
1115
+ let producer = try MockCommandProducer ( core: core, productTypeIdentifier: " com.apple.product-type.framework " , platform: " macosx " )
1116
+ let delegate = try CapturingTaskGenerationDelegate ( producer: producer, userPreferences: . defaultForTesting)
1117
+ let dummyScope = MacroEvaluationScope ( table: configTable)
1118
+ let dummyFileType = try core. specRegistry. getSpec ( spec) as FileTypeSpec
1119
+
1120
+ // Create the build context for the command.
1121
+ let cbc = CommandBuildContext ( producer: producer, scope: dummyScope, inputs: [ FileToBuild ( absolutePath: Path . root. join ( " tmp/input.mm " ) , fileType: dummyFileType) ] , output: nil )
1122
+
1123
+ // Ask the specification to construct the tasks.
1124
+ await clangSpec. constructTasks ( cbc, delegate)
1125
+
1126
+ guard var task = delegate. shellTasks. last, task. ruleInfo. first == " CompileC " else {
1127
+ Issue . record ( " Failed to find compile task " )
1128
+ return
1129
+ }
1130
+ checkTask ( & task)
1131
+ }
1132
+
1133
+ for cxxLangMode in [ " c++20 " , " c++23 " , " gnu++20 " , " gnu++23 " ] {
1134
+ try await createTask ( with: table, cxxLangMode: cxxLangMode, spec: " sourcecode.cpp.objcpp " ) { task in
1135
+ #expect( task. ruleInfo == [ " CompileC " , Path . root. join ( " tmp/output/obj-normal/x86_64/input.o " ) . str, Path . root. join ( " tmp/input.mm " ) . str, " normal " , " x86_64 " , " objective-c++ " , " com.apple.compilers.llvm.clang.1_0 " ] )
1136
+ #expect( task. execDescription == " Compile input.mm (x86_64) " )
1137
+ #expect( clangSpec. interestingPath ( for: Task ( & task) ) ? . str == Path . root. join ( " tmp/input.mm " ) . str)
1138
+ task. checkCommandLineMatches ( [ " -std= \( cxxLangMode) " , . anySequence, " -fno-cxx-modules " , . anySequence] )
1139
+ task. checkCommandLineDoesNotContain ( " -fmodules " )
1140
+ task. checkCommandLineDoesNotContain ( " -fcxx-modules " )
1141
+
1142
+ task. checkInputs ( [
1143
+ . path( Path . root. join ( " tmp/input.mm " ) . str)
1144
+ ] )
1145
+ task. checkOutputs ( [
1146
+ . path( Path . root. join ( " tmp/output/obj-normal/x86_64/input.o " ) . str)
1147
+ ] )
1148
+ #expect( task. dependencyData != nil )
1149
+ #expect( task. payload != nil )
1150
+ }
1151
+ }
1152
+
1153
+ // Validate that the presence of -fcxx-modules shows up **after** the disablement flag.
1154
+ table. push ( BuiltinMacros . OTHER_CPLUSPLUSFLAGS, literal: [ " -fcxx-modules " ] )
1155
+ try await createTask ( with: table, cxxLangMode: " c++20 " , spec: " sourcecode.cpp.objcpp " ) { task in
1156
+ #expect( task. ruleInfo == [ " CompileC " , Path . root. join ( " tmp/output/obj-normal/x86_64/input.o " ) . str, Path . root. join ( " tmp/input.mm " ) . str, " normal " , " x86_64 " , " objective-c++ " , " com.apple.compilers.llvm.clang.1_0 " ] )
1157
+ #expect( task. execDescription == " Compile input.mm (x86_64) " )
1158
+ task. checkCommandLineMatches ( [ " -fno-cxx-modules " , . anySequence, " -fcxx-modules " ] )
1159
+ }
1160
+
1161
+ // Validate that no explicit module options are passed on language modes less than c++20.
1162
+ // Note, the current compiler default is c++14.
1163
+ for cxxLangMode in [ " c++98 " , " gnu++0x " , " c++17 " , " gnu++14 " , " compiler-default " ] {
1164
+ try await createTask ( with: table, cxxLangMode: cxxLangMode, spec: " sourcecode.cpp.objcpp " ) { task in
1165
+ #expect( task. ruleInfo == [ " CompileC " , Path . root. join ( " tmp/output/obj-normal/x86_64/input.o " ) . str, Path . root. join ( " tmp/input.mm " ) . str, " normal " , " x86_64 " , " objective-c++ " , " com.apple.compilers.llvm.clang.1_0 " ] )
1166
+ #expect( task. execDescription == " Compile input.mm (x86_64) " )
1167
+ #expect( clangSpec. interestingPath ( for: Task ( & task) ) ? . str == Path . root. join ( " tmp/input.mm " ) . str)
1168
+ task. checkCommandLineDoesNotContain ( " -fno-cxx-modules " )
1169
+
1170
+ task. checkInputs ( [
1171
+ . path( Path . root. join ( " tmp/input.mm " ) . str)
1172
+ ] )
1173
+ task. checkOutputs ( [
1174
+ . path( Path . root. join ( " tmp/output/obj-normal/x86_64/input.o " ) . str)
1175
+ ] )
1176
+ #expect( task. dependencyData != nil )
1177
+ #expect( task. payload != nil )
1178
+ }
1179
+ }
1180
+
1181
+
1182
+ }
1183
+
1078
1184
1079
1185
/// Check that C_COMPILER_LAUNCHER is honored.
1080
1186
@Test ( . skipHostOS( . windows, " output file is coming out as tmp \\ input.o (non-absolute) " ) )
0 commit comments