1
2 #include <getopt.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <cstdlib>
8 #include <filesystem>
9 #include <map>
10 #include <set>
11 #include <vector>
12
13 #include "Collation.h"
14 #include "frameworks/proto_logging/stats/atoms.pb.h"
15 #include "frameworks/proto_logging/stats/attribution_node.pb.h"
16 #include "java_writer.h"
17 #include "java_writer_q.h"
18 #include "native_writer.h"
19 #include "native_writer_vendor.h"
20 #include "rust_writer.h"
21 #include "utils.h"
22
23 namespace android {
24 namespace stats_log_api_gen {
25
26 namespace fs = std::filesystem;
27 using android::os::statsd::Atom;
28
print_usage()29 static void print_usage() {
30 fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
31 fprintf(stderr, "\n");
32 fprintf(stderr, "OPTIONS\n");
33 fprintf(stderr, " --cpp FILENAME the cpp file to output for write helpers\n");
34 fprintf(stderr, " --header FILENAME the header file to output for write helpers\n");
35 fprintf(stderr, " --help this message\n");
36 fprintf(stderr, " --java FILENAME the java file to output\n");
37 fprintf(stderr, " --rust FILENAME the rust file to output\n");
38 fprintf(stderr, " --rustHeader FILENAME the rust file to output for write helpers\n");
39 fprintf(stderr,
40 " --rustHeaderCrate NAME header crate to be used while "
41 "generating the code. Note: this should be the same as the crate_name "
42 "created by rust_library for the header \n");
43 fprintf(stderr, " --module NAME optional, module name to generate outputs for\n");
44 fprintf(stderr,
45 " --namespace COMMA,SEP,NAMESPACE required for cpp/header with "
46 "module\n");
47 fprintf(stderr,
48 " comma separated namespace of "
49 "the files\n");
50 fprintf(stderr,
51 " --importHeader NAME required for cpp/jni to say which header to "
52 "import "
53 "for write helpers\n");
54 fprintf(stderr, " --javaPackage PACKAGE the package for the java file.\n");
55 fprintf(stderr, " required for java with module\n");
56 fprintf(stderr, " --javaClass CLASS the class name of the java class.\n");
57 fprintf(stderr, " --minApiLevel API_LEVEL lowest API level to support.\n");
58 fprintf(stderr, " Default is \"current\".\n");
59 fprintf(stderr,
60 " --worksource Include support for logging WorkSource "
61 "objects.\n");
62 fprintf(stderr,
63 " --compileApiLevel API_LEVEL specify which API level generated code is "
64 "compiled against. (Java only).\n");
65 fprintf(stderr, " Default is \"current\".\n");
66 fprintf(stderr,
67 " --bootstrap If this logging is from a bootstrap process. "
68 "Only supported for cpp. Do not use unless necessary.\n");
69 fprintf(stderr,
70 " --vendor-proto Path to the proto file for vendor atoms logging\n"
71 "code generation.\n");
72 }
73
74 /**
75 * Do the argument parsing and execute the tasks.
76 */
run(int argc,char const * const * argv)77 static int run(int argc, char const* const* argv) {
78 string cppFilename;
79 string headerFilename;
80 string javaFilename;
81 string javaPackage;
82 string javaClass;
83 string rustFilename;
84 string rustHeaderFilename;
85 string rustHeaderCrate;
86 string moduleName = DEFAULT_MODULE_NAME;
87 string cppNamespace = DEFAULT_CPP_NAMESPACE;
88 string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
89 string vendorProto;
90 bool supportWorkSource = false;
91 int minApiLevel = API_LEVEL_CURRENT;
92 int compileApiLevel = API_LEVEL_CURRENT;
93 bool bootstrap = false;
94
95 int index = 1;
96 while (index < argc) {
97 if (0 == strcmp("--help", argv[index])) {
98 print_usage();
99 return 0;
100 } else if (0 == strcmp("--cpp", argv[index])) {
101 index++;
102 if (index >= argc) {
103 print_usage();
104 return 1;
105 }
106 cppFilename = argv[index];
107 } else if (0 == strcmp("--header", argv[index])) {
108 index++;
109 if (index >= argc) {
110 print_usage();
111 return 1;
112 }
113 headerFilename = argv[index];
114 } else if (0 == strcmp("--java", argv[index])) {
115 index++;
116 if (index >= argc) {
117 print_usage();
118 return 1;
119 }
120 javaFilename = argv[index];
121 } else if (0 == strcmp("--rust", argv[index])) {
122 index++;
123 if (index >= argc) {
124 print_usage();
125 return 1;
126 }
127 rustFilename = argv[index];
128 } else if (0 == strcmp("--rustHeader", argv[index])) {
129 index++;
130 if (index >= argc) {
131 print_usage();
132 return 1;
133 }
134 rustHeaderFilename = argv[index];
135 } else if (0 == strcmp("--rustHeaderCrate", argv[index])) {
136 index++;
137 if (index >= argc) {
138 print_usage();
139 return 1;
140 }
141 rustHeaderCrate = argv[index];
142 } else if (0 == strcmp("--module", argv[index])) {
143 index++;
144 if (index >= argc) {
145 print_usage();
146 return 1;
147 }
148 moduleName = argv[index];
149 } else if (0 == strcmp("--namespace", argv[index])) {
150 index++;
151 if (index >= argc) {
152 print_usage();
153 return 1;
154 }
155 cppNamespace = argv[index];
156 } else if (0 == strcmp("--importHeader", argv[index])) {
157 index++;
158 if (index >= argc) {
159 print_usage();
160 return 1;
161 }
162 cppHeaderImport = argv[index];
163 } else if (0 == strcmp("--javaPackage", argv[index])) {
164 index++;
165 if (index >= argc) {
166 print_usage();
167 return 1;
168 }
169 javaPackage = argv[index];
170 } else if (0 == strcmp("--javaClass", argv[index])) {
171 index++;
172 if (index >= argc) {
173 print_usage();
174 return 1;
175 }
176 javaClass = argv[index];
177 } else if (0 == strcmp("--supportQ", argv[index])) {
178 minApiLevel = API_Q;
179 } else if (0 == strcmp("--worksource", argv[index])) {
180 supportWorkSource = true;
181 } else if (0 == strcmp("--minApiLevel", argv[index])) {
182 index++;
183 if (index >= argc) {
184 print_usage();
185 return 1;
186 }
187 if (0 != strcmp("current", argv[index])) {
188 minApiLevel = atoi(argv[index]);
189 }
190 } else if (0 == strcmp("--compileApiLevel", argv[index])) {
191 index++;
192 if (index >= argc) {
193 print_usage();
194 return 1;
195 }
196 if (0 != strcmp("current", argv[index])) {
197 compileApiLevel = atoi(argv[index]);
198 }
199 } else if (0 == strcmp("--bootstrap", argv[index])) {
200 bootstrap = true;
201 } else if (0 == strcmp("--vendor-proto", argv[index])) {
202 index++;
203 if (index >= argc) {
204 print_usage();
205 return 1;
206 }
207
208 vendorProto = argv[index];
209 }
210
211 index++;
212 }
213 if (index < argc) {
214 fprintf(stderr, "Error: Unknown command line argument\n");
215 print_usage();
216 return 1;
217 }
218
219 if (cppFilename.empty() && headerFilename.empty() && javaFilename.empty() &&
220 rustFilename.empty() && rustHeaderFilename.empty()) {
221 print_usage();
222 return 1;
223 }
224 if (DEFAULT_MODULE_NAME == moduleName &&
225 (minApiLevel != API_LEVEL_CURRENT || compileApiLevel != API_LEVEL_CURRENT)) {
226 // Default module only supports current API level.
227 fprintf(stderr, "%s cannot support older API levels\n", moduleName.c_str());
228 return 1;
229 }
230
231 if (compileApiLevel < API_R) {
232 // Cannot compile against pre-R.
233 fprintf(stderr, "compileApiLevel must be %d or higher.\n", API_R);
234 return 1;
235 }
236
237 if (minApiLevel < API_Q) {
238 // Cannot support pre-Q.
239 fprintf(stderr, "minApiLevel must be %d or higher.\n", API_Q);
240 return 1;
241 }
242
243 if (minApiLevel == API_LEVEL_CURRENT) {
244 if (minApiLevel > compileApiLevel) {
245 // If minApiLevel is not specified, assume it is not higher than compileApiLevel.
246 minApiLevel = compileApiLevel;
247 }
248 } else {
249 if (minApiLevel > compileApiLevel) {
250 // If specified, minApiLevel should always be lower than compileApiLevel.
251 fprintf(stderr,
252 "Invalid minApiLevel or compileApiLevel. If minApiLevel and"
253 " compileApiLevel are specified, minApiLevel should not be higher"
254 " than compileApiLevel.\n");
255 return 1;
256 }
257 }
258 if (bootstrap) {
259 if (cppFilename.empty() && headerFilename.empty()) {
260 fprintf(stderr, "Bootstrap flag can only be used for cpp/header files.\n");
261 return 1;
262 }
263 if (supportWorkSource) {
264 fprintf(stderr, "Bootstrap flag does not support worksources");
265 return 1;
266 }
267 if ((minApiLevel != API_LEVEL_CURRENT) || (compileApiLevel != API_LEVEL_CURRENT)) {
268 fprintf(stderr, "Bootstrap flag does not support older API levels");
269 return 1;
270 }
271 }
272
273 // Collate the parameters
274 int errorCount = 0;
275
276 Atoms atoms;
277
278 MFErrorCollector errorCollector;
279 google::protobuf::compiler::DiskSourceTree sourceTree;
280 google::protobuf::compiler::Importer importer(&sourceTree, &errorCollector);
281
282 if (vendorProto.empty()) {
283 errorCount = collate_atoms(Atom::descriptor(), moduleName, &atoms);
284 } else {
285 const google::protobuf::FileDescriptor* fileDescriptor;
286 sourceTree.MapPath("", fs::current_path().c_str());
287
288 const char* androidBuildTop = std::getenv("ANDROID_BUILD_TOP");
289
290 fs::path protobufSrc = androidBuildTop != nullptr ? androidBuildTop : fs::current_path();
291 protobufSrc /= "external/protobuf/src";
292 sourceTree.MapPath("", protobufSrc.c_str());
293
294 if (androidBuildTop != nullptr) {
295 sourceTree.MapPath("", androidBuildTop);
296 }
297
298 fileDescriptor = importer.Import(vendorProto);
299 errorCount =
300 collate_atoms(fileDescriptor->FindMessageTypeByName("Atom"), moduleName, &atoms);
301 }
302
303 if (errorCount != 0) {
304 return 1;
305 }
306
307 AtomDecl attributionDecl;
308 vector<java_type_t> attributionSignature;
309 collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
310 &attributionSignature);
311
312 // Write the .cpp file
313 if (!cppFilename.empty()) {
314 // If this is for a specific module, the namespace must also be provided.
315 if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
316 fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
317 return 1;
318 }
319 // If this is for a specific module, the header file to import must also be
320 // provided.
321 if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) {
322 fprintf(stderr, "Must supply --headerImport if supplying a specific module\n");
323 return 1;
324 }
325 FILE* out = fopen(cppFilename.c_str(), "w");
326 if (out == nullptr) {
327 fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
328 return 1;
329 }
330 if (vendorProto.empty()) {
331 errorCount = android::stats_log_api_gen::write_stats_log_cpp(
332 out, atoms, attributionDecl, cppNamespace, cppHeaderImport, minApiLevel,
333 bootstrap);
334 } else {
335 errorCount = android::stats_log_api_gen::write_stats_log_cpp_vendor(
336 out, atoms, attributionDecl, cppNamespace, cppHeaderImport);
337 }
338 fclose(out);
339 }
340
341 // Write the .h file
342 if (!headerFilename.empty()) {
343 // If this is for a specific module, the namespace must also be provided.
344 if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
345 fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
346 }
347 FILE* out = fopen(headerFilename.c_str(), "w");
348 if (out == nullptr) {
349 fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
350 return 1;
351 }
352
353 if (vendorProto.empty()) {
354 errorCount = android::stats_log_api_gen::write_stats_log_header(
355 out, atoms, attributionDecl, cppNamespace, minApiLevel, bootstrap);
356 } else {
357 errorCount = android::stats_log_api_gen::write_stats_log_header_vendor(
358 out, atoms, attributionDecl, cppNamespace);
359 }
360 fclose(out);
361 }
362
363 // Write the .java file
364 if (!javaFilename.empty()) {
365 if (javaClass.empty()) {
366 fprintf(stderr, "Must supply --javaClass if supplying a Java filename");
367 return 1;
368 }
369
370 if (javaPackage.empty()) {
371 fprintf(stderr, "Must supply --javaPackage if supplying a Java filename");
372 return 1;
373 }
374
375 if (moduleName.empty()) {
376 fprintf(stderr, "Must supply --module if supplying a Java filename");
377 return 1;
378 }
379
380 FILE* out = fopen(javaFilename.c_str(), "w");
381 if (out == nullptr) {
382 fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
383 return 1;
384 }
385
386 if (vendorProto.empty()) {
387 errorCount = android::stats_log_api_gen::write_stats_log_java(
388 out, atoms, attributionDecl, javaClass, javaPackage, minApiLevel,
389 compileApiLevel, supportWorkSource);
390 } else {
391 if (supportWorkSource) {
392 fprintf(stderr, "The attribution chain is not supported for vendor atoms");
393 return 1;
394 }
395
396 errorCount = android::stats_log_api_gen::write_stats_log_java_vendor(out, atoms,
397 javaClass, javaPackage);
398 }
399
400 fclose(out);
401 }
402
403 // Write the main .rs file
404 if (!rustFilename.empty()) {
405 if (rustHeaderCrate.empty()) {
406 fprintf(stderr, "rustHeaderCrate flag is either not passed or is empty");
407 return 1;
408 }
409
410 FILE* out = fopen(rustFilename.c_str(), "w");
411 if (out == nullptr) {
412 fprintf(stderr, "Unable to open file for write: %s\n", rustFilename.c_str());
413 return 1;
414 }
415
416 errorCount += android::stats_log_api_gen::write_stats_log_rust(
417 out, atoms, attributionDecl, minApiLevel, rustHeaderCrate.c_str());
418
419 fclose(out);
420 }
421
422 // Write the header .rs file
423 if (!rustHeaderFilename.empty()) {
424 if (rustHeaderCrate.empty()) {
425 fprintf(stderr, "rustHeaderCrate flag is either not passed or is empty");
426 return 1;
427 }
428
429 FILE* out = fopen(rustHeaderFilename.c_str(), "w");
430 if (out == nullptr) {
431 fprintf(stderr, "Unable to open file for write: %s\n", rustHeaderFilename.c_str());
432 return 1;
433 }
434
435 android::stats_log_api_gen::write_stats_log_rust_header(out, atoms, attributionDecl,
436 rustHeaderCrate.c_str());
437
438 fclose(out);
439 }
440
441 return errorCount;
442 }
443
444 } // namespace stats_log_api_gen
445 } // namespace android
446
447 /**
448 * Main.
449 */
main(int argc,char const * const * argv)450 int main(int argc, char const* const* argv) {
451 GOOGLE_PROTOBUF_VERIFY_VERSION;
452
453 return android::stats_log_api_gen::run(argc, argv);
454 }
455