Skip to content

Commit

Permalink
Reland "[clang-scan-deps] Migrate to OptTable"
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D139949
  • Loading branch information
abrachet committed Apr 14, 2023
1 parent 9b17f5e commit 384fca5
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 118 deletions.
7 changes: 7 additions & 0 deletions clang/tools/clang-scan-deps/CMakeLists.txt
Expand Up @@ -5,8 +5,15 @@ set(LLVM_LINK_COMPONENTS
TargetParser
)

set(LLVM_TARGET_DEFINITIONS Opts.td)
tablegen(LLVM Opts.inc -gen-opt-parser-defs)
add_public_tablegen_target(ScanDepsOptsTableGen)

add_clang_tool(clang-scan-deps
ClangScanDeps.cpp

DEPENDS
ScanDepsOptsTableGen
)

set(CLANG_SCAN_DEPS_LIB_DEPS
Expand Down
292 changes: 174 additions & 118 deletions clang/tools/clang-scan-deps/ClangScanDeps.cpp
Expand Up @@ -30,11 +30,184 @@
#include <optional>
#include <thread>

#include "Opts.inc"

using namespace clang;
using namespace tooling::dependencies;

namespace {

using namespace llvm::opt;
enum ID {
OPT_INVALID = 0, // This is not an option ID.
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
OPT_##ID,
#include "Opts.inc"
#undef OPTION
};

#define PREFIX(NAME, VALUE) \
constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
NAME##_init, std::size(NAME##_init) - 1);
#include "Opts.inc"
#undef PREFIX

const llvm::opt::OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
{PREFIX, NAME, HELPTEXT, \
METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, \
OPT_##ALIAS, ALIASARGS, VALUES},
#include "Opts.inc"
#undef OPTION
};

class ScanDepsOptTable : public llvm::opt::GenericOptTable {
public:
ScanDepsOptTable() : GenericOptTable(InfoTable) {
setGroupedShortOptions(true);
}
};

enum ResourceDirRecipeKind {
RDRK_ModifyCompilerPath,
RDRK_InvokeCompiler,
};

static ScanningMode ScanMode = ScanningMode::DependencyDirectivesScan;
static ScanningOutputFormat Format = ScanningOutputFormat::Make;
static std::string ModuleFilesDir;
static bool OptimizeArgs;
static bool EagerLoadModules;
static unsigned NumThreads = 0;
static std::string CompilationDB;
static std::string ModuleName;
static std::vector<std::string> ModuleDepTargets;
static bool DeprecatedDriverCommand;
static ResourceDirRecipeKind ResourceDirRecipe;
static bool Verbose;
static std::vector<const char *> CommandLine;

#ifndef NDEBUG
static constexpr bool DoRoundTripDefault = true;
#else
static constexpr bool DoRoundTripDefault = false;
#endif

static bool RoundTripArgs = DoRoundTripDefault;

static void ParseArgs(int argc, char **argv) {
ScanDepsOptTable Tbl;
llvm::StringRef ToolName = argv[0];
llvm::BumpPtrAllocator A;
llvm::StringSaver Saver{A};
llvm::opt::InputArgList Args =
Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
llvm::errs() << Msg << '\n';
std::exit(1);
});

if (Args.hasArg(OPT_help)) {
Tbl.printHelp(llvm::outs(), "clang-scan-deps [options]", "clang-scan-deps");
std::exit(0);
}
if (Args.hasArg(OPT_version)) {
llvm::outs() << ToolName << '\n';
llvm::cl::PrintVersionMessage();
std::exit(0);
}
if (const llvm::opt::Arg *A = Args.getLastArg(OPT_mode_EQ)) {
auto ModeType =
llvm::StringSwitch<std::optional<ScanningMode>>(A->getValue())
.Case("preprocess-dependency-directives",
ScanningMode::DependencyDirectivesScan)
.Case("preprocess", ScanningMode::CanonicalPreprocessing)
.Default(std::nullopt);
if (!ModeType) {
llvm::errs() << ToolName
<< ": for the --mode option: Cannot find option named '"
<< A->getValue() << "'\n";
std::exit(1);
}
ScanMode = *ModeType;
}

if (const llvm::opt::Arg *A = Args.getLastArg(OPT_format_EQ)) {
auto FormatType =
llvm::StringSwitch<std::optional<ScanningOutputFormat>>(A->getValue())
.Case("make", ScanningOutputFormat::Make)
.Case("p1689", ScanningOutputFormat::P1689)
.Case("experimental-full", ScanningOutputFormat::Full)
.Default(std::nullopt);
if (!FormatType) {
llvm::errs() << ToolName
<< ": for the --format option: Cannot find option named '"
<< A->getValue() << "'\n";
std::exit(1);
}
Format = *FormatType;
}

if (const llvm::opt::Arg *A = Args.getLastArg(OPT_module_files_dir_EQ))
ModuleFilesDir = A->getValue();

OptimizeArgs = Args.hasArg(OPT_optimize_args);
EagerLoadModules = Args.hasArg(OPT_eager_load_pcm);

if (const llvm::opt::Arg *A = Args.getLastArg(OPT_j)) {
StringRef S{A->getValue()};
if (!llvm::to_integer(S, NumThreads, 0)) {
llvm::errs() << ToolName << ": for the -j option: '" << S
<< "' value invalid for uint argument!\n";
std::exit(1);
}
}

if (const llvm::opt::Arg *A = Args.getLastArg(OPT_compilation_database_EQ)) {
CompilationDB = A->getValue();
} else if (Format != ScanningOutputFormat::P1689) {
llvm::errs() << ToolName
<< ": for the --compiilation-database option: must be "
"specified at least once!";
std::exit(1);
}

if (const llvm::opt::Arg *A = Args.getLastArg(OPT_module_name_EQ))
ModuleName = A->getValue();

for (const llvm::opt::Arg *A : Args.filtered(OPT_dependency_target_EQ))
ModuleDepTargets.emplace_back(A->getValue());

DeprecatedDriverCommand = Args.hasArg(OPT_deprecated_driver_command);

if (const llvm::opt::Arg *A = Args.getLastArg(OPT_resource_dir_recipe_EQ)) {
auto Kind =
llvm::StringSwitch<std::optional<ResourceDirRecipeKind>>(A->getValue())
.Case("modify-compiler-path", RDRK_ModifyCompilerPath)
.Case("invoke-compiler", RDRK_InvokeCompiler)
.Default(std::nullopt);
if (!Kind) {
llvm::errs() << ToolName
<< ": for the --resource-dir-recipe option: Cannot find "
"option named '"
<< A->getValue() << "'\n";
std::exit(1);
}
ResourceDirRecipe = *Kind;
}

Verbose = Args.hasArg(OPT_verbose);

RoundTripArgs = Args.hasArg(OPT_round_trip_args);

if (auto *A = Args.getLastArgNoClaim(OPT_DASH_DASH))
CommandLine.insert(CommandLine.end(), A->getValues().begin(),
A->getValues().end());
}

class SharedStream {
public:
SharedStream(raw_ostream &OS) : OS(OS) {}
Expand Down Expand Up @@ -112,120 +285,6 @@ class ResourceDirectoryCache {
std::mutex CacheLock;
};

llvm::cl::opt<bool> Help("h", llvm::cl::desc("Alias for -help"),
llvm::cl::Hidden);

llvm::cl::OptionCategory DependencyScannerCategory("Tool options");

static llvm::cl::opt<ScanningMode> ScanMode(
"mode",
llvm::cl::desc("The preprocessing mode used to compute the dependencies"),
llvm::cl::values(
clEnumValN(ScanningMode::DependencyDirectivesScan,
"preprocess-dependency-directives",
"The set of dependencies is computed by preprocessing with "
"special lexing after scanning the source files to get the "
"directives that might affect the dependencies"),
clEnumValN(ScanningMode::CanonicalPreprocessing, "preprocess",
"The set of dependencies is computed by preprocessing the "
"source files")),
llvm::cl::init(ScanningMode::DependencyDirectivesScan),
llvm::cl::cat(DependencyScannerCategory));

static llvm::cl::opt<ScanningOutputFormat> Format(
"format", llvm::cl::desc("The output format for the dependencies"),
llvm::cl::values(
clEnumValN(ScanningOutputFormat::Make, "make",
"Makefile compatible dep file"),
clEnumValN(ScanningOutputFormat::P1689, "p1689",
"Generate standard c++ modules dependency P1689 format"),
clEnumValN(ScanningOutputFormat::Full, "experimental-full",
"Full dependency graph suitable"
" for explicitly building modules. This format "
"is experimental and will change.")),
llvm::cl::init(ScanningOutputFormat::Make),
llvm::cl::cat(DependencyScannerCategory));

static llvm::cl::opt<std::string> ModuleFilesDir(
"module-files-dir",
llvm::cl::desc(
"The build directory for modules. Defaults to the value of "
"'-fmodules-cache-path=' from command lines for implicit modules."),
llvm::cl::cat(DependencyScannerCategory));

static llvm::cl::opt<bool> OptimizeArgs(
"optimize-args",
llvm::cl::desc("Whether to optimize command-line arguments of modules."),
llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory));

static llvm::cl::opt<bool> EagerLoadModules(
"eager-load-pcm",
llvm::cl::desc("Load PCM files eagerly (instead of lazily on import)."),
llvm::cl::init(false), llvm::cl::cat(DependencyScannerCategory));

llvm::cl::opt<unsigned>
NumThreads("j", llvm::cl::Optional,
llvm::cl::desc("Number of worker threads to use (default: use "
"all concurrent threads)"),
llvm::cl::init(0), llvm::cl::cat(DependencyScannerCategory));

llvm::cl::opt<std::string>
CompilationDB("compilation-database",
llvm::cl::desc("Compilation database"), llvm::cl::Optional,
llvm::cl::cat(DependencyScannerCategory));

llvm::cl::opt<std::string> P1689TargettedCommand(
llvm::cl::Positional, llvm::cl::ZeroOrMore,
llvm::cl::desc("The command line flags for the target of which "
"the dependencies are to be computed."));

llvm::cl::opt<std::string> ModuleName(
"module-name", llvm::cl::Optional,
llvm::cl::desc("the module of which the dependencies are to be computed"),
llvm::cl::cat(DependencyScannerCategory));

llvm::cl::list<std::string> ModuleDepTargets(
"dependency-target",
llvm::cl::desc("The names of dependency targets for the dependency file"),
llvm::cl::cat(DependencyScannerCategory));

enum ResourceDirRecipeKind {
RDRK_ModifyCompilerPath,
RDRK_InvokeCompiler,
};

static llvm::cl::opt<ResourceDirRecipeKind> ResourceDirRecipe(
"resource-dir-recipe",
llvm::cl::desc("How to produce missing '-resource-dir' argument"),
llvm::cl::values(
clEnumValN(RDRK_ModifyCompilerPath, "modify-compiler-path",
"Construct the resource directory from the compiler path in "
"the compilation database. This assumes it's part of the "
"same toolchain as this clang-scan-deps. (default)"),
clEnumValN(RDRK_InvokeCompiler, "invoke-compiler",
"Invoke the compiler with '-print-resource-dir' and use the "
"reported path as the resource directory. (deprecated)")),
llvm::cl::init(RDRK_ModifyCompilerPath),
llvm::cl::cat(DependencyScannerCategory));

#ifndef NDEBUG
static constexpr bool DoRoundTripDefault = true;
#else
static constexpr bool DoRoundTripDefault = false;
#endif

llvm::cl::opt<bool>
RoundTripArgs("round-trip-args", llvm::cl::Optional,
llvm::cl::desc("verify that command-line arguments are "
"canonical by parsing and re-serializing"),
llvm::cl::init(DoRoundTripDefault),
llvm::cl::cat(DependencyScannerCategory));

llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional,
llvm::cl::desc("Use verbose output."),
llvm::cl::init(false),
llvm::cl::cat(DependencyScannerCategory));

} // end anonymous namespace

/// Takes the result of a dependency scan and prints error / dependency files
Expand Down Expand Up @@ -600,9 +659,7 @@ static std::string getModuleCachePath(ArrayRef<std::string> Args) {
static std::unique_ptr<tooling::CompilationDatabase>
getCompilationDataBase(int argc, const char **argv, std::string &ErrorMessage) {
llvm::InitLLVM X(argc, argv);
llvm::cl::HideUnrelatedOptions(DependencyScannerCategory);
if (!llvm::cl::ParseCommandLineOptions(argc, argv))
return nullptr;
ParseArgs(argc, const_cast<char **>(argv));

if (!CompilationDB.empty())
return tooling::JSONCompilationDatabase::loadFromFile(
Expand All @@ -623,7 +680,6 @@ getCompilationDataBase(int argc, const char **argv, std::string &ErrorMessage) {
"P1689 per file mode.";
return nullptr;
}
std::vector<const char *> CommandLine(DoubleDash + 1, argv + argc);

llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
CompilerInstance::createDiagnostics(new DiagnosticOptions);
Expand Down
38 changes: 38 additions & 0 deletions clang/tools/clang-scan-deps/Opts.td
@@ -0,0 +1,38 @@
include "llvm/Option/OptParser.td"

class F<string name, string help> : Flag<["-"], name>, HelpText<help>;
class Arg<string name, string help> : Separate<["-"], name>, HelpText<help>;

multiclass Eq<string name, string help> {
def NAME #_EQ : Joined<["-", "--"], name #"=">, HelpText<help>;
def : Separate<["-", "--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
}

def help : Flag<["--"], "help">, HelpText<"Display this help">;
def version : Flag<["--"], "version">, HelpText<"Display the version">;

defm mode : Eq<"mode", "The preprocessing mode used to compute the dependencies">;

defm format : Eq<"format", "The output format for the dependencies">;

defm module_files_dir : Eq<"module-files-dir",
"The build directory for modules. Defaults to the value of '-fmodules-cache-path=' from command lines for implicit modules">;

def optimize_args : F<"optimize-args", "Whether to optimize command-line arguments of modules">;
def eager_load_pcm : F<"eager-load-pcm", "Load PCM files eagerly (instead of lazily on import)">;

def j : Arg<"j", "Number of worker threads to use (default: use all concurrent threads)">;

defm compilation_database : Eq<"compilation-database", "Compilation database">;
defm module_name : Eq<"module-name", "the module of which the dependencies are to be computed">;
defm dependency_target : Eq<"dependency-target", "The names of dependency targets for the dependency file">;

def deprecated_driver_command : F<"deprecated-driver-command", "use a single driver command to build the tu (deprecated)">;

defm resource_dir_recipe : Eq<"resource-dir-recipe", "How to produce missing '-resource-dir' argument">;

def verbose : F<"v", "Use verbose output">;

def round_trip_args : F<"round-trip-args", "verify that command-line arguments are canonical by parsing and re-serializing">;

def DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>;
8 changes: 8 additions & 0 deletions llvm/utils/gn/secondary/clang/tools/clang-scan-deps/BUILD.gn
@@ -1,6 +1,14 @@
import("//llvm/utils/TableGen/tablegen.gni")

tablegen("Opts") {
visibility = [ ":clang-scan-deps" ]
args = [ "-gen-opt-parser-defs" ]
}

executable("clang-scan-deps") {
configs += [ "//llvm/utils/gn/build:clang_code" ]
deps = [
":Opts",
"//clang/lib/AST",
"//clang/lib/Basic",
"//clang/lib/CodeGen",
Expand Down

0 comments on commit 384fca5

Please sign in to comment.