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),
89*bdd1243dSDimitry 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,
93*bdd1243dSDimitry 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"),
98*bdd1243dSDimitry 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"),
103*bdd1243dSDimitry 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