1 /*
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
17 #include "Coordinator.h"
18
19 #include <dirent.h>
20 #include <sys/stat.h>
21
22 #include <algorithm>
23 #include <iterator>
24
25 #include <android-base/logging.h>
26 #include <hidl-hash/Hash.h>
27 #include <hidl-util/Formatter.h>
28 #include <hidl-util/StringHelper.h>
29 #include <iostream>
30
31 #include "AST.h"
32 #include "Interface.h"
33 #include "hidl-gen_l.h"
34
existdir(const char * name)35 static bool existdir(const char *name) {
36 DIR *dir = opendir(name);
37 if (dir == nullptr) {
38 return false;
39 }
40 closedir(dir);
41 return true;
42 }
43
44 namespace android {
45
getRootPath() const46 const std::string &Coordinator::getRootPath() const {
47 return mRootPath;
48 }
49
setRootPath(const std::string & rootPath)50 void Coordinator::setRootPath(const std::string &rootPath) {
51 mRootPath = rootPath;
52
53 if (!mRootPath.empty() && !StringHelper::EndsWith(mRootPath, "/")) {
54 mRootPath += "/";
55 }
56 }
57
setOutputPath(const std::string & outputPath)58 void Coordinator::setOutputPath(const std::string& outputPath) {
59 mOutputPath = outputPath;
60 }
61
setVerbose(bool verbose)62 void Coordinator::setVerbose(bool verbose) {
63 mVerbose = verbose;
64 }
65
setRequireFrozen(bool requireFrozen)66 void Coordinator::setRequireFrozen(bool requireFrozen) {
67 mRequireFrozen = requireFrozen;
68 }
69
isVerbose() const70 bool Coordinator::isVerbose() const {
71 return mVerbose;
72 }
73
setDepFile(const std::string & depFile)74 void Coordinator::setDepFile(const std::string& depFile) {
75 mDepFile = depFile;
76 }
77
getOwner() const78 const std::string& Coordinator::getOwner() const {
79 return mOwner;
80 }
setOwner(const std::string & owner)81 void Coordinator::setOwner(const std::string& owner) {
82 mOwner = owner;
83 }
84
addPackagePath(const std::string & root,const std::string & path,std::string * error)85 status_t Coordinator::addPackagePath(const std::string& root, const std::string& path, std::string* error) {
86 FQName package = FQName(root, "0.0", "");
87 for (const PackageRoot &packageRoot : mPackageRoots) {
88 if (packageRoot.root.inPackage(root) || package.inPackage(packageRoot.root.package())) {
89 if (error != nullptr) {
90 *error = "ERROR: conflicting package roots " +
91 packageRoot.root.package() +
92 " and " +
93 root;
94 }
95
96 return UNKNOWN_ERROR;
97 }
98 }
99
100 mPackageRoots.push_back({path, package});
101 return OK;
102 }
addDefaultPackagePath(const std::string & root,const std::string & path)103 void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) {
104 addPackagePath(root, path, nullptr /* error */);
105 }
106
getFormatter(const FQName & fqName,Location location,const std::string & fileName) const107 Formatter Coordinator::getFormatter(const FQName& fqName, Location location,
108 const std::string& fileName) const {
109 if (location == Location::STANDARD_OUT) {
110 return Formatter(stdout);
111 }
112
113 std::string filepath;
114 status_t err = getFilepath(fqName, location, fileName, &filepath);
115 if (err != OK) {
116 return Formatter::invalid();
117 }
118
119 onFileAccess(filepath, "w");
120
121 if (!Coordinator::MakeParentHierarchy(filepath)) {
122 fprintf(stderr, "ERROR: could not make directories for %s.\n", filepath.c_str());
123 return Formatter::invalid();
124 }
125
126 FILE* file = fopen(filepath.c_str(), "w");
127
128 if (file == nullptr) {
129 fprintf(stderr, "ERROR: could not open file %s: %d\n", filepath.c_str(), errno);
130 return Formatter::invalid();
131 }
132
133 return Formatter(file);
134 }
135
getFilepath(const FQName & fqName,Location location,const std::string & fileName,std::string * path) const136 status_t Coordinator::getFilepath(const FQName& fqName, Location location,
137 const std::string& fileName, std::string* path) const {
138 status_t err;
139 std::string packagePath;
140 std::string packageRootPath;
141
142 switch (location) {
143 case Location::DIRECT: { /* nothing */
144 *path = mOutputPath + fileName;
145 } break;
146 case Location::PACKAGE_ROOT: {
147 err = getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
148 if (err != OK) return err;
149
150 *path = mOutputPath + packagePath + fileName;
151 } break;
152 case Location::GEN_OUTPUT: {
153 err = convertPackageRootToPath(fqName, &packageRootPath);
154 if (err != OK) return err;
155 err = getPackagePath(fqName, true /* relative */, false /* sanitized */, &packagePath);
156 if (err != OK) return err;
157
158 *path = mOutputPath + packageRootPath + packagePath + fileName;
159 } break;
160 case Location::GEN_SANITIZED: {
161 err = convertPackageRootToPath(fqName, &packageRootPath);
162 if (err != OK) return err;
163 err = getPackagePath(fqName, true /* relative */, true /* sanitized */, &packagePath);
164 if (err != OK) return err;
165
166 *path = mOutputPath + packageRootPath + packagePath + fileName;
167 } break;
168 default: { CHECK(false) << "Invalid location: " << static_cast<size_t>(location); }
169 }
170
171 return OK;
172 }
173
onFileAccess(const std::string & path,const std::string & mode) const174 void Coordinator::onFileAccess(const std::string& path, const std::string& mode) const {
175 if (mode == "r") {
176 // This is a global list. It's not cleared when a second fqname is processed for
177 // two reasons:
178 // 1). If there is a bug in hidl-gen, the dependencies on the first project from
179 // the second would be required to recover correctly when the bug is fixed.
180 // 2). This option is never used in Android builds.
181 mReadFiles.insert(makeRelative(path));
182 }
183
184 if (!mVerbose) {
185 return;
186 }
187
188 fprintf(stderr,
189 "VERBOSE: file access %s %s\n", path.c_str(), mode.c_str());
190 }
191
writeDepFile(const std::string & forFile) const192 status_t Coordinator::writeDepFile(const std::string& forFile) const {
193 // No dep file requested
194 if (mDepFile.empty()) return OK;
195
196 onFileAccess(mDepFile, "w");
197
198 FILE* file = fopen(mDepFile.c_str(), "w");
199 if (file == nullptr) {
200 fprintf(stderr, "ERROR: could not open dep file at %s.\n", mDepFile.c_str());
201 return UNKNOWN_ERROR;
202 }
203
204 Formatter out(file, 2 /* spacesPerIndent */);
205 out << StringHelper::LTrim(forFile, mOutputPath) << ": \\\n";
206 out.indent([&] {
207 for (const std::string& file : mReadFiles) {
208 out << makeRelative(file) << " \\\n";
209 }
210 });
211 return OK;
212 }
213
parse(const FQName & fqName,std::set<AST * > * parsedASTs,Enforce enforcement) const214 AST* Coordinator::parse(const FQName& fqName, std::set<AST*>* parsedASTs,
215 Enforce enforcement) const {
216 AST* ret;
217 status_t err = parseOptional(fqName, &ret, parsedASTs, enforcement);
218 if (err != OK) CHECK(ret == nullptr); // internal consistency
219
220 // only in a handful of places do we want to distinguish between
221 // a missing file and a bad AST. Everywhere else, we just want to
222 // throw an error if we expect an AST to be present but it is not.
223 return ret;
224 }
225
parseOptional(const FQName & fqName,AST ** ast,std::set<AST * > * parsedASTs,Enforce enforcement) const226 status_t Coordinator::parseOptional(const FQName& fqName, AST** ast, std::set<AST*>* parsedASTs,
227 Enforce enforcement) const {
228 CHECK(fqName.isFullyQualified());
229
230 auto it = mCache.find(fqName);
231 if (it != mCache.end()) {
232 *ast = (*it).second;
233
234 if (*ast != nullptr && parsedASTs != nullptr) {
235 parsedASTs->insert(*ast);
236 }
237
238 if (*ast == nullptr) {
239 // circular import OR that AST has errors in it
240 return UNKNOWN_ERROR;
241 }
242
243 return OK;
244 }
245
246 // Add this to the cache immediately, so we can discover circular imports.
247 mCache[fqName] = nullptr;
248
249 std::string packagePath;
250 status_t err =
251 getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
252 if (err != OK) return err;
253
254 const std::string path = makeAbsolute(packagePath + fqName.name() + ".hal");
255
256 *ast = new AST(this, &Hash::getHash(path));
257
258 if (fqName.name() != "types") {
259 // If types.hal for this AST's package existed, make it's defined
260 // types available to the (about to be parsed) AST right away.
261 (*ast)->addImplicitImport(fqName.getTypesForPackage());
262 }
263
264 std::unique_ptr<FILE, std::function<void(FILE*)>> file(fopen(path.c_str(), "rb"), fclose);
265
266 if (file == nullptr) {
267 mCache.erase(fqName); // nullptr in cache is used to find circular imports
268 delete *ast;
269 *ast = nullptr;
270 return OK; // File does not exist, nullptr AST* == file doesn't exist.
271 }
272
273 onFileAccess(path, "r");
274
275 // parse file takes ownership of file
276 if (parseFile(*ast, std::move(file)) != OK || (*ast)->postParse() != OK) {
277 delete *ast;
278 *ast = nullptr;
279 return UNKNOWN_ERROR;
280 }
281
282 if ((*ast)->package().package() != fqName.package() ||
283 (*ast)->package().version() != fqName.version()) {
284 fprintf(stderr,
285 "ERROR: File at '%s' does not match expected package and/or "
286 "version.\n",
287 path.c_str());
288
289 err = UNKNOWN_ERROR;
290 } else {
291 if ((*ast)->isInterface()) {
292 if (fqName.name() == "types") {
293 fprintf(stderr,
294 "ERROR: File at '%s' declares an interface '%s' "
295 "instead of the expected types common to the package.\n",
296 path.c_str(), (*ast)->getInterface()->definedName().c_str());
297
298 err = UNKNOWN_ERROR;
299 } else if ((*ast)->getInterface()->definedName() != fqName.name()) {
300 fprintf(stderr,
301 "ERROR: File at '%s' does not declare interface type "
302 "'%s'.\n",
303 path.c_str(),
304 fqName.name().c_str());
305
306 err = UNKNOWN_ERROR;
307 }
308 } else if (fqName.name() != "types") {
309 fprintf(stderr,
310 "ERROR: File at '%s' declares types rather than the "
311 "expected interface type '%s'.\n",
312 path.c_str(),
313 fqName.name().c_str());
314
315 err = UNKNOWN_ERROR;
316 } else if ((*ast)->definesInterfaces()) {
317 fprintf(stderr,
318 "ERROR: types.hal file at '%s' declares at least one "
319 "interface type.\n",
320 path.c_str());
321
322 err = UNKNOWN_ERROR;
323 }
324 }
325
326 if (err != OK) {
327 delete *ast;
328 *ast = nullptr;
329 return err;
330 }
331
332 if (parsedASTs != nullptr) {
333 parsedASTs->insert(*ast);
334 }
335
336 // put it into the cache now, so that enforceRestrictionsOnPackage can
337 // parse fqName.
338 mCache[fqName] = *ast;
339
340 // For each .hal file that hidl-gen parses, the whole package will be checked.
341 err = enforceRestrictionsOnPackage(fqName, enforcement);
342 if (err != OK) {
343 mCache[fqName] = nullptr;
344 delete *ast;
345 *ast = nullptr;
346 return err;
347 }
348
349 return OK;
350 }
351
findPackageRoot(const FQName & fqName) const352 const Coordinator::PackageRoot* Coordinator::findPackageRoot(const FQName& fqName) const {
353 CHECK(!fqName.package().empty()) << fqName.string();
354
355 // Find the right package prefix and path for this FQName. For
356 // example, if FQName is "android.hardware.nfc@1.0::INfc", and the
357 // prefix:root is set to [ "android.hardware:hardware/interfaces",
358 // "vendor.qcom.hardware:vendor/qcom"], then we will identify the
359 // prefix "android.hardware" and the package root
360 // "hardware/interfaces".
361
362 auto ret = mPackageRoots.end();
363 for (auto it = mPackageRoots.begin(); it != mPackageRoots.end(); it++) {
364 if (!fqName.inPackage(it->root.package())) {
365 continue;
366 }
367
368 if (ret != mPackageRoots.end()) {
369 std::cerr << "ERROR: Multiple package roots found for " << fqName.string() << " ("
370 << it->root.package() << " and " << ret->root.package() << ")\n";
371 return nullptr;
372 }
373
374 ret = it;
375 }
376
377 if (ret == mPackageRoots.end()) {
378 std::cerr << "ERROR: Package root not specified for " << fqName.string() << "\n";
379 return nullptr;
380 }
381
382 return &(*ret);
383 }
384
makeAbsolute(const std::string & path) const385 std::string Coordinator::makeAbsolute(const std::string& path) const {
386 if (StringHelper::StartsWith(path, "/") || mRootPath.empty()) {
387 return path;
388 }
389
390 return mRootPath + path;
391 }
392
makeRelative(const std::string & filename) const393 std::string Coordinator::makeRelative(const std::string& filename) const {
394 return StringHelper::LTrim(filename, mRootPath);
395 }
396
getPackageRoot(const FQName & fqName,std::string * root) const397 status_t Coordinator::getPackageRoot(const FQName& fqName, std::string* root) const {
398 const PackageRoot* packageRoot = findPackageRoot(fqName);
399 if (packageRoot == nullptr) {
400 return UNKNOWN_ERROR;
401 }
402 *root = packageRoot->root.package();
403 return OK;
404 }
405
getPackageRootPath(const FQName & fqName,std::string * path) const406 status_t Coordinator::getPackageRootPath(const FQName& fqName, std::string* path) const {
407 const PackageRoot* packageRoot = findPackageRoot(fqName);
408 if (packageRoot == nullptr) {
409 return UNKNOWN_ERROR;
410 }
411 *path = packageRoot->path;
412 return OK;
413 }
414
getPackagePath(const FQName & fqName,bool relative,bool sanitized,std::string * path) const415 status_t Coordinator::getPackagePath(const FQName& fqName, bool relative, bool sanitized,
416 std::string* path) const {
417 const PackageRoot* packageRoot = findPackageRoot(fqName);
418 if (packageRoot == nullptr) return UNKNOWN_ERROR;
419
420 // Given FQName of "android.hardware.nfc.test@1.0::IFoo" and a prefix
421 // "android.hardware", the suffix is "nfc.test".
422 std::string suffix = StringHelper::LTrim(fqName.package(), packageRoot->root.package());
423 suffix = StringHelper::LTrim(suffix, ".");
424
425 std::vector<std::string> suffixComponents;
426 StringHelper::SplitString(suffix, '.', &suffixComponents);
427
428 std::vector<std::string> components;
429 if (!relative) {
430 components.push_back(StringHelper::RTrimAll(packageRoot->path, "/"));
431 }
432 components.insert(components.end(), suffixComponents.begin(), suffixComponents.end());
433 components.push_back(sanitized ? fqName.sanitizedVersion() : fqName.version());
434
435 *path = StringHelper::JoinStrings(components, "/") + "/";
436 return OK;
437 }
438
getPackageInterfaceFiles(const FQName & package,std::vector<std::string> * fileNames) const439 status_t Coordinator::getPackageInterfaceFiles(
440 const FQName &package,
441 std::vector<std::string> *fileNames) const {
442 if (fileNames) fileNames->clear();
443
444 std::string packagePath;
445 status_t err =
446 getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath);
447 if (err != OK) return err;
448
449 const std::string path = makeAbsolute(packagePath);
450 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
451
452 if (dir == nullptr) {
453 fprintf(stderr, "ERROR: Could not open package path %s for package %s:\n%s\n",
454 packagePath.c_str(), package.string().c_str(), path.c_str());
455 return -errno;
456 }
457
458 if (fileNames == nullptr) {
459 return OK;
460 }
461
462 struct dirent *ent;
463 while ((ent = readdir(dir.get())) != nullptr) {
464 // filesystems may not support d_type and return DT_UNKNOWN
465 if (ent->d_type == DT_UNKNOWN) {
466 struct stat sb;
467 const auto filename = packagePath + std::string(ent->d_name);
468 if (stat(filename.c_str(), &sb) == -1) {
469 fprintf(stderr, "ERROR: Could not stat %s\n", filename.c_str());
470 return -errno;
471 }
472 if ((sb.st_mode & S_IFMT) != S_IFREG) {
473 continue;
474 }
475 } else if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
476 continue;
477 }
478
479 const auto suffix = ".hal";
480 const auto suffix_len = std::strlen(suffix);
481 const auto d_namelen = strlen(ent->d_name);
482
483 if (d_namelen < suffix_len
484 || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) {
485 continue;
486 }
487
488 fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len));
489 }
490
491 std::sort(fileNames->begin(), fileNames->end(),
492 [](const std::string& lhs, const std::string& rhs) -> bool {
493 if (lhs == "types") {
494 return true;
495 }
496 if (rhs == "types") {
497 return false;
498 }
499 return lhs < rhs;
500 });
501
502 return OK;
503 }
504
appendPackageInterfacesToVector(const FQName & package,std::vector<FQName> * packageInterfaces) const505 status_t Coordinator::appendPackageInterfacesToVector(
506 const FQName &package,
507 std::vector<FQName> *packageInterfaces) const {
508 packageInterfaces->clear();
509
510 std::vector<std::string> fileNames;
511 status_t err = getPackageInterfaceFiles(package, &fileNames);
512
513 if (err != OK) {
514 return err;
515 }
516
517 for (const auto &fileName : fileNames) {
518 FQName subFQName(package.package(), package.version(), fileName);
519 packageInterfaces->push_back(subFQName);
520 }
521
522 return OK;
523 }
524
convertPackageRootToPath(const FQName & fqName,std::string * path) const525 status_t Coordinator::convertPackageRootToPath(const FQName& fqName, std::string* path) const {
526 std::string packageRoot;
527 status_t err = getPackageRoot(fqName, &packageRoot);
528 if (err != OK) return err;
529
530 if (*(packageRoot.end()--) != '.') {
531 packageRoot += '.';
532 }
533
534 std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
535
536 *path = packageRoot; // now converted to a path
537 return OK;
538 }
539
isTypesOnlyPackage(const FQName & package,bool * result) const540 status_t Coordinator::isTypesOnlyPackage(const FQName& package, bool* result) const {
541 std::vector<FQName> packageInterfaces;
542
543 status_t err = appendPackageInterfacesToVector(package, &packageInterfaces);
544
545 if (err != OK) {
546 *result = false;
547 return err;
548 }
549
550 *result = packageInterfaces.size() == 1 && packageInterfaces[0].name() == "types";
551 return OK;
552 }
553
addUnreferencedTypes(const std::vector<FQName> & packageInterfaces,std::set<FQName> * unreferencedDefinitions,std::set<FQName> * unreferencedImports) const554 status_t Coordinator::addUnreferencedTypes(const std::vector<FQName>& packageInterfaces,
555 std::set<FQName>* unreferencedDefinitions,
556 std::set<FQName>* unreferencedImports) const {
557 CHECK(unreferencedDefinitions != nullptr);
558 CHECK(unreferencedImports != nullptr);
559
560 std::set<FQName> packageDefinedTypes;
561 std::set<FQName> packageReferencedTypes;
562 std::set<FQName> packageImportedTypes;
563 std::set<FQName> typesDefinedTypes; // only types.hal types
564
565 for (const auto& fqName : packageInterfaces) {
566 AST* ast = parse(fqName, nullptr /*imported*/, Coordinator::Enforce::NONE);
567 if (!ast) {
568 std::cerr << "ERROR: Could not parse " << fqName.string() << ". Aborting." << std::endl;
569
570 return UNKNOWN_ERROR;
571 }
572
573 ast->addDefinedTypes(&packageDefinedTypes);
574 ast->addReferencedTypes(&packageReferencedTypes);
575 ast->getAllImportedNamesGranular(&packageImportedTypes);
576
577 if (fqName.name() == "types") {
578 ast->addDefinedTypes(&typesDefinedTypes);
579 }
580 }
581
582 #if 0
583 for (const auto &fqName : packageDefinedTypes) {
584 std::cout << "VERBOSE: DEFINED " << fqName.string() << std::endl;
585 }
586
587 for (const auto &fqName : packageImportedTypes) {
588 std::cout << "VERBOSE: IMPORTED " << fqName.string() << std::endl;
589 }
590
591 for (const auto &fqName : packageReferencedTypes) {
592 std::cout << "VERBOSE: REFERENCED " << fqName.string() << std::endl;
593 }
594
595 for (const auto &fqName : typesDefinedTypes) {
596 std::cout << "VERBOSE: DEFINED in types.hal " << fqName.string() << std::endl;
597 }
598 #endif
599
600 for (const auto& fqName : packageReferencedTypes) {
601 packageDefinedTypes.erase(fqName);
602 packageImportedTypes.erase(fqName);
603 }
604
605 // A package implicitly imports its own types.hal, only track them in one set.
606 for (const auto& fqName : typesDefinedTypes) {
607 packageImportedTypes.erase(fqName);
608 }
609
610 // defined but not referenced
611 unreferencedDefinitions->insert(packageDefinedTypes.begin(), packageDefinedTypes.end());
612 // imported but not referenced
613 unreferencedImports->insert(packageImportedTypes.begin(), packageImportedTypes.end());
614 return OK;
615 }
616
enforceRestrictionsOnPackage(const FQName & fqName,Enforce enforcement) const617 status_t Coordinator::enforceRestrictionsOnPackage(const FQName& fqName,
618 Enforce enforcement) const {
619 CHECK(enforcement == Enforce::FULL || enforcement == Enforce::NO_HASH ||
620 enforcement == Enforce::NONE);
621
622 // need fqName to be something like android.hardware.foo@1.0.
623 // name and valueName is ignored.
624 if (fqName.package().empty() || fqName.version().empty()) {
625 std::cerr << "ERROR: Cannot enforce restrictions on package " << fqName.string()
626 << ": package or version is missing." << std::endl;
627 return BAD_VALUE;
628 }
629
630 if (enforcement == Enforce::NONE) {
631 return OK;
632 }
633
634 FQName package = fqName.getPackageAndVersion();
635 // look up cache.
636 if (mPackagesEnforced.find(package) != mPackagesEnforced.end()) {
637 return OK;
638 }
639
640 // enforce all rules.
641 status_t err;
642
643 err = enforceMinorVersionUprevs(package, enforcement);
644 if (err != OK) {
645 return err;
646 }
647
648 if (enforcement != Enforce::NO_HASH) {
649 err = enforceHashes(package);
650 if (err != OK) {
651 return err;
652 }
653 }
654
655 // cache it so that it won't need to be enforced again.
656 mPackagesEnforced.insert(package);
657 return OK;
658 }
659
packageExists(const FQName & package,bool * result) const660 status_t Coordinator::packageExists(const FQName& package, bool* result) const {
661 std::string packagePath;
662 status_t err =
663 getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath);
664 if (err != OK) return err;
665
666 if (existdir(makeAbsolute(packagePath).c_str())) {
667 *result = true;
668 return OK;
669 }
670
671 *result = false;
672 return OK;
673 }
674
enforceMinorVersionUprevs(const FQName & currentPackage,Enforce enforcement) const675 status_t Coordinator::enforceMinorVersionUprevs(const FQName& currentPackage,
676 Enforce enforcement) const {
677 if(!currentPackage.hasVersion()) {
678 std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
679 << ": missing version." << std::endl;
680 return UNKNOWN_ERROR;
681 }
682
683 if (currentPackage.getPackageMinorVersion() == 0) {
684 return OK; // ignore for @x.0
685 }
686
687 bool hasPrevPackage = false;
688 FQName prevPackage = currentPackage;
689 while (prevPackage.getPackageMinorVersion() > 0) {
690 prevPackage = prevPackage.downRev();
691
692 bool result;
693 status_t err = packageExists(prevPackage, &result);
694 if (err != OK) return err;
695
696 if (result) {
697 hasPrevPackage = true;
698 break;
699 }
700 }
701 if (!hasPrevPackage) {
702 // no @x.z, where z < y, exist.
703 return OK;
704 }
705
706 if (prevPackage != currentPackage.downRev()) {
707 std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
708 << ": Found package " << prevPackage.string() << " but missing "
709 << currentPackage.downRev().string() << "; you cannot skip a minor version."
710 << std::endl;
711 return UNKNOWN_ERROR;
712 }
713
714 bool prevIsTypesOnly;
715 status_t err = isTypesOnlyPackage(prevPackage, &prevIsTypesOnly);
716 if (err != OK) return err;
717
718 if (prevIsTypesOnly) {
719 // A types only package can be extended in any way.
720 return OK;
721 }
722
723 std::vector<FQName> packageInterfaces;
724 err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
725 if (err != OK) {
726 return err;
727 }
728
729 bool extendedInterface = false;
730 for (const FQName ¤tFQName : packageInterfaces) {
731 if (currentFQName.name() == "types") {
732 continue; // ignore types.hal
733 }
734
735 const Interface *iface = nullptr;
736 AST* currentAST = parse(currentFQName, nullptr /* parsedASTs */, enforcement);
737 if (currentAST != nullptr) {
738 iface = currentAST->getInterface();
739 }
740 if (iface == nullptr) {
741 if (currentAST == nullptr) {
742 std::cerr << "WARNING: Skipping " << currentFQName.string()
743 << " because it could not be found or parsed"
744 << " or " << currentPackage.string() << " doesn't pass all requirements."
745 << std::endl;
746 } else {
747 std::cerr << "WARNING: Skipping " << currentFQName.string()
748 << " because the file might contain more than one interface."
749 << std::endl;
750 }
751 continue;
752 }
753
754 if (iface->superType() == nullptr) {
755 CHECK(iface->isIBase());
756 continue;
757 }
758
759 // Assume that currentFQName == android.hardware.foo@2.2::IFoo.
760 FQName lastFQName(prevPackage.package(), prevPackage.version(),
761 currentFQName.name());
762 AST *lastAST = parse(lastFQName);
763
764 for (; lastFQName.getPackageMinorVersion() > 0 &&
765 (lastAST == nullptr || lastAST->getInterface() == nullptr)
766 ; lastFQName = lastFQName.downRev(), lastAST = parse(lastFQName)) {
767 // nothing
768 }
769
770 // Then lastFQName == android.hardware.foo@2.1::IFoo or
771 // lastFQName == android.hardware.foo@2.0::IFoo if 2.1 doesn't exist.
772
773 bool lastFQNameExists = lastAST != nullptr && lastAST->getInterface() != nullptr;
774
775 if (!lastFQNameExists) {
776 continue;
777 }
778
779 if (iface->superType()->fqName() != lastFQName) {
780 std::cerr << "ERROR: Cannot enforce minor version uprevs for "
781 << currentPackage.string() << ": " << iface->fqName().string() << " extends "
782 << iface->superType()->fqName().string()
783 << ", which is not allowed. It must extend " << lastFQName.string()
784 << std::endl;
785 return UNKNOWN_ERROR;
786 }
787
788 // at least one interface must extend the previous version
789 // @2.0::IFoo does not work. It must be @2.1::IFoo for at least one interface.
790 if (lastFQName.getPackageAndVersion() == prevPackage.getPackageAndVersion()) {
791 extendedInterface = true;
792 }
793
794 if (mVerbose) {
795 std::cout << "VERBOSE: EnforceMinorVersionUprevs: " << currentFQName.string()
796 << " passes." << std::endl;
797 }
798 }
799
800 if (!extendedInterface) {
801 // No interface extends the interface with the same name in @x.(y-1).
802 std::cerr << "ERROR: " << currentPackage.string()
803 << " doesn't pass minor version uprev requirement. "
804 << "Requires at least one interface to extend an interface with the same name "
805 << "from " << prevPackage.string() << "." << std::endl;
806 return UNKNOWN_ERROR;
807 }
808
809 return OK;
810 }
811
checkHash(const FQName & fqName) const812 Coordinator::HashStatus Coordinator::checkHash(const FQName& fqName) const {
813 AST* ast = parse(fqName);
814 if (ast == nullptr) return HashStatus::ERROR;
815
816 std::string rootPath;
817 status_t err = getPackageRootPath(fqName, &rootPath);
818 if (err != OK) return HashStatus::ERROR;
819
820 std::string hashPath = makeAbsolute(rootPath) + "/current.txt";
821 std::string error;
822 bool fileExists;
823 std::vector<std::string> frozen =
824 Hash::lookupHash(hashPath, fqName.string(), &error, &fileExists);
825 if (fileExists) onFileAccess(hashPath, "r");
826
827 if (error.size() > 0) {
828 std::cerr << "ERROR: " << error << std::endl;
829 return HashStatus::ERROR;
830 }
831
832 // hash not defined, interface not frozen
833 if (frozen.size() == 0) {
834 if (isVerbose()) {
835 std::cerr << "VERBOSE: clearing runtime hash for " << fqName.string()
836 << " because it is not frozen and so its hash cannot be depended upon as an "
837 "indication of stability."
838 << std::endl;
839 }
840 // This ensures that it can be detected.
841 Hash::clearHash(ast->getFilename());
842
843 if (mRequireFrozen) {
844 std::cerr << "ERROR: Unfrozen " << fqName.string()
845 << " cannot be referenced from context specifying -F (require_frozen)."
846 << std::endl;
847 return HashStatus::ERROR;
848 }
849
850 return HashStatus::UNFROZEN;
851 }
852
853 std::string currentHash = ast->getFileHash()->hexString();
854
855 if (std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
856 std::cerr << "ERROR: " << fqName.string() << " has hash " << currentHash
857 << " which does not match hash on record. This interface has "
858 << "been frozen. Do not change it!" << std::endl;
859 return HashStatus::CHANGED;
860 }
861
862 return HashStatus::FROZEN;
863 }
864
getUnfrozenDependencies(const FQName & fqName,std::set<FQName> * result) const865 status_t Coordinator::getUnfrozenDependencies(const FQName& fqName,
866 std::set<FQName>* result) const {
867 CHECK(result != nullptr);
868
869 AST* ast = parse(fqName);
870 if (ast == nullptr) return UNKNOWN_ERROR;
871
872 std::set<FQName> imported;
873 ast->getImportedPackages(&imported);
874
875 // consider current package as dependency for types.hal and also to make
876 // sure that if anything is frozen in a package, everything is.
877 imported.insert(fqName.getPackageAndVersion());
878
879 // no circular dependency is already guaranteed by parsing
880 // indirect dependencies will be checked when the imported interface frozen checks are done
881 for (const FQName& importedPackage : imported) {
882 std::vector<FQName> packageInterfaces;
883 status_t err = appendPackageInterfacesToVector(importedPackage, &packageInterfaces);
884 if (err != OK) {
885 return err;
886 }
887
888 for (const FQName& importedName : packageInterfaces) {
889 HashStatus status = checkHash(importedName);
890 switch (status) {
891 case HashStatus::CHANGED:
892 case HashStatus::ERROR:
893 return UNKNOWN_ERROR;
894 case HashStatus::FROZEN:
895 continue;
896 case HashStatus::UNFROZEN:
897 result->insert(importedName);
898 continue;
899 default:
900 LOG(FATAL) << static_cast<uint64_t>(status);
901 }
902 }
903 }
904
905 return OK;
906 }
907
enforceHashes(const FQName & currentPackage) const908 status_t Coordinator::enforceHashes(const FQName& currentPackage) const {
909 std::vector<FQName> packageInterfaces;
910 status_t err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
911 if (err != OK) {
912 return err;
913 }
914
915 for (const FQName& currentFQName : packageInterfaces) {
916 HashStatus status = checkHash(currentFQName);
917 switch (status) {
918 case HashStatus::CHANGED:
919 case HashStatus::ERROR:
920 return UNKNOWN_ERROR;
921 case HashStatus::FROZEN: {
922 std::set<FQName> unfrozenDependencies;
923 err = getUnfrozenDependencies(currentFQName, &unfrozenDependencies);
924 if (err != OK) return err;
925
926 if (!unfrozenDependencies.empty()) {
927 std::cerr << "ERROR: Frozen interface " << currentFQName.string()
928 << " cannot depend or be adjacent to unfrozen thing(s):" << std::endl;
929 for (const FQName& name : unfrozenDependencies) {
930 std::cerr << " (unfrozen) " << name.string() << std::endl;
931 }
932 return UNKNOWN_ERROR;
933 }
934 }
935 continue;
936 case HashStatus::UNFROZEN:
937 continue;
938 default:
939 LOG(FATAL) << static_cast<uint64_t>(status);
940 }
941 }
942
943 return err;
944 }
945
MakeParentHierarchy(const std::string & path)946 bool Coordinator::MakeParentHierarchy(const std::string &path) {
947 static const mode_t kMode = 0755;
948
949 size_t start = 1; // Ignore leading '/'
950 size_t slashPos;
951 while ((slashPos = path.find('/', start)) != std::string::npos) {
952 std::string partial = path.substr(0, slashPos);
953
954 struct stat st;
955 if (stat(partial.c_str(), &st) < 0) {
956 if (errno != ENOENT) {
957 return false;
958 }
959
960 int res = mkdir(partial.c_str(), kMode);
961 if (res < 0) {
962 return false;
963 }
964 } else if (!S_ISDIR(st.st_mode)) {
965 return false;
966 }
967
968 start = slashPos + 1;
969 }
970
971 return true;
972 }
973
emitOptionsUsageString(Formatter & out)974 void Coordinator::emitOptionsUsageString(Formatter& out) {
975 out << "[-p <root path>] (-r <interface root>)+ [-R] [-F] [-v] [-d <depfile>]";
976 }
977
emitOptionsDetailString(Formatter & out)978 void Coordinator::emitOptionsDetailString(Formatter& out) {
979 out << "-p <root path>: Android build root, defaults to $ANDROID_BUILD_TOP or pwd.\n"
980 << "-R: Do not add default package roots if not specified in -r.\n"
981 << "-F: Require all referenced ASTs to be frozen.\n"
982 << "-r <package:path root>: E.g., android.hardware:hardware/interfaces.\n"
983 << "-v: verbose output.\n"
984 << "-d <depfile>: location of depfile to write to.\n";
985 }
986
parseOptions(int argc,char ** argv,const std::string & options,const HandleArg & handleArg)987 void Coordinator::parseOptions(int argc, char** argv, const std::string& options,
988 const HandleArg& handleArg) {
989 // reset global state for getopt
990 optind = 1;
991
992 bool suppressDefaultPackagePaths = false;
993
994 int res;
995 std::string optstr = options + "p:r:RFvd:";
996 while ((res = getopt(argc, argv, optstr.c_str())) >= 0) {
997 switch (res) {
998 case 'v': {
999 setVerbose(true);
1000 break;
1001 }
1002 case 'd': {
1003 setDepFile(optarg);
1004 break;
1005 }
1006 case 'p': {
1007 if (!getRootPath().empty()) {
1008 fprintf(stderr, "ERROR: -p <root path> can only be specified once.\n");
1009 exit(1);
1010 }
1011 setRootPath(optarg);
1012 break;
1013 }
1014 case 'r': {
1015 std::string val(optarg);
1016 auto index = val.find_first_of(':');
1017 if (index == std::string::npos) {
1018 fprintf(stderr, "ERROR: -r option must contain ':': %s\n", val.c_str());
1019 exit(1);
1020 }
1021
1022 auto root = val.substr(0, index);
1023 auto path = val.substr(index + 1);
1024
1025 std::string error;
1026 status_t err = addPackagePath(root, path, &error);
1027 if (err != OK) {
1028 fprintf(stderr, "%s\n", error.c_str());
1029 exit(1);
1030 }
1031
1032 break;
1033 }
1034 case 'R': {
1035 suppressDefaultPackagePaths = true;
1036 break;
1037 }
1038 case 'F': {
1039 setRequireFrozen(true);
1040 break;
1041 }
1042 // something downstream should handle these cases
1043 default: { handleArg(res, optarg); }
1044 }
1045 // glibc sets optarg to NULL for options without an argument, but POSIX doesn't
1046 // require it musl libc does not. Reset it here so that the next call to
1047 // handleArg doesn't pass a stale value.
1048 optarg = nullptr;
1049 }
1050
1051 if (getRootPath().empty()) {
1052 const char* ANDROID_BUILD_TOP = getenv("ANDROID_BUILD_TOP");
1053 if (ANDROID_BUILD_TOP != nullptr) {
1054 setRootPath(ANDROID_BUILD_TOP);
1055 }
1056 }
1057
1058 if (!suppressDefaultPackagePaths) {
1059 addDefaultPackagePath("android.hardware", "hardware/interfaces");
1060 addDefaultPackagePath("android.hidl", "system/libhidl/transport");
1061 addDefaultPackagePath("android.frameworks", "frameworks/hardware/interfaces");
1062 addDefaultPackagePath("android.system", "system/hardware/interfaces");
1063 }
1064 }
1065
1066 } // namespace android
1067
1068