Files
clice/src/Compiler/Driver.h
2025-11-30 15:21:27 +08:00

146 lines
4.7 KiB
C++

#pragma once
#include "Compiler/Command.h"
#include "Support/Logging.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "clang/Driver/Driver.h"
namespace clice {
namespace {
namespace opt = llvm::opt;
namespace driver = clang::driver;
/// Checks if dash-dash (`--`) parsing is enabled. If enabled, all arguments
/// after a standalone `--` are treated as positional arguments (e.g., input files).
bool enable_dash_dash_parsing(const opt::OptTable& table);
/// Checks if grouped short options are enabled. If enabled, a short option group
/// like `-ab` is parsed as separate options `-a` and `-b`.
bool enable_grouped_short_options(const opt::OptTable& table);
/// Get the specific toolchain of given target, we mainly use it to get msvc toolchain.
const driver::ToolChain& get_toolchain(driver::Driver& driver,
const opt::ArgList& Args,
const llvm::Triple& Target);
template <auto MP1, auto MP2, auto MP3>
struct Thief {
friend bool enable_dash_dash_parsing(const opt::OptTable& table) {
return table.*MP1;
}
friend bool enable_grouped_short_options(const opt::OptTable& table) {
return table.*MP2;
}
friend const driver::ToolChain& get_toolchain(driver::Driver& driver,
const opt::ArgList& args,
const llvm::Triple& target) {
return (driver.*MP3)(args, target);
}
};
template struct Thief<&opt::OptTable::DashDashParsing,
&opt::OptTable::GroupedShortOptions,
&driver::Driver::getToolChain>;
class ArgumentParser final : public llvm::opt::ArgList {
public:
ArgumentParser(llvm::BumpPtrAllocator* allocator) : allocator(allocator) {}
~ArgumentParser() {
/// We never use the private `Args` field, so make sure it's empty.
if(getArgs().size() != 0) {
std::abort();
}
}
const char* getArgString(unsigned index) const override {
return arguments[index];
}
unsigned getNumInputArgStrings() const override {
return arguments.size();
}
const char* MakeArgStringRef(llvm::StringRef s) const override {
auto p = allocator->Allocate<char>(s.size() + 1);
std::ranges::copy(s, p);
p[s.size()] = '\0';
return p;
}
inline static auto& option_table = clang::driver::getDriverOptTable();
void set_arguments(llvm::ArrayRef<const char*> arguments) {
if(getArgs().size() != 0) {
std::abort();
}
this->arguments = arguments;
}
std::unique_ptr<llvm::opt::Arg> parse_one(unsigned& index) {
/// Make sure we are not using
assert(!enable_dash_dash_parsing(option_table));
assert(!enable_grouped_short_options(option_table));
return option_table.ParseOneArg(*this, index);
}
void parse(llvm::ArrayRef<const char*> arguments, const auto& on_parse, const auto& on_error) {
this->arguments = arguments;
unsigned it = 0;
while(it != arguments.size()) {
llvm::StringRef s = arguments[it];
if(s.empty()) [[unlikely]] {
it += 1;
continue;
}
auto prev = it;
auto arg = parse_one(it);
assert(it > prev && "parser failed to consume argument");
if(!arg) [[unlikely]] {
assert(it >= arguments.size() && "unexpected parser error!");
assert(it - prev - 1 && "no missing arguments!");
/// FIXME: When parsing fails, the parser may have encountered unknown
/// arguments (e.g., options for a different compiler like nvcc).
/// We should allow the user to provide a custom option registry
/// (mainly for these pass-through arguments).
///
/// This would let us ignore them correctly. For example, when
/// parsing `nvcc --option-dir x.txt main.cpp`, our parser fails
/// because it discards `--option-dir` but doesn't know it also
/// consumes the next argument (`x.txt`).
///
/// With a custom registry, we could register that `--option-dir`
/// takes one argument, allowing us to skip both and continue
/// parsing from `main.cpp`.
on_error(prev, it - prev - 1);
break;
}
on_parse(std::move(arg));
}
}
private:
llvm::BumpPtrAllocator* allocator;
llvm::ArrayRef<const char*> arguments;
};
} // namespace
} // namespace clice