blob: 752e5f81da17e34072a4d9f6a159dac42f9f5748 [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,
34 const std::string &outputDir) {
Andreas Huberdca261f2016-08-04 13:47:51 -070035
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,
42 "Could not parse %s. Aborting.\n",
43 fqName.string().c_str());
44
45 return UNKNOWN_ERROR;
46 }
47
48 status_t err = ast->generateCpp(outputDir);
49
50 return err;
51}
52
53static status_t generateSourcesForPackage(
54 const FQName &packageFQName,
55 const char *hidl_gen,
56 Coordinator *coordinator,
57 const std::string &outputDir) {
58
59 CHECK(packageFQName.isValid() &&
60 !packageFQName.isFullyQualified() &&
61 packageFQName.name().empty());
62
63 std::vector<FQName> packageInterfaces;
64
65 status_t err =
66 coordinator->appendPackageInterfacesToSet(packageFQName,
67 &packageInterfaces);
68
69 if (err != OK) {
70 return err;
71 }
72
73 for (const auto &fqName : packageInterfaces) {
74 err = generateSourcesForFile(fqName, hidl_gen, coordinator, outputDir);
75 if (err != OK) {
76 return err;
77 }
78 }
79
80 return OK;
Andreas Huberb82318c2016-08-02 14:45:54 -070081}
82
Andreas Huberd2943e12016-08-05 11:59:31 -070083static std::string makeLibraryName(const FQName &packageFQName) {
Iliyan Malcheve2c595a2016-08-07 21:20:04 -070084 return packageFQName.string();
Andreas Huberd2943e12016-08-05 11:59:31 -070085}
86
Iliyan Malchev5bb14022016-08-09 15:04:39 -070087static status_t generateMakefileForPackage(
88 const FQName &packageFQName,
Iliyan Malchevb66c3992016-08-07 21:18:13 -070089 const char *hidl_gen,
Andreas Huberd2943e12016-08-05 11:59:31 -070090 Coordinator *coordinator,
Iliyan Malchev5bb14022016-08-09 15:04:39 -070091 const std::string &) {
92
93 CHECK(packageFQName.isValid() &&
94 !packageFQName.isFullyQualified() &&
95 packageFQName.name().empty());
96
Andreas Huberd2943e12016-08-05 11:59:31 -070097 std::vector<FQName> packageInterfaces;
98
99 status_t err =
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700100 coordinator->appendPackageInterfacesToSet(packageFQName,
101 &packageInterfaces);
Andreas Huberd2943e12016-08-05 11:59:31 -0700102
103 if (err != OK) {
104 return err;
105 }
106
107 std::set<FQName> importedPackages;
108 for (const auto &fqName : packageInterfaces) {
109 AST *ast = coordinator->parse(fqName);
110
111 if (ast == NULL) {
112 fprintf(stderr,
113 "Could not parse %s. Aborting.\n",
114 fqName.string().c_str());
115
116 return UNKNOWN_ERROR;
117 }
118
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700119 ast->getImportedPackages(&importedPackages);
Andreas Huberd2943e12016-08-05 11:59:31 -0700120 }
121
122 std::string path =
123 coordinator->getPackagePath(packageFQName, false /* relative */);
124
125 path.append("Android.mk");
126
127 CHECK(Coordinator::MakeParentHierarchy(path));
128 FILE *file = fopen(path.c_str(), "w");
129
130 if (file == NULL) {
131 return -errno;
132 }
133
134 const std::string libraryName = makeLibraryName(packageFQName);
135
136 Formatter out(file);
137
138 out << "LOCAL_PATH := $(call my-dir)\n"
139 << "include $(CLEAR_VARS)\n\n"
140 << "LOCAL_MODULE := "
141 << libraryName
142 << "\n"
143 << "LOCAL_MODULE_CLASS := SHARED_LIBRARIES\n\n"
144 << "intermediates := $(local-generated-sources-dir)\n\n"
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700145 << "HIDL := $(HOST_OUT_EXECUTABLES)/"
146 << hidl_gen << "$(HOST_EXECUTABLE_SUFFIX)";
Andreas Huberd2943e12016-08-05 11:59:31 -0700147
Andreas Huberd2943e12016-08-05 11:59:31 -0700148 for (const auto &fqName : packageInterfaces) {
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700149
150 out << "\n"
151 << "\n#"
152 << "\n# Build " << fqName.name() << ".hal"
153 << "\n#";
154 out << "\nGEN := $(intermediates)/"
Andreas Huberd2943e12016-08-05 11:59:31 -0700155 << coordinator->convertPackageRootToPath(packageFQName)
156 << coordinator->getPackagePath(packageFQName, true /* relative */);
Andreas Huberd2943e12016-08-05 11:59:31 -0700157 if (fqName.name() == "types") {
158 out << "types.cpp";
159 } else {
160 out << fqName.name().substr(1) << "All.cpp";
161 }
162
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700163 out << "\n$(GEN): $(HIDL)";
164 out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
165 out << "\n$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/"
166 << fqName.name() << ".hal";
167 out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
168 << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
169 out.indent();
170 out.indent();
171 out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
172 << "\n-Lc++ -r"
173 << coordinator->getPackageRoot(packageFQName) << ":"
174 << coordinator->getPackageRootPath(packageFQName) << "\\";
175 out << "\n"
176 << packageFQName.string()
177 << "::$(patsubst %.hal,%,$(notdir $(PRIVATE_DEPS)))"
178 << "\n";
179 out.unindent();
180 out.unindent();
181
182 out << "\n$(GEN): $(LOCAL_PATH)/" << fqName.name() << ".hal";
183 out << "\n\t$(transform-generated-source)";
184 out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
Andreas Huberd2943e12016-08-05 11:59:31 -0700185 }
186
Andreas Huberd2943e12016-08-05 11:59:31 -0700187 out << "\n"
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700188 << "\nLOCAL_EXPORT_C_INCLUDE_DIRS := $(intermediates)"
189 << "\nLOCAL_SHARED_LIBRARIES := \\";
190 out.indent();
191 out << "\nlibhwbinder \\"
192 << "\nlibutils \\";
193
194 for (const auto &importedPackage : importedPackages) {
195 out << "\n" << makeLibraryName(importedPackage) << " \\";
196 }
197 out << "\n";
198 out.unindent();
199
200 out << "\ninclude $(BUILD_SHARED_LIBRARY)\n";
Andreas Huberd2943e12016-08-05 11:59:31 -0700201
202 return OK;
203}
204
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700205OutputHandler::ValRes validateForMakefile(const FQName &fqName) {
206 if (fqName.package().empty()) {
207 fprintf(stderr, "Expecting package name\n");
208 return OutputHandler::FAILED;
209 }
210
211 if (fqName.version().empty()) {
212 fprintf(stderr, "Expecting package version\n");
213 return OutputHandler::FAILED;
214 }
215
216 if (!fqName.name().empty()) {
217 fprintf(stderr,
218 "Expecting only package name and version.\n");
219 return OutputHandler::FAILED;
220 }
221
222 return OutputHandler::PASS_PACKAGE;
223}
224
225OutputHandler::ValRes validateForCpp(const FQName &fqName) {
226 if (fqName.package().empty()) {
227 fprintf(stderr, "Expecting package name\n");
228 return OutputHandler::FAILED;
229 }
230
231 if (fqName.version().empty()) {
232 fprintf(stderr, "Expecting package version\n");
233 return OutputHandler::FAILED;
234 }
235
236 return fqName.name().empty() ?
237 OutputHandler::PASS_PACKAGE :
238 OutputHandler::PASS_FULL;
239}
240
241static std::vector<OutputHandler> formats = {
242 {"c++",
243 true,
244 validateForCpp,
245 [](const FQName &fqName,
246 const char *hidl_gen, Coordinator *coordinator,
247 const std::string &outputDir) -> status_t {
248 if (fqName.isFullyQualified()) {
249 return generateSourcesForFile(fqName,
250 hidl_gen,
251 coordinator,
252 outputDir);
253 }
254 else {
255 return generateSourcesForPackage(fqName,
256 hidl_gen,
257 coordinator,
258 outputDir);
259 }
260 }
261 },
262
263 {"makefile",
264 false,
265 validateForMakefile,
266 generateMakefileForPackage,
267 },
268};
269
270static void usage(const char *me) {
271 fprintf(stderr,
272 "usage: %s -o output-path -L <language> (-r interface-root)+ fqname+\n",
273 me);
274
275 fprintf(stderr, " -o output path\n");
276
277 fprintf(stderr, " -L <language> (one of");
278 for (auto &e : formats) {
279 fprintf(stderr, " %s", e.mKey.c_str());
280 }
281 fprintf(stderr, ")\n");
282
283 fprintf(stderr,
284 " -r package:path root "
285 "(e.g., android.hardware:hardware/interfaces)\n");
286}
287
Andreas Huberb82318c2016-08-02 14:45:54 -0700288int main(int argc, char **argv) {
289 std::string outputDir;
Andreas Huberdca261f2016-08-04 13:47:51 -0700290 std::vector<std::string> packageRootPaths;
291 std::vector<std::string> packageRoots;
Andreas Huberb82318c2016-08-02 14:45:54 -0700292
Andreas Huber737080b2016-08-02 15:38:04 -0700293 const char *me = argv[0];
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700294 OutputHandler *outputFormat = nullptr;
Andreas Huber737080b2016-08-02 15:38:04 -0700295
Andreas Huberb82318c2016-08-02 14:45:54 -0700296 int res;
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700297 while ((res = getopt(argc, argv, "ho:r:L:")) >= 0) {
Andreas Huberb82318c2016-08-02 14:45:54 -0700298 switch (res) {
299 case 'o':
300 {
301 outputDir = optarg;
302 break;
303 }
304
Andreas Huberdca261f2016-08-04 13:47:51 -0700305 case 'r':
306 {
307 std::string val(optarg);
308 auto index = val.find_first_of(':');
309 CHECK(index != std::string::npos);
310
311 auto package = val.substr(0, index);
312 auto path = val.substr(index + 1);
313 packageRootPaths.push_back(path);
314 packageRoots.push_back(package);
315 break;
316 }
317
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700318 case 'L':
319 {
320 CHECK(outputFormat == nullptr); // only one -L option
321 for (auto &e : formats) {
322 if (e.mKey == optarg) {
323 outputFormat = &e;
324 break;
325 }
326 }
327 CHECK(outputFormat != nullptr);
328 break;
329 }
330
Andreas Huberb82318c2016-08-02 14:45:54 -0700331 case '?':
332 case 'h':
333 default:
334 {
Andreas Huber737080b2016-08-02 15:38:04 -0700335 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700336 exit(1);
337 break;
338 }
339 }
340 }
341
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700342 if (outputFormat == nullptr) {
343 usage(me);
344 exit(1);
345 }
346
Andreas Huberb82318c2016-08-02 14:45:54 -0700347 argc -= optind;
348 argv += optind;
349
Andreas Huberdca261f2016-08-04 13:47:51 -0700350 if (packageRootPaths.empty()) {
351 // Pick reasonable defaults.
352
353 packageRoots.push_back("android.hardware");
354
355 const char *TOP = getenv("TOP");
356 CHECK(TOP != NULL);
357
358 std::string path = TOP;
359 path.append("/hardware/interfaces");
360
361 packageRootPaths.push_back(path);
362 }
363
Andreas Huber737080b2016-08-02 15:38:04 -0700364 // Valid options are now in argv[0] .. argv[argc - 1].
365
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700366 if (!outputFormat->mNeedsOutputDir) {
Andreas Huberd2943e12016-08-05 11:59:31 -0700367 outputDir.clear(); // Unused.
368 } else if (outputDir.empty()) {
Andreas Huber737080b2016-08-02 15:38:04 -0700369 usage(me);
Andreas Huberb82318c2016-08-02 14:45:54 -0700370 exit(1);
371 } else {
372 const size_t len = outputDir.size();
373 if (outputDir[len - 1] != '/') {
374 outputDir += "/";
375 }
376 }
377
Andreas Huberdca261f2016-08-04 13:47:51 -0700378 Coordinator coordinator(packageRootPaths, packageRoots);
Andreas Huber5345ec22016-07-29 13:33:27 -0700379
Andreas Huber737080b2016-08-02 15:38:04 -0700380 for (int i = 0; i < argc; ++i) {
Andreas Huber68f24592016-07-29 14:53:48 -0700381 FQName fqName(argv[i]);
Andreas Huber68f24592016-07-29 14:53:48 -0700382
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700383 if (!fqName.isValid()) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700384 fprintf(stderr,
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700385 "Invalid fully-qualified name.\n");
386 exit(1);
387 }
Andreas Huber881227d2016-08-02 14:20:21 -0700388
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700389 OutputHandler::ValRes valid = outputFormat->validate(fqName);
390 if (valid == OutputHandler::FAILED) {
Andreas Hubere61e3f72016-08-03 10:22:03 -0700391 exit(1);
392 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700393
394 status_t err =
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700395 outputFormat->generate(fqName, me, &coordinator, outputDir);
Andreas Huberd2943e12016-08-05 11:59:31 -0700396
397 if (err != OK) {
398 break;
399 }
Andreas Hubereb1081f2016-07-28 13:13:24 -0700400 }
Andreas Huberc9410c72016-07-28 12:18:40 -0700401
Andreas Huberd2943e12016-08-05 11:59:31 -0700402 return 0;
Andreas Huberc9410c72016-07-28 12:18:40 -0700403}