1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <cstdlib>
16 #include <memory>
17 #include <string>
18 #include <vector>
19
20 #include "absl/base/attributes.h"
21 #include "absl/base/no_destructor.h"
22 #include "absl/status/status.h"
23 #include "absl/strings/match.h"
24 #include "absl/strings/str_format.h"
25 #include "clang/Tooling/CommonOptionsParser.h"
26 #include "clang/Tooling/CompilationDatabase.h"
27 #include "clang/Tooling/Tooling.h"
28 #include "llvm/Support/CommandLine.h"
29 #include "llvm/Support/Error.h"
30 #include "sandboxed_api/tools/clang_generator/compilation_database.h"
31 #include "sandboxed_api/tools/clang_generator/emitter.h"
32 #include "sandboxed_api/tools/clang_generator/generator.h"
33 #include "sandboxed_api/util/file_helpers.h"
34 #include "sandboxed_api/util/fileops.h"
35 #include "sandboxed_api/util/path.h"
36 #include "sandboxed_api/util/status_macros.h"
37
38 namespace sapi {
39 namespace {
40
41 absl::NoDestructor<llvm::cl::OptionCategory> g_tool_category(
42 "Sandboxed API Options");
43
44 absl::NoDestructor<llvm::cl::extrahelp> g_common_help(
45 clang::tooling::CommonOptionsParser::HelpMessage);
46 absl::NoDestructor<llvm::cl::extrahelp> g_extra_help(
47 "Full documentation at: "
48 "<https://developers.google.com/code-sandboxing/sandboxed-api>\n"
49 "Report bugs to <https://github.com/google/sandboxed-api/issues>\n");
50
51 // Command line options
52 absl::NoDestructor<llvm::cl::opt<std::string>> g_sapi_embed_dir(
53 "sapi_embed_dir", llvm::cl::desc("Directory with embedded includes"),
54 llvm::cl::cat(*g_tool_category));
55
56 absl::NoDestructor<llvm::cl::opt<std::string>> g_sapi_embed_name(
57 "sapi_embed_name", llvm::cl::desc("Identifier of the embed object"),
58 llvm::cl::cat(*g_tool_category));
59
60 absl::NoDestructor<llvm::cl::list<std::string>> g_sapi_functions(
61 "sapi_functions", llvm::cl::CommaSeparated,
62 llvm::cl::desc("List of functions to generate a Sandboxed API for. If "
63 "empty, generates a SAPI for all functions found."),
64 llvm::cl::cat(*g_tool_category));
65
66 ABSL_DEPRECATED("Pass the input files directly to the tool.")
67 absl::NoDestructor<llvm::cl::list<std::string>> g_sapi_in(
68 "sapi_in", llvm::cl::CommaSeparated,
69 llvm::cl::desc("List of input files to analyze (DEPRECATED)"),
70 llvm::cl::cat(*g_tool_category));
71
72 ABSL_DEPRECATED("Ignored for compatibility.")
73 absl::NoDestructor<llvm::cl::opt<std::string>> g_sapi_isystem(
74 "sapi_isystem",
75 llvm::cl::desc(
76 "Parameter file with extra system include paths (DEPRECATED)"),
77 llvm::cl::cat(*g_tool_category));
78
79 absl::NoDestructor<llvm::cl::opt<bool>> g_sapi_limit_scan_depth(
80 "sapi_limit_scan_depth",
81 llvm::cl::desc("Whether to only scan for functions "
82 "in the top-most translation unit"),
83 llvm::cl::cat(*g_tool_category));
84
85 absl::NoDestructor<llvm::cl::opt<std::string>> g_sapi_name(
86 "sapi_name", llvm::cl::desc("Name of the Sandboxed API library"),
87 llvm::cl::cat(*g_tool_category));
88
89 absl::NoDestructor<llvm::cl::opt<std::string>> g_sapi_ns(
90 "sapi_ns", llvm::cl::desc("C++ namespace to wrap Sandboxed API class in"),
91 llvm::cl::cat(*g_tool_category));
92
93 absl::NoDestructor<llvm::cl::opt<std::string>> g_sapi_out(
94 "sapi_out",
95 llvm::cl::desc("Output path of the generated header. If empty, simply "
96 "appends .sapi.h "
97 "to the basename of the first source file specified."),
98 llvm::cl::cat(*g_tool_category));
99
100 } // namespace
101
GeneratorOptionsFromFlags(const std::vector<std::string> & sources)102 GeneratorOptions GeneratorOptionsFromFlags(
103 const std::vector<std::string>& sources) {
104 GeneratorOptions options;
105 options.work_dir = file_util::fileops::GetCWD();
106 options.set_function_names(*g_sapi_functions);
107 for (const auto& input : sources) {
108 // Keep absolute paths as is, turn
109 options.in_files.insert(absl::StartsWith(input, "/")
110 ? input
111 : file::JoinPath(options.work_dir, input));
112 }
113 options.set_limit_scan_depth(*g_sapi_limit_scan_depth);
114 options.name = *g_sapi_name;
115 options.namespace_name = *g_sapi_ns;
116 options.out_file =
117 !g_sapi_out->empty() ? *g_sapi_out : GetOutputFilename(sources.front());
118 options.embed_dir = *g_sapi_embed_dir;
119 options.embed_name = *g_sapi_embed_name;
120 return options;
121 }
122
GeneratorMain(int argc,char * argv[])123 absl::Status GeneratorMain(int argc, char* argv[]) {
124 auto expected_opt_parser = OptionsParser::create(
125 argc, const_cast<const char**>(argv), *g_tool_category,
126 llvm::cl::ZeroOrMore,
127 "Generates a Sandboxed API header for C/C++ translation units.");
128 if (!expected_opt_parser) {
129 return absl::InternalError(llvm::toString(expected_opt_parser.takeError()));
130 }
131 OptionsParser& opt_parser = expected_opt_parser.get();
132
133 std::vector<std::string> sources = opt_parser.getSourcePathList();
134 for (const auto& sapi_in : *g_sapi_in) { // NOLINT
135 sources.push_back(sapi_in);
136 }
137 if (sources.empty()) {
138 return absl::InvalidArgumentError("Error: No input files.");
139 }
140
141 auto options = GeneratorOptionsFromFlags(sources);
142
143 std::unique_ptr<clang::tooling::CompilationDatabase> db =
144 FromCxxAjustedCompileCommands(
145 NonOwningCompileCommands(opt_parser.getCompilations()));
146 clang::tooling::ClangTool tool(*db, sources);
147
148 if (!g_sapi_isystem->empty()) { // NOLINT(deprecated)
149 absl::FPrintF(
150 stderr,
151 "Note: Ignoring deprecated command-line option: sapi_isystem\n");
152 }
153
154 // Process SAPI header generation.
155 Emitter emitter;
156 if (int result =
157 tool.run(std::make_unique<GeneratorFactory>(emitter, options).get());
158 result != 0) {
159 return absl::UnknownError("Error: Header generation failed.");
160 }
161
162 SAPI_ASSIGN_OR_RETURN(std::string header, emitter.EmitHeader(options));
163 SAPI_RETURN_IF_ERROR(
164 file::SetContents(options.out_file, header, file::Defaults()));
165 return absl::OkStatus();
166 }
167
168 } // namespace sapi
169
main(int argc,char * argv[])170 int main(int argc, char* argv[]) {
171 if (absl::Status status = sapi::GeneratorMain(argc, argv); !status.ok()) {
172 absl::FPrintF(stderr, "%s\n", status.message());
173 return EXIT_FAILURE;
174 }
175 return EXIT_SUCCESS;
176 }
177