Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,8 @@ def err_drv_reduced_module_output_overrided : Warning<
"please consider use '-fmodule-output=' to specify the output file for reduced BMI explicitly">,
InGroup<DiagGroup<"reduced-bmi-output-overrided">>;

def err_drv_modules_driver_requires_reduced_bmi : Error<
"'-fmodules-driver' is currently incompatible with '-fno-modules-reduced-bmi'">;
def remark_performing_driver_managed_module_build : Remark<
"performing driver managed module build">, InGroup<ModulesDriver>;
def remark_modules_manifest_not_found : Remark<
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/ModulesDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class Compilation;

namespace clang::driver::modules {

/// Emits diagnostics for arguments incompatible with -fmodules-driver.
void diagnoseModulesDriverArgs(llvm::opt::DerivedArgList &DAL,
DiagnosticsEngine &Diags);

/// The parsed Standard library module manifest.
struct StdModuleManifest {
struct Module {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1837,6 +1837,8 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
if (UseModulesDriver) {
Diags.Report(diag::remark_performing_driver_managed_module_build);

modules::diagnoseModulesDriverArgs(C->getArgs(), Diags);

// Read the Standard library module manifest and, if available, add all
// discovered modules to this Compilation. Jobs for modules specified in
// the manifest that are not required by any command-line input are pruned
Expand Down
100 changes: 89 additions & 11 deletions clang/lib/Driver/ModulesDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "clang/Driver/Job.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include "clang/Driver/Types.h"
#include "clang/Frontend/StandaloneDiagnostic.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DepthFirstIterator.h"
Expand All @@ -47,6 +48,14 @@ using namespace clang;
using namespace driver;
using namespace modules;

void driver::modules::diagnoseModulesDriverArgs(llvm::opt::DerivedArgList &DAL,
DiagnosticsEngine &Diags) {
if (!DAL.hasFlag(options::OPT_fmodules_reduced_bmi,
options::OPT_fno_modules_reduced_bmi, true)) {
Diags.Report(diag::err_drv_modules_driver_requires_reduced_bmi);
}
}

namespace clang::driver::modules {
static bool fromJSON(const llvm::json::Value &Params,
StdModuleManifest::Module::LocalArguments &LocalArgs,
Expand Down Expand Up @@ -1252,6 +1261,16 @@ static SmallVector<JobNode *> createNodesForUnusedStdlibModuleJobs(
return StdlibModuleNodesToPrune;
}

// Returns the derived argument list for the tool chain responsible
// for creating \p Job.
static const DerivedArgList &getToolChainArgs(Compilation &C,
const Command &Job) {
const auto &TC = Job.getCreator().getToolChain();
const auto &SourceAction = Job.getSource();
return C.getArgsForToolChain(&TC, SourceAction.getOffloadingArch(),
SourceAction.getOffloadingDeviceKind());
}

/// Creates a job for the Clang module described by \p MD.
static std::unique_ptr<Command>
createClangModulePrecompileJob(Compilation &C, const Command &ImportingJob,
Expand All @@ -1263,9 +1282,7 @@ createClangModulePrecompileJob(Compilation &C, const Command &ImportingJob,
Action *PA = C.MakeAction<PrecompileJobAction>(IA, types::ID::TY_ModuleFile);
PA->propagateOffloadInfo(&ImportingJob.getSource());

const auto &TC = ImportingJob.getCreator().getToolChain();
const auto &TCArgs = C.getArgsForToolChain(&TC, PA->getOffloadingArch(),
PA->getOffloadingDeviceKind());
const auto &TCArgs = getToolChainArgs(C, ImportingJob);

const auto &BuildArgs = MD.getBuildArguments();
ArgStringList JobArgs;
Expand Down Expand Up @@ -1319,12 +1336,7 @@ installScanCommandLines(Compilation &C,
ArgStringList JobArgs;
JobArgs.reserve(BuildArgs.size());

const auto &SourceAction = Job.getSource();
const auto &TC = Job.getCreator().getToolChain();
auto &TCArgs =
C.getArgsForToolChain(&TC, SourceAction.getOffloadingArch(),
SourceAction.getOffloadingDeviceKind());

auto &TCArgs = getToolChainArgs(C, Job);
for (const auto &Arg : BuildArgs)
JobArgs.push_back(TCArgs.MakeArgString(Arg));

Expand Down Expand Up @@ -1527,6 +1539,73 @@ static void createAndConnectRoot(CompilationGraph &Graph) {
}
}

/// Creates a temporary output path for \p ModuleName.
static std::string createModuleOutputPath(const Compilation &C,
StringRef ModuleName) {
// Sanitize the ':' included in parition names. It is illegal for filenames on
// Windows.
SmallString<32> SanitizedModuleName(ModuleName);
llvm::replace(SanitizedModuleName, ':', '-');
auto ModuleOutputPath = C.getDriver().GetTemporaryPath(
SanitizedModuleName, types::getTypeTempSuffix(types::TY_ModuleFile));
return ModuleOutputPath;
}

/// Adds the '-fmodule-output=' argument for the module produced by \p Node.
static void configureNamedModuleOutputArg(Compilation &C,
NamedModuleJobNode &Node,
StringRef ModuleOutputPath) {
auto &Job = *Node.Job;
const auto &TCArgs = getToolChainArgs(C, Job);
auto JobArgs = Job.getArguments();
JobArgs.push_back(
TCArgs.MakeArgString("-fmodule-output=" + ModuleOutputPath));
Job.replaceArguments(std::move(JobArgs));
}

/// Propagates the '-fmodule-file=' mapping for the named module described by
/// \p Node to each dependent job.
static void propagateModuleFileMappingArg(Compilation &C,
NamedModuleJobNode &Node,
StringRef ModuleOutputPath) {
const StringRef ModuleName = Node.InputDeps.ModuleName;

auto DependentNodes = llvm::drop_begin(llvm::depth_first<CGNode *>(&Node));
auto DependentScannedNodes = llvm::map_range(
llvm::make_filter_range(DependentNodes, llvm::IsaPred<ScannedJobNode>),
llvm::CastTo<ScannedJobNode>);

for (ScannedJobNode *DependentNode : DependentScannedNodes) {
auto &DependentJob = *DependentNode->Job;
const auto &TCArgs = getToolChainArgs(C, DependentJob);
auto JobArgs = DependentJob.getArguments();
JobArgs.push_back(TCArgs.MakeArgString("-fmodule-file=" + ModuleName + "=" +
ModuleOutputPath));
DependentJob.replaceArguments(std::move(JobArgs));
}
}

/// Finalizes command lines for C++20 named module dependencies.
///
/// The command lines produced by dependency scanning are only adjusted to
/// handle discovered Clang modules. For C++20 named modules, we update the
/// command-lines here.
static void fixupNamedModuleCommandLines(Compilation &C,
CompilationGraph &Graph) {
const auto NamedModuleNodes = llvm::map_range(
llvm::make_filter_range(Graph, llvm::IsaPred<NamedModuleJobNode>),
llvm::CastTo<NamedModuleJobNode>);

for (NamedModuleJobNode *Node : NamedModuleNodes) {
const StringRef ModuleName = Node->InputDeps.ModuleName;
const auto ModuleOutputPath = createModuleOutputPath(C, ModuleName);
C.addTempFile(C.getArgs().MakeArgString(ModuleOutputPath));

configureNamedModuleOutputArg(C, *Node, ModuleOutputPath);
propagateModuleFileMappingArg(C, *Node, ModuleOutputPath);
}
}

/// Moves jobs from \p Graph into \p C in the graph's topological order.
static void feedJobsBackIntoCompilation(Compilation &C,
CompilationGraph &&Graph) {
Expand Down Expand Up @@ -1600,7 +1679,6 @@ void driver::modules::runModulesDriver(
if (!Diags.isLastDiagnosticIgnored())
llvm::WriteGraph<const CompilationGraph *>(llvm::errs(), &Graph);

// TODO: Fix-up command-lines for named module imports.

fixupNamedModuleCommandLines(C, Graph);
feedJobsBackIntoCompilation(C, std::move(Graph));
}
111 changes: 111 additions & 0 deletions clang/test/Driver/modules-driver-both-modules-types.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Checks that -fmodules-driver correctly handles compilations using both
// Standard C++20 modules and Clang modules.
// Importing a Standard C++20 module into Clang module is not supported yet.

// RUN: split-file %s %t
// RUN: rm -rf %t/modules-cache

// RUN: %clang -c -std=c++23 \
// RUN: -fmodules-driver -Rmodules-driver \
// RUN: -fmodules -Rmodule-import \
// RUN: -fmodule-map-file=%t/module.modulemap \
// RUN: -fmodules-cache-path=%t/modules-cache \
// RUN: %t/main.cpp %t/A.cppm %t/A-part1.cppm %t/A-part1-impl.cppm 2>&1 \
// RUN: | sed 's:\\\\\?:/:g' \
// RUN: | FileCheck -DPREFIX=%/t --check-prefix=CHECK-REMARKS %s

// The scan itself will also produce [-Rmodule-import] remarks.
// Let's skip past them, we only care about the final -cc1 commands.
// CHECK-REMARKS: clang: remark: printing module dependency graph [-Rmodules-driver]
// CHECK-REMARKS-NEXT: digraph "Module Dependency Graph" {
// CHECK-REMARKS: }

// CHECK-REMARKS: [[PREFIX]]/A-part1-impl.cppm:2:2: remark: importing module 'root' from
// CHECK-REMARKS: [[PREFIX]]/A-part1.cppm:2:2: remark: importing module 'root' from
// CHECK-REMARKS: [[PREFIX]]/A.cppm:2:2: remark: importing module 'root' from
// CHECK-REMARKS: [[PREFIX]]/A.cppm:4:8: remark: importing module 'A:part1' from
// CHECK-REMARKS: [[PREFIX]]/A.cppm:4:8: remark: importing module 'root' into 'A:part1' from
// CHECK-REMARKS: [[PREFIX]]/main.cpp:1:1: remark: importing module 'A' from
// CHECK-REMARKS: [[PREFIX]]/main.cpp:1:1: remark: importing module 'root' into 'A' from
// CHECK-REMARKS: [[PREFIX]]/main.cpp:1:1: remark: importing module 'A:part1' into 'A' from
// CHECK-REMARKS: [[PREFIX]]/main.cpp:1:1: remark: importing module 'root' into 'A:part1' from

// RUN: %clang -std=c++23 \
// RUN: -fmodules-driver -Rmodules-driver \
// RUN: -fmodules -Rmodule-import \
// RUN: -fmodule-map-file=%t/module.modulemap \
// RUN: -fmodules-cache-path=%t/modules-cache \
// RUN: %t/main.cpp %t/A.cppm %t/A-part1.cppm %t/A-part1-impl.cppm \
// RUN: -### 2>&1 \
// RUN: | sed 's:\\\\\?:/:g' \
// RUN: | FileCheck -DPREFIX=%/t --check-prefix=CHECK-CC1 %s

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "-o" "[[ROOTPCM:[^"]+]]"
// CHECK-CC1-SAME: "-emit-module"
// CHECK-CC1-SAME: "[[PREFIX]]/module.modulemap"
// CHECK-CC1-SAME: "-fmodule-name=root"
// CHECK-CC1-SAME: "-fno-implicit-modules"

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "[[PREFIX]]/A-part1-impl.cppm"
// CHECK-CC1-SAME: "-fmodule-file=root=[[ROOTPCM]]"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-output=[[A_PART1_IMPL_PCM:[^"]+]]"

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "[[PREFIX]]/A-part1.cppm"
// CHECK-CC1-SAME: "-fmodule-file=root=[[ROOTPCM]]"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-output=[[A_PART1_PCM:[^"]+]]"

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "[[PREFIX]]/A.cppm"
// CHECK-CC1-SAME: "-fmodule-file=root=[[ROOTPCM]]"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-output=[[A_PCM:[^"]+]]"
// CHECK-CC1-SAME: "-fmodule-file=A:part1=[[A_PART1_PCM]]"

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "[[PREFIX]]/main.cpp"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-file=A=[[A_PCM]]"
// CHECK-CC1-SAME: "-fmodule-file=A:part1=[[A_PART1_PCM]]"

//--- main.cpp
import A;

int main() {
a();
}

//--- A.cppm
module;
#include "root.h"
export module A;
export import :part1;

export int a() {
return part1() + root();
}

//--- A-part1.cppm
module;
#include "root.h"
export module A:part1;
export int part1();

//--- A-part1-impl.cppm
module;
#include "root.h"
module A:part1_impl;

int part1() {
return root();
}

//--- module.modulemap
module root { header "root.h" export * }

//--- root.h
inline int root() { return 1; }
88 changes: 88 additions & 0 deletions clang/test/Driver/modules-driver-cxx-modules-only.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Checks that -fmodules-driver correctly handles compilations using
// Standard C++20 modules.

// RUN: split-file %s %t

// RUN: %clang -c -std=c++23 \
// RUN: -fmodules-driver -Rmodules-driver -Rmodule-import \
// RUN: %t/main.cpp %t/A.cppm %t/A-part1.cppm %t/A-part1-impl.cppm %t/B.cppm 2>&1 \
// RUN: | sed 's:\\\\\?:/:g' \
// RUN: | FileCheck -DPREFIX=%/t --check-prefix=CHECK-REMARKS %s

// CHECK-REMARKS: [[PREFIX]]/A.cppm:2:8: remark: importing module 'A:part1' from
// CHECK-REMARKS: [[PREFIX]]/A.cppm:3:1: remark: importing module 'B' from
// CHECK-REMARKS: [[PREFIX]]/main.cpp:1:1: remark: importing module 'A' from
// CHECK-REMARKS: [[PREFIX]]/main.cpp:1:1: remark: importing module 'A:part1' into 'A' from
// CHECK-REMARKS: [[PREFIX]]/main.cpp:1:1: remark: importing module 'B' into 'A' from
// CHECK-REMARKS: [[PREFIX]]/main.cpp:2:1: remark: importing module 'B' from

// RUN: %clang -std=c++23 \
// RUN: -fmodules-driver -Rmodules-driver -Rmodule-import \
// RUN: %t/main.cpp %t/A.cppm %t/A-part1.cppm %t/A-part1-impl.cppm %t/B.cppm \
// RUN: -### 2>&1 \
// RUN: | sed 's:\\\\\?:/:g' \
// RUN: | FileCheck --check-prefix=CHECK-CC1 %s

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "{{.*}}/B.cppm"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-output=[[B_PCM:[^"]+]]"

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "{{.*}}/A-part1-impl.cppm"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-output=[[A_PART1_IMPL_PCM:[^"]+]]"

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "{{.*}}/A-part1.cppm"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-output=[[A_PART1_PCM:[^"]+]]"

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "{{.*}}/A.cppm"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-output=[[A_PCM:[^"]+]]"
// CHECK-CC1-SAME: "-fmodule-file=A:part1=[[A_PART1_PCM]]"
// CHECK-CC1-SAME: "-fmodule-file=B=[[B_PCM]]"

// CHECK-CC1: "-cc1"
// CHECK-CC1-SAME: "{{.*}}/main.cpp"
// CHECK-CC1-SAME: "-fno-implicit-modules"
// CHECK-CC1-SAME: "-fmodule-file=A=[[A_PCM]]"
// CHECK-CC1-SAME: "-fmodule-file=A:part1=[[A_PART1_PCM]]"
// CHECK-CC1-SAME: "-fmodule-file=B=[[B_PCM]]"

//--- main.cpp
import A;
import B;

int main() {
return a() + b();
}

//--- A.cppm
export module A;
export import :part1;
import B;

export int a() {
return part1() + b();
}

//--- A-part1.cppm
export module A:part1;
export int part1();

//--- A-part1-impl.cppm
module A:part1_impl;

int part1() {
return 30;
}

//--- B.cppm
export module B;

export int b() {
return 12;
}
Loading
Loading