Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2285,6 +2285,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 @@ -5556,8 +5556,14 @@ } // Output to a temporary file? - if ((!AtTopLevel && !isSaveTempsEnabled() && - !C.getArgs().hasArg(options::OPT__SLASH_Fo)) || + if ((!AtTopLevel && + !isSaveTempsEnabled() && + !C.getArgs().hasArg(options::OPT__SLASH_Fo) && + // We don't generate module file to temporary file if + // we specified `-fmodule-output` + (!isa(JA) || + JA.getType() != types::TY_ModuleFile || + !C.getArgs().hasArg(options::OPT_fmodule_output))) || CCGenDiagnostics) { StringRef Name = llvm::sys::path::filename(BaseInput); std::pair Split = Name.split('.'); @@ -5714,9 +5720,27 @@ 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 (!AtTopLevel && isa(JA) && + JA.getType() == types::TY_ModuleFile && + C.getArgs().hasArg(options::OPT_fmodule_output) && + 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,26 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: split-file %s %t +// +// Tests that the .pcm file will be generated in the same direcotry 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 + +//--- 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" + +//--- 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"