mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Build the clang::CompilerInvocation in the driver. (#5784)
Add driver flags to specify clang driver arguments. --------- Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
co-authored by
Chandler Carruth
parent
bcba76aca7
commit
553dd6e531
+15
-110
@@ -45,9 +45,6 @@
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// The fake file name to use for the synthesized includes file.
|
||||
static constexpr const char IncludesFileName[] = "<carbon Cpp imports>";
|
||||
|
||||
// Generates C++ file contents to #include all requested imports.
|
||||
static auto GenerateCppIncludesHeaderCode(
|
||||
Context& context, llvm::ArrayRef<Parse::Tree::PackagingNames> imports)
|
||||
@@ -101,99 +98,6 @@ static auto AddImportIRInst(Context& context,
|
||||
|
||||
namespace {
|
||||
|
||||
// Used to convert diagnostics from the Clang driver to Carbon diagnostics.
|
||||
class CarbonClangDriverDiagnosticConsumer : public clang::DiagnosticConsumer {
|
||||
public:
|
||||
// Creates an instance with the location that triggers calling Clang.
|
||||
// `context` must not be null.
|
||||
explicit CarbonClangDriverDiagnosticConsumer(
|
||||
Diagnostics::NoLocEmitter* emitter)
|
||||
: emitter_(emitter) {}
|
||||
|
||||
// Generates a Carbon warning for each Clang warning and a Carbon error for
|
||||
// each Clang error or fatal.
|
||||
auto HandleDiagnostic(clang::DiagnosticsEngine::Level diag_level,
|
||||
const clang::Diagnostic& info) -> void override {
|
||||
DiagnosticConsumer::HandleDiagnostic(diag_level, info);
|
||||
|
||||
llvm::SmallString<256> message;
|
||||
info.FormatDiagnostic(message);
|
||||
|
||||
switch (diag_level) {
|
||||
case clang::DiagnosticsEngine::Ignored:
|
||||
case clang::DiagnosticsEngine::Note:
|
||||
case clang::DiagnosticsEngine::Remark: {
|
||||
// TODO: Emit notes and remarks.
|
||||
break;
|
||||
}
|
||||
case clang::DiagnosticsEngine::Warning:
|
||||
case clang::DiagnosticsEngine::Error:
|
||||
case clang::DiagnosticsEngine::Fatal: {
|
||||
CARBON_DIAGNOSTIC(CppInteropDriverWarning, Warning, "{0}", std::string);
|
||||
CARBON_DIAGNOSTIC(CppInteropDriverError, Error, "{0}", std::string);
|
||||
emitter_->Emit(diag_level == clang::DiagnosticsEngine::Warning
|
||||
? CppInteropDriverWarning
|
||||
: CppInteropDriverError,
|
||||
message.str().str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Diagnostic emitter. Note that driver diagnostics don't have meaningful
|
||||
// locations attached.
|
||||
Diagnostics::NoLocEmitter* emitter_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Builds a clang `CompilerInvocation` describing the options to use to build an
|
||||
// imported C++ AST.
|
||||
// TODO: Cache the compiler invocation created here and reuse it if building
|
||||
// multiple AST units. Consider building the `CompilerInvocation` from the
|
||||
// driver and passing it into check. This would also allow us to have a shared
|
||||
// set of defaults between the Clang invocation we use for imports and the
|
||||
// invocation we use for `carbon clang`.
|
||||
static auto BuildCompilerInvocation(
|
||||
Context& context, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
const std::string& clang_path, const std::string& target)
|
||||
-> std::unique_ptr<clang::CompilerInvocation> {
|
||||
Diagnostics::NoLocEmitter emitter(context.emitter());
|
||||
CarbonClangDriverDiagnosticConsumer diagnostics_consumer(&emitter);
|
||||
|
||||
const char* driver_args[] = {
|
||||
clang_path.c_str(),
|
||||
// Propagate the target to Clang.
|
||||
"-target",
|
||||
target.c_str(),
|
||||
// Require PIE. Note its default is configurable in Clang.
|
||||
"-fPIE",
|
||||
// Parse as a C++ (not C) header.
|
||||
"-x",
|
||||
"c++",
|
||||
IncludesFileName,
|
||||
};
|
||||
|
||||
// Build a diagnostics engine. Note that we don't have any diagnostic options
|
||||
// yet; they're produced by running the driver.
|
||||
clang::DiagnosticOptions driver_diag_opts;
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> driver_diags(
|
||||
clang::CompilerInstance::createDiagnostics(*fs, driver_diag_opts,
|
||||
&diagnostics_consumer,
|
||||
/*ShouldOwnClient=*/false));
|
||||
|
||||
// Ask the driver to process the arguments and build a corresponding clang
|
||||
// frontend invocation.
|
||||
auto invocation =
|
||||
clang::createInvocation(driver_args, {.Diags = driver_diags, .VFS = fs});
|
||||
|
||||
// Emit any queued diagnostics from parsing our driver arguments.
|
||||
return invocation;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Used to convert Clang diagnostics to Carbon diagnostics.
|
||||
class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
|
||||
public:
|
||||
@@ -310,16 +214,8 @@ class CarbonClangDiagnosticConsumer : public clang::DiagnosticConsumer {
|
||||
static auto GenerateAst(Context& context,
|
||||
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
const std::string& clang_path,
|
||||
const std::string& target)
|
||||
std::shared_ptr<clang::CompilerInvocation> invocation)
|
||||
-> std::pair<std::unique_ptr<clang::ASTUnit>, bool> {
|
||||
// Build the options to use to invoke the Clang frontend.
|
||||
std::shared_ptr<clang::CompilerInvocation> invocation =
|
||||
BuildCompilerInvocation(context, fs, clang_path, target);
|
||||
if (!invocation) {
|
||||
return {nullptr, true};
|
||||
}
|
||||
|
||||
// Build a diagnostics engine.
|
||||
CarbonClangDiagnosticConsumer diagnostics_consumer(&context,
|
||||
invocation.get());
|
||||
@@ -328,11 +224,20 @@ static auto GenerateAst(Context& context,
|
||||
*fs, invocation->getDiagnosticOpts(), &diagnostics_consumer,
|
||||
/*ShouldOwnClient=*/false));
|
||||
|
||||
// Extract the input from the frontend invocation and make sure it makes
|
||||
// sense.
|
||||
const auto& inputs = invocation->getFrontendOpts().Inputs;
|
||||
CARBON_CHECK(inputs.size() == 1 &&
|
||||
inputs[0].getKind().getLanguage() == clang::Language::CXX &&
|
||||
inputs[0].getKind().getFormat() == clang::InputKind::Source);
|
||||
llvm::StringRef file_name = inputs[0].getFile();
|
||||
|
||||
// Remap the imports file name to the corresponding `#include`s.
|
||||
// TODO: Modify the frontend options to specify this memory buffer as input
|
||||
// instead of remapping the file.
|
||||
std::string includes = GenerateCppIncludesHeaderCode(context, imports);
|
||||
auto includes_buffer =
|
||||
llvm::MemoryBuffer::getMemBuffer(includes, IncludesFileName);
|
||||
invocation->getPreprocessorOpts().addRemappedFile(IncludesFileName,
|
||||
auto includes_buffer = llvm::MemoryBuffer::getMemBuffer(includes, file_name);
|
||||
invocation->getPreprocessorOpts().addRemappedFile(file_name,
|
||||
includes_buffer.get());
|
||||
|
||||
// Create the AST unit.
|
||||
@@ -388,7 +293,7 @@ static auto AddNamespace(Context& context, PackageNameId cpp_package_id,
|
||||
auto ImportCppFiles(Context& context,
|
||||
llvm::ArrayRef<Parse::Tree::PackagingNames> imports,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
llvm::StringRef clang_path, llvm::StringRef target)
|
||||
std::shared_ptr<clang::CompilerInvocation> invocation)
|
||||
-> std::unique_ptr<clang::ASTUnit> {
|
||||
if (imports.empty()) {
|
||||
return nullptr;
|
||||
@@ -404,7 +309,7 @@ auto ImportCppFiles(Context& context,
|
||||
auto name_scope_id = AddNamespace(context, package_id, imports);
|
||||
|
||||
auto [generated_ast, ast_has_error] =
|
||||
GenerateAst(context, imports, fs, clang_path.str(), target.str());
|
||||
GenerateAst(context, imports, fs, std::move(invocation));
|
||||
|
||||
SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id);
|
||||
name_scope.set_is_closed_import(true);
|
||||
|
||||
Reference in New Issue
Block a user