Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -213,6 +213,12 @@ temporarily reversed with ``-Xclang -fno-modules-validate-textual-header-includes``, but this flag will be removed in a future Clang release. +- Now the clang will generate the BMI implicitly when we compile a module unit + directly. See + `How to produce a BMI + `_ + for details. + New Compiler Flags ------------------ Index: clang/include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticCommonKinds.td +++ clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -347,6 +347,8 @@ // Modules def err_module_format_unhandled : Error< "no handler registered for module format '%0'">, DefaultFatal; +def err_creating_default_module_cache_path : Error< + "unable to create default module cache path \"%0\": %1">; // TransformActions // TODO: Use a custom category name to distinguish rewriter errors. Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -5499,6 +5499,40 @@ return "-"; } + if (isa(JA) && JA.getType() == types::TY_ModuleFile) { + SmallString<128> Path; + if (Arg *A = C.getArgs().getLastArg(options::OPT_fmodules_cache_path)) + Path = A->getValue(); + else + Driver::getDefaultModuleCachePath(Path); + + std::error_code EC = + llvm::sys::fs::create_directories(Path, /*IgnoreExisting =*/true); + if (EC) + Diag(clang::diag::err_creating_default_module_cache_path) + << Path << EC.message(); + + if (Arg *A = C.getArgs().getLastArg(options::OPT_fmodule_name_EQ)) { + StringRef Name = A->getValue(); + // Replace Module Name "Mod:Part" to "Mod-Part" since ":" is not + // available in some file systems. + auto [PrimaryModuleName, PartitionName] = Name.rsplit(':'); + llvm::sys::path::append(Path, PrimaryModuleName); + if (!PartitionName.empty()) { + Path += "-"; + Path += PartitionName; + } + } else { + StringRef Name = llvm::sys::path::filename(BaseInput); + llvm::sys::path::append(Path, Name.rsplit('.').first); + } + + Path += "."; + Path += types::getTypeTempSuffix(JA.getType(), IsCLMode()); + + return C.addResultFile(C.getArgs().MakeArgString(Path.c_str()), &JA); + } + if (IsDXCMode() && !C.getArgs().hasArg(options::OPT_o)) return "-"; Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -3658,6 +3658,7 @@ const ArgList &Args, const InputInfo &Input, const InputInfo &Output, ArgStringList &CmdArgs, bool &HaveModules) { + bool HasCXXModules = HaveModules; // -fmodules enables the use of precompiled modules (off by default). // Users can pass -fno-cxx-modules to turn off modules support for // C++/Objective-C++ programs. @@ -3699,33 +3700,34 @@ options::OPT_fno_implicit_modules, HaveClangModules)) { if (HaveModules) CmdArgs.push_back("-fno-implicit-modules"); - } else if (HaveModules) { + } else if (HaveModules) ImplicitModules = true; - // -fmodule-cache-path specifies where our implicitly-built module files - // should be written. - SmallString<128> Path; - if (Arg *A = Args.getLastArg(options::OPT_fmodules_cache_path)) - Path = A->getValue(); - bool HasPath = true; - if (C.isForDiagnostics()) { - // When generating crash reports, we want to emit the modules along with - // the reproduction sources, so we ignore any provided module path. - Path = Output.getFilename(); - llvm::sys::path::replace_extension(Path, ".cache"); - llvm::sys::path::append(Path, "modules"); - } else if (Path.empty()) { - // No module path was provided: use the default. - HasPath = Driver::getDefaultModuleCachePath(Path); - } - - // `HasPath` will only be false if getDefaultModuleCachePath() fails. - // That being said, that failure is unlikely and not caching is harmless. - if (HasPath) { - const char Arg[] = "-fmodules-cache-path="; - Path.insert(Path.begin(), Arg, Arg + strlen(Arg)); - CmdArgs.push_back(Args.MakeArgString(Path)); - } + // -fmodule-cache-path specifies where our implicitly-built module files + // should be written. + SmallString<128> ModulesCachePath; + if (Arg *A = Args.getLastArg(options::OPT_fmodules_cache_path)) + ModulesCachePath = A->getValue(); + + bool HasModulesCachePath = true; + if (C.isForDiagnostics()) { + // When generating crash reports, we want to emit the modules along with + // the reproduction sources, so we ignore any provided module path. + ModulesCachePath = Output.getFilename(); + llvm::sys::path::replace_extension(ModulesCachePath, ".cache"); + llvm::sys::path::append(ModulesCachePath, "modules"); + } else if (ModulesCachePath.empty()) { + // No module path was provided: use the default. + HasModulesCachePath = Driver::getDefaultModuleCachePath(ModulesCachePath); + } + + // `HasModulesCachePath` will only be false if getDefaultModuleCachePath() + // fails. That being said, that failure is unlikely and not caching is + // harmless. + if (HasModulesCachePath && ImplicitModules) { + const char Arg[] = "-fmodules-cache-path="; + ModulesCachePath.insert(ModulesCachePath.begin(), Arg, Arg + strlen(Arg)); + CmdArgs.push_back(Args.MakeArgString(ModulesCachePath)); } if (HaveModules) { @@ -3735,6 +3737,11 @@ std::string("-fprebuilt-module-path=") + A->getValue())); A->claim(); } + + if (HasCXXModules) + CmdArgs.push_back(Args.MakeArgString( + std::string("-fprebuilt-module-path=") + ModulesCachePath)); + if (Args.hasFlag(options::OPT_fprebuilt_implicit_modules, options::OPT_fno_prebuilt_implicit_modules, false)) CmdArgs.push_back("-fprebuilt-implicit-modules"); Index: clang/test/Driver/create_module_cache.cpp =================================================================== --- /dev/null +++ clang/test/Driver/create_module_cache.cpp @@ -0,0 +1,28 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: split-file %s %t +// +// RUN: not %clang -std=c++20 %t/M.cppm -fmodules-cache-path= -c -o %t/M.tmp.o 2>&1 | FileCheck %t/M.cppm --check-prefix=ERROR +// RUN: %clang -std=c++20 %t/M.cppm -fmodules-cache-path=%t/abc -c -o - +// RUN: ls %t | FileCheck %t/M.cppm --check-prefix=CHECK-AVAILABLE +// +// RUN: %clang -std=c++20 %t/Use.cpp -fmodules-cache-path=abc -### 2>&1 | FileCheck %t/Use.cpp +// +// Check that the compiler will generate M-Part.pcm correctly. +// RUN: %clang -std=c++20 %t/Part.cppm -fmodules-cache-path=%t/abc -fmodule-name=M:Part -c -o - +// RUN: ls %t/abc | FileCheck %t/Part.cppm --check-prefix=CHECK-AVAILABLE + +//--- M.cppm +export module M; + +// ERROR: unable to create default module cache path "": No such file or directory +// CHECK-AVAILABLE: abc + +//--- Use.cpp +import M; + +// CHECK: -fprebuilt-module-path=abc + +//--- Part.cppm +export module M:Part; +// CHECK-AVAILABLE: M-Part.pcm Index: clang/test/Modules/one-phase-compilation-named-modules.cppm =================================================================== --- /dev/null +++ clang/test/Modules/one-phase-compilation-named-modules.cppm @@ -0,0 +1,29 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: split-file %s %t +// +// RUN: %clang -std=c++20 %t/M.cppm -c -o - -fmodules-cache-path=%t/pcm.cache +// RUN: %clang -std=c++20 %t/Use.cpp -fsyntax-only -fprebuilt-module-path=%t/pcm.cache -Xclang -verify +// +// RUN: rm -f %t/M.pcm +// Tests that the Sema will detect the incorrect module name. +// RUN: %clang -std=c++20 %t/MismatchedName.cppm -o - -fmodules-cache-path=%t/pcm.cache \ +// RUN: -fmodule-name=WRONG_MODULE_NAME -fsyntax-only -Xclang -verify +// +// RUN: %clang -std=c++20 %t/MismatchedName.cppm -c -o - -fmodules-cache-path=%t/pcm.cache -fmodule-name=M +// RUN: %clang -std=c++20 %t/Use.cpp -fsyntax-only -fprebuilt-module-path=%t/pcm.cache -Xclang -verify + +//--- M.cppm +export module M; +export int getValue(); + +//--- MismatchedName.cppm +export module M; // expected-error {{module name 'WRONG_MODULE_NAME' specified on command line does not match name of module}} +export int getValue(); // expected-error {{export declaration can only be used within a module interface unit after the module declaration}} + +//--- Use.cpp +// expected-no-diagnostics +import M; +int Use() { + return getValue(); +}