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