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