10b57cec5SDimitry Andric //===--- CommonOptionsParser.cpp - common options for clang tools ---------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This file implements the CommonOptionsParser class used to parse common
100b57cec5SDimitry Andric //  command-line options for clang tools, so that they can be run as separate
110b57cec5SDimitry Andric //  command-line applications with a consistent common interface for handling
120b57cec5SDimitry Andric //  compilation database and input files.
130b57cec5SDimitry Andric //
140b57cec5SDimitry Andric //  It provides a common subset of command-line options, common algorithm
150b57cec5SDimitry Andric //  for locating a compilation database and source files, and help messages
160b57cec5SDimitry Andric //  for the basic command-line interface.
170b57cec5SDimitry Andric //
180b57cec5SDimitry Andric //  It creates a CompilationDatabase and reads common command-line options.
190b57cec5SDimitry Andric //
200b57cec5SDimitry Andric //  This class uses the Clang Tooling infrastructure, see
210b57cec5SDimitry Andric //    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
220b57cec5SDimitry Andric //  for details on setting it up with LLVM source tree.
230b57cec5SDimitry Andric //
240b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric #include "clang/Tooling/CommonOptionsParser.h"
270b57cec5SDimitry Andric #include "clang/Tooling/Tooling.h"
280b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric using namespace clang::tooling;
310b57cec5SDimitry Andric using namespace llvm;
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric const char *const CommonOptionsParser::HelpMessage =
340b57cec5SDimitry Andric     "\n"
350b57cec5SDimitry Andric     "-p <build-path> is used to read a compile command database.\n"
360b57cec5SDimitry Andric     "\n"
370b57cec5SDimitry Andric     "\tFor example, it can be a CMake build directory in which a file named\n"
380b57cec5SDimitry Andric     "\tcompile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n"
390b57cec5SDimitry Andric     "\tCMake option to get this output). When no build path is specified,\n"
400b57cec5SDimitry Andric     "\ta search for compile_commands.json will be attempted through all\n"
410b57cec5SDimitry Andric     "\tparent paths of the first input file . See:\n"
420b57cec5SDimitry Andric     "\thttps://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n"
430b57cec5SDimitry Andric     "\texample of setting up Clang Tooling on a source tree.\n"
440b57cec5SDimitry Andric     "\n"
450b57cec5SDimitry Andric     "<source0> ... specify the paths of source files. These paths are\n"
460b57cec5SDimitry Andric     "\tlooked up in the compile command database. If the path of a file is\n"
470b57cec5SDimitry Andric     "\tabsolute, it needs to point into CMake's source tree. If the path is\n"
480b57cec5SDimitry Andric     "\trelative, the current working directory needs to be in the CMake\n"
490b57cec5SDimitry Andric     "\tsource tree and the file must be in a subdirectory of the current\n"
500b57cec5SDimitry Andric     "\tworking directory. \"./\" prefixes in the relative files will be\n"
510b57cec5SDimitry Andric     "\tautomatically removed, but the rest of a relative path must be a\n"
520b57cec5SDimitry Andric     "\tsuffix of a path in the compile command database.\n"
530b57cec5SDimitry Andric     "\n";
540b57cec5SDimitry Andric 
appendArgumentsAdjuster(ArgumentsAdjuster Adjuster)550b57cec5SDimitry Andric void ArgumentsAdjustingCompilations::appendArgumentsAdjuster(
560b57cec5SDimitry Andric     ArgumentsAdjuster Adjuster) {
570b57cec5SDimitry Andric   Adjusters.push_back(std::move(Adjuster));
580b57cec5SDimitry Andric }
590b57cec5SDimitry Andric 
getCompileCommands(StringRef FilePath) const600b57cec5SDimitry Andric std::vector<CompileCommand> ArgumentsAdjustingCompilations::getCompileCommands(
610b57cec5SDimitry Andric     StringRef FilePath) const {
620b57cec5SDimitry Andric   return adjustCommands(Compilations->getCompileCommands(FilePath));
630b57cec5SDimitry Andric }
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric std::vector<std::string>
getAllFiles() const660b57cec5SDimitry Andric ArgumentsAdjustingCompilations::getAllFiles() const {
670b57cec5SDimitry Andric   return Compilations->getAllFiles();
680b57cec5SDimitry Andric }
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric std::vector<CompileCommand>
getAllCompileCommands() const710b57cec5SDimitry Andric ArgumentsAdjustingCompilations::getAllCompileCommands() const {
720b57cec5SDimitry Andric   return adjustCommands(Compilations->getAllCompileCommands());
730b57cec5SDimitry Andric }
740b57cec5SDimitry Andric 
adjustCommands(std::vector<CompileCommand> Commands) const750b57cec5SDimitry Andric std::vector<CompileCommand> ArgumentsAdjustingCompilations::adjustCommands(
760b57cec5SDimitry Andric     std::vector<CompileCommand> Commands) const {
770b57cec5SDimitry Andric   for (CompileCommand &Command : Commands)
780b57cec5SDimitry Andric     for (const auto &Adjuster : Adjusters)
790b57cec5SDimitry Andric       Command.CommandLine = Adjuster(Command.CommandLine, Command.Filename);
800b57cec5SDimitry Andric   return Commands;
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric 
init(int & argc,const char ** argv,cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)830b57cec5SDimitry Andric llvm::Error CommonOptionsParser::init(
840b57cec5SDimitry Andric     int &argc, const char **argv, cl::OptionCategory &Category,
850b57cec5SDimitry Andric     llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric   static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
880b57cec5SDimitry Andric                                         cl::Optional, cl::cat(Category),
89bdd1243dSDimitry Andric                                         cl::sub(cl::SubCommand::getAll()));
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   static cl::list<std::string> SourcePaths(
920b57cec5SDimitry Andric       cl::Positional, cl::desc("<source0> [... <sourceN>]"), OccurrencesFlag,
93bdd1243dSDimitry Andric       cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric   static cl::list<std::string> ArgsAfter(
960b57cec5SDimitry Andric       "extra-arg",
970b57cec5SDimitry Andric       cl::desc("Additional argument to append to the compiler command line"),
98bdd1243dSDimitry Andric       cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric   static cl::list<std::string> ArgsBefore(
1010b57cec5SDimitry Andric       "extra-arg-before",
1020b57cec5SDimitry Andric       cl::desc("Additional argument to prepend to the compiler command line"),
103bdd1243dSDimitry Andric       cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
1040b57cec5SDimitry Andric 
1050b57cec5SDimitry Andric   cl::ResetAllOptionOccurrences();
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric   cl::HideUnrelatedOptions(Category);
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   std::string ErrorMessage;
1100b57cec5SDimitry Andric   Compilations =
1110b57cec5SDimitry Andric       FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
1120b57cec5SDimitry Andric   if (!ErrorMessage.empty())
1130b57cec5SDimitry Andric     ErrorMessage.append("\n");
1140b57cec5SDimitry Andric   llvm::raw_string_ostream OS(ErrorMessage);
1150b57cec5SDimitry Andric   // Stop initializing if command-line option parsing failed.
1160b57cec5SDimitry Andric   if (!cl::ParseCommandLineOptions(argc, argv, Overview, &OS)) {
1170b57cec5SDimitry Andric     OS.flush();
118fe6060f1SDimitry Andric     return llvm::make_error<llvm::StringError>(ErrorMessage,
1190b57cec5SDimitry Andric                                                llvm::inconvertibleErrorCode());
1200b57cec5SDimitry Andric   }
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric   cl::PrintOptionValues();
1230b57cec5SDimitry Andric 
1240b57cec5SDimitry Andric   SourcePathList = SourcePaths;
1250b57cec5SDimitry Andric   if ((OccurrencesFlag == cl::ZeroOrMore || OccurrencesFlag == cl::Optional) &&
1260b57cec5SDimitry Andric       SourcePathList.empty())
1270b57cec5SDimitry Andric     return llvm::Error::success();
1280b57cec5SDimitry Andric   if (!Compilations) {
1290b57cec5SDimitry Andric     if (!BuildPath.empty()) {
1300b57cec5SDimitry Andric       Compilations =
1310b57cec5SDimitry Andric           CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage);
1320b57cec5SDimitry Andric     } else {
1330b57cec5SDimitry Andric       Compilations = CompilationDatabase::autoDetectFromSource(SourcePaths[0],
1340b57cec5SDimitry Andric                                                                ErrorMessage);
1350b57cec5SDimitry Andric     }
1360b57cec5SDimitry Andric     if (!Compilations) {
1370b57cec5SDimitry Andric       llvm::errs() << "Error while trying to load a compilation database:\n"
1380b57cec5SDimitry Andric                    << ErrorMessage << "Running without flags.\n";
1390b57cec5SDimitry Andric       Compilations.reset(
1400b57cec5SDimitry Andric           new FixedCompilationDatabase(".", std::vector<std::string>()));
1410b57cec5SDimitry Andric     }
1420b57cec5SDimitry Andric   }
1430b57cec5SDimitry Andric   auto AdjustingCompilations =
144a7dea167SDimitry Andric       std::make_unique<ArgumentsAdjustingCompilations>(
1450b57cec5SDimitry Andric           std::move(Compilations));
1460b57cec5SDimitry Andric   Adjuster =
1470b57cec5SDimitry Andric       getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN);
1480b57cec5SDimitry Andric   Adjuster = combineAdjusters(
1490b57cec5SDimitry Andric       std::move(Adjuster),
1500b57cec5SDimitry Andric       getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
1510b57cec5SDimitry Andric   AdjustingCompilations->appendArgumentsAdjuster(Adjuster);
1520b57cec5SDimitry Andric   Compilations = std::move(AdjustingCompilations);
1530b57cec5SDimitry Andric   return llvm::Error::success();
1540b57cec5SDimitry Andric }
1550b57cec5SDimitry Andric 
create(int & argc,const char ** argv,llvm::cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)1560b57cec5SDimitry Andric llvm::Expected<CommonOptionsParser> CommonOptionsParser::create(
1570b57cec5SDimitry Andric     int &argc, const char **argv, llvm::cl::OptionCategory &Category,
1580b57cec5SDimitry Andric     llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
1590b57cec5SDimitry Andric   CommonOptionsParser Parser;
1600b57cec5SDimitry Andric   llvm::Error Err =
1610b57cec5SDimitry Andric       Parser.init(argc, argv, Category, OccurrencesFlag, Overview);
1620b57cec5SDimitry Andric   if (Err)
1630b57cec5SDimitry Andric     return std::move(Err);
1640b57cec5SDimitry Andric   return std::move(Parser);
1650b57cec5SDimitry Andric }
1660b57cec5SDimitry Andric 
CommonOptionsParser(int & argc,const char ** argv,cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)1670b57cec5SDimitry Andric CommonOptionsParser::CommonOptionsParser(
1680b57cec5SDimitry Andric     int &argc, const char **argv, cl::OptionCategory &Category,
1690b57cec5SDimitry Andric     llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
1700b57cec5SDimitry Andric   llvm::Error Err = init(argc, argv, Category, OccurrencesFlag, Overview);
1710b57cec5SDimitry Andric   if (Err) {
1720b57cec5SDimitry Andric     llvm::report_fatal_error(
173349cc55cSDimitry Andric         Twine("CommonOptionsParser: failed to parse command-line arguments. ") +
1740b57cec5SDimitry Andric         llvm::toString(std::move(Err)));
1750b57cec5SDimitry Andric   }
1760b57cec5SDimitry Andric }
177