Index: clang/include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticDriverKinds.td +++ clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -489,6 +489,9 @@ "-ftest-module-file-extension argument '%0' is not of the required form " "'blockname:major:minor:hashed:user info'">; +def err_drv_module_output_with_multiple_arch : Error< + "option '-fmodule-output' can't be used with multiple arch options">; + def err_drv_extract_api_wrong_kind : Error< "header file '%0' input '%1' does not match the type of prior input " "in api extraction; use '-x %2' to override">; Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2304,6 +2304,9 @@ PosFlag, NegFlag, BothFlags<[NoXarchOption, CC1Option]>>; +def fmodule_output : Flag<["-"], "fmodule-output">, Flags<[NoXarchOption, CC1Option]>, + HelpText<"Save intermediate module file results when compiling a standard C++ module unit.">; + def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group, Flags<[CC1Option]>, MetaVarName<"">, HelpText<"Specify the interval (in seconds) between attempts to prune the module cache">, Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -5566,9 +5566,21 @@ &JA); } + if (MultipleArchs && C.getArgs().hasArg(options::OPT_fmodule_output)) + Diag(clang::diag::err_drv_module_output_with_multiple_arch); + + // If we're emitting a module output with the speicifed option + // `-fmodule-output`. + bool EmittingModuleOutput = !AtTopLevel && isa(JA) && + JA.getType() == types::TY_ModuleFile && + C.getArgs().hasArg(options::OPT_fmodule_output); + // Output to a temporary file? if ((!AtTopLevel && !isSaveTempsEnabled() && - !C.getArgs().hasArg(options::OPT__SLASH_Fo)) || + !C.getArgs().hasArg(options::OPT__SLASH_Fo) && + // We don't generate module file to temporary file if + // we specified `-fmodule-output` + !EmittingModuleOutput) || CCGenDiagnostics) { StringRef Name = llvm::sys::path::filename(BaseInput); std::pair Split = Name.split('.'); @@ -5725,9 +5737,24 @@ else llvm::sys::path::append(BasePath, NamedOutput); return C.addResultFile(C.getArgs().MakeArgString(BasePath.c_str()), &JA); - } else { - return C.addResultFile(NamedOutput, &JA); } + + // When we specified `-fmodule-output` and `-o`, the module file + // will be generated into the same directory with the output file + // and the name of the module file would be the input file with the + // new suffix '.pcm'. + if (EmittingModuleOutput && C.getArgs().hasArg(options::OPT_o)) { + SmallString<128> OutputPath; + OutputPath = C.getArgs().getLastArg(options::OPT_o)->getValue(); + llvm::sys::path::remove_filename(OutputPath); + if (OutputPath.empty()) + OutputPath = NamedOutput; + else + llvm::sys::path::append(OutputPath, NamedOutput); + return C.addResultFile(C.getArgs().MakeArgString(OutputPath.c_str()), &JA); + } + + return C.addResultFile(NamedOutput, &JA); } std::string Driver::GetFilePath(StringRef Name, const ToolChain &TC) const { Index: clang/test/Driver/lit.local.cfg =================================================================== --- clang/test/Driver/lit.local.cfg +++ clang/test/Driver/lit.local.cfg @@ -1,6 +1,6 @@ from lit.llvm import llvm_config -config.suffixes = ['.c', '.cpp', '.h', '.m', '.mm', '.S', '.s', '.f90', '.F90', '.f95', +config.suffixes = ['.c', '.cpp', '.cppm', '.h', '.m', '.mm', '.S', '.s', '.f90', '.F90', '.f95', '.cu', '.rs', '.cl', '.clcpp', '.hip', '.hlsl'] config.substitutions = list(config.substitutions) config.substitutions.insert(0, Index: clang/test/Driver/module-output.cppm =================================================================== --- /dev/null +++ clang/test/Driver/module-output.cppm @@ -0,0 +1,33 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: split-file %s %t +// +// Tests that the .pcm file will be generated in the same directory with the specified +// output and the name of the .pcm file should be the same with the input file. +// RUN: %clang -std=c++20 %t/Hello.cppm -fmodule-output -c -o %t/output/Hello.o \ +// RUN: -### 2>&1 | FileCheck %t/Hello.cppm +// +// Tests that the output file will be generated in the output directory when we +// specified multiple input files. +// RUN: %clang -std=c++20 %t/Hello.cppm %t/AnotherModule.cppm -fmodule-output -o \ +// RUN: %t/output/a.out -### 2>&1 | FileCheck %t/AnotherModule.cppm +// +// Tests that clang will reject the command line if it specifies -fmodule-output with +// multiple archs. +// RUN: %clang %t/Hello.cppm -fmodule-output -arch i386 -arch x86_64 -### -target \ +// RUN: x86_64-apple-darwin 2>&1 | FileCheck %t/Hello.cppm -check-prefix=MULTIPLE-ARCH + +//--- Hello.cppm +export module Hello; + +// CHECK: "-emit-module-interface" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/output/Hello.pcm" "-x" "c++" "{{.*}}/Hello.cppm" +// CHECK: "-emit-obj" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/output/Hello.o" "-x" "pcm" "{{.*}}/output/Hello.pcm" + +// MULTIPLE-ARCH: option '-fmodule-output' can't be used with multiple arch options + +//--- AnotherModule.cppm +export module AnotherModule; +// CHECK: "-emit-module-interface" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/output/Hello.pcm" "-x" "c++" "{{.*}}/Hello.cppm" +// CHECK: "-emit-obj" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/Hello-{{.*}}.o" "-x" "pcm" "{{.*}}/output/Hello.pcm" +// CHECK: "-emit-module-interface" {{.*}}"-main-file-name" "AnotherModule.cppm" {{.*}}"-o" "{{.*}}/output/AnotherModule.pcm" "-x" "c++" "{{.*}}/AnotherModule.cppm" +// CHECK: "-emit-obj" {{.*}}"-main-file-name" "AnotherModule.cppm" {{.*}}"-o" "{{.*}}/AnotherModule-{{.*}}.o" "-x" "pcm" "{{.*}}/output/AnotherModule.pcm"