blob: 2c79bdbcfb75b9418ff8c7c93d8d0e760da2ae16 [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();
200 out << "\nlibhwbinder \\"
201 << "\nlibutils \\";
202
203 for (const auto &importedPackage : importedPackages) {
204 out << "\n" << makeLibraryName(importedPackage) << " \\";
205 }
206 out << "\n";
207 out.unindent();
208
209 out << "\ninclude $(BUILD_SHARED_LIBRARY)\n";
Andreas Huberd2943e12016-08-05 11:59:31 -0700210
211 return OK;
212}
213
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700214OutputHandler::ValRes validateForMakefile(const FQName &fqName) {
215 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700216 fprintf(stderr, "ERROR: Expecting package name\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700217 return OutputHandler::FAILED;
218 }
219
220 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700221 fprintf(stderr, "ERROR: Expecting package version\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700222 return OutputHandler::FAILED;
223 }
224
225 if (!fqName.name().empty()) {
226 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700227 "ERROR: Expecting only package name and version.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700228 return OutputHandler::FAILED;
229 }
230
231 return OutputHandler::PASS_PACKAGE;
232}
233
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700234OutputHandler::ValRes validateForSource(const FQName &fqName) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700235 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700236 fprintf(stderr, "ERROR: Expecting package name\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700237 return OutputHandler::FAILED;
238 }
239
240 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700241 fprintf(stderr, "ERROR: Expecting package version\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700242 return OutputHandler::FAILED;
243 }
244
245 return fqName.name().empty() ?
246 OutputHandler::PASS_PACKAGE :
247 OutputHandler::PASS_FULL;
248}
249
250static std::vector<OutputHandler> formats = {
251 {"c++",
Andreas Huber2831d512016-08-15 09:33:47 -0700252 true /* mNeedsOutputDir */,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700253 validateForSource,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700254 [](const FQName &fqName,
255 const char *hidl_gen, Coordinator *coordinator,
256 const std::string &outputDir) -> status_t {
257 if (fqName.isFullyQualified()) {
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700258 return generateSourcesForFile(fqName,
259 hidl_gen,
260 coordinator,
261 outputDir, "c++");
262 } else {
263 return generateSourcesForPackage(fqName,
264 hidl_gen,
265 coordinator,
266 outputDir, "c++");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700267 }
268 }
269 },
270
Andreas Huber2831d512016-08-15 09:33:47 -0700271 {"java",
272 true /* mNeedsOutputDir */,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700273 validateForSource,
Andreas Huber2831d512016-08-15 09:33:47 -0700274 [](const FQName &fqName,
275 const char *hidl_gen, Coordinator *coordinator,
276 const std::string &outputDir) -> status_t {
277 if (fqName.isFullyQualified()) {
278 return generateSourcesForFile(fqName,
279 hidl_gen,
280 coordinator,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700281 outputDir, "java");
Andreas Huber2831d512016-08-15 09:33:47 -0700282 }
283 else {
284 return generateSourcesForPackage(fqName,
285 hidl_gen,
286 coordinator,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700287 outputDir, "java");
Andreas Huber2831d512016-08-15 09:33:47 -0700288 }
289 }
290 },
291
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700292 {"vts",
293 true,
294 validateForSource,
295 [](const FQName &fqName,
296 const char * hidl_gen,
297 Coordinator *coordinator,
298 const std::string &outputDir) -> status_t {
299 if (fqName.isFullyQualified()) {
300 return generateSourcesForFile(fqName,
301 hidl_gen,
302 coordinator,
303 outputDir, "vts");
304 } else {
305 return generateSourcesForPackage(fqName,
306 hidl_gen,
307 coordinator,
308 outputDir, "vts");
309 }
310 }
311 },
312
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700313 {"makefile",
Andreas Huber2831d512016-08-15 09:33:47 -0700314 false /* mNeedsOutputDir */,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700315 validateForMakefile,
316 generateMakefileForPackage,
317 },
318};
319
320static void usage(const char *me) {
321 fprintf(stderr,
322 "usage: %s -o output-path -L <language> (-r interface-root)+ fqname+\n",
323 me);
324
325 fprintf(stderr, " -o output path\n");
326
327 fprintf(stderr, " -L <language> (one of");
328 for (auto &e : formats) {
329 fprintf(stderr, " %s", e.mKey.c_str());
330 }
331 fprintf(stderr, ")\n");
332
333 fprintf(stderr,
334 " -r package:path root "
335 "(e.g., android.hardware:hardware/interfaces)\n");
336}
337
Andreas Huberb82318c2016-08-02 14:45:54 -0700338int main(int argc, char **argv) {
339 std::string outputDir;
Andreas Huberdca261f2016-08-04 13:47:51 -0700340 std::vector<std::string> packageRootPaths;
341 std::vector<std::string> packageRoots;
Andreas Huberb82318c2016-08-02 14:45:54 -0700342
Andreas Huber737080b2016-08-02 15:38:04 -0700343 const char *me = argv[0];
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700344 OutputHandler *outputFormat = nullptr;
Andreas Huber737080b2016-08-02 15:38:04 -0700345
Andreas Huberb82318c2016-08-02 14:45:54 -0700346 int res;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700347 while ((res = getopt(argc, argv, "ho:r:L:")) >= 0) {
Andreas Huberb82318c2016-08-02 14:45:54 -0700348 switch (res) {
349 case 'o':
350 {
351 outputDir = optarg;
352 break;
353 }
354
Andreas Huberdca261f2016-08-04 13:47:51 -0700355 case 'r':
356 {
357 std::string val(optarg);
358 auto index = val.find_first_of(':');
359 CHECK(index != std::string::npos);
360
361 auto package = val.substr(0, index);
362 auto path = val.substr(index + 1);
363 packageRootPaths.push_back(path);
364 packageRoots.push_back(package);
365 break;
366 }
367
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700368 case 'L':
369 {
370 CHECK(outputFormat == nullptr); // only one -L option
371 for (auto &e : formats) {
372 if (e.mKey == optarg) {
373 outputFormat = &e;
374 break;
375 }
376 }
377 CHECK(outputFormat != nullptr);
378 break;
379 }
380
Andreas Huberb82318c2016-08-02 14:45:54 -0700381 case '?':
382 case 'h':
383 default:
384 {
Andreas Huber737080b2016-08-02 15:38:04 -0700385 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700386 exit(1);
387 break;
388 }
389 }
390 }
391
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700392 if (outputFormat == nullptr) {
393 usage(me);
394 exit(1);
395 }
396
Andreas Huberb82318c2016-08-02 14:45:54 -0700397 argc -= optind;
398 argv += optind;
399
Andreas Huberdca261f2016-08-04 13:47:51 -0700400 if (packageRootPaths.empty()) {
401 // Pick reasonable defaults.
402
403 packageRoots.push_back("android.hardware");
404
405 const char *TOP = getenv("TOP");
406 CHECK(TOP != NULL);
407
408 std::string path = TOP;
409 path.append("/hardware/interfaces");
410
411 packageRootPaths.push_back(path);
412 }
413
Andreas Huber737080b2016-08-02 15:38:04 -0700414 // Valid options are now in argv[0] .. argv[argc - 1].
415
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700416 if (!outputFormat->mNeedsOutputDir) {
Andreas Huberd2943e12016-08-05 11:59:31 -0700417 outputDir.clear(); // Unused.
418 } else if (outputDir.empty()) {
Andreas Huber737080b2016-08-02 15:38:04 -0700419 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700420 exit(1);
421 } else {
422 const size_t len = outputDir.size();
423 if (outputDir[len - 1] != '/') {
424 outputDir += "/";
425 }
426 }
427
Andreas Huberdca261f2016-08-04 13:47:51 -0700428 Coordinator coordinator(packageRootPaths, packageRoots);
Andreas Huber5345ec22016-07-29 13:33:27 -0700429
Andreas Huber737080b2016-08-02 15:38:04 -0700430 for (int i = 0; i < argc; ++i) {
Andreas Huber68f24592016-07-29 14:53:48 -0700431 FQName fqName(argv[i]);
Andreas Huber68f24592016-07-29 14:53:48 -0700432
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700433 if (!fqName.isValid()) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700434 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700435 "ERROR: Invalid fully-qualified name.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700436 exit(1);
437 }
Andreas Huber881227d2016-08-02 14:20:21 -0700438
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700439 OutputHandler::ValRes valid = outputFormat->validate(fqName);
440 if (valid == OutputHandler::FAILED) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700441 exit(1);
442 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700443
444 status_t err =
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700445 outputFormat->generate(fqName, me, &coordinator, outputDir);
Andreas Huberd2943e12016-08-05 11:59:31 -0700446
447 if (err != OK) {
448 break;
449 }
Andreas Hubereb1081f2016-07-28 13:13:24 -0700450 }
Andreas Huberc9410c72016-07-28 12:18:40 -0700451
Andreas Huberd2943e12016-08-05 11:59:31 -0700452 return 0;
Andreas Huberc9410c72016-07-28 12:18:40 -0700453}