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 <cstdio>
16 #include <cstdlib>
17 #include <memory>
18 #include <string>
19 #include <vector>
20
21 #include "absl/status/status.h"
22 #include "absl/status/statusor.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 "llvm/Support/CommandLine.h"
28 #include "sandboxed_api/tools/clang_generator/compilation_database.h"
29 #include "sandboxed_api/tools/clang_generator/generator.h"
30 #include "sandboxed_api/util/file_helpers.h"
31 #include "sandboxed_api/util/fileops.h"
32 #include "sandboxed_api/util/path.h"
33 #include "sandboxed_api/util/status_macros.h"
34
35 namespace sapi {
36 namespace {
37
38 static auto* g_tool_category =
39 new llvm::cl::OptionCategory("Sandboxed API Options");
40
41 static auto* g_common_help =
42 new llvm::cl::extrahelp(clang::tooling::CommonOptionsParser::HelpMessage);
43 static auto* g_extra_help = new llvm::cl::extrahelp(
44 "Full documentation at: "
45 "<https://developers.google.com/code-sandboxing/sandboxed-api/>\n"
46 "Report bugs to <https://github.com/google/sandboxed-api/issues>\n");
47
48 // Command line options
49 static auto* g_sapi_embed_dir = new llvm::cl::opt<std::string>(
50 "sapi_embed_dir", llvm::cl::desc("Directory with embedded includes"),
51 llvm::cl::cat(*g_tool_category));
52 static auto* g_sapi_embed_name = new llvm::cl::opt<std::string>(
53 "sapi_embed_name", llvm::cl::desc("Identifier of the embed object"),
54 llvm::cl::cat(*g_tool_category));
55 static auto* g_sapi_functions = new llvm::cl::list<std::string>(
56 "sapi_functions", llvm::cl::CommaSeparated,
57 llvm::cl::desc("List of functions to generate a Sandboxed API for. If "
58 "empty, generates a SAPI for all functions found."),
59 llvm::cl::cat(*g_tool_category));
60 static auto* g_sapi_in = new llvm::cl::list<std::string>(
61 "sapi_in", llvm::cl::CommaSeparated,
62 llvm::cl::desc("List of input files to analyze (deprecated)"),
63 llvm::cl::cat(*g_tool_category));
64 static auto* g_sapi_isystem = new llvm::cl::opt<std::string>(
65 "sapi_isystem",
66 llvm::cl::desc("Parameter file with extra system include paths (ignored "
67 "for compatibility)"),
68 llvm::cl::cat(*g_tool_category));
69 static auto* g_sapi_limit_scan_depth = new llvm::cl::opt<bool>(
70 "sapi_limit_scan_depth",
71 llvm::cl::desc(
72 "Whether to only scan for functions in the top-most translation unit"),
73 llvm::cl::cat(*g_tool_category));
74 static auto* g_sapi_name = new llvm::cl::opt<std::string>(
75 "sapi_name", llvm::cl::desc("Name of the Sandboxed API library"),
76 llvm::cl::cat(*g_tool_category));
77 static auto* g_sapi_ns = new llvm::cl::opt<std::string>(
78 "sapi_ns", llvm::cl::desc("C++ namespace to wrap Sandboxed API class in"),
79 llvm::cl::cat(*g_tool_category));
80 static auto* g_sapi_out = new llvm::cl::opt<std::string>(
81 "sapi_out",
82 llvm::cl::desc(
83 "Output path of the generated header. If empty, simply appends .sapi.h "
84 "to the basename of the first source file specified."),
85 llvm::cl::cat(*g_tool_category));
86
87 } // namespace
88
GeneratorOptionsFromFlags(const std::vector<std::string> & sources)89 GeneratorOptions GeneratorOptionsFromFlags(
90 const std::vector<std::string>& sources) {
91 GeneratorOptions options;
92 options.work_dir = sapi::file_util::fileops::GetCWD();
93 options.set_function_names(*g_sapi_functions);
94 for (const auto& input : sources) {
95 // Keep absolute paths as is, turn
96 options.in_files.insert(
97 absl::StartsWith(input, "/")
98 ? input
99 : sapi::file::JoinPath(options.work_dir, input));
100 }
101 options.set_limit_scan_depth(*g_sapi_limit_scan_depth);
102 options.name = *g_sapi_name;
103 options.namespace_name = *g_sapi_ns;
104 options.out_file =
105 !g_sapi_out->empty() ? *g_sapi_out : GetOutputFilename(sources.front());
106 options.embed_dir = *g_sapi_embed_dir;
107 options.embed_name = *g_sapi_embed_name;
108 return options;
109 }
110
GeneratorMain(int argc,char * argv[])111 absl::Status GeneratorMain(int argc, char* argv[]) {
112 auto expected_opt_parser = OptionsParser::create(
113 argc, const_cast<const char**>(argv), *sapi::g_tool_category,
114 llvm::cl::ZeroOrMore,
115 "Generates a Sandboxed API header for C/C++ translation units.");
116 if (!expected_opt_parser) {
117 return absl::InternalError(llvm::toString(expected_opt_parser.takeError()));
118 }
119 OptionsParser& opt_parser = expected_opt_parser.get();
120
121 std::vector<std::string> sources = opt_parser.getSourcePathList();
122 for (const auto& sapi_in : *sapi::g_sapi_in) {
123 sources.push_back(sapi_in);
124 }
125 if (sources.empty()) {
126 return absl::InvalidArgumentError("error: no input files");
127 }
128
129 auto options = sapi::GeneratorOptionsFromFlags(sources);
130 sapi::Emitter emitter;
131
132 std::unique_ptr<clang::tooling::CompilationDatabase> db =
133 FromCxxAjustedCompileCommands(
134 NonOwningCompileCommands(opt_parser.getCompilations()));
135 clang::tooling::ClangTool tool(*db, sources);
136
137 if (!g_sapi_isystem->empty()) {
138 absl::FPrintF(
139 stderr,
140 "note: ignoring deprecated command-line option: sapi_isystem\n");
141 }
142
143 if (int result = tool.run(
144 std::make_unique<sapi::GeneratorFactory>(emitter, options).get());
145 result != 0) {
146 return absl::UnknownError("header generation failed");
147 }
148
149 SAPI_ASSIGN_OR_RETURN(std::string header, emitter.EmitHeader(options));
150
151 SAPI_RETURN_IF_ERROR(sapi::file::SetContents(options.out_file, header,
152 sapi::file::Defaults()));
153 return absl::OkStatus();
154 }
155
156 } // namespace sapi
157
main(int argc,char * argv[])158 int main(int argc, char* argv[]) {
159 if (absl::Status status = sapi::GeneratorMain(argc, argv); !status.ok()) {
160 absl::FPrintF(stderr, "%s\n", status.message());
161 return EXIT_FAILURE;
162 }
163 return EXIT_SUCCESS;
164 }
165