• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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