blob: 59a842e58bad272bf25f55cba1919ce1ac8a5f6b [file] [log] [blame]
Andreas Huberc9410c72016-07-28 12:18:40 -07001#include "AST.h"
Andreas Huber5345ec22016-07-29 13:33:27 -07002#include "Coordinator.h"
Andreas Huberd2943e12016-08-05 11:59:31 -07003#include "Formatter.h"
Andreas Huber84f89de2016-07-28 15:39:51 -07004#include "FQName.h"
Andreas Huberc9410c72016-07-28 12:18:40 -07005
Andreas Huber68f24592016-07-29 14:53:48 -07006#include <android-base/logging.h>
Iliyan Malchev5bb14022016-08-09 15:04:39 -07007#include <set>
Andreas Huberc9410c72016-07-28 12:18:40 -07008#include <stdio.h>
Andreas Huberdca261f2016-08-04 13:47:51 -07009#include <string>
Andreas Huberb82318c2016-08-02 14:45:54 -070010#include <unistd.h>
Andreas Huberdca261f2016-08-04 13:47:51 -070011#include <vector>
Andreas Huberc9410c72016-07-28 12:18:40 -070012
13using namespace android;
14
Iliyan Malchev5bb14022016-08-09 15:04:39 -070015struct OutputHandler {
16 std::string mKey;
17 bool mNeedsOutputDir;
18 enum ValRes {
19 FAILED,
20 PASS_PACKAGE,
21 PASS_FULL
22 };
23 ValRes (*validate)(const FQName &);
24 status_t (*generate)(const FQName &fqName,
25 const char *hidl_gen,
26 Coordinator *coordinator,
27 const std::string &outputDir);
28};
Andreas Huberdca261f2016-08-04 13:47:51 -070029
Iliyan Malchev5bb14022016-08-09 15:04:39 -070030static status_t generateSourcesForFile(
31 const FQName &fqName,
32 const char *,
33 Coordinator *coordinator,
Andreas Huber2831d512016-08-15 09:33:47 -070034 const std::string &outputDir,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070035 const std::string &lang) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -070036 CHECK(fqName.isFullyQualified());
37
38 AST *ast = coordinator->parse(fqName);
39
40 if (ast == NULL) {
41 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -070042 "ERROR: Could not parse %s. Aborting.\n",
Iliyan Malchev5bb14022016-08-09 15:04:39 -070043 fqName.string().c_str());
44
45 return UNKNOWN_ERROR;
46 }
47
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070048 if (lang == "c++") {
49 return ast->generateCpp(outputDir);
50 }
51 if (lang == "java") {
52 return ast->generateJava(outputDir);
53 }
54 if (lang == "vts") {
55 return ast->generateVts(outputDir);
56 }
57 // Unknown language.
58 return UNKNOWN_ERROR;
Iliyan Malchev5bb14022016-08-09 15:04:39 -070059}
60
61static status_t generateSourcesForPackage(
62 const FQName &packageFQName,
63 const char *hidl_gen,
64 Coordinator *coordinator,
Andreas Huber2831d512016-08-15 09:33:47 -070065 const std::string &outputDir,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070066 const std::string &lang) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -070067 CHECK(packageFQName.isValid() &&
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070068 !packageFQName.isFullyQualified() &&
69 packageFQName.name().empty());
Iliyan Malchev5bb14022016-08-09 15:04:39 -070070
71 std::vector<FQName> packageInterfaces;
72
73 status_t err =
74 coordinator->appendPackageInterfacesToSet(packageFQName,
75 &packageInterfaces);
76
77 if (err != OK) {
78 return err;
79 }
80
81 for (const auto &fqName : packageInterfaces) {
Andreas Huber2831d512016-08-15 09:33:47 -070082 err = generateSourcesForFile(
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070083 fqName, hidl_gen, coordinator, outputDir, lang);
Iliyan Malchev5bb14022016-08-09 15:04:39 -070084 if (err != OK) {
85 return err;
86 }
87 }
88
89 return OK;
Andreas Huberb82318c2016-08-02 14:45:54 -070090}
91
Andreas Huberd2943e12016-08-05 11:59:31 -070092static std::string makeLibraryName(const FQName &packageFQName) {
Iliyan Malcheve2c595a2016-08-07 21:20:04 -070093 return packageFQName.string();
Andreas Huberd2943e12016-08-05 11:59:31 -070094}
95
Iliyan Malchev5bb14022016-08-09 15:04:39 -070096static status_t generateMakefileForPackage(
97 const FQName &packageFQName,
Iliyan Malchevb66c3992016-08-07 21:18:13 -070098 const char *hidl_gen,
Andreas Huberd2943e12016-08-05 11:59:31 -070099 Coordinator *coordinator,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700100 const std::string &) {
101
102 CHECK(packageFQName.isValid() &&
103 !packageFQName.isFullyQualified() &&
104 packageFQName.name().empty());
105
Andreas Huberd2943e12016-08-05 11:59:31 -0700106 std::vector<FQName> packageInterfaces;
107
108 status_t err =
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700109 coordinator->appendPackageInterfacesToSet(packageFQName,
110 &packageInterfaces);
Andreas Huberd2943e12016-08-05 11:59:31 -0700111
112 if (err != OK) {
113 return err;
114 }
115
116 std::set<FQName> importedPackages;
117 for (const auto &fqName : packageInterfaces) {
118 AST *ast = coordinator->parse(fqName);
119
120 if (ast == NULL) {
121 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700122 "ERROR: Could not parse %s. Aborting.\n",
Andreas Huberd2943e12016-08-05 11:59:31 -0700123 fqName.string().c_str());
124
125 return UNKNOWN_ERROR;
126 }
127
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700128 ast->getImportedPackages(&importedPackages);
Andreas Huberd2943e12016-08-05 11:59:31 -0700129 }
130
131 std::string path =
132 coordinator->getPackagePath(packageFQName, false /* relative */);
133
134 path.append("Android.mk");
135
136 CHECK(Coordinator::MakeParentHierarchy(path));
137 FILE *file = fopen(path.c_str(), "w");
138
139 if (file == NULL) {
140 return -errno;
141 }
142
143 const std::string libraryName = makeLibraryName(packageFQName);
144
145 Formatter out(file);
146
147 out << "LOCAL_PATH := $(call my-dir)\n"
148 << "include $(CLEAR_VARS)\n\n"
149 << "LOCAL_MODULE := "
150 << libraryName
151 << "\n"
152 << "LOCAL_MODULE_CLASS := SHARED_LIBRARIES\n\n"
153 << "intermediates := $(local-generated-sources-dir)\n\n"
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700154 << "HIDL := $(HOST_OUT_EXECUTABLES)/"
155 << hidl_gen << "$(HOST_EXECUTABLE_SUFFIX)";
Andreas Huberd2943e12016-08-05 11:59:31 -0700156
Andreas Huberd2943e12016-08-05 11:59:31 -0700157 for (const auto &fqName : packageInterfaces) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700158
159 out << "\n"
160 << "\n#"
161 << "\n# Build " << fqName.name() << ".hal"
162 << "\n#";
163 out << "\nGEN := $(intermediates)/"
Andreas Huberd2943e12016-08-05 11:59:31 -0700164 << coordinator->convertPackageRootToPath(packageFQName)
165 << coordinator->getPackagePath(packageFQName, true /* relative */);
Andreas Huberd2943e12016-08-05 11:59:31 -0700166 if (fqName.name() == "types") {
167 out << "types.cpp";
168 } else {
169 out << fqName.name().substr(1) << "All.cpp";
170 }
171
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700172 out << "\n$(GEN): $(HIDL)";
173 out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
174 out << "\n$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/"
175 << fqName.name() << ".hal";
176 out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
177 << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
178 out.indent();
179 out.indent();
180 out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
181 << "\n-Lc++ -r"
182 << coordinator->getPackageRoot(packageFQName) << ":"
183 << coordinator->getPackageRootPath(packageFQName) << "\\";
184 out << "\n"
185 << packageFQName.string()
186 << "::$(patsubst %.hal,%,$(notdir $(PRIVATE_DEPS)))"
187 << "\n";
188 out.unindent();
189 out.unindent();
190
191 out << "\n$(GEN): $(LOCAL_PATH)/" << fqName.name() << ".hal";
192 out << "\n\t$(transform-generated-source)";
193 out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
Andreas Huberd2943e12016-08-05 11:59:31 -0700194 }
195
Andreas Huberd2943e12016-08-05 11:59:31 -0700196 out << "\n"
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700197 << "\nLOCAL_EXPORT_C_INCLUDE_DIRS := $(intermediates)"
198 << "\nLOCAL_SHARED_LIBRARIES := \\";
199 out.indent();
Martijn Coenen7473fab2016-08-19 14:05:40 +0200200 out << "\nlibhidl \\"
201 << "\nlibhwbinder \\"
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700202 << "\nlibutils \\";
203
204 for (const auto &importedPackage : importedPackages) {
205 out << "\n" << makeLibraryName(importedPackage) << " \\";
206 }
207 out << "\n";
208 out.unindent();
209
Keun Soo Yim21496da2016-08-24 07:24:03 -0700210 if (path.find("hardware/interfaces/tests/") != std::string::npos) {
211 out << "\nLOCAL_COMPATIBILITY_SUITE := vts"
212 << "\n-include test/vts/tools/build/Android.packaging_sharedlib.mk";
213 // TODO(yim): b/30589200 remove the above -include line after the
214 // build rule change is merged.
215 }
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700216 out << "\ninclude $(BUILD_SHARED_LIBRARY)\n";
Andreas Huberd2943e12016-08-05 11:59:31 -0700217
218 return OK;
219}
220
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700221OutputHandler::ValRes validateForMakefile(const FQName &fqName) {
222 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700223 fprintf(stderr, "ERROR: Expecting package name\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700224 return OutputHandler::FAILED;
225 }
226
227 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700228 fprintf(stderr, "ERROR: Expecting package version\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700229 return OutputHandler::FAILED;
230 }
231
232 if (!fqName.name().empty()) {
233 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700234 "ERROR: Expecting only package name and version.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700235 return OutputHandler::FAILED;
236 }
237
238 return OutputHandler::PASS_PACKAGE;
239}
240
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700241OutputHandler::ValRes validateForSource(const FQName &fqName) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700242 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700243 fprintf(stderr, "ERROR: Expecting package name\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700244 return OutputHandler::FAILED;
245 }
246
247 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700248 fprintf(stderr, "ERROR: Expecting package version\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700249 return OutputHandler::FAILED;
250 }
251
252 return fqName.name().empty() ?
253 OutputHandler::PASS_PACKAGE :
254 OutputHandler::PASS_FULL;
255}
256
257static std::vector<OutputHandler> formats = {
258 {"c++",
Andreas Huber2831d512016-08-15 09:33:47 -0700259 true /* mNeedsOutputDir */,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700260 validateForSource,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700261 [](const FQName &fqName,
262 const char *hidl_gen, Coordinator *coordinator,
263 const std::string &outputDir) -> status_t {
264 if (fqName.isFullyQualified()) {
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700265 return generateSourcesForFile(fqName,
266 hidl_gen,
267 coordinator,
268 outputDir, "c++");
269 } else {
270 return generateSourcesForPackage(fqName,
271 hidl_gen,
272 coordinator,
273 outputDir, "c++");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700274 }
275 }
276 },
277
Andreas Huber2831d512016-08-15 09:33:47 -0700278 {"java",
279 true /* mNeedsOutputDir */,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700280 validateForSource,
Andreas Huber2831d512016-08-15 09:33:47 -0700281 [](const FQName &fqName,
282 const char *hidl_gen, Coordinator *coordinator,
283 const std::string &outputDir) -> status_t {
284 if (fqName.isFullyQualified()) {
285 return generateSourcesForFile(fqName,
286 hidl_gen,
287 coordinator,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700288 outputDir, "java");
Andreas Huber2831d512016-08-15 09:33:47 -0700289 }
290 else {
291 return generateSourcesForPackage(fqName,
292 hidl_gen,
293 coordinator,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700294 outputDir, "java");
Andreas Huber2831d512016-08-15 09:33:47 -0700295 }
296 }
297 },
298
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700299 {"vts",
300 true,
301 validateForSource,
302 [](const FQName &fqName,
303 const char * hidl_gen,
304 Coordinator *coordinator,
305 const std::string &outputDir) -> status_t {
306 if (fqName.isFullyQualified()) {
307 return generateSourcesForFile(fqName,
308 hidl_gen,
309 coordinator,
310 outputDir, "vts");
311 } else {
312 return generateSourcesForPackage(fqName,
313 hidl_gen,
314 coordinator,
315 outputDir, "vts");
316 }
317 }
318 },
319
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700320 {"makefile",
Andreas Huber2831d512016-08-15 09:33:47 -0700321 false /* mNeedsOutputDir */,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700322 validateForMakefile,
323 generateMakefileForPackage,
324 },
325};
326
327static void usage(const char *me) {
328 fprintf(stderr,
329 "usage: %s -o output-path -L <language> (-r interface-root)+ fqname+\n",
330 me);
331
332 fprintf(stderr, " -o output path\n");
333
334 fprintf(stderr, " -L <language> (one of");
335 for (auto &e : formats) {
336 fprintf(stderr, " %s", e.mKey.c_str());
337 }
338 fprintf(stderr, ")\n");
339
340 fprintf(stderr,
341 " -r package:path root "
342 "(e.g., android.hardware:hardware/interfaces)\n");
343}
344
Andreas Huberb82318c2016-08-02 14:45:54 -0700345int main(int argc, char **argv) {
346 std::string outputDir;
Andreas Huberdca261f2016-08-04 13:47:51 -0700347 std::vector<std::string> packageRootPaths;
348 std::vector<std::string> packageRoots;
Andreas Huberb82318c2016-08-02 14:45:54 -0700349
Andreas Huber737080b2016-08-02 15:38:04 -0700350 const char *me = argv[0];
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700351 OutputHandler *outputFormat = nullptr;
Andreas Huber737080b2016-08-02 15:38:04 -0700352
Andreas Huberb82318c2016-08-02 14:45:54 -0700353 int res;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700354 while ((res = getopt(argc, argv, "ho:r:L:")) >= 0) {
Andreas Huberb82318c2016-08-02 14:45:54 -0700355 switch (res) {
356 case 'o':
357 {
358 outputDir = optarg;
359 break;
360 }
361
Andreas Huberdca261f2016-08-04 13:47:51 -0700362 case 'r':
363 {
364 std::string val(optarg);
365 auto index = val.find_first_of(':');
366 CHECK(index != std::string::npos);
367
368 auto package = val.substr(0, index);
369 auto path = val.substr(index + 1);
370 packageRootPaths.push_back(path);
371 packageRoots.push_back(package);
372 break;
373 }
374
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700375 case 'L':
376 {
377 CHECK(outputFormat == nullptr); // only one -L option
378 for (auto &e : formats) {
379 if (e.mKey == optarg) {
380 outputFormat = &e;
381 break;
382 }
383 }
384 CHECK(outputFormat != nullptr);
385 break;
386 }
387
Andreas Huberb82318c2016-08-02 14:45:54 -0700388 case '?':
389 case 'h':
390 default:
391 {
Andreas Huber737080b2016-08-02 15:38:04 -0700392 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700393 exit(1);
394 break;
395 }
396 }
397 }
398
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700399 if (outputFormat == nullptr) {
400 usage(me);
401 exit(1);
402 }
403
Andreas Huberb82318c2016-08-02 14:45:54 -0700404 argc -= optind;
405 argv += optind;
406
Andreas Huberdca261f2016-08-04 13:47:51 -0700407 if (packageRootPaths.empty()) {
408 // Pick reasonable defaults.
409
410 packageRoots.push_back("android.hardware");
411
412 const char *TOP = getenv("TOP");
413 CHECK(TOP != NULL);
414
415 std::string path = TOP;
416 path.append("/hardware/interfaces");
417
418 packageRootPaths.push_back(path);
419 }
420
Andreas Huber737080b2016-08-02 15:38:04 -0700421 // Valid options are now in argv[0] .. argv[argc - 1].
422
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700423 if (!outputFormat->mNeedsOutputDir) {
Andreas Huberd2943e12016-08-05 11:59:31 -0700424 outputDir.clear(); // Unused.
425 } else if (outputDir.empty()) {
Andreas Huber737080b2016-08-02 15:38:04 -0700426 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700427 exit(1);
428 } else {
429 const size_t len = outputDir.size();
430 if (outputDir[len - 1] != '/') {
431 outputDir += "/";
432 }
433 }
434
Andreas Huberdca261f2016-08-04 13:47:51 -0700435 Coordinator coordinator(packageRootPaths, packageRoots);
Andreas Huber5345ec22016-07-29 13:33:27 -0700436
Andreas Huber737080b2016-08-02 15:38:04 -0700437 for (int i = 0; i < argc; ++i) {
Andreas Huber68f24592016-07-29 14:53:48 -0700438 FQName fqName(argv[i]);
Andreas Huber68f24592016-07-29 14:53:48 -0700439
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700440 if (!fqName.isValid()) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700441 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700442 "ERROR: Invalid fully-qualified name.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700443 exit(1);
444 }
Andreas Huber881227d2016-08-02 14:20:21 -0700445
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700446 OutputHandler::ValRes valid = outputFormat->validate(fqName);
447 if (valid == OutputHandler::FAILED) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700448 exit(1);
449 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700450
451 status_t err =
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700452 outputFormat->generate(fqName, me, &coordinator, outputDir);
Andreas Huberd2943e12016-08-05 11:59:31 -0700453
454 if (err != OK) {
455 break;
456 }
Andreas Hubereb1081f2016-07-28 13:13:24 -0700457 }
Andreas Huberc9410c72016-07-28 12:18:40 -0700458
Andreas Huberd2943e12016-08-05 11:59:31 -0700459 return 0;
Andreas Huberc9410c72016-07-28 12:18:40 -0700460}