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