blob: 7fb048e676a5532ef6e7aa516c48ce6f126d4a46 [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 Huberd2943e12016-08-05 11:59:31 -070019#include "Formatter.h"
Andreas Huber84f89de2016-07-28 15:39:51 -070020#include "FQName.h"
Andreas Huberc9410c72016-07-28 12:18:40 -070021
Andreas Huber68f24592016-07-29 14:53:48 -070022#include <android-base/logging.h>
Iliyan Malchev5bb14022016-08-09 15:04:39 -070023#include <set>
Andreas Huberc9410c72016-07-28 12:18:40 -070024#include <stdio.h>
Andreas Huberdca261f2016-08-04 13:47:51 -070025#include <string>
Andreas Huberb82318c2016-08-02 14:45:54 -070026#include <unistd.h>
Andreas Huberdca261f2016-08-04 13:47:51 -070027#include <vector>
Andreas Huberc9410c72016-07-28 12:18:40 -070028
29using namespace android;
30
Iliyan Malchev5bb14022016-08-09 15:04:39 -070031struct OutputHandler {
32 std::string mKey;
33 bool mNeedsOutputDir;
34 enum ValRes {
35 FAILED,
36 PASS_PACKAGE,
37 PASS_FULL
38 };
39 ValRes (*validate)(const FQName &);
40 status_t (*generate)(const FQName &fqName,
41 const char *hidl_gen,
42 Coordinator *coordinator,
43 const std::string &outputDir);
44};
Andreas Huberdca261f2016-08-04 13:47:51 -070045
Iliyan Malchev5bb14022016-08-09 15:04:39 -070046static status_t generateSourcesForFile(
47 const FQName &fqName,
48 const char *,
49 Coordinator *coordinator,
Andreas Huber2831d512016-08-15 09:33:47 -070050 const std::string &outputDir,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070051 const std::string &lang) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -070052 CHECK(fqName.isFullyQualified());
53
54 AST *ast = coordinator->parse(fqName);
55
56 if (ast == NULL) {
57 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -070058 "ERROR: Could not parse %s. Aborting.\n",
Iliyan Malchev5bb14022016-08-09 15:04:39 -070059 fqName.string().c_str());
60
61 return UNKNOWN_ERROR;
62 }
63
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070064 if (lang == "c++") {
65 return ast->generateCpp(outputDir);
66 }
67 if (lang == "java") {
68 return ast->generateJava(outputDir);
69 }
70 if (lang == "vts") {
71 return ast->generateVts(outputDir);
72 }
73 // Unknown language.
74 return UNKNOWN_ERROR;
Iliyan Malchev5bb14022016-08-09 15:04:39 -070075}
76
77static status_t generateSourcesForPackage(
78 const FQName &packageFQName,
79 const char *hidl_gen,
80 Coordinator *coordinator,
Andreas Huber2831d512016-08-15 09:33:47 -070081 const std::string &outputDir,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070082 const std::string &lang) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -070083 CHECK(packageFQName.isValid() &&
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070084 !packageFQName.isFullyQualified() &&
85 packageFQName.name().empty());
Iliyan Malchev5bb14022016-08-09 15:04:39 -070086
87 std::vector<FQName> packageInterfaces;
88
89 status_t err =
90 coordinator->appendPackageInterfacesToSet(packageFQName,
91 &packageInterfaces);
92
93 if (err != OK) {
94 return err;
95 }
96
97 for (const auto &fqName : packageInterfaces) {
Andreas Huber2831d512016-08-15 09:33:47 -070098 err = generateSourcesForFile(
Zhuoyao Zhang5158db42016-08-10 10:25:20 -070099 fqName, hidl_gen, coordinator, outputDir, lang);
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700100 if (err != OK) {
101 return err;
102 }
103 }
104
105 return OK;
Andreas Huberb82318c2016-08-02 14:45:54 -0700106}
107
Andreas Huberd2943e12016-08-05 11:59:31 -0700108static std::string makeLibraryName(const FQName &packageFQName) {
Iliyan Malcheve2c595a2016-08-07 21:20:04 -0700109 return packageFQName.string();
Andreas Huberd2943e12016-08-05 11:59:31 -0700110}
111
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700112static status_t generateMakefileForPackage(
113 const FQName &packageFQName,
Iliyan Malchevb66c3992016-08-07 21:18:13 -0700114 const char *hidl_gen,
Andreas Huberd2943e12016-08-05 11:59:31 -0700115 Coordinator *coordinator,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700116 const std::string &) {
117
118 CHECK(packageFQName.isValid() &&
119 !packageFQName.isFullyQualified() &&
120 packageFQName.name().empty());
121
Andreas Huberd2943e12016-08-05 11:59:31 -0700122 std::vector<FQName> packageInterfaces;
123
124 status_t err =
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700125 coordinator->appendPackageInterfacesToSet(packageFQName,
126 &packageInterfaces);
Andreas Huberd2943e12016-08-05 11:59:31 -0700127
128 if (err != OK) {
129 return err;
130 }
131
132 std::set<FQName> importedPackages;
133 for (const auto &fqName : packageInterfaces) {
134 AST *ast = coordinator->parse(fqName);
135
136 if (ast == NULL) {
137 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700138 "ERROR: Could not parse %s. Aborting.\n",
Andreas Huberd2943e12016-08-05 11:59:31 -0700139 fqName.string().c_str());
140
141 return UNKNOWN_ERROR;
142 }
143
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700144 ast->getImportedPackages(&importedPackages);
Andreas Huberd2943e12016-08-05 11:59:31 -0700145 }
146
147 std::string path =
148 coordinator->getPackagePath(packageFQName, false /* relative */);
149
150 path.append("Android.mk");
151
152 CHECK(Coordinator::MakeParentHierarchy(path));
153 FILE *file = fopen(path.c_str(), "w");
154
155 if (file == NULL) {
156 return -errno;
157 }
158
159 const std::string libraryName = makeLibraryName(packageFQName);
160
161 Formatter out(file);
162
163 out << "LOCAL_PATH := $(call my-dir)\n"
164 << "include $(CLEAR_VARS)\n\n"
165 << "LOCAL_MODULE := "
166 << libraryName
167 << "\n"
168 << "LOCAL_MODULE_CLASS := SHARED_LIBRARIES\n\n"
169 << "intermediates := $(local-generated-sources-dir)\n\n"
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700170 << "HIDL := $(HOST_OUT_EXECUTABLES)/"
171 << hidl_gen << "$(HOST_EXECUTABLE_SUFFIX)";
Andreas Huberd2943e12016-08-05 11:59:31 -0700172
Andreas Huberd2943e12016-08-05 11:59:31 -0700173 for (const auto &fqName : packageInterfaces) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700174
175 out << "\n"
176 << "\n#"
177 << "\n# Build " << fqName.name() << ".hal"
178 << "\n#";
179 out << "\nGEN := $(intermediates)/"
Andreas Huberd2943e12016-08-05 11:59:31 -0700180 << coordinator->convertPackageRootToPath(packageFQName)
181 << coordinator->getPackagePath(packageFQName, true /* relative */);
Andreas Huberd2943e12016-08-05 11:59:31 -0700182 if (fqName.name() == "types") {
183 out << "types.cpp";
184 } else {
185 out << fqName.name().substr(1) << "All.cpp";
186 }
187
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700188 out << "\n$(GEN): $(HIDL)";
189 out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
190 out << "\n$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/"
191 << fqName.name() << ".hal";
192 out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
193 << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
194 out.indent();
195 out.indent();
196 out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
197 << "\n-Lc++ -r"
198 << coordinator->getPackageRoot(packageFQName) << ":"
199 << coordinator->getPackageRootPath(packageFQName) << "\\";
200 out << "\n"
201 << packageFQName.string()
202 << "::$(patsubst %.hal,%,$(notdir $(PRIVATE_DEPS)))"
203 << "\n";
204 out.unindent();
205 out.unindent();
206
207 out << "\n$(GEN): $(LOCAL_PATH)/" << fqName.name() << ".hal";
208 out << "\n\t$(transform-generated-source)";
209 out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
Andreas Huberd2943e12016-08-05 11:59:31 -0700210 }
211
Andreas Huberd2943e12016-08-05 11:59:31 -0700212 out << "\n"
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700213 << "\nLOCAL_EXPORT_C_INCLUDE_DIRS := $(intermediates)"
214 << "\nLOCAL_SHARED_LIBRARIES := \\";
215 out.indent();
Martijn Coenen7473fab2016-08-19 14:05:40 +0200216 out << "\nlibhidl \\"
217 << "\nlibhwbinder \\"
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700218 << "\nlibutils \\";
219
220 for (const auto &importedPackage : importedPackages) {
221 out << "\n" << makeLibraryName(importedPackage) << " \\";
222 }
223 out << "\n";
224 out.unindent();
Keun Soo Yimc2903d22016-08-26 18:56:22 -0700225 out << "\nLOCAL_MULTILIB := both";
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700226
Keun Soo Yim21496da2016-08-24 07:24:03 -0700227 if (path.find("hardware/interfaces/tests/") != std::string::npos) {
228 out << "\nLOCAL_COMPATIBILITY_SUITE := vts"
229 << "\n-include test/vts/tools/build/Android.packaging_sharedlib.mk";
230 // TODO(yim): b/30589200 remove the above -include line after the
231 // build rule change is merged.
232 }
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700233 out << "\ninclude $(BUILD_SHARED_LIBRARY)\n";
Andreas Huberd2943e12016-08-05 11:59:31 -0700234
235 return OK;
236}
237
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700238OutputHandler::ValRes validateForMakefile(const FQName &fqName) {
239 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700240 fprintf(stderr, "ERROR: Expecting package name\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700241 return OutputHandler::FAILED;
242 }
243
244 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700245 fprintf(stderr, "ERROR: Expecting package version\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700246 return OutputHandler::FAILED;
247 }
248
249 if (!fqName.name().empty()) {
250 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700251 "ERROR: Expecting only package name and version.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700252 return OutputHandler::FAILED;
253 }
254
255 return OutputHandler::PASS_PACKAGE;
256}
257
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700258OutputHandler::ValRes validateForSource(const FQName &fqName) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700259 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700260 fprintf(stderr, "ERROR: Expecting package name\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700261 return OutputHandler::FAILED;
262 }
263
264 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700265 fprintf(stderr, "ERROR: Expecting package version\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700266 return OutputHandler::FAILED;
267 }
268
269 return fqName.name().empty() ?
270 OutputHandler::PASS_PACKAGE :
271 OutputHandler::PASS_FULL;
272}
273
274static std::vector<OutputHandler> formats = {
275 {"c++",
Andreas Huber2831d512016-08-15 09:33:47 -0700276 true /* mNeedsOutputDir */,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700277 validateForSource,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700278 [](const FQName &fqName,
279 const char *hidl_gen, Coordinator *coordinator,
280 const std::string &outputDir) -> status_t {
281 if (fqName.isFullyQualified()) {
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700282 return generateSourcesForFile(fqName,
283 hidl_gen,
284 coordinator,
285 outputDir, "c++");
286 } else {
287 return generateSourcesForPackage(fqName,
288 hidl_gen,
289 coordinator,
290 outputDir, "c++");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700291 }
292 }
293 },
294
Andreas Huber2831d512016-08-15 09:33:47 -0700295 {"java",
296 true /* mNeedsOutputDir */,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700297 validateForSource,
Andreas Huber2831d512016-08-15 09:33:47 -0700298 [](const FQName &fqName,
299 const char *hidl_gen, Coordinator *coordinator,
300 const std::string &outputDir) -> status_t {
301 if (fqName.isFullyQualified()) {
302 return generateSourcesForFile(fqName,
303 hidl_gen,
304 coordinator,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700305 outputDir, "java");
Andreas Huber2831d512016-08-15 09:33:47 -0700306 }
307 else {
308 return generateSourcesForPackage(fqName,
309 hidl_gen,
310 coordinator,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700311 outputDir, "java");
Andreas Huber2831d512016-08-15 09:33:47 -0700312 }
313 }
314 },
315
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700316 {"vts",
317 true,
318 validateForSource,
319 [](const FQName &fqName,
320 const char * hidl_gen,
321 Coordinator *coordinator,
322 const std::string &outputDir) -> status_t {
323 if (fqName.isFullyQualified()) {
324 return generateSourcesForFile(fqName,
325 hidl_gen,
326 coordinator,
327 outputDir, "vts");
328 } else {
329 return generateSourcesForPackage(fqName,
330 hidl_gen,
331 coordinator,
332 outputDir, "vts");
333 }
334 }
335 },
336
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700337 {"makefile",
Andreas Huber2831d512016-08-15 09:33:47 -0700338 false /* mNeedsOutputDir */,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700339 validateForMakefile,
340 generateMakefileForPackage,
341 },
342};
343
344static void usage(const char *me) {
345 fprintf(stderr,
346 "usage: %s -o output-path -L <language> (-r interface-root)+ fqname+\n",
347 me);
348
349 fprintf(stderr, " -o output path\n");
350
351 fprintf(stderr, " -L <language> (one of");
352 for (auto &e : formats) {
353 fprintf(stderr, " %s", e.mKey.c_str());
354 }
355 fprintf(stderr, ")\n");
356
357 fprintf(stderr,
358 " -r package:path root "
359 "(e.g., android.hardware:hardware/interfaces)\n");
360}
361
Andreas Huberb82318c2016-08-02 14:45:54 -0700362int main(int argc, char **argv) {
363 std::string outputDir;
Andreas Huberdca261f2016-08-04 13:47:51 -0700364 std::vector<std::string> packageRootPaths;
365 std::vector<std::string> packageRoots;
Andreas Huberb82318c2016-08-02 14:45:54 -0700366
Andreas Huber737080b2016-08-02 15:38:04 -0700367 const char *me = argv[0];
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700368 OutputHandler *outputFormat = nullptr;
Andreas Huber737080b2016-08-02 15:38:04 -0700369
Andreas Huberb82318c2016-08-02 14:45:54 -0700370 int res;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700371 while ((res = getopt(argc, argv, "ho:r:L:")) >= 0) {
Andreas Huberb82318c2016-08-02 14:45:54 -0700372 switch (res) {
373 case 'o':
374 {
375 outputDir = optarg;
376 break;
377 }
378
Andreas Huberdca261f2016-08-04 13:47:51 -0700379 case 'r':
380 {
381 std::string val(optarg);
382 auto index = val.find_first_of(':');
383 CHECK(index != std::string::npos);
384
385 auto package = val.substr(0, index);
386 auto path = val.substr(index + 1);
387 packageRootPaths.push_back(path);
388 packageRoots.push_back(package);
389 break;
390 }
391
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700392 case 'L':
393 {
394 CHECK(outputFormat == nullptr); // only one -L option
395 for (auto &e : formats) {
396 if (e.mKey == optarg) {
397 outputFormat = &e;
398 break;
399 }
400 }
401 CHECK(outputFormat != nullptr);
402 break;
403 }
404
Andreas Huberb82318c2016-08-02 14:45:54 -0700405 case '?':
406 case 'h':
407 default:
408 {
Andreas Huber737080b2016-08-02 15:38:04 -0700409 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700410 exit(1);
411 break;
412 }
413 }
414 }
415
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700416 if (outputFormat == nullptr) {
417 usage(me);
418 exit(1);
419 }
420
Andreas Huberb82318c2016-08-02 14:45:54 -0700421 argc -= optind;
422 argv += optind;
423
Andreas Huberdca261f2016-08-04 13:47:51 -0700424 if (packageRootPaths.empty()) {
425 // Pick reasonable defaults.
426
427 packageRoots.push_back("android.hardware");
428
429 const char *TOP = getenv("TOP");
430 CHECK(TOP != NULL);
431
432 std::string path = TOP;
433 path.append("/hardware/interfaces");
434
435 packageRootPaths.push_back(path);
436 }
437
Andreas Huber737080b2016-08-02 15:38:04 -0700438 // Valid options are now in argv[0] .. argv[argc - 1].
439
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700440 if (!outputFormat->mNeedsOutputDir) {
Andreas Huberd2943e12016-08-05 11:59:31 -0700441 outputDir.clear(); // Unused.
442 } else if (outputDir.empty()) {
Andreas Huber737080b2016-08-02 15:38:04 -0700443 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700444 exit(1);
445 } else {
446 const size_t len = outputDir.size();
447 if (outputDir[len - 1] != '/') {
448 outputDir += "/";
449 }
450 }
451
Andreas Huberdca261f2016-08-04 13:47:51 -0700452 Coordinator coordinator(packageRootPaths, packageRoots);
Andreas Huber5345ec22016-07-29 13:33:27 -0700453
Andreas Huber737080b2016-08-02 15:38:04 -0700454 for (int i = 0; i < argc; ++i) {
Andreas Huber68f24592016-07-29 14:53:48 -0700455 FQName fqName(argv[i]);
Andreas Huber68f24592016-07-29 14:53:48 -0700456
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700457 if (!fqName.isValid()) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700458 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700459 "ERROR: Invalid fully-qualified name.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700460 exit(1);
461 }
Andreas Huber881227d2016-08-02 14:20:21 -0700462
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700463 OutputHandler::ValRes valid = outputFormat->validate(fqName);
464 if (valid == OutputHandler::FAILED) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700465 exit(1);
466 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700467
468 status_t err =
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700469 outputFormat->generate(fqName, me, &coordinator, outputDir);
Andreas Huberd2943e12016-08-05 11:59:31 -0700470
471 if (err != OK) {
Steven Moreland261370a2016-08-29 15:36:59 -0700472 exit(1);
Andreas Huberd2943e12016-08-05 11:59:31 -0700473 }
Andreas Hubereb1081f2016-07-28 13:13:24 -0700474 }
Andreas Huberc9410c72016-07-28 12:18:40 -0700475
Andreas Huberd2943e12016-08-05 11:59:31 -0700476 return 0;
Andreas Huberc9410c72016-07-28 12:18:40 -0700477}