101387267SAlexander Kornienko //===--- CommonOptionsParser.cpp - common options for clang tools ---------===//
201387267SAlexander Kornienko //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
601387267SAlexander Kornienko //
701387267SAlexander Kornienko //===----------------------------------------------------------------------===//
801387267SAlexander Kornienko //
901387267SAlexander Kornienko // This file implements the CommonOptionsParser class used to parse common
1001387267SAlexander Kornienko // command-line options for clang tools, so that they can be run as separate
1101387267SAlexander Kornienko // command-line applications with a consistent common interface for handling
1201387267SAlexander Kornienko // compilation database and input files.
1301387267SAlexander Kornienko //
1401387267SAlexander Kornienko // It provides a common subset of command-line options, common algorithm
1501387267SAlexander Kornienko // for locating a compilation database and source files, and help messages
1601387267SAlexander Kornienko // for the basic command-line interface.
1701387267SAlexander Kornienko //
1801387267SAlexander Kornienko // It creates a CompilationDatabase and reads common command-line options.
1901387267SAlexander Kornienko //
2001387267SAlexander Kornienko // This class uses the Clang Tooling infrastructure, see
2101387267SAlexander Kornienko // http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
2201387267SAlexander Kornienko // for details on setting it up with LLVM source tree.
2301387267SAlexander Kornienko //
2401387267SAlexander Kornienko //===----------------------------------------------------------------------===//
2501387267SAlexander Kornienko
2601387267SAlexander Kornienko #include "clang/Tooling/CommonOptionsParser.h"
2701387267SAlexander Kornienko #include "clang/Tooling/Tooling.h"
28e6f85435SEric Liu #include "llvm/Support/CommandLine.h"
2901387267SAlexander Kornienko
3001387267SAlexander Kornienko using namespace clang::tooling;
3101387267SAlexander Kornienko using namespace llvm;
3201387267SAlexander Kornienko
334bcd58b8SAlexander Kornienko const char *const CommonOptionsParser::HelpMessage =
3401387267SAlexander Kornienko "\n"
3501387267SAlexander Kornienko "-p <build-path> is used to read a compile command database.\n"
3601387267SAlexander Kornienko "\n"
3701387267SAlexander Kornienko "\tFor example, it can be a CMake build directory in which a file named\n"
3801387267SAlexander Kornienko "\tcompile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n"
3901387267SAlexander Kornienko "\tCMake option to get this output). When no build path is specified,\n"
40524741fbSEdwin Vane "\ta search for compile_commands.json will be attempted through all\n"
41524741fbSEdwin Vane "\tparent paths of the first input file . See:\n"
42b101c39fSAlexander Kornienko "\thttps://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n"
4301387267SAlexander Kornienko "\texample of setting up Clang Tooling on a source tree.\n"
4401387267SAlexander Kornienko "\n"
4501387267SAlexander Kornienko "<source0> ... specify the paths of source files. These paths are\n"
4601387267SAlexander Kornienko "\tlooked up in the compile command database. If the path of a file is\n"
4701387267SAlexander Kornienko "\tabsolute, it needs to point into CMake's source tree. If the path is\n"
4801387267SAlexander Kornienko "\trelative, the current working directory needs to be in the CMake\n"
4901387267SAlexander Kornienko "\tsource tree and the file must be in a subdirectory of the current\n"
5001387267SAlexander Kornienko "\tworking directory. \"./\" prefixes in the relative files will be\n"
5101387267SAlexander Kornienko "\tautomatically removed, but the rest of a relative path must be a\n"
5201387267SAlexander Kornienko "\tsuffix of a path in the compile command database.\n"
5301387267SAlexander Kornienko "\n";
5401387267SAlexander Kornienko
appendArgumentsAdjuster(ArgumentsAdjuster Adjuster)555d2fd55fSJohannes Altmanninger void ArgumentsAdjustingCompilations::appendArgumentsAdjuster(
565d2fd55fSJohannes Altmanninger ArgumentsAdjuster Adjuster) {
577320b99bSBenjamin Kramer Adjusters.push_back(std::move(Adjuster));
5861686099SAlexander Kornienko }
5961686099SAlexander Kornienko
getCompileCommands(StringRef FilePath) const605d2fd55fSJohannes Altmanninger std::vector<CompileCommand> ArgumentsAdjustingCompilations::getCompileCommands(
615d2fd55fSJohannes Altmanninger StringRef FilePath) const {
6261686099SAlexander Kornienko return adjustCommands(Compilations->getCompileCommands(FilePath));
6361686099SAlexander Kornienko }
6461686099SAlexander Kornienko
655d2fd55fSJohannes Altmanninger std::vector<std::string>
getAllFiles() const665d2fd55fSJohannes Altmanninger ArgumentsAdjustingCompilations::getAllFiles() const {
6761686099SAlexander Kornienko return Compilations->getAllFiles();
6861686099SAlexander Kornienko }
6961686099SAlexander Kornienko
705d2fd55fSJohannes Altmanninger std::vector<CompileCommand>
getAllCompileCommands() const715d2fd55fSJohannes Altmanninger ArgumentsAdjustingCompilations::getAllCompileCommands() const {
7261686099SAlexander Kornienko return adjustCommands(Compilations->getAllCompileCommands());
7361686099SAlexander Kornienko }
7461686099SAlexander Kornienko
adjustCommands(std::vector<CompileCommand> Commands) const755d2fd55fSJohannes Altmanninger std::vector<CompileCommand> ArgumentsAdjustingCompilations::adjustCommands(
765d2fd55fSJohannes Altmanninger std::vector<CompileCommand> Commands) const {
7761686099SAlexander Kornienko for (CompileCommand &Command : Commands)
7861686099SAlexander Kornienko for (const auto &Adjuster : Adjusters)
79857b10f4SAlexander Kornienko Command.CommandLine = Adjuster(Command.CommandLine, Command.Filename);
8061686099SAlexander Kornienko return Commands;
8161686099SAlexander Kornienko }
8261686099SAlexander Kornienko
init(int & argc,const char ** argv,cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)83e6f85435SEric Liu llvm::Error CommonOptionsParser::init(
843474b558SAlexander Kornienko int &argc, const char **argv, cl::OptionCategory &Category,
853474b558SAlexander Kornienko llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
86b5e774ebSAlexander Kornienko
87b5e774ebSAlexander Kornienko static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
883d712c46SAlex Lorenz cl::Optional, cl::cat(Category),
893d712c46SAlex Lorenz cl::sub(*cl::AllSubCommands));
9001387267SAlexander Kornienko
9101387267SAlexander Kornienko static cl::list<std::string> SourcePaths(
923474b558SAlexander Kornienko cl::Positional, cl::desc("<source0> [... <sourceN>]"), OccurrencesFlag,
933d712c46SAlex Lorenz cl::cat(Category), cl::sub(*cl::AllSubCommands));
94b5e774ebSAlexander Kornienko
9561686099SAlexander Kornienko static cl::list<std::string> ArgsAfter(
9661686099SAlexander Kornienko "extra-arg",
9761686099SAlexander Kornienko cl::desc("Additional argument to append to the compiler command line"),
983d712c46SAlex Lorenz cl::cat(Category), cl::sub(*cl::AllSubCommands));
9961686099SAlexander Kornienko
10061686099SAlexander Kornienko static cl::list<std::string> ArgsBefore(
10161686099SAlexander Kornienko "extra-arg-before",
10261686099SAlexander Kornienko cl::desc("Additional argument to prepend to the compiler command line"),
1033d712c46SAlex Lorenz cl::cat(Category), cl::sub(*cl::AllSubCommands));
10461686099SAlexander Kornienko
105e6f85435SEric Liu cl::ResetAllOptionOccurrences();
106e6f85435SEric Liu
1070a9f607fSChris Bieneman cl::HideUnrelatedOptions(Category);
10801387267SAlexander Kornienko
109c46064c2SSerge Pavlov std::string ErrorMessage;
110c46064c2SSerge Pavlov Compilations =
111c46064c2SSerge Pavlov FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
112e6f85435SEric Liu if (!ErrorMessage.empty())
113e6f85435SEric Liu ErrorMessage.append("\n");
114e6f85435SEric Liu llvm::raw_string_ostream OS(ErrorMessage);
115e6f85435SEric Liu // Stop initializing if command-line option parsing failed.
116e6f85435SEric Liu if (!cl::ParseCommandLineOptions(argc, argv, Overview, &OS)) {
117e6f85435SEric Liu OS.flush();
118d47ee525Sserge-sans-paille return llvm::make_error<llvm::StringError>(ErrorMessage,
119e6f85435SEric Liu llvm::inconvertibleErrorCode());
120e6f85435SEric Liu }
121e6f85435SEric Liu
122c1694c6aSAlexander Kornienko cl::PrintOptionValues();
123c1694c6aSAlexander Kornienko
12401387267SAlexander Kornienko SourcePathList = SourcePaths;
1253474b558SAlexander Kornienko if ((OccurrencesFlag == cl::ZeroOrMore || OccurrencesFlag == cl::Optional) &&
1263474b558SAlexander Kornienko SourcePathList.empty())
127e6f85435SEric Liu return llvm::Error::success();
12801387267SAlexander Kornienko if (!Compilations) {
12901387267SAlexander Kornienko if (!BuildPath.empty()) {
130cdba84c0SDavid Blaikie Compilations =
131cdba84c0SDavid Blaikie CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage);
13201387267SAlexander Kornienko } else {
133cdba84c0SDavid Blaikie Compilations = CompilationDatabase::autoDetectFromSource(SourcePaths[0],
134cdba84c0SDavid Blaikie ErrorMessage);
13501387267SAlexander Kornienko }
1366003384eSManuel Klimek if (!Compilations) {
1376003384eSManuel Klimek llvm::errs() << "Error while trying to load a compilation database:\n"
1386003384eSManuel Klimek << ErrorMessage << "Running without flags.\n";
1396003384eSManuel Klimek Compilations.reset(
1406003384eSManuel Klimek new FixedCompilationDatabase(".", std::vector<std::string>()));
1416003384eSManuel Klimek }
14201387267SAlexander Kornienko }
14361686099SAlexander Kornienko auto AdjustingCompilations =
1442b3d49b6SJonas Devlieghere std::make_unique<ArgumentsAdjustingCompilations>(
14561686099SAlexander Kornienko std::move(Compilations));
146826b7832SEric Liu Adjuster =
147826b7832SEric Liu getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN);
148826b7832SEric Liu Adjuster = combineAdjusters(
149826b7832SEric Liu std::move(Adjuster),
15074e1c46aSAlexander Kornienko getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
151826b7832SEric Liu AdjustingCompilations->appendArgumentsAdjuster(Adjuster);
15261686099SAlexander Kornienko Compilations = std::move(AdjustingCompilations);
153e6f85435SEric Liu return llvm::Error::success();
154e6f85435SEric Liu }
155e6f85435SEric Liu
create(int & argc,const char ** argv,llvm::cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)156e6f85435SEric Liu llvm::Expected<CommonOptionsParser> CommonOptionsParser::create(
157e6f85435SEric Liu int &argc, const char **argv, llvm::cl::OptionCategory &Category,
158e6f85435SEric Liu llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
159e6f85435SEric Liu CommonOptionsParser Parser;
160e6f85435SEric Liu llvm::Error Err =
161e6f85435SEric Liu Parser.init(argc, argv, Category, OccurrencesFlag, Overview);
162e6f85435SEric Liu if (Err)
163e6f85435SEric Liu return std::move(Err);
164e6f85435SEric Liu return std::move(Parser);
165e6f85435SEric Liu }
166e6f85435SEric Liu
CommonOptionsParser(int & argc,const char ** argv,cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)167e6f85435SEric Liu CommonOptionsParser::CommonOptionsParser(
168e6f85435SEric Liu int &argc, const char **argv, cl::OptionCategory &Category,
169e6f85435SEric Liu llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
170e6f85435SEric Liu llvm::Error Err = init(argc, argv, Category, OccurrencesFlag, Overview);
171e6f85435SEric Liu if (Err) {
172e6f85435SEric Liu llvm::report_fatal_error(
173*b9b90bb5SSimon Pilgrim Twine("CommonOptionsParser: failed to parse command-line arguments. ") +
174e6f85435SEric Liu llvm::toString(std::move(Err)));
175e6f85435SEric Liu }
17601387267SAlexander Kornienko }
177