blob: eaccc8e375e94c801cdfdd4e1c28f1daec9ee1ff [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();
225
Keun Soo Yim21496da2016-08-24 07:24:03 -0700226 if (path.find("hardware/interfaces/tests/") != std::string::npos) {
227 out << "\nLOCAL_COMPATIBILITY_SUITE := vts"
228 << "\n-include test/vts/tools/build/Android.packaging_sharedlib.mk";
229 // TODO(yim): b/30589200 remove the above -include line after the
230 // build rule change is merged.
231 }
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700232 out << "\ninclude $(BUILD_SHARED_LIBRARY)\n";
Andreas Huberd2943e12016-08-05 11:59:31 -0700233
234 return OK;
235}
236
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700237OutputHandler::ValRes validateForMakefile(const FQName &fqName) {
238 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700239 fprintf(stderr, "ERROR: Expecting package name\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700240 return OutputHandler::FAILED;
241 }
242
243 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700244 fprintf(stderr, "ERROR: Expecting package version\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700245 return OutputHandler::FAILED;
246 }
247
248 if (!fqName.name().empty()) {
249 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700250 "ERROR: Expecting only package name and version.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700251 return OutputHandler::FAILED;
252 }
253
254 return OutputHandler::PASS_PACKAGE;
255}
256
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700257OutputHandler::ValRes validateForSource(const FQName &fqName) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700258 if (fqName.package().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700259 fprintf(stderr, "ERROR: Expecting package name\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700260 return OutputHandler::FAILED;
261 }
262
263 if (fqName.version().empty()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700264 fprintf(stderr, "ERROR: Expecting package version\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700265 return OutputHandler::FAILED;
266 }
267
268 return fqName.name().empty() ?
269 OutputHandler::PASS_PACKAGE :
270 OutputHandler::PASS_FULL;
271}
272
273static std::vector<OutputHandler> formats = {
274 {"c++",
Andreas Huber2831d512016-08-15 09:33:47 -0700275 true /* mNeedsOutputDir */,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700276 validateForSource,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700277 [](const FQName &fqName,
278 const char *hidl_gen, Coordinator *coordinator,
279 const std::string &outputDir) -> status_t {
280 if (fqName.isFullyQualified()) {
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700281 return generateSourcesForFile(fqName,
282 hidl_gen,
283 coordinator,
284 outputDir, "c++");
285 } else {
286 return generateSourcesForPackage(fqName,
287 hidl_gen,
288 coordinator,
289 outputDir, "c++");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700290 }
291 }
292 },
293
Andreas Huber2831d512016-08-15 09:33:47 -0700294 {"java",
295 true /* mNeedsOutputDir */,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700296 validateForSource,
Andreas Huber2831d512016-08-15 09:33:47 -0700297 [](const FQName &fqName,
298 const char *hidl_gen, Coordinator *coordinator,
299 const std::string &outputDir) -> status_t {
300 if (fqName.isFullyQualified()) {
301 return generateSourcesForFile(fqName,
302 hidl_gen,
303 coordinator,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700304 outputDir, "java");
Andreas Huber2831d512016-08-15 09:33:47 -0700305 }
306 else {
307 return generateSourcesForPackage(fqName,
308 hidl_gen,
309 coordinator,
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700310 outputDir, "java");
Andreas Huber2831d512016-08-15 09:33:47 -0700311 }
312 }
313 },
314
Zhuoyao Zhang5158db42016-08-10 10:25:20 -0700315 {"vts",
316 true,
317 validateForSource,
318 [](const FQName &fqName,
319 const char * hidl_gen,
320 Coordinator *coordinator,
321 const std::string &outputDir) -> status_t {
322 if (fqName.isFullyQualified()) {
323 return generateSourcesForFile(fqName,
324 hidl_gen,
325 coordinator,
326 outputDir, "vts");
327 } else {
328 return generateSourcesForPackage(fqName,
329 hidl_gen,
330 coordinator,
331 outputDir, "vts");
332 }
333 }
334 },
335
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700336 {"makefile",
Andreas Huber2831d512016-08-15 09:33:47 -0700337 false /* mNeedsOutputDir */,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700338 validateForMakefile,
339 generateMakefileForPackage,
340 },
341};
342
343static void usage(const char *me) {
344 fprintf(stderr,
345 "usage: %s -o output-path -L <language> (-r interface-root)+ fqname+\n",
346 me);
347
348 fprintf(stderr, " -o output path\n");
349
350 fprintf(stderr, " -L <language> (one of");
351 for (auto &e : formats) {
352 fprintf(stderr, " %s", e.mKey.c_str());
353 }
354 fprintf(stderr, ")\n");
355
356 fprintf(stderr,
357 " -r package:path root "
358 "(e.g., android.hardware:hardware/interfaces)\n");
359}
360
Andreas Huberb82318c2016-08-02 14:45:54 -0700361int main(int argc, char **argv) {
362 std::string outputDir;
Andreas Huberdca261f2016-08-04 13:47:51 -0700363 std::vector<std::string> packageRootPaths;
364 std::vector<std::string> packageRoots;
Andreas Huberb82318c2016-08-02 14:45:54 -0700365
Andreas Huber737080b2016-08-02 15:38:04 -0700366 const char *me = argv[0];
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700367 OutputHandler *outputFormat = nullptr;
Andreas Huber737080b2016-08-02 15:38:04 -0700368
Andreas Huberb82318c2016-08-02 14:45:54 -0700369 int res;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700370 while ((res = getopt(argc, argv, "ho:r:L:")) >= 0) {
Andreas Huberb82318c2016-08-02 14:45:54 -0700371 switch (res) {
372 case 'o':
373 {
374 outputDir = optarg;
375 break;
376 }
377
Andreas Huberdca261f2016-08-04 13:47:51 -0700378 case 'r':
379 {
380 std::string val(optarg);
381 auto index = val.find_first_of(':');
382 CHECK(index != std::string::npos);
383
384 auto package = val.substr(0, index);
385 auto path = val.substr(index + 1);
386 packageRootPaths.push_back(path);
387 packageRoots.push_back(package);
388 break;
389 }
390
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700391 case 'L':
392 {
393 CHECK(outputFormat == nullptr); // only one -L option
394 for (auto &e : formats) {
395 if (e.mKey == optarg) {
396 outputFormat = &e;
397 break;
398 }
399 }
400 CHECK(outputFormat != nullptr);
401 break;
402 }
403
Andreas Huberb82318c2016-08-02 14:45:54 -0700404 case '?':
405 case 'h':
406 default:
407 {
Andreas Huber737080b2016-08-02 15:38:04 -0700408 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700409 exit(1);
410 break;
411 }
412 }
413 }
414
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700415 if (outputFormat == nullptr) {
416 usage(me);
417 exit(1);
418 }
419
Andreas Huberb82318c2016-08-02 14:45:54 -0700420 argc -= optind;
421 argv += optind;
422
Andreas Huberdca261f2016-08-04 13:47:51 -0700423 if (packageRootPaths.empty()) {
424 // Pick reasonable defaults.
425
426 packageRoots.push_back("android.hardware");
427
428 const char *TOP = getenv("TOP");
429 CHECK(TOP != NULL);
430
431 std::string path = TOP;
432 path.append("/hardware/interfaces");
433
434 packageRootPaths.push_back(path);
435 }
436
Andreas Huber737080b2016-08-02 15:38:04 -0700437 // Valid options are now in argv[0] .. argv[argc - 1].
438
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700439 if (!outputFormat->mNeedsOutputDir) {
Andreas Huberd2943e12016-08-05 11:59:31 -0700440 outputDir.clear(); // Unused.
441 } else if (outputDir.empty()) {
Andreas Huber737080b2016-08-02 15:38:04 -0700442 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700443 exit(1);
444 } else {
445 const size_t len = outputDir.size();
446 if (outputDir[len - 1] != '/') {
447 outputDir += "/";
448 }
449 }
450
Andreas Huberdca261f2016-08-04 13:47:51 -0700451 Coordinator coordinator(packageRootPaths, packageRoots);
Andreas Huber5345ec22016-07-29 13:33:27 -0700452
Andreas Huber737080b2016-08-02 15:38:04 -0700453 for (int i = 0; i < argc; ++i) {
Andreas Huber68f24592016-07-29 14:53:48 -0700454 FQName fqName(argv[i]);
Andreas Huber68f24592016-07-29 14:53:48 -0700455
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700456 if (!fqName.isValid()) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700457 fprintf(stderr,
Andreas Huber70a59e12016-08-16 12:57:01 -0700458 "ERROR: Invalid fully-qualified name.\n");
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700459 exit(1);
460 }
Andreas Huber881227d2016-08-02 14:20:21 -0700461
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700462 OutputHandler::ValRes valid = outputFormat->validate(fqName);
463 if (valid == OutputHandler::FAILED) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700464 exit(1);
465 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700466
467 status_t err =
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700468 outputFormat->generate(fqName, me, &coordinator, outputDir);
Andreas Huberd2943e12016-08-05 11:59:31 -0700469
470 if (err != OK) {
471 break;
472 }
Andreas Hubereb1081f2016-07-28 13:13:24 -0700473 }
Andreas Huberc9410c72016-07-28 12:18:40 -0700474
Andreas Huberd2943e12016-08-05 11:59:31 -0700475 return 0;
Andreas Huberc9410c72016-07-28 12:18:40 -0700476}