Files
clice/include/Compiler/Command.h
2025-09-07 23:27:39 +08:00

184 lines
5.5 KiB
C++

#pragma once
#include "Support/Enum.h"
#include "Support/Format.h"
#include <expected>
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Allocator.h"
#include "llvm/ADT/ArrayRef.h"
namespace clice {
struct CommandOptions {
/// Attach resource directory to the command.
bool resource_dir = false;
/// Query the compiler driver for additional information, such as system includes and target.
bool query_driver = false;
/// Suppress the warning log if failed to query driver info.
/// Set true in unittests to avoid cluttering test output.
bool suppress_log = false;
};
class CompilationDatabase {
public:
using Self = CompilationDatabase;
enum class UpdateKind : std::uint8_t {
Unchange,
Create,
Update,
Delete,
};
struct CommandInfo {
/// TODO: add sysroot or no stdinc command info.
llvm::StringRef directory;
/// The canonical command list.
llvm::ArrayRef<const char*> arguments;
};
struct DriverInfo {
/// The target of this driver.
llvm::StringRef target;
/// The default system includes of this driver.
llvm::ArrayRef<const char*> system_includes;
};
struct UpdateInfo {
/// The kind of update.
UpdateKind kind;
llvm::StringRef file;
};
struct LookupInfo {
llvm::StringRef directory;
std::vector<const char*> arguments;
};
struct QueryDriverError {
struct ErrorKind : refl::Enum<ErrorKind> {
enum Kind : std::uint8_t {
NotFoundInPATH,
FailToCreateTempFile,
InvokeDriverFail,
OutputFileNotReadable,
InvalidOutputFormat,
};
using Enum::Enum;
};
ErrorKind kind;
std::string detail;
};
CompilationDatabase();
auto save_string(this Self& self, llvm::StringRef string) -> llvm::StringRef;
auto save_cstring_list(this Self& self, llvm::ArrayRef<const char*> arguments)
-> llvm::ArrayRef<const char*>;
/// Get an the option for specific argument.
static std::optional<std::uint32_t> get_option_id(llvm::StringRef argument);
/// Query the compiler driver and return its driver info.
auto query_driver(this Self& self, llvm::StringRef driver)
-> std::expected<DriverInfo, QueryDriverError>;
/// Update with arguments.
auto update_command(this Self& self,
llvm::StringRef dictionary,
llvm::StringRef file,
llvm::ArrayRef<const char*> arguments) -> UpdateInfo;
/// Update with full command.
auto update_command(this Self& self,
llvm::StringRef dictionary,
llvm::StringRef file,
llvm::StringRef command) -> UpdateInfo;
/// Update commands from json file and return all updated file.
auto load_commands(this Self& self, llvm::StringRef json_content, llvm::StringRef workspace)
-> std::expected<std::vector<UpdateInfo>, std::string>;
/// Get compile command from database. `file` should has relative path of workspace.
auto get_command(this Self& self, llvm::StringRef file, CommandOptions options = {})
-> LookupInfo;
/// Load compile commands from given directories. If no valid commands are found,
/// search recursively from the workspace directory.
auto load_compile_database(this Self& self,
llvm::ArrayRef<std::string> compile_commands_dirs,
llvm::StringRef workspace) -> void;
private:
/// If file not found in CDB file, try to guess commands or use the default case.
auto guess_or_fallback(this Self& self, llvm::StringRef file) -> LookupInfo;
private:
/// The memory pool to hold all cstring and command list.
llvm::BumpPtrAllocator allocator;
/// A cache between input string and its cache cstring
/// in the allocator, make sure end with `\0`.
llvm::DenseSet<llvm::StringRef> string_cache;
/// A cache between input command and its cache array
/// in the allocator.
llvm::DenseSet<llvm::ArrayRef<const char*>> arguments_cache;
/// The clang options we want to filter in all cases, like -c and -o.
llvm::DenseSet<std::uint32_t> filtered_options;
/// A map between file path and its canonical command list.
llvm::DenseMap<const char*, CommandInfo> command_infos;
/// A map between driver path and its query driver info.
llvm::DenseMap<const char*, DriverInfo> driver_infos;
};
} // namespace clice
namespace llvm {
template <>
struct DenseMapInfo<llvm::ArrayRef<const char*>> {
using T = llvm::ArrayRef<const char*>;
inline static T getEmptyKey() {
return T(reinterpret_cast<T::const_pointer>(~0), T::size_type(0));
}
inline static T getTombstoneKey() {
return T(reinterpret_cast<T::const_pointer>(~1), T::size_type(0));
}
static unsigned getHashValue(const T& value) {
return llvm::hash_combine_range(value.begin(), value.end());
}
static bool isEqual(const T& lhs, const T& rhs) {
return lhs == rhs;
}
};
} // namespace llvm
template <>
struct std::formatter<clice::CompilationDatabase::QueryDriverError> :
std::formatter<llvm::StringRef> {
template <typename FormatContext>
auto format(clice::CompilationDatabase::QueryDriverError& e, FormatContext& ctx) const {
return std::format_to(ctx.out(), "{} {}", e.kind.name(), e.detail);
}
};