17330f729Sjoerg //===--- CommonOptionsParser.cpp - common options for clang tools ---------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file implements the CommonOptionsParser class used to parse common
107330f729Sjoerg // command-line options for clang tools, so that they can be run as separate
117330f729Sjoerg // command-line applications with a consistent common interface for handling
127330f729Sjoerg // compilation database and input files.
137330f729Sjoerg //
147330f729Sjoerg // It provides a common subset of command-line options, common algorithm
157330f729Sjoerg // for locating a compilation database and source files, and help messages
167330f729Sjoerg // for the basic command-line interface.
177330f729Sjoerg //
187330f729Sjoerg // It creates a CompilationDatabase and reads common command-line options.
197330f729Sjoerg //
207330f729Sjoerg // This class uses the Clang Tooling infrastructure, see
217330f729Sjoerg // http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
227330f729Sjoerg // for details on setting it up with LLVM source tree.
237330f729Sjoerg //
247330f729Sjoerg //===----------------------------------------------------------------------===//
257330f729Sjoerg
267330f729Sjoerg #include "clang/Tooling/CommonOptionsParser.h"
277330f729Sjoerg #include "clang/Tooling/Tooling.h"
287330f729Sjoerg #include "llvm/Support/CommandLine.h"
297330f729Sjoerg
307330f729Sjoerg using namespace clang::tooling;
317330f729Sjoerg using namespace llvm;
327330f729Sjoerg
337330f729Sjoerg const char *const CommonOptionsParser::HelpMessage =
347330f729Sjoerg "\n"
357330f729Sjoerg "-p <build-path> is used to read a compile command database.\n"
367330f729Sjoerg "\n"
377330f729Sjoerg "\tFor example, it can be a CMake build directory in which a file named\n"
387330f729Sjoerg "\tcompile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n"
397330f729Sjoerg "\tCMake option to get this output). When no build path is specified,\n"
407330f729Sjoerg "\ta search for compile_commands.json will be attempted through all\n"
417330f729Sjoerg "\tparent paths of the first input file . See:\n"
427330f729Sjoerg "\thttps://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an\n"
437330f729Sjoerg "\texample of setting up Clang Tooling on a source tree.\n"
447330f729Sjoerg "\n"
457330f729Sjoerg "<source0> ... specify the paths of source files. These paths are\n"
467330f729Sjoerg "\tlooked up in the compile command database. If the path of a file is\n"
477330f729Sjoerg "\tabsolute, it needs to point into CMake's source tree. If the path is\n"
487330f729Sjoerg "\trelative, the current working directory needs to be in the CMake\n"
497330f729Sjoerg "\tsource tree and the file must be in a subdirectory of the current\n"
507330f729Sjoerg "\tworking directory. \"./\" prefixes in the relative files will be\n"
517330f729Sjoerg "\tautomatically removed, but the rest of a relative path must be a\n"
527330f729Sjoerg "\tsuffix of a path in the compile command database.\n"
537330f729Sjoerg "\n";
547330f729Sjoerg
appendArgumentsAdjuster(ArgumentsAdjuster Adjuster)557330f729Sjoerg void ArgumentsAdjustingCompilations::appendArgumentsAdjuster(
567330f729Sjoerg ArgumentsAdjuster Adjuster) {
577330f729Sjoerg Adjusters.push_back(std::move(Adjuster));
587330f729Sjoerg }
597330f729Sjoerg
getCompileCommands(StringRef FilePath) const607330f729Sjoerg std::vector<CompileCommand> ArgumentsAdjustingCompilations::getCompileCommands(
617330f729Sjoerg StringRef FilePath) const {
627330f729Sjoerg return adjustCommands(Compilations->getCompileCommands(FilePath));
637330f729Sjoerg }
647330f729Sjoerg
657330f729Sjoerg std::vector<std::string>
getAllFiles() const667330f729Sjoerg ArgumentsAdjustingCompilations::getAllFiles() const {
677330f729Sjoerg return Compilations->getAllFiles();
687330f729Sjoerg }
697330f729Sjoerg
707330f729Sjoerg std::vector<CompileCommand>
getAllCompileCommands() const717330f729Sjoerg ArgumentsAdjustingCompilations::getAllCompileCommands() const {
727330f729Sjoerg return adjustCommands(Compilations->getAllCompileCommands());
737330f729Sjoerg }
747330f729Sjoerg
adjustCommands(std::vector<CompileCommand> Commands) const757330f729Sjoerg std::vector<CompileCommand> ArgumentsAdjustingCompilations::adjustCommands(
767330f729Sjoerg std::vector<CompileCommand> Commands) const {
777330f729Sjoerg for (CompileCommand &Command : Commands)
787330f729Sjoerg for (const auto &Adjuster : Adjusters)
797330f729Sjoerg Command.CommandLine = Adjuster(Command.CommandLine, Command.Filename);
807330f729Sjoerg return Commands;
817330f729Sjoerg }
827330f729Sjoerg
init(int & argc,const char ** argv,cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)837330f729Sjoerg llvm::Error CommonOptionsParser::init(
847330f729Sjoerg int &argc, const char **argv, cl::OptionCategory &Category,
857330f729Sjoerg llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
867330f729Sjoerg
877330f729Sjoerg static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
887330f729Sjoerg cl::Optional, cl::cat(Category),
897330f729Sjoerg cl::sub(*cl::AllSubCommands));
907330f729Sjoerg
917330f729Sjoerg static cl::list<std::string> SourcePaths(
927330f729Sjoerg cl::Positional, cl::desc("<source0> [... <sourceN>]"), OccurrencesFlag,
937330f729Sjoerg cl::cat(Category), cl::sub(*cl::AllSubCommands));
947330f729Sjoerg
957330f729Sjoerg static cl::list<std::string> ArgsAfter(
967330f729Sjoerg "extra-arg",
977330f729Sjoerg cl::desc("Additional argument to append to the compiler command line"),
987330f729Sjoerg cl::cat(Category), cl::sub(*cl::AllSubCommands));
997330f729Sjoerg
1007330f729Sjoerg static cl::list<std::string> ArgsBefore(
1017330f729Sjoerg "extra-arg-before",
1027330f729Sjoerg cl::desc("Additional argument to prepend to the compiler command line"),
1037330f729Sjoerg cl::cat(Category), cl::sub(*cl::AllSubCommands));
1047330f729Sjoerg
1057330f729Sjoerg cl::ResetAllOptionOccurrences();
1067330f729Sjoerg
1077330f729Sjoerg cl::HideUnrelatedOptions(Category);
1087330f729Sjoerg
1097330f729Sjoerg std::string ErrorMessage;
1107330f729Sjoerg Compilations =
1117330f729Sjoerg FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
1127330f729Sjoerg if (!ErrorMessage.empty())
1137330f729Sjoerg ErrorMessage.append("\n");
1147330f729Sjoerg llvm::raw_string_ostream OS(ErrorMessage);
1157330f729Sjoerg // Stop initializing if command-line option parsing failed.
1167330f729Sjoerg if (!cl::ParseCommandLineOptions(argc, argv, Overview, &OS)) {
1177330f729Sjoerg OS.flush();
118*e038c9c4Sjoerg return llvm::make_error<llvm::StringError>(ErrorMessage,
1197330f729Sjoerg llvm::inconvertibleErrorCode());
1207330f729Sjoerg }
1217330f729Sjoerg
1227330f729Sjoerg cl::PrintOptionValues();
1237330f729Sjoerg
1247330f729Sjoerg SourcePathList = SourcePaths;
1257330f729Sjoerg if ((OccurrencesFlag == cl::ZeroOrMore || OccurrencesFlag == cl::Optional) &&
1267330f729Sjoerg SourcePathList.empty())
1277330f729Sjoerg return llvm::Error::success();
1287330f729Sjoerg if (!Compilations) {
1297330f729Sjoerg if (!BuildPath.empty()) {
1307330f729Sjoerg Compilations =
1317330f729Sjoerg CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage);
1327330f729Sjoerg } else {
1337330f729Sjoerg Compilations = CompilationDatabase::autoDetectFromSource(SourcePaths[0],
1347330f729Sjoerg ErrorMessage);
1357330f729Sjoerg }
1367330f729Sjoerg if (!Compilations) {
1377330f729Sjoerg llvm::errs() << "Error while trying to load a compilation database:\n"
1387330f729Sjoerg << ErrorMessage << "Running without flags.\n";
1397330f729Sjoerg Compilations.reset(
1407330f729Sjoerg new FixedCompilationDatabase(".", std::vector<std::string>()));
1417330f729Sjoerg }
1427330f729Sjoerg }
1437330f729Sjoerg auto AdjustingCompilations =
1447330f729Sjoerg std::make_unique<ArgumentsAdjustingCompilations>(
1457330f729Sjoerg std::move(Compilations));
1467330f729Sjoerg Adjuster =
1477330f729Sjoerg getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN);
1487330f729Sjoerg Adjuster = combineAdjusters(
1497330f729Sjoerg std::move(Adjuster),
1507330f729Sjoerg getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
1517330f729Sjoerg AdjustingCompilations->appendArgumentsAdjuster(Adjuster);
1527330f729Sjoerg Compilations = std::move(AdjustingCompilations);
1537330f729Sjoerg return llvm::Error::success();
1547330f729Sjoerg }
1557330f729Sjoerg
create(int & argc,const char ** argv,llvm::cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)1567330f729Sjoerg llvm::Expected<CommonOptionsParser> CommonOptionsParser::create(
1577330f729Sjoerg int &argc, const char **argv, llvm::cl::OptionCategory &Category,
1587330f729Sjoerg llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
1597330f729Sjoerg CommonOptionsParser Parser;
1607330f729Sjoerg llvm::Error Err =
1617330f729Sjoerg Parser.init(argc, argv, Category, OccurrencesFlag, Overview);
1627330f729Sjoerg if (Err)
1637330f729Sjoerg return std::move(Err);
1647330f729Sjoerg return std::move(Parser);
1657330f729Sjoerg }
1667330f729Sjoerg
CommonOptionsParser(int & argc,const char ** argv,cl::OptionCategory & Category,llvm::cl::NumOccurrencesFlag OccurrencesFlag,const char * Overview)1677330f729Sjoerg CommonOptionsParser::CommonOptionsParser(
1687330f729Sjoerg int &argc, const char **argv, cl::OptionCategory &Category,
1697330f729Sjoerg llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
1707330f729Sjoerg llvm::Error Err = init(argc, argv, Category, OccurrencesFlag, Overview);
1717330f729Sjoerg if (Err) {
1727330f729Sjoerg llvm::report_fatal_error(
1737330f729Sjoerg "CommonOptionsParser: failed to parse command-line arguments. " +
1747330f729Sjoerg llvm::toString(std::move(Err)));
1757330f729Sjoerg }
1767330f729Sjoerg }
177