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