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