Skip to content

Commit

Permalink
Refactor FindPreludeFiles into InstallPaths (carbon-language#4268)
Browse files Browse the repository at this point in the history
From the driver's perspective, `FindPreludeFiles` is closely tied to
`compile`. This makes it difficult to refactor commands without
affecting the test dependencies on `FindPreludeFiles`. `InstallPaths`
seems like a decent home since it is responsible for the install
structure.

I'm switching to an `Error` return to allow callers to choose how to
handle it (e.g., in file tests, we typically don't want the direct error
stream).
  • Loading branch information
jonmeow authored Sep 4, 2024
1 parent 1412ecd commit e382e6f
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 83 deletions.
1 change: 1 addition & 0 deletions testing/base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ cc_test(
":source_gen_lib",
"//common:set",
"//toolchain/driver",
"//toolchain/install:install_paths_test_helpers",
"@googletest//:gtest",
"@llvm-project//llvm:Support",
],
Expand Down
15 changes: 2 additions & 13 deletions testing/base/source_gen_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "common/set.h"
#include "testing/base/global_exe_path.h"
#include "toolchain/driver/driver.h"
#include "toolchain/install/install_paths_test_helpers.h"

namespace Carbon::Testing {
namespace {
Expand Down Expand Up @@ -147,19 +148,7 @@ auto TestCompile(llvm::StringRef source) -> bool {
InstallPaths::MakeForBazelRunfiles(Testing::GetExePath()));
Driver driver(fs, &installation, llvm::outs(), llvm::errs());

// Load the prelude into our VFS.
//
// TODO: Factor this and analogous code in file_test into a Driver helper.
auto prelude =
Driver::FindPreludeFiles(installation.core_package(), llvm::errs());
CARBON_CHECK(!prelude.empty());
for (const auto& path : prelude) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
llvm::MemoryBuffer::getFile(path);
CARBON_CHECK(file) << file.getError().message();
CARBON_CHECK(fs.addFile(path, /*ModificationTime=*/0, std::move(*file)))
<< "Duplicate file: " << path;
}
AddPreludeFilesToVfs(installation, &fs);

fs.addFile("test.carbon", /*ModificationTime=*/0,
llvm::MemoryBuffer::getMemBuffer(source));
Expand Down
1 change: 1 addition & 0 deletions toolchain/driver/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ cc_binary(
"//testing/base:benchmark_main",
"//testing/base:global_exe_path",
"//testing/base:source_gen_lib",
"//toolchain/install:install_paths_test_helpers",
"@google_benchmark//:benchmark",
"@llvm-project//llvm:Support",
],
Expand Down
15 changes: 2 additions & 13 deletions toolchain/driver/compile_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "testing/base/global_exe_path.h"
#include "testing/base/source_gen.h"
#include "toolchain/driver/driver.h"
#include "toolchain/install/install_paths_test_helpers.h"

namespace Carbon::Testing {
namespace {
Expand All @@ -22,19 +23,7 @@ class CompileBenchmark {
CompileBenchmark()
: installation_(InstallPaths::MakeForBazelRunfiles(GetExePath())),
driver_(fs_, &installation_, llvm::outs(), llvm::errs()) {
// Load the prelude into our VFS.
//
// TODO: Factor this and analogous code in file_test into a Driver helper.
auto prelude =
Driver::FindPreludeFiles(installation_.core_package(), llvm::errs());
CARBON_CHECK(!prelude.empty());
for (const auto& path : prelude) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
llvm::MemoryBuffer::getFile(path);
CARBON_CHECK(file) << file.getError().message();
CARBON_CHECK(fs_.addFile(path, /*ModificationTime=*/0, std::move(*file)))
<< "Duplicate file: " << path;
}
AddPreludeFilesToVfs(installation_, &fs_);
}

// Setup a set of source files in the VFS for the driver. Each string input is
Expand Down
53 changes: 8 additions & 45 deletions toolchain/driver/driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,44 +34,6 @@

namespace Carbon {

auto Driver::FindPreludeFiles(llvm::StringRef core_package_dir,
llvm::raw_ostream& error_stream)
-> llvm::SmallVector<std::string> {
llvm::SmallVector<std::string> result;

// Include <data>/core/prelude.carbon, which is the entry point into the
// prelude.
{
llvm::SmallString<256> prelude_file(core_package_dir);
llvm::sys::path::append(prelude_file, llvm::sys::path::Style::posix,
"prelude.carbon");
result.push_back(prelude_file.str().str());
}

// Glob for <data>/core/prelude/**/*.carbon and add all the files we find.
llvm::SmallString<256> prelude_dir(core_package_dir);
llvm::sys::path::append(prelude_dir, llvm::sys::path::Style::posix,
"prelude");
std::error_code ec;
for (llvm::sys::fs::recursive_directory_iterator prelude_files_it(
prelude_dir, ec, /*follow_symlinks=*/false);
prelude_files_it != llvm::sys::fs::recursive_directory_iterator();
prelude_files_it.increment(ec)) {
if (ec) {
error_stream << "ERROR: Could not find prelude: " << ec.message() << "\n";
result.clear();
return result;
}

auto prelude_file = prelude_files_it->path();
if (llvm::sys::path::extension(prelude_file) == ".carbon") {
result.push_back(prelude_file);
}
}

return result;
}

struct Driver::CodegenOptions {
void Build(CommandLine::CommandBuilder& b) {
b.AddStringOption(
Expand Down Expand Up @@ -865,13 +827,14 @@ auto Driver::Compile(const CompileOptions& options,
// Find the files comprising the prelude if we are importing it.
// TODO: Replace this with a search for library api files in a
// package-specific search path based on the library name.
bool want_prelude =
options.prelude_import && options.phase >= CompileOptions::Phase::Check;
auto prelude = want_prelude ? FindPreludeFiles(installation_->core_package(),
error_stream_)
: llvm::SmallVector<std::string>{};
if (want_prelude && prelude.empty()) {
return {.success = false};
llvm::SmallVector<std::string> prelude;
if (options.prelude_import && options.phase >= CompileOptions::Phase::Check) {
if (auto find = installation_->FindPreludeFiles(); find.ok()) {
prelude = std::move(*find);
} else {
error_stream_ << "ERROR: " << find.error() << "\n";
return {.success = false};
}
}

// Prepare CompilationUnits before building scope exit handlers.
Expand Down
7 changes: 0 additions & 7 deletions toolchain/driver/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@ class Driver {
// error stream (stderr by default).
auto RunCommand(llvm::ArrayRef<llvm::StringRef> args) -> RunResult;

// Finds the source files that define the prelude and returns a list of their
// filenames. On error, writes a message to `error_stream` and returns an
// empty list.
static auto FindPreludeFiles(llvm::StringRef core_package_dir,
llvm::raw_ostream& error_stream)
-> llvm::SmallVector<std::string>;

private:
struct Options;
struct CodegenOptions;
Expand Down
12 changes: 12 additions & 0 deletions toolchain/install/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,18 @@ cc_test(
],
)

cc_library(
name = "install_paths_test_helpers",
testonly = 1,
srcs = ["install_paths_test_helpers.cpp"],
hdrs = ["install_paths_test_helpers.h"],
deps = [
":install_paths",
"//testing/base:global_exe_path",
"@llvm-project//llvm:Support",
],
)

# Build rules to construct packaged versions of the toolchain's install.

pkg_files(
Expand Down
40 changes: 40 additions & 0 deletions toolchain/install/install_paths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,46 @@ auto InstallPaths::Make(llvm::StringRef install_prefix) -> InstallPaths {
return paths;
}

auto InstallPaths::FindPreludeFiles() 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>();

std::string dir = core_package();

// Include <data>/core/prelude.carbon, which is the entry point into the
// prelude.
{
llvm::SmallString<256> prelude_file(dir);
llvm::sys::path::append(prelude_file, llvm::sys::path::Style::posix,
"prelude.carbon");
result->push_back(prelude_file.str().str());
}

// Glob for <data>/core/prelude/**/*.carbon and add all the files we find.
llvm::SmallString<256> prelude_dir(dir);
llvm::sys::path::append(prelude_dir, llvm::sys::path::Style::posix,
"prelude");
std::error_code ec;
for (llvm::sys::fs::recursive_directory_iterator prelude_files_it(
prelude_dir, ec, /*follow_symlinks=*/false);
prelude_files_it != llvm::sys::fs::recursive_directory_iterator();
prelude_files_it.increment(ec)) {
if (ec) {
result = ErrorBuilder() << "Could not find prelude: " << ec.message();
return result;
}

auto prelude_file = prelude_files_it->path();
if (llvm::sys::path::extension(prelude_file) == ".carbon") {
result->push_back(prelude_file);
}
}

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.
Expand Down
9 changes: 9 additions & 0 deletions toolchain/install/install_paths.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef CARBON_TOOLCHAIN_INSTALL_INSTALL_PATHS_H_
#define CARBON_TOOLCHAIN_INSTALL_INSTALL_PATHS_H_

#include "common/error.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
Expand Down Expand Up @@ -59,6 +60,10 @@ namespace Carbon {
// TODO: Need to check the installation structure of LLVM on Windows and figure
// out what Carbon's should be within a Windows prefix and how much of the
// structure we can share with the Unix-y layout of the prefix.
//
// TODO: InstallPaths is typically called from places using a VFS (both tests
// and the Driver), but does not use a VFS itself. It currently only supports
// using the real filesystem, but should probably support a VFS.
class InstallPaths {
public:
// Provide the current executable's path to detect the correct installation
Expand All @@ -82,6 +87,10 @@ class InstallPaths {
// using Carbon in an environment with an unusual path to the installed files.
static auto Make(llvm::StringRef install_prefix) -> InstallPaths;

// Finds the source files that define the prelude and returns a list of their
// filenames. The list always includes at least one file.
auto FindPreludeFiles() const -> ErrorOr<llvm::SmallVector<std::string>>;

// Check for an error detecting the install paths correctly.
//
// A nullopt return means no errors encountered and the paths should work
Expand Down
29 changes: 29 additions & 0 deletions toolchain/install/install_paths_test_helpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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_test_helpers.h"

#include "testing/base/global_exe_path.h"

namespace Carbon::Testing {

// Prepares the VFS with prelude files from the real filesystem. Primarily for
// tests.
auto AddPreludeFilesToVfs(InstallPaths install_paths,
llvm::vfs::InMemoryFileSystem* vfs) -> void {
// Load the prelude into the test VFS.
auto real_fs = llvm::vfs::getRealFileSystem();
auto prelude = install_paths.FindPreludeFiles();
CARBON_CHECK(prelude.ok()) << prelude.error();

for (const auto& path : *prelude) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
real_fs->getBufferForFile(path);
CARBON_CHECK(file) << "Error getting file: " << file.getError().message();
bool added = vfs->addFile(path, /*ModificationTime=*/0, std::move(*file));
CARBON_CHECK(added) << "Duplicate file: " << path;
}
}

} // namespace Carbon::Testing
19 changes: 19 additions & 0 deletions toolchain/install/install_paths_test_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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

#ifndef CARBON_TOOLCHAIN_INSTALL_INSTALL_PATHS_TEST_HELPERS_H_
#define CARBON_TOOLCHAIN_INSTALL_INSTALL_PATHS_TEST_HELPERS_H_

#include "llvm/Support/VirtualFileSystem.h"
#include "toolchain/install/install_paths.h"

namespace Carbon::Testing {

// Prepares the VFS with prelude files from the real filesystem.
auto AddPreludeFilesToVfs(InstallPaths install_paths,
llvm::vfs::InMemoryFileSystem* vfs) -> void;

} // namespace Carbon::Testing

#endif // CARBON_TOOLCHAIN_INSTALL_INSTALL_PATHS_TEST_HELPERS_H_
6 changes: 1 addition & 5 deletions toolchain/testing/file_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ class ToolchainFileTest : public FileTestBase {
auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
llvm::vfs::InMemoryFileSystem& fs, llvm::raw_pwrite_stream& stdout,
llvm::raw_pwrite_stream& stderr) -> ErrorOr<RunResult> override {
auto prelude =
Driver::FindPreludeFiles(installation_.core_package(), stderr);
if (prelude.empty()) {
return Error("Could not find prelude");
}
CARBON_ASSIGN_OR_RETURN(auto prelude, installation_.FindPreludeFiles());
for (const auto& file : prelude) {
CARBON_RETURN_IF_ERROR(AddFile(fs, file));
}
Expand Down

0 comments on commit e382e6f

Please sign in to comment.