mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Stop using the clang tooling library to build an ASTUnit; that library is set up to process clang frontend arguments, assuming that something has already built frontend arguments from the compiler arguments. It is also too encapsulated and doesn't let us inspect and modify the compiler invocation before it's executed. Instead, build the AST unit directly in two phases: * FIrst, take a list of clang driver arguments and convert them into a list of compiler arguments, using `clang::createInvocation`. Internally, this uses the clang driver to build a frontend invocation, including building system-specific include paths as needed. * Then, directly build an ASTUnit from this compiler invocation. I've factored this so that we can split out the `createInvocation` step, with the intention that we may want to move it out of check and into the carbon driver with the rest of the driver-level argument handling, and we may want to customize some of the clang options before we invoke the clang frontend with that set of options. In order to make the invocation reusable, it no longer depends on the name of the carbon file importing the C++ code. In place of synthesizing a header file name as `<foo.carbon>.generated.cpp_imports.h`, we now insert line marker directives into the generated header so that errors in that header cause Clang to point a diagnostic back at the Carbon source file itself. This results in a minor improvement in the diagnostic output: we no longer refer to a nonexistent generated file. But the snippet still contains text that doesn't match the source code, so it remains imperfect. --------- Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
221 lines
7.5 KiB
C++
221 lines
7.5 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/install/install_paths.h"
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "common/check.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/VirtualFileSystem.h"
|
|
#include "tools/cpp/runfiles/runfiles.h"
|
|
|
|
namespace Carbon {
|
|
|
|
// The location within our Bazel output tree of the prefix_root.
|
|
static constexpr llvm::StringLiteral PrefixRoot =
|
|
"carbon/toolchain/install/prefix_root/";
|
|
|
|
// Path within an install prefix for our marker of a valid install.
|
|
static constexpr llvm::StringLiteral MarkerPath =
|
|
"lib/carbon/carbon_install.txt";
|
|
|
|
auto InstallPaths::MakeExeRelative(llvm::StringRef exe_path) -> InstallPaths {
|
|
InstallPaths paths;
|
|
|
|
// Map from the executable path from the executable path to an install
|
|
// prefix path.
|
|
if (!llvm::sys::fs::exists(exe_path)) {
|
|
paths.SetError(llvm::Twine("No file at executable path: ") + exe_path);
|
|
return paths;
|
|
}
|
|
paths = InstallPaths(exe_path);
|
|
|
|
// TODO: Detect a Windows executable path and use custom logic to map to the
|
|
// correct install prefix for that platform.
|
|
|
|
// We assume an executable will be in a `bin` directory and this is a
|
|
// FHS-like install prefix. We remove the filename and walk up to find the
|
|
// expected install prefix.
|
|
llvm::sys::path::remove_filename(paths.prefix_);
|
|
llvm::sys::path::append(paths.prefix_, llvm::sys::path::Style::posix,
|
|
"../../");
|
|
|
|
if (auto error = llvm::sys::fs::make_absolute(paths.prefix_)) {
|
|
paths.SetError(error.message());
|
|
return paths;
|
|
}
|
|
|
|
paths.CheckMarkerFile();
|
|
return paths;
|
|
}
|
|
|
|
auto InstallPaths::MakeForBazelRunfiles(llvm::StringRef exe_path)
|
|
-> InstallPaths {
|
|
using bazel::tools::cpp::runfiles::Runfiles;
|
|
std::string runtimes_error;
|
|
std::unique_ptr<Runfiles> runfiles(
|
|
Runfiles::Create(exe_path.str(), &runtimes_error));
|
|
CARBON_CHECK(runfiles != nullptr, "Failed to find runtimes tree: {0}",
|
|
runtimes_error);
|
|
|
|
std::string relative_marker_path = (PrefixRoot.str() + MarkerPath).str();
|
|
std::string runtimes_marker_path = runfiles->Rlocation(relative_marker_path);
|
|
|
|
// Start from the marker, remove that filename, and walk up to find the
|
|
// install prefix.
|
|
InstallPaths paths(runtimes_marker_path);
|
|
llvm::sys::path::remove_filename(paths.prefix_);
|
|
llvm::sys::path::append(paths.prefix_, llvm::sys::path::Style::posix,
|
|
"../../");
|
|
|
|
if (auto error = llvm::sys::fs::make_absolute(paths.prefix_)) {
|
|
paths.SetError(error.message());
|
|
return paths;
|
|
}
|
|
|
|
paths.CheckMarkerFile();
|
|
CARBON_CHECK(!paths.error(), "{0}", *paths.error());
|
|
return paths;
|
|
}
|
|
|
|
auto InstallPaths::Make(llvm::StringRef install_prefix) -> InstallPaths {
|
|
InstallPaths paths(install_prefix);
|
|
paths.CheckMarkerFile();
|
|
return paths;
|
|
}
|
|
|
|
auto InstallPaths::ReadPreludeManifest() const
|
|
-> ErrorOr<llvm::SmallVector<std::string>> {
|
|
return ReadManifest(core_package(), "prelude_manifest.txt");
|
|
}
|
|
|
|
auto InstallPaths::ReadClangHeadersManifest() const
|
|
-> ErrorOr<llvm::SmallVector<std::string>> {
|
|
llvm::SmallString<256> manifest_path(prefix_);
|
|
llvm::sys::path::append(manifest_path, llvm::sys::path::Style::posix, "..");
|
|
return ReadManifest(manifest_path, "clang_headers_manifest.txt");
|
|
}
|
|
|
|
auto InstallPaths::ReadManifest(llvm::StringRef manifest_path,
|
|
llvm::StringRef manifest_file) const
|
|
-> ErrorOr<llvm::SmallVector<std::string>> {
|
|
// This is structured to avoid a vector copy on success.
|
|
ErrorOr<llvm::SmallVector<std::string>> result =
|
|
llvm::SmallVector<std::string>();
|
|
|
|
llvm::SmallString<256> manifest;
|
|
llvm::sys::path::append(manifest, llvm::sys::path::Style::posix,
|
|
manifest_path, manifest_file);
|
|
|
|
auto fs = llvm::vfs::getRealFileSystem();
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
|
|
fs->getBufferForFile(manifest);
|
|
if (!file) {
|
|
result = ErrorBuilder() << "Loading manifest `" << manifest
|
|
<< "`: " << file.getError().message();
|
|
return result;
|
|
}
|
|
|
|
// The manifest should have one file per line.
|
|
llvm::StringRef buffer = file.get()->getBuffer();
|
|
while (true) {
|
|
auto [token, remainder] = llvm::getToken(buffer, "\n");
|
|
if (token.empty()) {
|
|
break;
|
|
}
|
|
llvm::SmallString<256> path;
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix, manifest_path,
|
|
token);
|
|
result->push_back(path.str().str());
|
|
buffer = remainder;
|
|
}
|
|
|
|
if (result->empty()) {
|
|
result = ErrorBuilder() << "Manifest `" << manifest << "` is empty";
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto InstallPaths::SetError(llvm::Twine message) -> void {
|
|
// Use an empty prefix on error as that should use the working directory which
|
|
// is the least likely problematic.
|
|
prefix_ = "";
|
|
error_ = {message.str()};
|
|
}
|
|
|
|
auto InstallPaths::CheckMarkerFile() -> void {
|
|
if (!llvm::sys::path::is_absolute(prefix_)) {
|
|
SetError(llvm::Twine("Not an absolute path: ") + prefix_);
|
|
}
|
|
|
|
llvm::SmallString<256> path(prefix_);
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix, MarkerPath);
|
|
if (!llvm::sys::fs::exists(path)) {
|
|
SetError(llvm::Twine("No install marker at path: ") + path);
|
|
}
|
|
}
|
|
|
|
auto InstallPaths::core_package() const -> std::string {
|
|
llvm::SmallString<256> path(prefix_);
|
|
// TODO: Adjust this to work equally well on Windows.
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
|
|
"lib/carbon/core");
|
|
return path.str().str();
|
|
}
|
|
|
|
auto InstallPaths::llvm_install_bin() const -> std::string {
|
|
llvm::SmallString<256> path(prefix_);
|
|
// TODO: Adjust this to work equally well on Windows.
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
|
|
"lib/carbon/llvm/bin/");
|
|
return path.str().str();
|
|
}
|
|
|
|
auto InstallPaths::clang_path() const -> std::string {
|
|
llvm::SmallString<256> path(prefix_);
|
|
// TODO: Adjust this to work equally well on Windows.
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
|
|
"lib/carbon/llvm/bin/clang");
|
|
return path.str().str();
|
|
}
|
|
|
|
auto InstallPaths::lld_path() const -> std::string {
|
|
llvm::SmallString<256> path(prefix_);
|
|
// TODO: Adjust this to work equally well on Windows.
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
|
|
"lib/carbon/llvm/bin/lld");
|
|
return path.str().str();
|
|
}
|
|
|
|
auto InstallPaths::ld_lld_path() const -> std::string {
|
|
llvm::SmallString<256> path(prefix_);
|
|
// TODO: Adjust this to work equally well on Windows.
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
|
|
"lib/carbon/llvm/bin/ld.lld");
|
|
return path.str().str();
|
|
}
|
|
|
|
auto InstallPaths::ld64_lld_path() const -> std::string {
|
|
llvm::SmallString<256> path(prefix_);
|
|
// TODO: Adjust this to work equally well on Windows.
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
|
|
"lib/carbon/llvm/bin/ld64.lld");
|
|
return path.str().str();
|
|
}
|
|
|
|
auto InstallPaths::llvm_tool_path(LLVMTool tool) const -> std::string {
|
|
llvm::SmallString<256> path(prefix_);
|
|
// TODO: Adjust this to work equally well on Windows.
|
|
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
|
|
"lib/carbon/llvm/bin", tool.bin_name());
|
|
return path.str().str();
|
|
}
|
|
|
|
} // namespace Carbon
|