1e5dd7070Spatrick //===--- CommonOptionsParser.cpp - common options for clang tools ---------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file implements the CommonOptionsParser class used to parse common
10e5dd7070Spatrick // command-line options for clang tools, so that they can be run as separate
11e5dd7070Spatrick // command-line applications with a consistent common interface for handling
12e5dd7070Spatrick // compilation database and input files.
13e5dd7070Spatrick //
14e5dd7070Spatrick // It provides a common subset of command-line options, common algorithm
15e5dd7070Spatrick // for locating a compilation database and source files, and help messages
16e5dd7070Spatrick // for the basic command-line interface.
17e5dd7070Spatrick //
18e5dd7070Spatrick // It creates a CompilationDatabase and reads common command-line options.
19e5dd7070Spatrick //
20e5dd7070Spatrick // This class uses the Clang Tooling infrastructure, see
21e5dd7070Spatrick // http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
22e5dd7070Spatrick // for details on setting it up with LLVM source tree.
23e5dd7070Spatrick //
24e5dd7070Spatrick //===----------------------------------------------------------------------===//
25e5dd7070Spatrick
26e5dd7070Spatrick #include "clang/Tooling/CommonOptionsParser.h"
27e5dd7070Spatrick #include "clang/Tooling/Tooling.h"
28e5dd7070Spatrick #include "llvm/Support/CommandLine.h"
29e5dd7070Spatrick
30e5dd7070Spatrick using namespace clang::tooling;
31e5dd7070Spatrick using namespace llvm;
32e5dd7070Spatrick
33e5dd7070Spatrick const char *const CommonOptionsParser::HelpMessage =
34e5dd7070Spatrick "\n"
35e5dd7070Spatrick "-p <build-path> is used to read a compile command database.\n"
36e5dd7070Spatrick "\n"
37e5dd7070Spatrick "\tFor example, it can be a CMake build directory in which a file named\n"
38e5dd7070Spatrick "\tcompile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n"
39e5dd7070Spatrick "\tCMake option to get this output). When no build path is specified,\n"
40e5dd7070Spatrick "\ta search for compile_commands.json will be attempted through all\n"
41e5dd7070Spatrick "\tparent paths of the first input file . See:\n"
42e5dd7070Spatrick "\thttps://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n"
43e5dd7070Spatrick "\texample of setting up Clang Tooling on a source tree.\n"
44e5dd7070Spatrick "\n"
45e5dd7070Spatrick "<source0> ... specify the paths of source files. These paths are\n"
46e5dd7070Spatrick "\tlooked up in the compile command database. If the path of a file is\n"
47e5dd7070Spatrick "\tabsolute, it needs to point into CMake's source tree. If the path is\n"
48e5dd7070Spatrick "\trelative, the current working directory needs to be in the CMake\n"
49e5dd7070Spatrick "\tsource tree and the file must be in a subdirectory of the current\n"
50e5dd7070Spatrick "\tworking directory. \"./\" prefixes in the relative files will be\n"
51e5dd7070Spatrick "\tautomatically removed, but the rest of a relative path must be a\n"
52e5dd7070Spatrick "\tsuffix of a path in the compile command database.\n"
53e5dd7070Spatrick "\n";
54e5dd7070Spatrick
appendArgumentsAdjuster(ArgumentsAdjuster Adjuster)55e5dd7070Spatrick void ArgumentsAdjustingCompilations::appendArgumentsAdjuster(
56e5dd7070Spatrick ArgumentsAdjuster Adjuster) {
57e5dd7070Spatrick Adjusters.push_back(std::move(Adjuster));
58e5dd7070Spatrick }
59e5dd7070Spatrick
getCompileCommands(StringRef FilePath) const60e5dd7070Spatrick std::vector<CompileCommand> ArgumentsAdjustingCompilations::getCompileCommands(
61e5dd7070Spatrick StringRef FilePath) const {
62e5dd7070Spatrick return adjustCommands(Compilations->getCompileCommands(FilePath));
63e5dd7070Spatrick }
64e5dd7070Spatrick
65e5dd7070Spatrick std::vector<std::string>
getAllFiles() const66e5dd7070Spatrick ArgumentsAdjustingCompilations::getAllFiles() const {
67e5dd7070Spatrick return Compilations->getAllFiles();
68e5dd7070Spatrick }
69e5dd7070Spatrick
70e5dd7070Spatrick std::vector<CompileCommand>
getAllCompileCommands() const71e5dd7070Spatrick ArgumentsAdjustingCompilations::getAllCompileCommands() const {
72e5dd7070Spatrick return adjustCommands(Compilations->getAllCompileCommands());
73e5dd7070Spatrick }
74e5dd7070Spatrick
adjustCommands(std::vector<CompileCommand> Commands) const75e5dd7070Spatrick std::vector<CompileCommand> ArgumentsAdjustingCompilations::adjustCommands(
76e5dd7070Spatrick std::vector<CompileCommand> Commands) const {
77e5dd7070Spatrick for (CompileCommand &Command : Commands)
78e5dd7070Spatrick for (const auto &Adjuster : Adjusters)
79e5dd7070Spatrick Command.CommandLine = Adjuster(Command.CommandLine, Command.Filename);
80e5dd7070Spatrick return Commands;
81e5dd7070Spatrick }
82e5dd7070Spatrick
init(int & argc,const char ** argv,cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)83e5dd7070Spatrick llvm::Error CommonOptionsParser::init(
84e5dd7070Spatrick int &argc, const char **argv, cl::OptionCategory &Category,
85e5dd7070Spatrick llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
86e5dd7070Spatrick
87e5dd7070Spatrick static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
88e5dd7070Spatrick cl::Optional, cl::cat(Category),
89*12c85518Srobert cl::sub(cl::SubCommand::getAll()));
90e5dd7070Spatrick
91e5dd7070Spatrick static cl::list<std::string> SourcePaths(
92e5dd7070Spatrick cl::Positional, cl::desc("<source0> [... <sourceN>]"), OccurrencesFlag,
93*12c85518Srobert cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
94e5dd7070Spatrick
95e5dd7070Spatrick static cl::list<std::string> ArgsAfter(
96e5dd7070Spatrick "extra-arg",
97e5dd7070Spatrick cl::desc("Additional argument to append to the compiler command line"),
98*12c85518Srobert cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
99e5dd7070Spatrick
100e5dd7070Spatrick static cl::list<std::string> ArgsBefore(
101e5dd7070Spatrick "extra-arg-before",
102e5dd7070Spatrick cl::desc("Additional argument to prepend to the compiler command line"),
103*12c85518Srobert cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
104e5dd7070Spatrick
105e5dd7070Spatrick cl::ResetAllOptionOccurrences();
106e5dd7070Spatrick
107e5dd7070Spatrick cl::HideUnrelatedOptions(Category);
108e5dd7070Spatrick
109e5dd7070Spatrick std::string ErrorMessage;
110e5dd7070Spatrick Compilations =
111e5dd7070Spatrick FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
112e5dd7070Spatrick if (!ErrorMessage.empty())
113e5dd7070Spatrick ErrorMessage.append("\n");
114e5dd7070Spatrick llvm::raw_string_ostream OS(ErrorMessage);
115e5dd7070Spatrick // Stop initializing if command-line option parsing failed.
116e5dd7070Spatrick if (!cl::ParseCommandLineOptions(argc, argv, Overview, &OS)) {
117e5dd7070Spatrick OS.flush();
118a9ac8606Spatrick return llvm::make_error<llvm::StringError>(ErrorMessage,
119e5dd7070Spatrick llvm::inconvertibleErrorCode());
120e5dd7070Spatrick }
121e5dd7070Spatrick
122e5dd7070Spatrick cl::PrintOptionValues();
123e5dd7070Spatrick
124e5dd7070Spatrick SourcePathList = SourcePaths;
125e5dd7070Spatrick if ((OccurrencesFlag == cl::ZeroOrMore || OccurrencesFlag == cl::Optional) &&
126e5dd7070Spatrick SourcePathList.empty())
127e5dd7070Spatrick return llvm::Error::success();
128e5dd7070Spatrick if (!Compilations) {
129e5dd7070Spatrick if (!BuildPath.empty()) {
130e5dd7070Spatrick Compilations =
131e5dd7070Spatrick CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage);
132e5dd7070Spatrick } else {
133e5dd7070Spatrick Compilations = CompilationDatabase::autoDetectFromSource(SourcePaths[0],
134e5dd7070Spatrick ErrorMessage);
135e5dd7070Spatrick }
136e5dd7070Spatrick if (!Compilations) {
137e5dd7070Spatrick llvm::errs() << "Error while trying to load a compilation database:\n"
138e5dd7070Spatrick << ErrorMessage << "Running without flags.\n";
139e5dd7070Spatrick Compilations.reset(
140e5dd7070Spatrick new FixedCompilationDatabase(".", std::vector<std::string>()));
141e5dd7070Spatrick }
142e5dd7070Spatrick }
143e5dd7070Spatrick auto AdjustingCompilations =
144e5dd7070Spatrick std::make_unique<ArgumentsAdjustingCompilations>(
145e5dd7070Spatrick std::move(Compilations));
146e5dd7070Spatrick Adjuster =
147e5dd7070Spatrick getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN);
148e5dd7070Spatrick Adjuster = combineAdjusters(
149e5dd7070Spatrick std::move(Adjuster),
150e5dd7070Spatrick getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
151e5dd7070Spatrick AdjustingCompilations->appendArgumentsAdjuster(Adjuster);
152e5dd7070Spatrick Compilations = std::move(AdjustingCompilations);
153e5dd7070Spatrick return llvm::Error::success();
154e5dd7070Spatrick }
155e5dd7070Spatrick
create(int & argc,const char ** argv,llvm::cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)156e5dd7070Spatrick llvm::Expected<CommonOptionsParser> CommonOptionsParser::create(
157e5dd7070Spatrick int &argc, const char **argv, llvm::cl::OptionCategory &Category,
158e5dd7070Spatrick llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
159e5dd7070Spatrick CommonOptionsParser Parser;
160e5dd7070Spatrick llvm::Error Err =
161e5dd7070Spatrick Parser.init(argc, argv, Category, OccurrencesFlag, Overview);
162e5dd7070Spatrick if (Err)
163e5dd7070Spatrick return std::move(Err);
164e5dd7070Spatrick return std::move(Parser);
165e5dd7070Spatrick }
166e5dd7070Spatrick
CommonOptionsParser(int & argc,const char ** argv,cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)167e5dd7070Spatrick CommonOptionsParser::CommonOptionsParser(
168e5dd7070Spatrick int &argc, const char **argv, cl::OptionCategory &Category,
169e5dd7070Spatrick llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
170e5dd7070Spatrick llvm::Error Err = init(argc, argv, Category, OccurrencesFlag, Overview);
171e5dd7070Spatrick if (Err) {
172e5dd7070Spatrick llvm::report_fatal_error(
173*12c85518Srobert Twine("CommonOptionsParser: failed to parse command-line arguments. ") +
174e5dd7070Spatrick llvm::toString(std::move(Err)));
175e5dd7070Spatrick }
176e5dd7070Spatrick }
177