blob: 8aad595b78686f0aaedef37c0084124d8514995c [file] [log] [blame] [edit]
//===--- ModulesDriver.cpp - Driver managed module builds -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines functionality to support driver managed builds for
/// compilations which use Clang modules or standard C++20 named modules.
///
//===----------------------------------------------------------------------===//
#include "clang/Driver/ModulesDriver.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LLVM.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Job.h"
#include "clang/Driver/Tool.h"
#include "clang/Driver/ToolChain.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/VirtualFileSystem.h"
using namespace llvm::opt;
using namespace clang;
using namespace driver;
using namespace modules;
namespace clang::driver::modules {
static bool fromJSON(const llvm::json::Value &Params,
StdModuleManifest::Module::LocalArguments &LocalArgs,
llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, P);
return O.mapOptional("system-include-directories",
LocalArgs.SystemIncludeDirs);
}
static bool fromJSON(const llvm::json::Value &Params,
StdModuleManifest::Module &ModuleEntry,
llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, P);
return O.map("is-std-library", ModuleEntry.IsStdlib) &&
O.map("logical-name", ModuleEntry.LogicalName) &&
O.map("source-path", ModuleEntry.SourcePath) &&
O.mapOptional("local-arguments", ModuleEntry.LocalArgs);
}
static bool fromJSON(const llvm::json::Value &Params,
StdModuleManifest &Manifest, llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, P);
return O.map("modules", Manifest.Modules);
}
} // namespace clang::driver::modules
/// Parses the Standard library module manifest from \p Buffer.
static Expected<StdModuleManifest> parseManifest(StringRef Buffer) {
auto ParsedOrErr = llvm::json::parse(Buffer);
if (!ParsedOrErr)
return ParsedOrErr.takeError();
StdModuleManifest Manifest;
llvm::json::Path::Root Root;
if (!fromJSON(*ParsedOrErr, Manifest, Root))
return Root.getError();
return Manifest;
}
/// Converts each file path in manifest from relative to absolute.
///
/// Each file path in the manifest is expected to be relative the manifest's
/// location \p ManifestPath itself.
static void makeManifestPathsAbsolute(
MutableArrayRef<StdModuleManifest::Module> ManifestEntries,
StringRef ManifestPath) {
StringRef ManifestDir = llvm::sys::path::parent_path(ManifestPath);
SmallString<256> TempPath;
auto PrependManifestDir = [&](StringRef Path) {
TempPath = ManifestDir;
llvm::sys::path::append(TempPath, Path);
return std::string(TempPath);
};
for (auto &Entry : ManifestEntries) {
Entry.SourcePath = PrependManifestDir(Entry.SourcePath);
if (!Entry.LocalArgs)
continue;
for (auto &IncludeDir : Entry.LocalArgs->SystemIncludeDirs)
IncludeDir = PrependManifestDir(IncludeDir);
}
}
Expected<StdModuleManifest>
driver::modules::readStdModuleManifest(StringRef ManifestPath,
llvm::vfs::FileSystem &VFS) {
auto MemBufOrErr = VFS.getBufferForFile(ManifestPath);
if (!MemBufOrErr)
return llvm::createFileError(ManifestPath, MemBufOrErr.getError());
auto ManifestOrErr = parseManifest((*MemBufOrErr)->getBuffer());
if (!ManifestOrErr)
return ManifestOrErr.takeError();
auto Manifest = std::move(*ManifestOrErr);
makeManifestPathsAbsolute(Manifest.Modules, ManifestPath);
return Manifest;
}
void driver::modules::buildStdModuleManifestInputs(
ArrayRef<StdModuleManifest::Module> ManifestEntries, Compilation &C,
InputList &Inputs) {
DerivedArgList &Args = C.getArgs();
const OptTable &Opts = C.getDriver().getOpts();
for (const auto &Entry : ManifestEntries) {
auto *InputArg =
makeInputArg(Args, Opts, Args.MakeArgString(Entry.SourcePath));
Inputs.emplace_back(types::TY_CXXModule, InputArg);
}
}
using ManifestEntryLookup =
llvm::DenseMap<StringRef, const StdModuleManifest::Module *>;
/// Builds a mapping from a module's source path to its entry in the manifest.
static ManifestEntryLookup
buildManifestLookupMap(ArrayRef<StdModuleManifest::Module> ManifestEntries) {
ManifestEntryLookup ManifestEntryBySource;
for (auto &Entry : ManifestEntries) {
[[maybe_unused]] const bool Inserted =
ManifestEntryBySource.try_emplace(Entry.SourcePath, &Entry).second;
assert(Inserted &&
"Manifest defines multiple modules with the same source path.");
}
return ManifestEntryBySource;
}
/// Returns the manifest entry corresponding to \p Job, or \c nullptr if none
/// exists.
static const StdModuleManifest::Module *
getManifestEntryForCommand(const Command &Job,
const ManifestEntryLookup &ManifestEntryBySource) {
for (const auto &II : Job.getInputInfos()) {
if (const auto It = ManifestEntryBySource.find(II.getFilename());
It != ManifestEntryBySource.end())
return It->second;
}
return nullptr;
}
/// Adds all \p SystemIncludeDirs to the \p CC1Args of \p Job.
static void
addSystemIncludeDirsFromManifest(Compilation &C, Command &Job,
ArgStringList &CC1Args,
ArrayRef<std::string> SystemIncludeDirs) {
const ToolChain &TC = Job.getCreator().getToolChain();
const DerivedArgList &TCArgs =
C.getArgsForToolChain(&TC, Job.getSource().getOffloadingArch(),
Job.getSource().getOffloadingDeviceKind());
for (const auto &IncludeDir : SystemIncludeDirs)
TC.addSystemInclude(TCArgs, CC1Args, IncludeDir);
}
static bool isCC1Job(const Command &Job) {
return StringRef(Job.getCreator().getName()) == "clang";
}
/// Apply command-line modifications specific for inputs originating from the
/// Standard library module manifest.
static void applyArgsForStdModuleManifestInputs(
Compilation &C, const ManifestEntryLookup &ManifestEntryBySource,
MutableArrayRef<std::unique_ptr<Command>> Jobs) {
for (auto &Job : Jobs) {
if (!isCC1Job(*Job))
continue;
const auto *Entry = getManifestEntryForCommand(*Job, ManifestEntryBySource);
if (!Entry)
continue;
auto CC1Args = Job->getArguments();
if (Entry->IsStdlib)
CC1Args.push_back("-Wno-reserved-module-identifier");
if (Entry->LocalArgs)
addSystemIncludeDirsFromManifest(C, *Job, CC1Args,
Entry->LocalArgs->SystemIncludeDirs);
Job->replaceArguments(CC1Args);
}
}
void driver::modules::runModulesDriver(
Compilation &C, ArrayRef<StdModuleManifest::Module> ManifestEntries) {
llvm::PrettyStackTraceString CrashInfo("Running modules driver.");
auto Jobs = C.getJobs().takeJobs();
const auto ManifestEntryBySource = buildManifestLookupMap(ManifestEntries);
// Apply manifest-entry specific command-line modifications before the scan as
// they might affect it.
applyArgsForStdModuleManifestInputs(C, ManifestEntryBySource, Jobs);
// TODO: Run the dependency scan.
// TODO: Prune jobs for modules specified in the manifest that are not
// required by any command-line input.
// TODO: Reorder and modify jobs based on the discovered dependencies.
// Add jobs back to the Compilation.
for (auto &Job : Jobs)
C.addCommand(std::move(Job));
}