blob: e5969cc4f087ac4fe50f40afd10f744abd58ee86 [file] [log] [blame]
Andreas Huber1aec3972016-08-26 09:26:32 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andreas Huberc9410c72016-07-28 12:18:40 -070017#include "AST.h"
Andreas Huber5345ec22016-07-29 13:33:27 -070018#include "Coordinator.h"
Andreas Huber0fa9e392016-08-31 09:05:44 -070019#include "Scope.h"
Andreas Huberc9410c72016-07-28 12:18:40 -070020
Andreas Huber308d8a22017-11-06 14:46:52 -080021#include <android-base/logging.h>
Steven Moreland5bdfa702017-04-18 23:20:39 -070022#include <hidl-hash/Hash.h>
Steven Moreland7ae3d542017-01-18 16:46:01 -080023#include <hidl-util/FQName.h>
Steven Morelanda8153982017-11-28 15:22:28 -080024#include <hidl-util/Formatter.h>
Steven Morelandd177b122016-12-12 09:15:37 -080025#include <hidl-util/StringHelper.h>
Steven Morelanda8153982017-11-28 15:22:28 -080026#include <stdio.h>
27#include <sys/stat.h>
28#include <unistd.h>
Andreas Huber308d8a22017-11-06 14:46:52 -080029#include <iostream>
Iliyan Malchev5bb14022016-08-09 15:04:39 -070030#include <set>
Andreas Huberdca261f2016-08-04 13:47:51 -070031#include <string>
Andreas Huberdca261f2016-08-04 13:47:51 -070032#include <vector>
Andreas Huberc9410c72016-07-28 12:18:40 -070033
34using namespace android;
35
Steven Moreland5abcf012018-02-08 18:50:18 -080036enum class OutputMode {
37 NEEDS_DIR, // -o output option expects a directory
38 NEEDS_FILE, // -o output option expects a file
39 NEEDS_SRC, // for changes inside the source tree itself
40 NOT_NEEDED // does not create files
Iliyan Malchev5bb14022016-08-09 15:04:39 -070041};
Andreas Huberdca261f2016-08-04 13:47:51 -070042
Steven Moreland5abcf012018-02-08 18:50:18 -080043enum class GenerationGranularity {
44 PER_PACKAGE, // Files generated for each package
45 PER_FILE, // Files generated for each hal file
46 PER_TYPE, // Files generated for each hal file + each type in HAL files
47};
Iliyan Malchev5bb14022016-08-09 15:04:39 -070048
Steven Moreland5abcf012018-02-08 18:50:18 -080049// Represents a file that is generated by an -L option for an FQName
50struct FileGenerator {
51 using ShouldGenerateFunction = std::function<bool(const FQName& fqName)>;
52 using FileNameForFQName = std::function<std::string(const FQName& fqName)>;
53 using GenerationFunction = std::function<status_t(Formatter& out, const FQName& fqName,
54 const Coordinator* coordinator)>;
55
56 ShouldGenerateFunction mShouldGenerateForFqName; // If generate function applies to this target
57 FileNameForFQName mFileNameForFqName; // Target -> filename
58 GenerationFunction mGenerationFunction; // Function to generate output for file
59
Steven Moreland394af5c2018-02-09 14:41:46 -080060 std::string getFileName(const FQName& fqName) const {
61 return mFileNameForFqName ? mFileNameForFqName(fqName) : "";
62 }
63
Steven Moreland255c9a12018-02-26 13:10:27 -080064 status_t getOutputFile(const FQName& fqName, const Coordinator* coordinator,
65 Coordinator::Location location, std::string* file) const {
Steven Moreland394af5c2018-02-09 14:41:46 -080066 if (!mShouldGenerateForFqName(fqName)) {
Steven Moreland255c9a12018-02-26 13:10:27 -080067 return OK;
Steven Moreland394af5c2018-02-09 14:41:46 -080068 }
69
Steven Moreland255c9a12018-02-26 13:10:27 -080070 return coordinator->getFilepath(fqName, location, getFileName(fqName), file);
Steven Moreland394af5c2018-02-09 14:41:46 -080071 }
72
Steven Moreland255c9a12018-02-26 13:10:27 -080073 status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
74 Coordinator::Location location,
75 std::vector<std::string>* outputFiles) const {
Steven Moreland394af5c2018-02-09 14:41:46 -080076 if (location == Coordinator::Location::STANDARD_OUT) {
Steven Moreland255c9a12018-02-26 13:10:27 -080077 return OK;
Steven Moreland394af5c2018-02-09 14:41:46 -080078 }
79
80 if (mShouldGenerateForFqName(fqName)) {
Steven Moreland255c9a12018-02-26 13:10:27 -080081 std::string fileName;
82 status_t err = getOutputFile(fqName, coordinator, location, &fileName);
83 if (err != OK) return err;
Steven Moreland394af5c2018-02-09 14:41:46 -080084
85 if (!fileName.empty()) {
86 outputFiles->push_back(fileName);
87 }
88 }
Steven Moreland255c9a12018-02-26 13:10:27 -080089 return OK;
Steven Moreland394af5c2018-02-09 14:41:46 -080090 }
91
Steven Moreland5abcf012018-02-08 18:50:18 -080092 status_t generate(const FQName& fqName, const Coordinator* coordinator,
93 Coordinator::Location location) const {
94 CHECK(mShouldGenerateForFqName != nullptr);
95 CHECK(mGenerationFunction != nullptr);
96
97 if (!mShouldGenerateForFqName(fqName)) {
98 return OK;
99 }
100
Steven Moreland394af5c2018-02-09 14:41:46 -0800101 Formatter out = coordinator->getFormatter(fqName, location, getFileName(fqName));
Steven Moreland255c9a12018-02-26 13:10:27 -0800102 if (!out.isValid()) {
103 return UNKNOWN_ERROR;
104 }
105
Steven Moreland5abcf012018-02-08 18:50:18 -0800106 return mGenerationFunction(out, fqName, coordinator);
107 }
108
109 // Helper methods for filling out this struct
110 static bool generateForTypes(const FQName& fqName) {
111 const auto names = fqName.names();
112 return names.size() > 0 && names[0] == "types";
113 }
114 static bool generateForInterfaces(const FQName& fqName) { return !generateForTypes(fqName); }
115 static bool alwaysGenerate(const FQName&) { return true; }
116};
117
118// Represents a -L option, takes a fqName and generates files
119struct OutputHandler {
Steven Moreland6ec9eb92018-02-16 14:21:49 -0800120 using ValidationFunction = std::function<bool(
121 const FQName& fqName, const Coordinator* coordinator, const std::string& language)>;
Steven Moreland5abcf012018-02-08 18:50:18 -0800122
123 std::string mKey; // -L in Android.bp
124 std::string mDescription; // for display in help menu
125 OutputMode mOutputMode; // how this option interacts with -o
126 Coordinator::Location mLocation; // how to compute location relative to the output directory
Steven Moreland394af5c2018-02-09 14:41:46 -0800127 GenerationGranularity mGenerationGranularity; // what to run generate function on
Steven Moreland5abcf012018-02-08 18:50:18 -0800128 ValidationFunction mValidate; // if a given fqName is allowed for this option
129 std::vector<FileGenerator> mGenerateFunctions; // run for each target at this granularity
130
131 const std::string& name() const { return mKey; }
132 const std::string& description() const { return mDescription; }
133
134 status_t generate(const FQName& fqName, const Coordinator* coordinator) const;
Steven Moreland6ec9eb92018-02-16 14:21:49 -0800135 status_t validate(const FQName& fqName, const Coordinator* coordinator,
136 const std::string& language) const {
137 return mValidate(fqName, coordinator, language);
Steven Moreland5abcf012018-02-08 18:50:18 -0800138 }
Steven Moreland394af5c2018-02-09 14:41:46 -0800139
140 status_t writeDepFile(const FQName& fqName, const Coordinator* coordinator) const;
141
142 private:
143 status_t appendTargets(const FQName& fqName, const Coordinator* coordinator,
144 std::vector<FQName>* targets) const;
145 status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
146 std::vector<std::string>* outputFiles) const;
Steven Moreland5abcf012018-02-08 18:50:18 -0800147};
148
149// Helper method for GenerationGranularity::PER_TYPE
150// IFoo -> IFoo, types.hal (containing Bar, Baz) -> types.Bar, types.Baz
151static status_t appendPerTypeTargets(const FQName& fqName, const Coordinator* coordinator,
152 std::vector<FQName>* exportedPackageInterfaces) {
153 CHECK(fqName.isFullyQualified());
154 if (fqName.name() != "types") {
155 exportedPackageInterfaces->push_back(fqName);
156 return OK;
157 }
158
159 AST* typesAST = coordinator->parse(fqName);
160 if (typesAST == nullptr) {
161 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
162 return UNKNOWN_ERROR;
163 }
164
165 std::vector<NamedType*> rootTypes = typesAST->getRootScope()->getSubTypes();
166 for (const NamedType* rootType : rootTypes) {
167 if (rootType->isTypeDef()) continue;
168
169 FQName rootTypeName(fqName.package(), fqName.version(), "types." + rootType->localName());
170 exportedPackageInterfaces->push_back(rootTypeName);
171 }
172 return OK;
173}
174
Steven Moreland394af5c2018-02-09 14:41:46 -0800175status_t OutputHandler::appendTargets(const FQName& fqName, const Coordinator* coordinator,
176 std::vector<FQName>* targets) const {
Steven Moreland5abcf012018-02-08 18:50:18 -0800177 switch (mGenerationGranularity) {
178 case GenerationGranularity::PER_PACKAGE: {
Steven Moreland394af5c2018-02-09 14:41:46 -0800179 targets->push_back(fqName.getPackageAndVersion());
Steven Moreland5abcf012018-02-08 18:50:18 -0800180 } break;
181 case GenerationGranularity::PER_FILE: {
182 if (fqName.isFullyQualified()) {
Steven Moreland394af5c2018-02-09 14:41:46 -0800183 targets->push_back(fqName);
Steven Moreland5abcf012018-02-08 18:50:18 -0800184 break;
185 }
Steven Moreland394af5c2018-02-09 14:41:46 -0800186 status_t err = coordinator->appendPackageInterfacesToVector(fqName, targets);
Steven Moreland5abcf012018-02-08 18:50:18 -0800187 if (err != OK) return err;
188 } break;
189 case GenerationGranularity::PER_TYPE: {
190 if (fqName.isFullyQualified()) {
Steven Moreland394af5c2018-02-09 14:41:46 -0800191 status_t err = appendPerTypeTargets(fqName, coordinator, targets);
Steven Moreland5abcf012018-02-08 18:50:18 -0800192 if (err != OK) return err;
193 }
194
195 std::vector<FQName> packageInterfaces;
196 status_t err = coordinator->appendPackageInterfacesToVector(fqName, &packageInterfaces);
197 if (err != OK) return err;
198 for (const FQName& packageInterface : packageInterfaces) {
Steven Moreland394af5c2018-02-09 14:41:46 -0800199 err = appendPerTypeTargets(packageInterface, coordinator, targets);
Steven Moreland5abcf012018-02-08 18:50:18 -0800200 if (err != OK) return err;
201 }
202 } break;
203 default:
204 CHECK(!"Should be here");
205 }
206
Steven Moreland394af5c2018-02-09 14:41:46 -0800207 return OK;
208}
209
210status_t OutputHandler::generate(const FQName& fqName, const Coordinator* coordinator) const {
211 std::vector<FQName> targets;
212 status_t err = appendTargets(fqName, coordinator, &targets);
213 if (err != OK) return err;
214
Steven Moreland5abcf012018-02-08 18:50:18 -0800215 for (const FQName& fqName : targets) {
216 for (const FileGenerator& file : mGenerateFunctions) {
217 status_t err = file.generate(fqName, coordinator, mLocation);
218 if (err != OK) return err;
219 }
220 }
221
222 return OK;
223}
224
Steven Moreland394af5c2018-02-09 14:41:46 -0800225status_t OutputHandler::appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
226 std::vector<std::string>* outputFiles) const {
227 std::vector<FQName> targets;
228 status_t err = appendTargets(fqName, coordinator, &targets);
229 if (err != OK) return err;
230
231 for (const FQName& fqName : targets) {
232 for (const FileGenerator& file : mGenerateFunctions) {
Steven Moreland255c9a12018-02-26 13:10:27 -0800233 err = file.appendOutputFiles(fqName, coordinator, mLocation, outputFiles);
234 if (err != OK) return err;
Steven Moreland394af5c2018-02-09 14:41:46 -0800235 }
236 }
237
238 return OK;
239}
240
241status_t OutputHandler::writeDepFile(const FQName& fqName, const Coordinator* coordinator) const {
242 std::vector<std::string> outputFiles;
243 status_t err = appendOutputFiles(fqName, coordinator, &outputFiles);
244 if (err != OK) return err;
245
246 // No need for dep files
247 if (outputFiles.empty()) {
248 return OK;
249 }
250
251 // Depfiles in Android for genrules should be for the 'main file'. Because hidl-gen doesn't have
252 // a main file for most targets, we are just outputting a depfile for one single file only.
253 const std::string forFile = outputFiles[0];
254
255 return coordinator->writeDepFile(forFile);
256}
257
Steven Moreland5abcf012018-02-08 18:50:18 -0800258// Use an AST function as a OutputHandler GenerationFunction
Steven Moreland6ec9eb92018-02-16 14:21:49 -0800259static FileGenerator::GenerationFunction astGenerationFunction(void (AST::*generate)(Formatter&)
Steven Moreland5abcf012018-02-08 18:50:18 -0800260 const = nullptr) {
261 return [generate](Formatter& out, const FQName& fqName,
262 const Coordinator* coordinator) -> status_t {
263 AST* ast = coordinator->parse(fqName);
264 if (ast == nullptr) {
265 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
266 return UNKNOWN_ERROR;
267 }
268
269 if (generate == nullptr) return OK; // just parsing AST
Steven Moreland6ec9eb92018-02-16 14:21:49 -0800270 (ast->*generate)(out);
271
272 return OK;
Steven Moreland5abcf012018-02-08 18:50:18 -0800273 };
274}
275
276// Common pattern: single file for package or standard out
277static FileGenerator singleFileGenerator(
278 const std::string& fileName, const FileGenerator::GenerationFunction& generationFunction) {
279 return {
280 FileGenerator::alwaysGenerate, [fileName](const FQName&) { return fileName; },
281 generationFunction,
282 };
283}
284
285static status_t generateJavaForPackage(Formatter& out, const FQName& fqName,
286 const Coordinator* coordinator) {
287 AST* ast;
Andreas Huberd29724f2016-09-14 09:33:13 -0700288 std::string limitToType;
Andreas Huber0fa9e392016-08-31 09:05:44 -0700289
Steven Moreland5abcf012018-02-08 18:50:18 -0800290 // Required for legacy -Lmakefile files
Andreas Huber0fa9e392016-08-31 09:05:44 -0700291 if (fqName.name().find("types.") == 0) {
Andreas Huberd29724f2016-09-14 09:33:13 -0700292 limitToType = fqName.name().substr(strlen("types."));
Andreas Huber0fa9e392016-08-31 09:05:44 -0700293
Yifan Hongfece6ec2017-01-12 17:04:04 -0800294 FQName typesName = fqName.getTypesForPackage();
Andreas Huber0fa9e392016-08-31 09:05:44 -0700295 ast = coordinator->parse(typesName);
296 } else {
297 ast = coordinator->parse(fqName);
298 }
Steven Moreland5abcf012018-02-08 18:50:18 -0800299 if (ast == nullptr) {
300 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700301 return UNKNOWN_ERROR;
302 }
Steven Moreland6ec9eb92018-02-16 14:21:49 -0800303 ast->generateJava(out, limitToType);
304 return OK;
Steven Moreland5abcf012018-02-08 18:50:18 -0800305};
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700306
Steven Morelandb65e5d92018-02-08 12:44:51 -0800307static status_t dumpDefinedButUnreferencedTypeNames(const FQName& packageFQName,
308 const Coordinator* coordinator) {
309 std::vector<FQName> packageInterfaces;
310 status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces);
311 if (err != OK) return err;
312
Steven Moreland40b86352018-02-01 16:03:30 -0800313 std::set<FQName> unreferencedDefinitions;
314 std::set<FQName> unreferencedImports;
Steven Morelandb65e5d92018-02-08 12:44:51 -0800315 err = coordinator->addUnreferencedTypes(packageInterfaces, &unreferencedDefinitions,
316 &unreferencedImports);
317 if (err != OK) return err;
Andreas Huber308d8a22017-11-06 14:46:52 -0800318
Steven Moreland40b86352018-02-01 16:03:30 -0800319 for (const auto& fqName : unreferencedDefinitions) {
Andreas Huber308d8a22017-11-06 14:46:52 -0800320 std::cerr
321 << "VERBOSE: DEFINED-BUT-NOT-REFERENCED "
322 << fqName.string()
323 << std::endl;
324 }
325
Steven Moreland40b86352018-02-01 16:03:30 -0800326 for (const auto& fqName : unreferencedImports) {
Andreas Huber4f4b3822017-11-15 13:18:50 -0800327 std::cerr
328 << "VERBOSE: IMPORTED-BUT-NOT-REFERENCED "
329 << fqName.string()
330 << std::endl;
331 }
332
Andreas Huber308d8a22017-11-06 14:46:52 -0800333 return OK;
334}
335
Andreas Huberd2943e12016-08-05 11:59:31 -0700336static std::string makeLibraryName(const FQName &packageFQName) {
Iliyan Malcheve2c595a2016-08-07 21:20:04 -0700337 return packageFQName.string();
Andreas Huberd2943e12016-08-05 11:59:31 -0700338}
Steven Moreland5715fed2017-01-16 11:06:47 -0800339
Steven Morelande6d7f092018-02-08 13:25:45 -0800340static status_t isPackageJavaCompatible(const FQName& packageFQName, const Coordinator* coordinator,
341 bool* compatible) {
Andreas Huber75ae95d2016-10-12 16:08:26 -0700342 std::vector<FQName> todo;
343 status_t err =
344 coordinator->appendPackageInterfacesToVector(packageFQName, &todo);
345
346 if (err != OK) {
347 return err;
348 }
349
350 std::set<FQName> seen;
351 for (const auto &iface : todo) {
352 seen.insert(iface);
353 }
354
355 // Form the transitive closure of all imported interfaces (and types.hal-s)
356 // If any one of them is not java compatible, this package isn't either.
357 while (!todo.empty()) {
358 const FQName fqName = todo.back();
359 todo.pop_back();
360
361 AST *ast = coordinator->parse(fqName);
362
363 if (ast == nullptr) {
364 return UNKNOWN_ERROR;
365 }
366
367 if (!ast->isJavaCompatible()) {
368 *compatible = false;
369 return OK;
370 }
371
372 std::set<FQName> importedPackages;
373 ast->getImportedPackages(&importedPackages);
374
375 for (const auto &package : importedPackages) {
376 std::vector<FQName> packageInterfaces;
377 status_t err = coordinator->appendPackageInterfacesToVector(
378 package, &packageInterfaces);
379
380 if (err != OK) {
381 return err;
382 }
383
384 for (const auto &iface : packageInterfaces) {
385 if (seen.find(iface) != seen.end()) {
386 continue;
387 }
388
389 todo.push_back(iface);
390 seen.insert(iface);
391 }
392 }
393 }
394
395 *compatible = true;
396 return OK;
397}
398
Andreas Huber5a9fe2c2016-10-13 15:31:10 -0700399static bool packageNeedsJavaCode(
400 const std::vector<FQName> &packageInterfaces, AST *typesAST) {
Steven Morelandadc25022017-10-11 12:42:19 -0700401 if (packageInterfaces.size() == 0) {
402 return false;
403 }
404
Andreas Huber5a9fe2c2016-10-13 15:31:10 -0700405 // If there is more than just a types.hal file to this package we'll
406 // definitely need to generate Java code.
407 if (packageInterfaces.size() > 1
408 || packageInterfaces[0].name() != "types") {
409 return true;
410 }
411
412 CHECK(typesAST != nullptr);
413
414 // We'll have to generate Java code if types.hal contains any non-typedef
415 // type declarations.
416
Timur Iskhakovcb0ba522017-07-17 20:01:37 -0700417 Scope* rootScope = typesAST->getRootScope();
Andreas Huber5a9fe2c2016-10-13 15:31:10 -0700418 std::vector<NamedType *> subTypes = rootScope->getSubTypes();
419
420 for (const auto &subType : subTypes) {
421 if (!subType->isTypeDef()) {
422 return true;
423 }
424 }
425
426 return false;
427}
428
Steven Moreland6ec9eb92018-02-16 14:21:49 -0800429bool validateIsPackage(const FQName& fqName, const Coordinator*,
430 const std::string& /* language */) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700431 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700432 fprintf(stderr, "ERROR: Expecting package name\n");
Steven Moreland05951b32017-05-12 17:22:22 -0700433 return false;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700434 }
435
436 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700437 fprintf(stderr, "ERROR: Expecting package version\n");
Steven Moreland05951b32017-05-12 17:22:22 -0700438 return false;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700439 }
440
441 if (!fqName.name().empty()) {
442 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700443 "ERROR: Expecting only package name and version.\n");
Steven Moreland05951b32017-05-12 17:22:22 -0700444 return false;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700445 }
446
Steven Moreland05951b32017-05-12 17:22:22 -0700447 return true;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700448}
449
Steven Morelandf7f2a9a2017-07-21 18:05:38 -0700450bool isHidlTransportPackage(const FQName& fqName) {
Steven Morelande1b157e2018-03-06 14:18:32 -0800451 return fqName.package() == gIBaseFqName.package() ||
452 fqName.package() == gIManagerFqName.package();
Steven Moreland77cf7642017-06-15 14:49:39 -0700453}
454
Steven Morelandc7946c92017-08-08 14:48:32 -0700455bool isSystemProcessSupportedPackage(const FQName& fqName) {
456 // Technically, so is hidl IBase + IServiceManager, but
457 // these are part of libhidltransport.
Jiyong Park45ec8d12017-11-21 10:39:22 +0900458 return fqName.string() == "android.hardware.graphics.common@1.0" ||
Steven Moreland40f11352018-01-23 11:30:43 -0800459 fqName.string() == "android.hardware.graphics.common@1.1" ||
Steven Morelandc7946c92017-08-08 14:48:32 -0700460 fqName.string() == "android.hardware.graphics.mapper@2.0" ||
Chia-I Wu620cc392017-09-28 15:33:21 -0700461 fqName.string() == "android.hardware.graphics.mapper@2.1" ||
Steven Morelandc7946c92017-08-08 14:48:32 -0700462 fqName.string() == "android.hardware.renderscript@1.0" ||
Howard Chen01dfc6d2017-11-30 15:37:12 +0800463 fqName.string() == "android.hidl.memory.token@1.0" ||
Steven Morelandc7946c92017-08-08 14:48:32 -0700464 fqName.string() == "android.hidl.memory@1.0";
465}
466
Steven Moreland5edf5282017-07-20 12:56:58 -0700467bool isSystemPackage(const FQName &package) {
468 return package.inPackage("android.hidl") ||
469 package.inPackage("android.system") ||
470 package.inPackage("android.frameworks") ||
471 package.inPackage("android.hardware");
472}
473
Steven Morelanda8153982017-11-28 15:22:28 -0800474// TODO(b/69862859): remove special case
Steven Moreland255c9a12018-02-26 13:10:27 -0800475status_t isTestPackage(const FQName& fqName, const Coordinator* coordinator, bool* isTestPackage) {
Steven Morelanda8153982017-11-28 15:22:28 -0800476 const auto fileExists = [](const std::string& file) {
477 struct stat buf;
478 return stat(file.c_str(), &buf) == 0;
479 };
480
Steven Moreland255c9a12018-02-26 13:10:27 -0800481 std::string path;
482 status_t err = coordinator->getFilepath(fqName, Coordinator::Location::PACKAGE_ROOT,
483 ".hidl_for_test", &path);
484 if (err != OK) return err;
485
Steven Morelanda8153982017-11-28 15:22:28 -0800486 const bool exists = fileExists(path);
487
488 if (exists) {
489 coordinator->onFileAccess(path, "r");
490 }
491
Steven Moreland255c9a12018-02-26 13:10:27 -0800492 *isTestPackage = exists;
493 return OK;
Steven Morelanda8153982017-11-28 15:22:28 -0800494}
495
Steven Moreland5abcf012018-02-08 18:50:18 -0800496static status_t generateAdapterMainSource(Formatter& out, const FQName& packageFQName,
Steven Morelande6d7f092018-02-08 13:25:45 -0800497 const Coordinator* coordinator) {
Steven Moreland9a6da7a2017-09-15 16:21:24 -0700498 std::vector<FQName> packageInterfaces;
499 status_t err =
500 coordinator->appendPackageInterfacesToVector(packageFQName,
501 &packageInterfaces);
502 if (err != OK) {
503 return err;
504 }
505
506 out << "#include <hidladapter/HidlBinderAdapter.h>\n";
507
508 for (auto &interface : packageInterfaces) {
509 if (interface.name() == "types") {
510 continue;
511 }
512 AST::generateCppPackageInclude(out, interface, interface.getInterfaceAdapterName());
513 }
514
515 out << "int main(int argc, char** argv) ";
516 out.block([&] {
517 out << "return ::android::hardware::adapterMain<\n";
518 out.indent();
519 for (auto &interface : packageInterfaces) {
520 if (interface.name() == "types") {
521 continue;
522 }
523 out << interface.getInterfaceAdapterFqName().cppName();
524
525 if (&interface != &packageInterfaces.back()) {
526 out << ",\n";
527 }
528 }
529 out << ">(\"" << packageFQName.string() << "\", argc, argv);\n";
530 out.unindent();
531 }).endl();
532 return OK;
533}
534
Steven Moreland5abcf012018-02-08 18:50:18 -0800535static status_t generateAndroidBpForPackage(Formatter& out, const FQName& packageFQName,
Steven Morelande6d7f092018-02-08 13:25:45 -0800536 const Coordinator* coordinator) {
Steven Moreland77730892018-03-06 14:39:41 -0800537 CHECK(!packageFQName.isFullyQualified() && packageFQName.name().empty());
Steven Moreland40069d52017-10-03 09:36:51 -0700538
539 std::vector<FQName> packageInterfaces;
540
541 status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces);
542
543 if (err != OK) {
544 return err;
545 }
546
547 std::set<FQName> importedPackagesHierarchy;
Steven Moreland887b84c2017-10-09 18:40:58 -0700548 std::vector<const Type *> exportedTypes;
Steven Moreland40069d52017-10-03 09:36:51 -0700549 AST* typesAST = nullptr;
550
551 for (const auto& fqName : packageInterfaces) {
552 AST* ast = coordinator->parse(fqName);
553
Yi Kong56758da2018-07-24 16:21:37 -0700554 if (ast == nullptr) {
Steven Moreland40069d52017-10-03 09:36:51 -0700555 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
556
557 return UNKNOWN_ERROR;
558 }
559
560 if (fqName.name() == "types") {
561 typesAST = ast;
562 }
563
564 ast->getImportedPackagesHierarchy(&importedPackagesHierarchy);
Steven Moreland887b84c2017-10-09 18:40:58 -0700565 ast->appendToExportedTypesVector(&exportedTypes);
Steven Moreland40069d52017-10-03 09:36:51 -0700566 }
567
Steven Moreland313a0132017-11-03 14:23:14 -0700568 bool needsJavaCode = packageNeedsJavaCode(packageInterfaces, typesAST);
569
570 bool genJavaConstants = needsJavaCode && !exportedTypes.empty();
Steven Moreland887b84c2017-10-09 18:40:58 -0700571
572 bool isJavaCompatible;
573 err = isPackageJavaCompatible(packageFQName, coordinator, &isJavaCompatible);
574 if (err != OK) return err;
Steven Moreland313a0132017-11-03 14:23:14 -0700575 bool genJavaLibrary = needsJavaCode && isJavaCompatible;
576
Steven Moreland255c9a12018-02-26 13:10:27 -0800577 bool generateForTest;
578 err = isTestPackage(packageFQName, coordinator, &generateForTest);
579 if (err != OK) return err;
580
Steven Moreland313a0132017-11-03 14:23:14 -0700581 bool isVndk = !generateForTest && isSystemPackage(packageFQName);
582 bool isVndkSp = isVndk && isSystemProcessSupportedPackage(packageFQName);
Steven Moreland887b84c2017-10-09 18:40:58 -0700583
Steven Moreland255c9a12018-02-26 13:10:27 -0800584 std::string packageRoot;
585 err = coordinator->getPackageRoot(packageFQName, &packageRoot);
586 if (err != OK) return err;
587
Steven Moreland313a0132017-11-03 14:23:14 -0700588 out << "// This file is autogenerated by hidl-gen -Landroidbp.\n\n";
Steven Moreland40069d52017-10-03 09:36:51 -0700589
Steven Moreland313a0132017-11-03 14:23:14 -0700590 out << "hidl_interface ";
Steven Moreland40069d52017-10-03 09:36:51 -0700591 out.block([&] {
Steven Moreland313a0132017-11-03 14:23:14 -0700592 out << "name: \"" << makeLibraryName(packageFQName) << "\",\n";
Steven Moreland89a9ebb2017-12-04 10:18:00 -0800593 if (!coordinator->getOwner().empty()) {
594 out << "owner: \"" << coordinator->getOwner() << "\",\n";
595 }
Steven Moreland255c9a12018-02-26 13:10:27 -0800596 out << "root: \"" << packageRoot << "\",\n";
Steven Moreland313a0132017-11-03 14:23:14 -0700597 if (isVndk) {
598 out << "vndk: ";
599 out.block([&]() {
600 out << "enabled: true,\n";
601 if (isVndkSp) {
602 out << "support_system_process: true,\n";
603 }
604 }) << ",\n";
605 }
606 (out << "srcs: [\n").indent([&] {
Steven Moreland40069d52017-10-03 09:36:51 -0700607 for (const auto& fqName : packageInterfaces) {
608 out << "\"" << fqName.name() << ".hal\",\n";
609 }
Steven Moreland313a0132017-11-03 14:23:14 -0700610 }) << "],\n";
611 if (!importedPackagesHierarchy.empty()) {
612 (out << "interfaces: [\n").indent([&] {
613 for (const auto& fqName : importedPackagesHierarchy) {
614 out << "\"" << fqName.string() << "\",\n";
615 }
616 }) << "],\n";
617 }
618 if (typesAST != nullptr) {
619 (out << "types: [\n").indent([&] {
620 std::vector<NamedType *> subTypes = typesAST->getRootScope()->getSubTypes();
621 std::sort(
622 subTypes.begin(),
623 subTypes.end(),
624 [](const NamedType *a, const NamedType *b) -> bool {
625 return a->fqName() < b->fqName();
626 });
627
628 for (const auto &type : subTypes) {
629 if (type->isTypeDef()) {
630 continue;
631 }
632
633 out << "\"" << type->localName() << "\",\n";
634 }
635 }) << "],\n";
636 }
637 // Explicity call this out for developers.
638 out << "gen_java: " << (genJavaLibrary ? "true" : "false") << ",\n";
639 if (genJavaConstants) {
640 out << "gen_java_constants: true,\n";
641 }
Steven Moreland40069d52017-10-03 09:36:51 -0700642 }).endl().endl();
643
Steven Moreland40069d52017-10-03 09:36:51 -0700644 return OK;
645}
646
Steven Moreland5abcf012018-02-08 18:50:18 -0800647static status_t generateAndroidBpImplForPackage(Formatter& out, const FQName& packageFQName,
Steven Morelande6d7f092018-02-08 13:25:45 -0800648 const Coordinator* coordinator) {
Iliyan Malchev4923f932016-09-09 13:04:59 -0700649 const std::string libraryName = makeLibraryName(packageFQName) + "-impl";
Steven Moreland197d56c2016-09-09 10:03:58 -0700650
651 std::vector<FQName> packageInterfaces;
652
653 status_t err =
Steven Morelandaa186832016-09-26 13:51:43 -0700654 coordinator->appendPackageInterfacesToVector(packageFQName,
655 &packageInterfaces);
Steven Moreland197d56c2016-09-09 10:03:58 -0700656
657 if (err != OK) {
658 return err;
659 }
660
661 std::set<FQName> importedPackages;
662
663 for (const auto &fqName : packageInterfaces) {
664 AST *ast = coordinator->parse(fqName);
665
Yi Kong56758da2018-07-24 16:21:37 -0700666 if (ast == nullptr) {
Steven Moreland197d56c2016-09-09 10:03:58 -0700667 fprintf(stderr,
668 "ERROR: Could not parse %s. Aborting.\n",
669 fqName.string().c_str());
670
671 return UNKNOWN_ERROR;
672 }
673
674 ast->getImportedPackages(&importedPackages);
675 }
676
Steven Moreland7fa84262018-09-26 17:12:14 -0700677 out << "// FIXME: your file license if you have one\n\n";
Yifan Hong958ee462016-12-06 17:09:51 -0800678 out << "cc_library_shared {\n";
Yifan Hong33223ca2016-12-13 15:07:35 -0800679 out.indent([&] {
Steven Moreland92f1f452018-01-04 17:55:27 -0800680 out << "// FIXME: this should only be -impl for a passthrough hal.\n"
681 << "// In most cases, to convert this to a binderized implementation, you should:\n"
682 << "// - change '-impl' to '-service' here and make it a cc_binary instead of a\n"
683 << "// cc_library_shared.\n"
684 << "// - add a *.rc file for this module.\n"
685 << "// - delete HIDL_FETCH_I* functions.\n"
686 << "// - call configureRpcThreadpool and registerAsService on the instance.\n"
687 << "// You may also want to append '-impl/-service' with a specific identifier like\n"
688 << "// '-vendor' or '-<hardware identifier>' etc to distinguish it.\n";
Steven Moreland89a9ebb2017-12-04 10:18:00 -0800689 out << "name: \"" << libraryName << "\",\n";
690 if (!coordinator->getOwner().empty()) {
691 out << "owner: \"" << coordinator->getOwner() << "\",\n";
692 }
Steven Moreland92f1f452018-01-04 17:55:27 -0800693 out << "relative_install_path: \"hw\",\n";
694 if (coordinator->getOwner().empty()) {
695 out << "// FIXME: this should be 'vendor: true' for modules that will eventually be\n"
696 "// on AOSP.\n";
697 }
698 out << "proprietary: true,\n";
699 out << "srcs: [\n";
Yifan Hong33223ca2016-12-13 15:07:35 -0800700 out.indent([&] {
Yifan Hong958ee462016-12-06 17:09:51 -0800701 for (const auto &fqName : packageInterfaces) {
702 if (fqName.name() == "types") {
703 continue;
704 }
705 out << "\"" << fqName.getInterfaceBaseName() << ".cpp\",\n";
706 }
707 });
708 out << "],\n"
709 << "shared_libs: [\n";
Yifan Hong33223ca2016-12-13 15:07:35 -0800710 out.indent([&] {
Yifan Hong958ee462016-12-06 17:09:51 -0800711 out << "\"libhidlbase\",\n"
712 << "\"libhidltransport\",\n"
Yifan Hong958ee462016-12-06 17:09:51 -0800713 << "\"libutils\",\n"
714 << "\"" << makeLibraryName(packageFQName) << "\",\n";
Yifan Honge0f7d692016-10-20 17:51:33 -0700715
Yifan Hong958ee462016-12-06 17:09:51 -0800716 for (const auto &importedPackage : importedPackages) {
Steven Morelanddadd1842017-05-09 13:24:54 -0700717 if (isHidlTransportPackage(importedPackage)) {
718 continue;
719 }
720
Yifan Hong958ee462016-12-06 17:09:51 -0800721 out << "\"" << makeLibraryName(importedPackage) << "\",\n";
722 }
723 });
724 out << "],\n";
725 });
726 out << "}\n";
Steven Moreland197d56c2016-09-09 10:03:58 -0700727
728 return OK;
729}
730
Steven Moreland6ec9eb92018-02-16 14:21:49 -0800731bool validateForSource(const FQName& fqName, const Coordinator* coordinator,
732 const std::string& language) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700733 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700734 fprintf(stderr, "ERROR: Expecting package name\n");
Steven Moreland05951b32017-05-12 17:22:22 -0700735 return false;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700736 }
737
738 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700739 fprintf(stderr, "ERROR: Expecting package version\n");
Steven Moreland05951b32017-05-12 17:22:22 -0700740 return false;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700741 }
742
Andreas Huber0fa9e392016-08-31 09:05:44 -0700743 const std::string &name = fqName.name();
744 if (!name.empty()) {
745 if (name.find('.') == std::string::npos) {
Steven Moreland05951b32017-05-12 17:22:22 -0700746 return true;
Andreas Huber0fa9e392016-08-31 09:05:44 -0700747 }
748
749 if (language != "java" || name.find("types.") != 0) {
750 // When generating java sources for "types.hal", output can be
751 // constrained to just one of the top-level types declared
752 // by using the extended syntax
753 // android.hardware.Foo@1.0::types.TopLevelTypeName.
754 // In all other cases (different language, not 'types') the dot
755 // notation in the name is illegal in this context.
Steven Moreland05951b32017-05-12 17:22:22 -0700756 return false;
Andreas Huber0fa9e392016-08-31 09:05:44 -0700757 }
758
Steven Moreland05951b32017-05-12 17:22:22 -0700759 return true;
Andreas Huber0fa9e392016-08-31 09:05:44 -0700760 }
761
Steven Moreland6ec9eb92018-02-16 14:21:49 -0800762 if (language == "java") {
763 bool isJavaCompatible;
764 status_t err = isPackageJavaCompatible(fqName, coordinator, &isJavaCompatible);
765 if (err != OK) return false;
766
767 if (!isJavaCompatible) {
768 fprintf(stderr,
769 "ERROR: %s is not Java compatible. The Java backend"
770 " does NOT support union types nor native handles. "
771 "In addition, vectors of arrays are limited to at most "
772 "one-dimensional arrays and vectors of {vectors,interfaces} are"
773 " not supported.\n",
774 fqName.string().c_str());
775 return false;
776 }
777 }
778
Steven Moreland05951b32017-05-12 17:22:22 -0700779 return true;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700780}
781
Steven Moreland5abcf012018-02-08 18:50:18 -0800782FileGenerator::GenerationFunction generateExportHeaderForPackage(bool forJava) {
783 return [forJava](Formatter& out, const FQName& packageFQName,
784 const Coordinator* coordinator) -> status_t {
Steven Moreland77730892018-03-06 14:39:41 -0800785 CHECK(!packageFQName.package().empty() && !packageFQName.version().empty() &&
786 packageFQName.name().empty());
Andreas Huber019d21d2016-10-03 12:59:47 -0700787
Steven Morelandf47912d2017-05-12 16:25:44 -0700788 std::vector<FQName> packageInterfaces;
Andreas Huber019d21d2016-10-03 12:59:47 -0700789
Steven Morelandf47912d2017-05-12 16:25:44 -0700790 status_t err = coordinator->appendPackageInterfacesToVector(
791 packageFQName, &packageInterfaces);
Andreas Huber019d21d2016-10-03 12:59:47 -0700792
Steven Morelandf47912d2017-05-12 16:25:44 -0700793 if (err != OK) {
794 return err;
Andreas Huber019d21d2016-10-03 12:59:47 -0700795 }
796
Steven Morelandf47912d2017-05-12 16:25:44 -0700797 std::vector<const Type *> exportedTypes;
Andreas Huber019d21d2016-10-03 12:59:47 -0700798
Steven Morelandf47912d2017-05-12 16:25:44 -0700799 for (const auto &fqName : packageInterfaces) {
800 AST *ast = coordinator->parse(fqName);
801
Yi Kong56758da2018-07-24 16:21:37 -0700802 if (ast == nullptr) {
Steven Morelandf47912d2017-05-12 16:25:44 -0700803 fprintf(stderr,
804 "ERROR: Could not parse %s. Aborting.\n",
805 fqName.string().c_str());
806
807 return UNKNOWN_ERROR;
808 }
809
810 ast->appendToExportedTypesVector(&exportedTypes);
811 }
812
813 if (exportedTypes.empty()) {
814 return OK;
815 }
816
Steven Morelanda885d252017-09-25 18:44:43 -0700817 if (!out.isValid()) {
818 return UNKNOWN_ERROR;
Steven Morelandf47912d2017-05-12 16:25:44 -0700819 }
820
Steven Moreland255c9a12018-02-26 13:10:27 -0800821 std::string packagePath;
822 err = coordinator->getPackagePath(packageFQName, false /* relative */,
823 false /* sanitized */, &packagePath);
824 if (err != OK) return err;
825
Steven Morelandf47912d2017-05-12 16:25:44 -0700826 out << "// This file is autogenerated by hidl-gen. Do not edit manually.\n"
827 << "// Source: " << packageFQName.string() << "\n"
Steven Moreland255c9a12018-02-26 13:10:27 -0800828 << "// Location: " << packagePath << "\n\n";
Steven Morelandf47912d2017-05-12 16:25:44 -0700829
830 std::string guard;
831 if (forJava) {
832 out << "package " << packageFQName.javaPackage() << ";\n\n";
833 out << "public class Constants {\n";
834 out.indent();
835 } else {
836 guard = "HIDL_GENERATED_";
837 guard += StringHelper::Uppercase(packageFQName.tokenName());
838 guard += "_";
839 guard += "EXPORTED_CONSTANTS_H_";
840
841 out << "#ifndef "
842 << guard
843 << "\n#define "
844 << guard
845 << "\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n";
846 }
847
848 for (const auto &type : exportedTypes) {
849 type->emitExportedHeader(out, forJava);
850 }
851
852 if (forJava) {
853 out.unindent();
854 out << "}\n";
855 } else {
856 out << "#ifdef __cplusplus\n}\n#endif\n\n#endif // "
857 << guard
858 << "\n";
859 }
860
Andreas Huber019d21d2016-10-03 12:59:47 -0700861 return OK;
Steven Morelandf47912d2017-05-12 16:25:44 -0700862 };
Andreas Huber019d21d2016-10-03 12:59:47 -0700863}
864
Steven Moreland5abcf012018-02-08 18:50:18 -0800865static status_t generateHashOutput(Formatter& out, const FQName& fqName,
866 const Coordinator* coordinator) {
867 CHECK(fqName.isFullyQualified());
Steven Morelandf2e44692017-04-18 20:19:09 -0700868
Steven Moreland5abcf012018-02-08 18:50:18 -0800869 AST* ast = coordinator->parse(fqName, {} /* parsed */,
870 Coordinator::Enforce::NO_HASH /* enforcement */);
871
Yi Kong56758da2018-07-24 16:21:37 -0700872 if (ast == nullptr) {
Steven Moreland5abcf012018-02-08 18:50:18 -0800873 fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
874
875 return UNKNOWN_ERROR;
Steven Morelandf2e44692017-04-18 20:19:09 -0700876 }
877
Steven Moreland5abcf012018-02-08 18:50:18 -0800878 out << Hash::getHash(ast->getFilename()).hexString() << " " << fqName.string() << "\n";
Steven Morelandf2e44692017-04-18 20:19:09 -0700879
880 return OK;
881}
882
Steven Moreland5abcf012018-02-08 18:50:18 -0800883template <typename T>
884std::vector<T> operator+(const std::vector<T>& lhs, const std::vector<T>& rhs) {
885 std::vector<T> ret;
886 ret.reserve(lhs.size() + rhs.size());
887 ret.insert(ret.begin(), lhs.begin(), lhs.end());
888 ret.insert(ret.end(), rhs.begin(), rhs.end());
889 return ret;
890}
891
Steven Moreland0241a842017-10-06 10:26:07 -0700892// clang-format off
Steven Moreland5abcf012018-02-08 18:50:18 -0800893static const std::vector<FileGenerator> kCppHeaderFormats = {
894 {
895 FileGenerator::alwaysGenerate,
896 [](const FQName& fqName) { return fqName.name() + ".h"; },
897 astGenerationFunction(&AST::generateInterfaceHeader),
Steven Moreland3b1ce262017-04-21 14:19:59 -0700898 },
Steven Moreland5abcf012018-02-08 18:50:18 -0800899 {
900 FileGenerator::alwaysGenerate,
901 [](const FQName& fqName) {
902 return fqName.isInterfaceName() ? fqName.getInterfaceHwName() + ".h" : "hwtypes.h";
903 },
904 astGenerationFunction(&AST::generateHwBinderHeader),
905 },
906 {
907 FileGenerator::generateForInterfaces,
908 [](const FQName& fqName) { return fqName.getInterfaceStubName() + ".h"; },
909 astGenerationFunction(&AST::generateStubHeader),
910 },
911 {
912 FileGenerator::generateForInterfaces,
913 [](const FQName& fqName) { return fqName.getInterfaceProxyName() + ".h"; },
914 astGenerationFunction(&AST::generateProxyHeader),
915 },
916 {
917 FileGenerator::generateForInterfaces,
918 [](const FQName& fqName) { return fqName.getInterfacePassthroughName() + ".h"; },
919 astGenerationFunction(&AST::generatePassthroughHeader),
920 },
921};
Steven Moreland3b1ce262017-04-21 14:19:59 -0700922
Steven Moreland5abcf012018-02-08 18:50:18 -0800923static const std::vector<FileGenerator> kCppSourceFormats = {
924 {
925 FileGenerator::alwaysGenerate,
926 [](const FQName& fqName) {
927 return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + "All.cpp" : "types.cpp";
928 },
929 astGenerationFunction(&AST::generateCppSource),
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700930 },
Steven Moreland5abcf012018-02-08 18:50:18 -0800931};
Steven Moreland1cbf0362017-05-09 14:32:53 -0700932
Steven Moreland5abcf012018-02-08 18:50:18 -0800933static const std::vector<FileGenerator> kCppImplHeaderFormats = {
934 {
935 FileGenerator::generateForInterfaces,
936 [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".h"; },
937 astGenerationFunction(&AST::generateCppImplHeader),
Andreas Huber019d21d2016-10-03 12:59:47 -0700938 },
Steven Moreland5abcf012018-02-08 18:50:18 -0800939};
Andreas Huber019d21d2016-10-03 12:59:47 -0700940
Steven Moreland5abcf012018-02-08 18:50:18 -0800941static const std::vector<FileGenerator> kCppImplSourceFormats = {
942 {
943 FileGenerator::generateForInterfaces,
944 [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".cpp"; },
945 astGenerationFunction(&AST::generateCppImplSource),
Steven Moreland9c387612016-09-07 09:54:26 -0700946 },
Steven Moreland5abcf012018-02-08 18:50:18 -0800947};
Steven Moreland9c387612016-09-07 09:54:26 -0700948
Steven Moreland5abcf012018-02-08 18:50:18 -0800949static const std::vector<FileGenerator> kCppAdapterHeaderFormats = {
950 {
951 FileGenerator::alwaysGenerate,
952 [](const FQName& fqName) {
953 return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".h" : "Atypes.h";
954 },
955 astGenerationFunction(&AST::generateCppAdapterHeader),
Steven Moreland9a6da7a2017-09-15 16:21:24 -0700956 },
Steven Moreland5abcf012018-02-08 18:50:18 -0800957};
Steven Moreland9a6da7a2017-09-15 16:21:24 -0700958
Steven Moreland5abcf012018-02-08 18:50:18 -0800959static const std::vector<FileGenerator> kCppAdapterSourceFormats = {
960 {
961 FileGenerator::alwaysGenerate,
962 [](const FQName& fqName) {
963 return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".cpp" : "Atypes.cpp";
964 },
965 astGenerationFunction(&AST::generateCppAdapterSource),
Andreas Huber2831d512016-08-15 09:33:47 -0700966 },
Steven Moreland5abcf012018-02-08 18:50:18 -0800967};
Andreas Huber2831d512016-08-15 09:33:47 -0700968
Steven Moreland5abcf012018-02-08 18:50:18 -0800969static const std::vector<OutputHandler> kFormats = {
970 {
971 "check",
972 "Parses the interface to see if valid but doesn't write any files.",
973 OutputMode::NOT_NEEDED,
974 Coordinator::Location::STANDARD_OUT,
975 GenerationGranularity::PER_FILE,
976 validateForSource,
977 {
978 {
979 FileGenerator::alwaysGenerate,
980 nullptr /* filename for fqname */,
981 astGenerationFunction(),
982 },
983 },
Andreas Huber1c507272016-10-05 14:33:21 -0700984 },
Steven Moreland5abcf012018-02-08 18:50:18 -0800985 {
986 "c++",
987 "(internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.",
988 OutputMode::NEEDS_DIR,
989 Coordinator::Location::GEN_OUTPUT,
990 GenerationGranularity::PER_FILE,
991 validateForSource,
992 kCppHeaderFormats + kCppSourceFormats,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700993 },
Steven Moreland5abcf012018-02-08 18:50:18 -0800994 {
995 "c++-headers",
996 "(internal) Generates C++ headers for interface files for talking to HIDL interfaces.",
997 OutputMode::NEEDS_DIR,
998 Coordinator::Location::GEN_OUTPUT,
999 GenerationGranularity::PER_FILE,
1000 validateForSource,
1001 kCppHeaderFormats,
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001002 },
Steven Moreland5abcf012018-02-08 18:50:18 -08001003 {
1004 "c++-sources",
1005 "(internal) Generates C++ sources for interface files for talking to HIDL interfaces.",
1006 OutputMode::NEEDS_DIR,
1007 Coordinator::Location::GEN_OUTPUT,
1008 GenerationGranularity::PER_FILE,
1009 validateForSource,
1010 kCppSourceFormats,
Dan Willemsen676abdc2016-09-28 19:42:22 -07001011 },
Steven Moreland5abcf012018-02-08 18:50:18 -08001012 {
1013 "export-header",
1014 "Generates a header file from @export enumerations to help maintain legacy code.",
1015 OutputMode::NEEDS_FILE,
1016 Coordinator::Location::DIRECT,
1017 GenerationGranularity::PER_PACKAGE,
1018 validateIsPackage,
1019 {singleFileGenerator("", generateExportHeaderForPackage(false /* forJava */))}
Steven Morelandf2e44692017-04-18 20:19:09 -07001020 },
Steven Moreland5abcf012018-02-08 18:50:18 -08001021 {
1022 "c++-impl",
1023 "Generates boilerplate implementation of a hidl interface in C++ (for convenience).",
1024 OutputMode::NEEDS_DIR,
1025 Coordinator::Location::DIRECT,
1026 GenerationGranularity::PER_FILE,
1027 validateForSource,
1028 kCppImplHeaderFormats + kCppImplSourceFormats,
1029 },
1030 {
1031 "c++-impl-headers",
1032 "c++-impl but headers only",
1033 OutputMode::NEEDS_DIR,
1034 Coordinator::Location::DIRECT,
1035 GenerationGranularity::PER_FILE,
1036 validateForSource,
1037 kCppImplHeaderFormats,
1038 },
1039 {
1040 "c++-impl-sources",
1041 "c++-impl but sources only",
1042 OutputMode::NEEDS_DIR,
1043 Coordinator::Location::DIRECT,
1044 GenerationGranularity::PER_FILE,
1045 validateForSource,
1046 kCppImplSourceFormats,
1047 },
1048 {
1049 "c++-adapter",
1050 "Takes a x.(y+n) interface and mocks an x.y interface.",
1051 OutputMode::NEEDS_DIR,
1052 Coordinator::Location::GEN_OUTPUT,
1053 GenerationGranularity::PER_FILE,
1054 validateForSource,
1055 kCppAdapterHeaderFormats + kCppAdapterSourceFormats,
1056 },
1057 {
1058 "c++-adapter-headers",
1059 "c++-adapter but helper headers only",
1060 OutputMode::NEEDS_DIR,
1061 Coordinator::Location::GEN_OUTPUT,
1062 GenerationGranularity::PER_FILE,
1063 validateForSource,
1064 kCppAdapterHeaderFormats,
1065 },
1066 {
1067 "c++-adapter-sources",
1068 "c++-adapter but helper sources only",
1069 OutputMode::NEEDS_DIR,
1070 Coordinator::Location::GEN_OUTPUT,
1071 GenerationGranularity::PER_FILE,
1072 validateForSource,
1073 kCppAdapterSourceFormats,
1074 },
1075 {
1076 "c++-adapter-main",
1077 "c++-adapter but the adapter binary source only",
1078 OutputMode::NEEDS_DIR,
1079 Coordinator::Location::DIRECT,
1080 GenerationGranularity::PER_PACKAGE,
1081 validateIsPackage,
1082 {singleFileGenerator("main.cpp", generateAdapterMainSource)},
1083 },
1084 {
1085 "java",
1086 "(internal) Generates Java library for talking to HIDL interfaces in Java.",
1087 OutputMode::NEEDS_DIR,
1088 Coordinator::Location::GEN_SANITIZED,
1089 GenerationGranularity::PER_TYPE,
1090 validateForSource,
1091 {
1092 {
1093 FileGenerator::alwaysGenerate,
1094 [](const FQName& fqName) {
1095 return StringHelper::LTrim(fqName.name(), "types.") + ".java";
1096 },
1097 generateJavaForPackage,
1098 },
1099 }
1100 },
1101 {
1102 "java-constants",
1103 "(internal) Like export-header but for Java (always created by -Lmakefile if @export exists).",
1104 OutputMode::NEEDS_DIR,
1105 Coordinator::Location::GEN_SANITIZED,
1106 GenerationGranularity::PER_PACKAGE,
1107 validateIsPackage,
1108 {singleFileGenerator("Constants.java", generateExportHeaderForPackage(true /* forJava */))}
1109 },
1110 {
1111 "vts",
1112 "(internal) Generates vts proto files for use in vtsd.",
1113 OutputMode::NEEDS_DIR,
1114 Coordinator::Location::GEN_OUTPUT,
1115 GenerationGranularity::PER_FILE,
1116 validateForSource,
1117 {
1118 {
1119 FileGenerator::alwaysGenerate,
1120 [](const FQName& fqName) {
1121 return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + ".vts" : "types.vts";
1122 },
1123 astGenerationFunction(&AST::generateVts),
1124 },
1125 }
1126 },
1127 {
1128 "makefile",
1129 "(removed) Used to generate makefiles for -Ljava and -Ljava-constants.",
1130 OutputMode::NEEDS_SRC,
1131 Coordinator::Location::PACKAGE_ROOT,
1132 GenerationGranularity::PER_PACKAGE,
Steven Moreland6ec9eb92018-02-16 14:21:49 -08001133 [](const FQName &, const Coordinator*, const std::string &) {
Steven Moreland5abcf012018-02-08 18:50:18 -08001134 fprintf(stderr, "ERROR: makefile output is not supported. Use -Landroidbp for all build file generation.\n");
1135 return false;
1136 },
1137 {},
1138 },
1139 {
1140 "androidbp",
1141 "(internal) Generates Soong bp files for -Lc++-headers, -Lc++-sources, -Ljava, -Ljava-constants, and -Lc++-adapter.",
1142 OutputMode::NEEDS_SRC,
1143 Coordinator::Location::PACKAGE_ROOT,
1144 GenerationGranularity::PER_PACKAGE,
1145 validateIsPackage,
1146 {singleFileGenerator("Android.bp", generateAndroidBpForPackage)},
1147 },
1148 {
1149 "androidbp-impl",
1150 "Generates boilerplate bp files for implementation created with -Lc++-impl.",
1151 OutputMode::NEEDS_DIR,
1152 Coordinator::Location::DIRECT,
1153 GenerationGranularity::PER_PACKAGE,
1154 validateIsPackage,
1155 {singleFileGenerator("Android.bp", generateAndroidBpImplForPackage)},
1156 },
1157 {
1158 "hash",
1159 "Prints hashes of interface in `current.txt` format to standard out.",
1160 OutputMode::NOT_NEEDED,
1161 Coordinator::Location::STANDARD_OUT,
1162 GenerationGranularity::PER_FILE,
1163 validateForSource,
1164 {
1165 {
1166 FileGenerator::alwaysGenerate,
1167 nullptr /* file name for fqName */,
1168 generateHashOutput,
1169 },
1170 }
Steven Morelandf2e44692017-04-18 20:19:09 -07001171 },
Yifan Honge4010112018-10-05 11:44:15 -07001172 {
1173 "dependencies",
1174 "Prints all depended types.",
1175 OutputMode::NOT_NEEDED,
1176 Coordinator::Location::STANDARD_OUT,
1177 GenerationGranularity::PER_FILE,
1178 validateForSource,
1179 {
1180 {
1181 FileGenerator::alwaysGenerate,
1182 nullptr /* file name for fqName */,
1183 astGenerationFunction(&AST::generateDependencies),
1184 },
1185 },
1186 },
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001187};
Steven Moreland0241a842017-10-06 10:26:07 -07001188// clang-format on
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001189
1190static void usage(const char *me) {
1191 fprintf(stderr,
Steven Moreland89a9ebb2017-12-04 10:18:00 -08001192 "usage: %s [-p <root path>] -o <output path> -L <language> [-O <owner>] (-r <interface "
Yifan Hong5418c112018-10-09 17:30:22 -07001193 "root>)+ [-R] [-v] [-d <depfile>] FQNAME...\n\n",
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001194 me);
1195
Steven Morelanda12df112018-02-15 14:32:46 -08001196 fprintf(stderr,
1197 "Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, to create output.\n\n");
1198
Steven Moreland97e52332017-05-12 16:47:19 -07001199 fprintf(stderr, " -h: Prints this menu.\n");
1200 fprintf(stderr, " -L <language>: The following options are available:\n");
Steven Moreland5abcf012018-02-08 18:50:18 -08001201 for (auto& e : kFormats) {
Steven Moreland97e52332017-05-12 16:47:19 -07001202 fprintf(stderr, " %-16s: %s\n", e.name().c_str(), e.description().c_str());
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001203 }
Steven Moreland89a9ebb2017-12-04 10:18:00 -08001204 fprintf(stderr, " -O <owner>: The owner of the module for -Landroidbp(-impl)?.\n");
Steven Moreland97e52332017-05-12 16:47:19 -07001205 fprintf(stderr, " -o <output path>: Location to output files.\n");
1206 fprintf(stderr, " -p <root path>: Android build root, defaults to $ANDROID_BUILD_TOP or pwd.\n");
Yifan Hong5418c112018-10-09 17:30:22 -07001207 fprintf(stderr, " -R: Do not add default package roots if not specified in -r\n");
Steven Moreland97e52332017-05-12 16:47:19 -07001208 fprintf(stderr, " -r <package:path root>: E.g., android.hardware:hardware/interfaces.\n");
Steven Morelanda838ceb2018-02-06 13:19:23 -08001209 fprintf(stderr, " -v: verbose output.\n");
Steven Moreland394af5c2018-02-09 14:41:46 -08001210 fprintf(stderr, " -d <depfile>: location of depfile to write to.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001211}
1212
Andreas Gampec4ce9262017-04-27 21:03:20 -07001213// hidl is intentionally leaky. Turn off LeakSanitizer by default.
1214extern "C" const char *__asan_default_options() {
Evgenii Stepanova3ee97c2017-08-16 14:59:29 -07001215 return "detect_leaks=0";
Andreas Gampec4ce9262017-04-27 21:03:20 -07001216}
1217
Andreas Huberb82318c2016-08-02 14:45:54 -07001218int main(int argc, char **argv) {
Andreas Huber737080b2016-08-02 15:38:04 -07001219 const char *me = argv[0];
Steven Moreland3db99f22017-05-11 16:21:46 -07001220 if (argc == 1) {
1221 usage(me);
1222 exit(1);
1223 }
1224
Steven Moreland5abcf012018-02-08 18:50:18 -08001225 const OutputHandler* outputFormat = nullptr;
Steven Moreland47792c42017-09-20 10:03:20 -07001226 Coordinator coordinator;
1227 std::string outputPath;
Yifan Hong5418c112018-10-09 17:30:22 -07001228 bool suppressDefaultPackagePaths = false;
Steven Moreland47792c42017-09-20 10:03:20 -07001229
Andreas Huberb82318c2016-08-02 14:45:54 -07001230 int res;
Yifan Hong5418c112018-10-09 17:30:22 -07001231 while ((res = getopt(argc, argv, "hp:o:O:r:L:vd:R")) >= 0) {
Andreas Huberb82318c2016-08-02 14:45:54 -07001232 switch (res) {
Steven Moreland394af5c2018-02-09 14:41:46 -08001233 case 'p': {
Steven Moreland0d93b142018-01-04 17:26:30 -08001234 if (!coordinator.getRootPath().empty()) {
1235 fprintf(stderr, "ERROR: -p <root path> can only be specified once.\n");
1236 exit(1);
1237 }
Steven Moreland47792c42017-09-20 10:03:20 -07001238 coordinator.setRootPath(optarg);
Steven Morelandf7fa0682017-05-11 16:14:55 -07001239 break;
1240 }
1241
Steven Moreland394af5c2018-02-09 14:41:46 -08001242 case 'v': {
Steven Moreland37c57ee2017-09-25 19:08:56 -07001243 coordinator.setVerbose(true);
1244 break;
1245 }
1246
Steven Moreland394af5c2018-02-09 14:41:46 -08001247 case 'd': {
1248 coordinator.setDepFile(optarg);
1249 break;
1250 }
1251
1252 case 'o': {
Steven Moreland0d93b142018-01-04 17:26:30 -08001253 if (!outputPath.empty()) {
1254 fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
1255 exit(1);
1256 }
Andreas Huber019d21d2016-10-03 12:59:47 -07001257 outputPath = optarg;
Andreas Huberb82318c2016-08-02 14:45:54 -07001258 break;
1259 }
1260
Steven Moreland89a9ebb2017-12-04 10:18:00 -08001261 case 'O': {
1262 if (!coordinator.getOwner().empty()) {
1263 fprintf(stderr, "ERROR: -O <owner> can only be specified once.\n");
1264 exit(1);
1265 }
1266 coordinator.setOwner(optarg);
1267 break;
1268 }
1269
Steven Moreland394af5c2018-02-09 14:41:46 -08001270 case 'r': {
Andreas Huberdca261f2016-08-04 13:47:51 -07001271 std::string val(optarg);
1272 auto index = val.find_first_of(':');
Steven Moreland4ff74202017-04-21 14:24:47 -07001273 if (index == std::string::npos) {
1274 fprintf(stderr, "ERROR: -r option must contain ':': %s\n", val.c_str());
1275 exit(1);
1276 }
Andreas Huberdca261f2016-08-04 13:47:51 -07001277
Steven Moreland13594d12017-09-28 23:27:50 +00001278 auto root = val.substr(0, index);
1279 auto path = val.substr(index + 1);
Steven Moreland47792c42017-09-20 10:03:20 -07001280
1281 std::string error;
1282 status_t err = coordinator.addPackagePath(root, path, &error);
1283 if (err != OK) {
1284 fprintf(stderr, "%s\n", error.c_str());
1285 exit(1);
1286 }
1287
Andreas Huberdca261f2016-08-04 13:47:51 -07001288 break;
1289 }
1290
Yifan Hong5418c112018-10-09 17:30:22 -07001291 case 'R': {
1292 suppressDefaultPackagePaths = true;
1293 break;
1294 }
1295
Steven Moreland394af5c2018-02-09 14:41:46 -08001296 case 'L': {
Steven Morelanddd583842017-04-19 13:09:57 -07001297 if (outputFormat != nullptr) {
1298 fprintf(stderr,
1299 "ERROR: only one -L option allowed. \"%s\" already specified.\n",
1300 outputFormat->name().c_str());
1301 exit(1);
1302 }
Steven Moreland5abcf012018-02-08 18:50:18 -08001303 for (auto& e : kFormats) {
Steven Morelanddd583842017-04-19 13:09:57 -07001304 if (e.name() == optarg) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001305 outputFormat = &e;
1306 break;
1307 }
1308 }
Steven Morelanddd583842017-04-19 13:09:57 -07001309 if (outputFormat == nullptr) {
1310 fprintf(stderr,
1311 "ERROR: unrecognized -L option: \"%s\".\n",
1312 optarg);
1313 exit(1);
1314 }
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001315 break;
1316 }
1317
Andreas Huberb82318c2016-08-02 14:45:54 -07001318 case '?':
1319 case 'h':
Steven Moreland394af5c2018-02-09 14:41:46 -08001320 default: {
Andreas Huber737080b2016-08-02 15:38:04 -07001321 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -07001322 exit(1);
1323 break;
1324 }
1325 }
1326 }
1327
Steven Moreland0d93b142018-01-04 17:26:30 -08001328 if (coordinator.getRootPath().empty()) {
1329 const char* ANDROID_BUILD_TOP = getenv("ANDROID_BUILD_TOP");
1330 if (ANDROID_BUILD_TOP != nullptr) {
1331 coordinator.setRootPath(ANDROID_BUILD_TOP);
1332 }
1333 }
1334
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001335 if (outputFormat == nullptr) {
Steven Morelanddd583842017-04-19 13:09:57 -07001336 fprintf(stderr,
1337 "ERROR: no -L option provided.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001338 exit(1);
1339 }
1340
Andreas Huberb82318c2016-08-02 14:45:54 -07001341 argc -= optind;
1342 argv += optind;
1343
Steven Moreland3effa832017-06-16 16:17:41 -07001344 if (argc == 0) {
1345 fprintf(stderr, "ERROR: no fqname specified.\n");
1346 usage(me);
1347 exit(1);
1348 }
1349
Andreas Huber737080b2016-08-02 15:38:04 -07001350 // Valid options are now in argv[0] .. argv[argc - 1].
1351
Andreas Huber019d21d2016-10-03 12:59:47 -07001352 switch (outputFormat->mOutputMode) {
Steven Moreland5abcf012018-02-08 18:50:18 -08001353 case OutputMode::NEEDS_DIR:
1354 case OutputMode::NEEDS_FILE: {
Andreas Huber019d21d2016-10-03 12:59:47 -07001355 if (outputPath.empty()) {
1356 usage(me);
1357 exit(1);
1358 }
1359
Steven Moreland5abcf012018-02-08 18:50:18 -08001360 if (outputFormat->mOutputMode == OutputMode::NEEDS_DIR) {
Howard Chen30f6b492017-08-24 16:50:12 +08001361 if (outputPath.back() != '/') {
Andreas Huber019d21d2016-10-03 12:59:47 -07001362 outputPath += "/";
1363 }
1364 }
1365 break;
Andreas Huberb82318c2016-08-02 14:45:54 -07001366 }
Steven Moreland5abcf012018-02-08 18:50:18 -08001367 case OutputMode::NEEDS_SRC: {
Steven Morelanda171b562017-05-12 15:18:03 -07001368 if (outputPath.empty()) {
Steven Moreland47792c42017-09-20 10:03:20 -07001369 outputPath = coordinator.getRootPath();
Steven Morelanda171b562017-05-12 15:18:03 -07001370 }
Howard Chen30f6b492017-08-24 16:50:12 +08001371 if (outputPath.back() != '/') {
1372 outputPath += "/";
1373 }
Steven Morelanda171b562017-05-12 15:18:03 -07001374
1375 break;
1376 }
Andreas Huber019d21d2016-10-03 12:59:47 -07001377
1378 default:
1379 outputPath.clear(); // Unused.
1380 break;
Andreas Huberb82318c2016-08-02 14:45:54 -07001381 }
1382
Steven Moreland6d3d3c82018-02-08 11:49:34 -08001383 coordinator.setOutputPath(outputPath);
1384
Yifan Hong5418c112018-10-09 17:30:22 -07001385 if (!suppressDefaultPackagePaths) {
1386 coordinator.addDefaultPackagePath("android.hardware", "hardware/interfaces");
1387 coordinator.addDefaultPackagePath("android.hidl", "system/libhidl/transport");
1388 coordinator.addDefaultPackagePath("android.frameworks", "frameworks/hardware/interfaces");
1389 coordinator.addDefaultPackagePath("android.system", "system/hardware/interfaces");
1390 }
Andreas Huber5345ec22016-07-29 13:33:27 -07001391
Andreas Huber737080b2016-08-02 15:38:04 -07001392 for (int i = 0; i < argc; ++i) {
Steven Morelande1b157e2018-03-06 14:18:32 -08001393 FQName fqName;
1394 if (!FQName::parse(argv[i], &fqName)) {
Steven Morelanda12df112018-02-15 14:32:46 -08001395 fprintf(stderr, "ERROR: Invalid fully-qualified name as argument: %s.\n", argv[i]);
Iliyan Malchev5bb14022016-08-09 15:04:39 -07001396 exit(1);
1397 }
Andreas Huber881227d2016-08-02 14:20:21 -07001398
Steven Morelandb65e5d92018-02-08 12:44:51 -08001399 // Dump extra verbose output
1400 if (coordinator.isVerbose()) {
1401 status_t err =
1402 dumpDefinedButUnreferencedTypeNames(fqName.getPackageAndVersion(), &coordinator);
1403 if (err != OK) return err;
1404 }
1405
Steven Moreland6ec9eb92018-02-16 14:21:49 -08001406 if (!outputFormat->validate(fqName, &coordinator, outputFormat->name())) {
Steven Morelandf2e44692017-04-18 20:19:09 -07001407 fprintf(stderr,
1408 "ERROR: output handler failed.\n");
Andreas Hubere61e3f72016-08-03 10:22:03 -07001409 exit(1);
1410 }
Andreas Huberd2943e12016-08-05 11:59:31 -07001411
Steven Moreland1125cf32018-02-08 13:15:27 -08001412 status_t err = outputFormat->generate(fqName, &coordinator);
Steven Moreland394af5c2018-02-09 14:41:46 -08001413 if (err != OK) exit(1);
Andreas Huberd2943e12016-08-05 11:59:31 -07001414
Steven Moreland394af5c2018-02-09 14:41:46 -08001415 err = outputFormat->writeDepFile(fqName, &coordinator);
1416 if (err != OK) exit(1);
Andreas Hubereb1081f2016-07-28 13:13:24 -07001417 }
Andreas Huberc9410c72016-07-28 12:18:40 -07001418
Andreas Huberd2943e12016-08-05 11:59:31 -07001419 return 0;
Andreas Huberc9410c72016-07-28 12:18:40 -07001420}