278 lines
8.1 KiB
C++
278 lines
8.1 KiB
C++
#include "command/argument_parser.h"
|
|
|
|
#include <array>
|
|
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "clang/Driver/Driver.h"
|
|
#include "clang/Driver/Options.h"
|
|
#include "clang/Driver/Types.h"
|
|
|
|
namespace clice {
|
|
|
|
namespace {
|
|
|
|
namespace opt = llvm::opt;
|
|
namespace driver = clang::driver;
|
|
|
|
/// Access private members of OptTable via the Thief pattern.
|
|
bool enable_dash_dash_parsing(const opt::OptTable& table);
|
|
bool enable_grouped_short_options(const opt::OptTable& table);
|
|
|
|
template <auto MP1, auto MP2>
|
|
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;
|
|
}
|
|
};
|
|
|
|
template struct Thief<&opt::OptTable::DashDashParsing, &opt::OptTable::GroupedShortOptions>;
|
|
|
|
auto& option_table = driver::getDriverOptTable();
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<llvm::opt::Arg> ArgumentParser::parse_one(unsigned& index) {
|
|
assert(!enable_dash_dash_parsing(option_table));
|
|
assert(!enable_grouped_short_options(option_table));
|
|
return option_table.ParseOneArg(*this, index, opt::Visibility(visibility_mask));
|
|
}
|
|
|
|
using ID = clang::driver::options::ID;
|
|
|
|
bool is_discarded_option(unsigned id) {
|
|
switch(id) {
|
|
/// Input file, unknown args, and output — we manage these ourselves.
|
|
case ID::OPT_INPUT:
|
|
case ID::OPT_UNKNOWN:
|
|
case ID::OPT__DASH_DASH:
|
|
case ID::OPT_c:
|
|
case ID::OPT_o:
|
|
case ID::OPT_dxc_Fc:
|
|
case ID::OPT_dxc_Fo:
|
|
case ID::OPT__SLASH_Fo:
|
|
case ID::OPT__SLASH_Fd:
|
|
|
|
/// PCH building.
|
|
case ID::OPT_emit_pch:
|
|
case ID::OPT_include_pch:
|
|
case ID::OPT__SLASH_Yu:
|
|
case ID::OPT__SLASH_Fp:
|
|
|
|
/// Dependency scan.
|
|
case ID::OPT_E:
|
|
case ID::OPT_M:
|
|
case ID::OPT_MM:
|
|
case ID::OPT_MD:
|
|
case ID::OPT_MMD:
|
|
case ID::OPT_MF:
|
|
case ID::OPT_MT:
|
|
case ID::OPT_MQ:
|
|
case ID::OPT_MG:
|
|
case ID::OPT_MP:
|
|
case ID::OPT_show_inst:
|
|
case ID::OPT_show_encoding:
|
|
case ID::OPT_show_includes:
|
|
case ID::OPT__SLASH_showFilenames:
|
|
case ID::OPT__SLASH_showFilenames_:
|
|
case ID::OPT__SLASH_showIncludes:
|
|
case ID::OPT__SLASH_showIncludes_user:
|
|
|
|
/// C++ modules — we handle these ourselves.
|
|
case ID::OPT_fmodule_file:
|
|
case ID::OPT_fmodule_output:
|
|
case ID::OPT_fprebuilt_module_path: return true;
|
|
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
bool is_user_content_option(unsigned id) {
|
|
switch(id) {
|
|
case ID::OPT_I:
|
|
case ID::OPT_isystem:
|
|
case ID::OPT_iquote:
|
|
case ID::OPT_idirafter:
|
|
case ID::OPT_D:
|
|
case ID::OPT_U:
|
|
case ID::OPT_include: return true;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
bool is_include_path_option(unsigned id) {
|
|
switch(id) {
|
|
case ID::OPT_I:
|
|
case ID::OPT_isystem:
|
|
case ID::OPT_iquote:
|
|
case ID::OPT_idirafter: return true;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
bool is_xclang_option(unsigned id) {
|
|
return id == ID::OPT_Xclang;
|
|
}
|
|
|
|
bool is_toolchain_option(unsigned id) {
|
|
switch(id) {
|
|
case ID::OPT_target:
|
|
case ID::OPT_target_legacy_spelling:
|
|
case ID::OPT_isysroot:
|
|
case ID::OPT__sysroot_EQ:
|
|
case ID::OPT__sysroot:
|
|
case ID::OPT_stdlib_EQ:
|
|
case ID::OPT_gcc_toolchain:
|
|
case ID::OPT_gcc_install_dir_EQ:
|
|
case ID::OPT_nostdinc:
|
|
case ID::OPT_nostdincxx:
|
|
case ID::OPT_std_EQ:
|
|
case ID::OPT_x: return true;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
std::optional<std::uint32_t> get_option_id(llvm::StringRef argument) {
|
|
llvm::SmallString<64> buffer = argument;
|
|
|
|
if(argument.ends_with("=")) {
|
|
buffer += "placeholder";
|
|
}
|
|
|
|
unsigned index = 1;
|
|
std::array arguments = {"clang++", buffer.c_str(), "placeholder"};
|
|
llvm::opt::InputArgList arg_list(arguments.data(), arguments.data() + arguments.size());
|
|
|
|
if(auto arg = option_table.ParseOneArg(arg_list, index)) {
|
|
return arg->getOption().getID();
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
llvm::StringRef resource_dir() {
|
|
static std::string dir = [] {
|
|
// Use address of this lambda to locate our binary via dladdr/proc.
|
|
static int anchor;
|
|
auto exe = llvm::sys::fs::getMainExecutable("", &anchor);
|
|
if(exe.empty()) {
|
|
return std::string{};
|
|
}
|
|
return clang::driver::Driver::GetResourcesPath(exe);
|
|
}();
|
|
return dir;
|
|
}
|
|
|
|
bool is_codegen_option(unsigned id, const llvm::opt::Option& opt) {
|
|
/// Debug info options form a group (-g, -gdwarf-*, -gsplit-dwarf, etc.).
|
|
if(opt.matches(ID::OPT_DebugInfo_Group)) {
|
|
return true;
|
|
}
|
|
|
|
switch(id) {
|
|
/// Position-independent code — pure codegen, no macro or semantic effect.
|
|
case ID::OPT_fPIC:
|
|
case ID::OPT_fno_PIC:
|
|
case ID::OPT_fpic:
|
|
case ID::OPT_fno_pic:
|
|
case ID::OPT_fPIE:
|
|
case ID::OPT_fno_PIE:
|
|
case ID::OPT_fpie:
|
|
case ID::OPT_fno_pie:
|
|
|
|
/// Frame pointer and unwind tables — pure codegen.
|
|
case ID::OPT_fomit_frame_pointer:
|
|
case ID::OPT_fno_omit_frame_pointer:
|
|
case ID::OPT_funwind_tables:
|
|
case ID::OPT_fno_unwind_tables:
|
|
case ID::OPT_fasynchronous_unwind_tables:
|
|
case ID::OPT_fno_asynchronous_unwind_tables:
|
|
|
|
/// Stack protection — pure codegen.
|
|
case ID::OPT_fstack_protector:
|
|
case ID::OPT_fstack_protector_strong:
|
|
case ID::OPT_fstack_protector_all:
|
|
case ID::OPT_fno_stack_protector:
|
|
|
|
/// Section splitting, LTO, semantic interposition — pure codegen/linker.
|
|
case ID::OPT_fdata_sections:
|
|
case ID::OPT_fno_data_sections:
|
|
case ID::OPT_ffunction_sections:
|
|
case ID::OPT_fno_function_sections:
|
|
case ID::OPT_flto:
|
|
case ID::OPT_flto_EQ:
|
|
case ID::OPT_fno_lto:
|
|
case ID::OPT_fsemantic_interposition:
|
|
case ID::OPT_fno_semantic_interposition:
|
|
case ID::OPT_fvisibility_inlines_hidden:
|
|
|
|
/// Diagnostics output formatting — doesn't affect analysis.
|
|
case ID::OPT_fcolor_diagnostics:
|
|
case ID::OPT_fno_color_diagnostics:
|
|
|
|
/// Floating-point codegen — doesn't define macros (unlike -ffast-math).
|
|
case ID::OPT_ftrapping_math:
|
|
case ID::OPT_fno_trapping_math: return true;
|
|
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
std::string print_argv(llvm::ArrayRef<const char*> args) {
|
|
std::string buf;
|
|
llvm::raw_string_ostream os(buf);
|
|
bool sep = false;
|
|
for(llvm::StringRef arg: args) {
|
|
if(sep)
|
|
os << ' ';
|
|
sep = true;
|
|
if(llvm::all_of(arg, llvm::isPrint) &&
|
|
arg.find_first_of(" \t\n\"\\") == llvm::StringRef::npos) {
|
|
os << arg;
|
|
continue;
|
|
}
|
|
os << '"';
|
|
os.write_escaped(arg, /*UseHexEscapes=*/true);
|
|
os << '"';
|
|
}
|
|
return std::move(os.str());
|
|
}
|
|
|
|
unsigned default_visibility(llvm::StringRef driver) {
|
|
namespace options = clang::driver::options;
|
|
auto name = llvm::sys::path::filename(driver);
|
|
name.consume_back(".exe");
|
|
|
|
auto is_cl = [](llvm::StringRef s) {
|
|
return s.equals_insensitive("cl") || s.equals_insensitive("clang-cl");
|
|
};
|
|
|
|
/// cl.exe and clang-cl.exe both need MSVC-style /options.
|
|
/// Also handle versioned names like clang-cl-17, clang-cl-17.0.1.
|
|
if(is_cl(name) || is_cl(name.rtrim("0123456789.-"))) {
|
|
return ~0u;
|
|
}
|
|
/// Exclude CLOption to prevent /U, /D, /I from matching Unix paths.
|
|
return ~static_cast<unsigned>(options::CLOption);
|
|
}
|
|
|
|
bool is_c_family_file(llvm::StringRef filename) {
|
|
namespace types = clang::driver::types;
|
|
auto ext = llvm::sys::path::extension(filename);
|
|
if(ext.empty()) {
|
|
return false;
|
|
}
|
|
/// Drop the leading dot: ".cpp" → "cpp".
|
|
auto type = types::lookupTypeForExtension(ext.drop_front());
|
|
return type != types::TY_INVALID && types::isAcceptedByClang(type);
|
|
}
|
|
|
|
} // namespace clice
|