diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/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">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2306,6 +2306,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">, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -5553,6 +5553,30 @@ return C.addTempFile(C.getArgs().MakeArgString(TmpName)); } +// Calculate the output path of the module file when compiling a module unit +// with the `-fmodule-output` option specified. The behavior is: +// - If the output object file of the module unit is specified, the output path +// of the module file should be the same with the output object file except +// the corresponding suffix. This requires both `-o` and `-c` are specified. +// - Otherwise, the output path of the module file will be the same with the +// input with the corresponding suffix. +static const char *GetModuleOutputPath(Compilation &C, const JobAction &JA, + const char *BaseInput) { + assert(isa(JA) && JA.getType() == types::TY_ModuleFile && + C.getArgs().hasArg(options::OPT_fmodule_output)); + + SmallString<64> OutputPath; + Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o); + if (FinalOutput && C.getArgs().hasArg(options::OPT_c)) + OutputPath = FinalOutput->getValue(); + else + OutputPath = BaseInput; + + const char *Extension = types::getTypeTempSuffix(JA.getType()); + llvm::sys::path::replace_extension(OutputPath, Extension); + return C.addResultFile(C.getArgs().MakeArgString(OutputPath.c_str()), &JA); +} + const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, const char *BaseInput, StringRef OrigBoundArch, bool AtTopLevel, @@ -5609,6 +5633,16 @@ &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 specified option + // `-fmodule-output`. + if (!AtTopLevel && isa(JA) && + JA.getType() == types::TY_ModuleFile && + C.getArgs().hasArg(options::OPT_fmodule_output)) + return GetModuleOutputPath(C, JA, BaseInput); + // Output to a temporary file? if ((!AtTopLevel && !isSaveTempsEnabled() && !C.getArgs().hasArg(options::OPT__SLASH_Fo)) || @@ -5771,9 +5805,9 @@ else llvm::sys::path::append(BasePath, NamedOutput); return C.addResultFile(C.getArgs().MakeArgString(BasePath.c_str()), &JA); - } else { - return C.addResultFile(NamedOutput, &JA); } + + return C.addResultFile(NamedOutput, &JA); } std::string Driver::GetFilePath(StringRef Name, const ToolChain &TC) const { diff --git a/clang/test/Driver/lit.local.cfg b/clang/test/Driver/lit.local.cfg --- a/clang/test/Driver/lit.local.cfg +++ b/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', '.hipi', '.hlsl'] config.substitutions = list(config.substitutions) config.substitutions.insert(0, diff --git a/clang/test/Driver/module-output.cppm b/clang/test/Driver/module-output.cppm new file mode 100644 --- /dev/null +++ b/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 input directory if the output +// file is not the corresponding object file. +// 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" "{{.*}}/Hello.pcm" "-x" "c++" "{{.*}}/Hello.cppm" +// CHECK: "-emit-obj" {{.*}}"-main-file-name" "Hello.cppm" {{.*}}"-o" "{{.*}}/Hello-{{.*}}.o" "-x" "pcm" "{{.*}}/Hello.pcm" +// CHECK: "-emit-module-interface" {{.*}}"-main-file-name" "AnotherModule.cppm" {{.*}}"-o" "{{.*}}/AnotherModule.pcm" "-x" "c++" "{{.*}}/AnotherModule.cppm" +// CHECK: "-emit-obj" {{.*}}"-main-file-name" "AnotherModule.cppm" {{.*}}"-o" "{{.*}}/AnotherModule-{{.*}}.o" "-x" "pcm" "{{.*}}/AnotherModule.pcm"