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