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/StringHelper.h>
28 #include <iostream>
29
30 #include "AST.h"
31 #include "Interface.h"
32 #include "hidl-gen_l.h"
33
existdir(const char * name)34 static bool existdir(const char *name) {
35 DIR *dir = opendir(name);
36 if (dir == NULL) {
37 return false;
38 }
39 closedir(dir);
40 return true;
41 }
42
43 namespace android {
44
getRootPath() const45 const std::string &Coordinator::getRootPath() const {
46 return mRootPath;
47 }
48
setRootPath(const std::string & rootPath)49 void Coordinator::setRootPath(const std::string &rootPath) {
50 mRootPath = rootPath;
51
52 if (!mRootPath.empty() && !StringHelper::EndsWith(mRootPath, "/")) {
53 mRootPath += "/";
54 }
55 }
56
setOutputPath(const std::string & outputPath)57 void Coordinator::setOutputPath(const std::string& outputPath) {
58 mOutputPath = outputPath;
59 }
60
setVerbose(bool verbose)61 void Coordinator::setVerbose(bool verbose) {
62 mVerbose = verbose;
63 }
64
isVerbose() const65 bool Coordinator::isVerbose() const {
66 return mVerbose;
67 }
68
setDepFile(const std::string & depFile)69 void Coordinator::setDepFile(const std::string& depFile) {
70 mDepFile = depFile;
71 }
72
getOwner() const73 const std::string& Coordinator::getOwner() const {
74 return mOwner;
75 }
setOwner(const std::string & owner)76 void Coordinator::setOwner(const std::string& owner) {
77 mOwner = owner;
78 }
79
addPackagePath(const std::string & root,const std::string & path,std::string * error)80 status_t Coordinator::addPackagePath(const std::string& root, const std::string& path, std::string* error) {
81 FQName package = FQName(root, "0.0", "");
82 for (const PackageRoot &packageRoot : mPackageRoots) {
83 if (packageRoot.root.inPackage(root) || package.inPackage(packageRoot.root.package())) {
84 if (error != nullptr) {
85 *error = "ERROR: conflicting package roots " +
86 packageRoot.root.package() +
87 " and " +
88 root;
89 }
90
91 return UNKNOWN_ERROR;
92 }
93 }
94
95 mPackageRoots.push_back({path, package});
96 return OK;
97 }
addDefaultPackagePath(const std::string & root,const std::string & path)98 void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) {
99 addPackagePath(root, path, nullptr /* error */);
100 }
101
getFormatter(const FQName & fqName,Location location,const std::string & fileName) const102 Formatter Coordinator::getFormatter(const FQName& fqName, Location location,
103 const std::string& fileName) const {
104 if (location == Location::STANDARD_OUT) {
105 return Formatter(stdout);
106 }
107
108 std::string filepath;
109 status_t err = getFilepath(fqName, location, fileName, &filepath);
110 if (err != OK) {
111 return Formatter::invalid();
112 }
113
114 onFileAccess(filepath, "w");
115
116 if (!Coordinator::MakeParentHierarchy(filepath)) {
117 fprintf(stderr, "ERROR: could not make directories for %s.\n", filepath.c_str());
118 return Formatter::invalid();
119 }
120
121 FILE* file = fopen(filepath.c_str(), "w");
122
123 if (file == nullptr) {
124 fprintf(stderr, "ERROR: could not open file %s: %d\n", filepath.c_str(), errno);
125 return Formatter::invalid();
126 }
127
128 return Formatter(file);
129 }
130
getFilepath(const FQName & fqName,Location location,const std::string & fileName,std::string * path) const131 status_t Coordinator::getFilepath(const FQName& fqName, Location location,
132 const std::string& fileName, std::string* path) const {
133 status_t err;
134 std::string packagePath;
135 std::string packageRootPath;
136
137 switch (location) {
138 case Location::DIRECT: { /* nothing */
139 *path = mOutputPath + fileName;
140 } break;
141 case Location::PACKAGE_ROOT: {
142 err = getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
143 if (err != OK) return err;
144
145 *path = mOutputPath + packagePath + fileName;
146 } break;
147 case Location::GEN_OUTPUT: {
148 err = convertPackageRootToPath(fqName, &packageRootPath);
149 if (err != OK) return err;
150 err = getPackagePath(fqName, true /* relative */, false /* sanitized */, &packagePath);
151 if (err != OK) return err;
152
153 *path = mOutputPath + packageRootPath + packagePath + fileName;
154 } break;
155 case Location::GEN_SANITIZED: {
156 err = convertPackageRootToPath(fqName, &packageRootPath);
157 if (err != OK) return err;
158 err = getPackagePath(fqName, true /* relative */, true /* sanitized */, &packagePath);
159 if (err != OK) return err;
160
161 *path = mOutputPath + packageRootPath + packagePath + fileName;
162 } break;
163 default: { CHECK(false) << "Invalid location: " << static_cast<size_t>(location); }
164 }
165
166 return OK;
167 }
168
onFileAccess(const std::string & path,const std::string & mode) const169 void Coordinator::onFileAccess(const std::string& path, const std::string& mode) const {
170 if (mode == "r") {
171 // This is a global list. It's not cleared when a second fqname is processed for
172 // two reasons:
173 // 1). If there is a bug in hidl-gen, the dependencies on the first project from
174 // the second would be required to recover correctly when the bug is fixed.
175 // 2). This option is never used in Android builds.
176 mReadFiles.insert(StringHelper::LTrim(path, mRootPath));
177 }
178
179 if (!mVerbose) {
180 return;
181 }
182
183 fprintf(stderr,
184 "VERBOSE: file access %s %s\n", path.c_str(), mode.c_str());
185 }
186
writeDepFile(const std::string & forFile) const187 status_t Coordinator::writeDepFile(const std::string& forFile) const {
188 // No dep file requested
189 if (mDepFile.empty()) return OK;
190
191 onFileAccess(mDepFile, "w");
192
193 FILE* file = fopen(mDepFile.c_str(), "w");
194 if (file == nullptr) {
195 fprintf(stderr, "ERROR: could not open dep file at %s.\n", mDepFile.c_str());
196 return UNKNOWN_ERROR;
197 }
198
199 Formatter out(file, 2 /* spacesPerIndent */);
200 out << StringHelper::LTrim(forFile, mOutputPath) << ": \\\n";
201 out.indent([&] {
202 for (const std::string& file : mReadFiles) {
203 out << StringHelper::LTrim(file, mRootPath) << " \\\n";
204 }
205 });
206 return OK;
207 }
208
parse(const FQName & fqName,std::set<AST * > * parsedASTs,Enforce enforcement) const209 AST* Coordinator::parse(const FQName& fqName, std::set<AST*>* parsedASTs,
210 Enforce enforcement) const {
211 AST* ret;
212 status_t err = parseOptional(fqName, &ret, parsedASTs, enforcement);
213 if (err != OK) CHECK(ret == nullptr); // internal consistency
214
215 // only in a handful of places do we want to distinguish between
216 // a missing file and a bad AST. Everywhere else, we just want to
217 // throw an error if we expect an AST to be present but it is not.
218 return ret;
219 }
220
parseOptional(const FQName & fqName,AST ** ast,std::set<AST * > * parsedASTs,Enforce enforcement) const221 status_t Coordinator::parseOptional(const FQName& fqName, AST** ast, std::set<AST*>* parsedASTs,
222 Enforce enforcement) const {
223 CHECK(fqName.isFullyQualified());
224
225 auto it = mCache.find(fqName);
226 if (it != mCache.end()) {
227 *ast = (*it).second;
228
229 if (*ast != nullptr && parsedASTs != nullptr) {
230 parsedASTs->insert(*ast);
231 }
232
233 if (*ast == nullptr) {
234 // circular import OR that AST has errors in it
235 return UNKNOWN_ERROR;
236 }
237
238 return OK;
239 }
240
241 // Add this to the cache immediately, so we can discover circular imports.
242 mCache[fqName] = nullptr;
243
244 AST *typesAST = nullptr;
245
246 if (fqName.name() != "types") {
247 // Any interface file implicitly imports its package's types.hal.
248 FQName typesName = fqName.getTypesForPackage();
249 // Do not enforce on imports. Do not add imports' imports to this AST.
250 status_t err = parseOptional(typesName, &typesAST, nullptr, Enforce::NONE);
251 if (err != OK) return err;
252
253 // fall through.
254 }
255
256 std::string packagePath;
257 status_t err =
258 getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
259 if (err != OK) return err;
260
261 const std::string path = makeAbsolute(packagePath + fqName.name() + ".hal");
262
263 *ast = new AST(this, &Hash::getHash(path));
264
265 if (typesAST != NULL) {
266 // If types.hal for this AST's package existed, make it's defined
267 // types available to the (about to be parsed) AST right away.
268 (*ast)->addImportedAST(typesAST);
269 }
270
271 std::unique_ptr<FILE, std::function<void(FILE*)>> file(fopen(path.c_str(), "rb"), fclose);
272
273 if (file == nullptr) {
274 mCache.erase(fqName); // nullptr in cache is used to find circular imports
275 delete *ast;
276 *ast = nullptr;
277 return OK; // File does not exist, nullptr AST* == file doesn't exist.
278 }
279
280 onFileAccess(path, "r");
281
282 // parse file takes ownership of file
283 if (parseFile(*ast, std::move(file)) != OK || (*ast)->postParse() != OK) {
284 delete *ast;
285 *ast = nullptr;
286 return UNKNOWN_ERROR;
287 }
288
289 if ((*ast)->package().package() != fqName.package() ||
290 (*ast)->package().version() != fqName.version()) {
291 fprintf(stderr,
292 "ERROR: File at '%s' does not match expected package and/or "
293 "version.\n",
294 path.c_str());
295
296 err = UNKNOWN_ERROR;
297 } else {
298 if ((*ast)->isInterface()) {
299 if (fqName.name() == "types") {
300 fprintf(stderr,
301 "ERROR: File at '%s' declares an interface '%s' "
302 "instead of the expected types common to the package.\n",
303 path.c_str(), (*ast)->getInterface()->localName().c_str());
304
305 err = UNKNOWN_ERROR;
306 } else if ((*ast)->getInterface()->localName() != fqName.name()) {
307 fprintf(stderr,
308 "ERROR: File at '%s' does not declare interface type "
309 "'%s'.\n",
310 path.c_str(),
311 fqName.name().c_str());
312
313 err = UNKNOWN_ERROR;
314 }
315 } else if (fqName.name() != "types") {
316 fprintf(stderr,
317 "ERROR: File at '%s' declares types rather than the "
318 "expected interface type '%s'.\n",
319 path.c_str(),
320 fqName.name().c_str());
321
322 err = UNKNOWN_ERROR;
323 } else if ((*ast)->containsInterfaces()) {
324 fprintf(stderr,
325 "ERROR: types.hal file at '%s' declares at least one "
326 "interface type.\n",
327 path.c_str());
328
329 err = UNKNOWN_ERROR;
330 }
331 }
332
333 if (err != OK) {
334 delete *ast;
335 *ast = nullptr;
336 return err;
337 }
338
339 if (parsedASTs != nullptr) {
340 parsedASTs->insert(*ast);
341 }
342
343 // put it into the cache now, so that enforceRestrictionsOnPackage can
344 // parse fqName.
345 mCache[fqName] = *ast;
346
347 // For each .hal file that hidl-gen parses, the whole package will be checked.
348 err = enforceRestrictionsOnPackage(fqName, enforcement);
349 if (err != OK) {
350 mCache[fqName] = nullptr;
351 delete *ast;
352 *ast = nullptr;
353 return err;
354 }
355
356 return OK;
357 }
358
findPackageRoot(const FQName & fqName) const359 const Coordinator::PackageRoot* Coordinator::findPackageRoot(const FQName& fqName) const {
360 CHECK(!fqName.package().empty());
361
362 // Find the right package prefix and path for this FQName. For
363 // example, if FQName is "android.hardware.nfc@1.0::INfc", and the
364 // prefix:root is set to [ "android.hardware:hardware/interfaces",
365 // "vendor.qcom.hardware:vendor/qcom"], then we will identify the
366 // prefix "android.hardware" and the package root
367 // "hardware/interfaces".
368
369 auto ret = mPackageRoots.end();
370 for (auto it = mPackageRoots.begin(); it != mPackageRoots.end(); it++) {
371 if (!fqName.inPackage(it->root.package())) {
372 continue;
373 }
374
375 if (ret != mPackageRoots.end()) {
376 std::cerr << "ERROR: Multiple package roots found for " << fqName.string() << " ("
377 << it->root.package() << " and " << ret->root.package() << ")\n";
378 return nullptr;
379 }
380
381 ret = it;
382 }
383
384 if (ret == mPackageRoots.end()) {
385 std::cerr << "ERROR: Package root not specified for " << fqName.string() << "\n";
386 return nullptr;
387 }
388
389 return &(*ret);
390 }
391
makeAbsolute(const std::string & path) const392 std::string Coordinator::makeAbsolute(const std::string& path) const {
393 if (StringHelper::StartsWith(path, "/") || mRootPath.empty()) {
394 return path;
395 }
396
397 return mRootPath + path;
398 }
399
getPackageRoot(const FQName & fqName,std::string * root) const400 status_t Coordinator::getPackageRoot(const FQName& fqName, std::string* root) const {
401 const PackageRoot* packageRoot = findPackageRoot(fqName);
402 if (root == nullptr) {
403 return UNKNOWN_ERROR;
404 }
405 *root = packageRoot->root.package();
406 return OK;
407 }
408
getPackageRootPath(const FQName & fqName,std::string * path) const409 status_t Coordinator::getPackageRootPath(const FQName& fqName, std::string* path) const {
410 const PackageRoot* packageRoot = findPackageRoot(fqName);
411 if (packageRoot == nullptr) {
412 return UNKNOWN_ERROR;
413 }
414 *path = packageRoot->path;
415 return OK;
416 }
417
getPackagePath(const FQName & fqName,bool relative,bool sanitized,std::string * path) const418 status_t Coordinator::getPackagePath(const FQName& fqName, bool relative, bool sanitized,
419 std::string* path) const {
420 const PackageRoot* packageRoot = findPackageRoot(fqName);
421 if (packageRoot == nullptr) return UNKNOWN_ERROR;
422
423 // Given FQName of "android.hardware.nfc.test@1.0::IFoo" and a prefix
424 // "android.hardware", the suffix is "nfc.test".
425 std::string suffix = StringHelper::LTrim(fqName.package(), packageRoot->root.package());
426 suffix = StringHelper::LTrim(suffix, ".");
427
428 std::vector<std::string> suffixComponents;
429 StringHelper::SplitString(suffix, '.', &suffixComponents);
430
431 std::vector<std::string> components;
432 if (!relative) {
433 components.push_back(StringHelper::RTrimAll(packageRoot->path, "/"));
434 }
435 components.insert(components.end(), suffixComponents.begin(), suffixComponents.end());
436 components.push_back(sanitized ? fqName.sanitizedVersion() : fqName.version());
437
438 *path = StringHelper::JoinStrings(components, "/") + "/";
439 return OK;
440 }
441
getPackageInterfaceFiles(const FQName & package,std::vector<std::string> * fileNames) const442 status_t Coordinator::getPackageInterfaceFiles(
443 const FQName &package,
444 std::vector<std::string> *fileNames) const {
445 fileNames->clear();
446
447 std::string packagePath;
448 status_t err =
449 getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath);
450 if (err != OK) return err;
451
452 const std::string path = makeAbsolute(packagePath);
453 DIR* dir = opendir(path.c_str());
454
455 if (dir == NULL) {
456 fprintf(stderr, "ERROR: Could not open package path %s for package %s:\n%s\n",
457 packagePath.c_str(), package.string().c_str(), path.c_str());
458 return -errno;
459 }
460
461 struct dirent *ent;
462 while ((ent = readdir(dir)) != NULL) {
463 if (ent->d_type != DT_REG) {
464 continue;
465 }
466
467 const auto suffix = ".hal";
468 const auto suffix_len = std::strlen(suffix);
469 const auto d_namelen = strlen(ent->d_name);
470
471 if (d_namelen < suffix_len
472 || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) {
473 continue;
474 }
475
476 fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len));
477 }
478
479 closedir(dir);
480 dir = NULL;
481
482 std::sort(fileNames->begin(), fileNames->end(),
483 [](const std::string& lhs, const std::string& rhs) -> bool {
484 if (lhs == "types") {
485 return true;
486 }
487 if (rhs == "types") {
488 return false;
489 }
490 return lhs < rhs;
491 });
492
493 return OK;
494 }
495
appendPackageInterfacesToVector(const FQName & package,std::vector<FQName> * packageInterfaces) const496 status_t Coordinator::appendPackageInterfacesToVector(
497 const FQName &package,
498 std::vector<FQName> *packageInterfaces) const {
499 packageInterfaces->clear();
500
501 std::vector<std::string> fileNames;
502 status_t err = getPackageInterfaceFiles(package, &fileNames);
503
504 if (err != OK) {
505 return err;
506 }
507
508 for (const auto &fileName : fileNames) {
509 FQName subFQName(package.package(), package.version(), fileName);
510 packageInterfaces->push_back(subFQName);
511 }
512
513 return OK;
514 }
515
convertPackageRootToPath(const FQName & fqName,std::string * path) const516 status_t Coordinator::convertPackageRootToPath(const FQName& fqName, std::string* path) const {
517 std::string packageRoot;
518 status_t err = getPackageRoot(fqName, &packageRoot);
519 if (err != OK) return err;
520
521 if (*(packageRoot.end()--) != '.') {
522 packageRoot += '.';
523 }
524
525 std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
526
527 *path = packageRoot; // now converted to a path
528 return OK;
529 }
530
isTypesOnlyPackage(const FQName & package,bool * result) const531 status_t Coordinator::isTypesOnlyPackage(const FQName& package, bool* result) const {
532 std::vector<FQName> packageInterfaces;
533
534 status_t err = appendPackageInterfacesToVector(package, &packageInterfaces);
535
536 if (err != OK) {
537 *result = false;
538 return err;
539 }
540
541 *result = packageInterfaces.size() == 1 && packageInterfaces[0].name() == "types";
542 return OK;
543 }
544
addUnreferencedTypes(const std::vector<FQName> & packageInterfaces,std::set<FQName> * unreferencedDefinitions,std::set<FQName> * unreferencedImports) const545 status_t Coordinator::addUnreferencedTypes(const std::vector<FQName>& packageInterfaces,
546 std::set<FQName>* unreferencedDefinitions,
547 std::set<FQName>* unreferencedImports) const {
548 CHECK(unreferencedDefinitions != nullptr);
549 CHECK(unreferencedImports != nullptr);
550
551 std::set<FQName> packageDefinedTypes;
552 std::set<FQName> packageReferencedTypes;
553 std::set<FQName> packageImportedTypes;
554 std::set<FQName> typesDefinedTypes; // only types.hal types
555
556 for (const auto& fqName : packageInterfaces) {
557 AST* ast = parse(fqName);
558 if (!ast) {
559 std::cerr << "ERROR: Could not parse " << fqName.string() << ". Aborting." << std::endl;
560
561 return UNKNOWN_ERROR;
562 }
563
564 ast->addDefinedTypes(&packageDefinedTypes);
565 ast->addReferencedTypes(&packageReferencedTypes);
566 ast->getAllImportedNamesGranular(&packageImportedTypes);
567
568 if (fqName.name() == "types") {
569 ast->addDefinedTypes(&typesDefinedTypes);
570 }
571 }
572
573 #if 0
574 for (const auto &fqName : packageDefinedTypes) {
575 std::cout << "VERBOSE: DEFINED " << fqName.string() << std::endl;
576 }
577
578 for (const auto &fqName : packageImportedTypes) {
579 std::cout << "VERBOSE: IMPORTED " << fqName.string() << std::endl;
580 }
581
582 for (const auto &fqName : packageReferencedTypes) {
583 std::cout << "VERBOSE: REFERENCED " << fqName.string() << std::endl;
584 }
585
586 for (const auto &fqName : typesDefinedTypes) {
587 std::cout << "VERBOSE: DEFINED in types.hal " << fqName.string() << std::endl;
588 }
589 #endif
590
591 for (const auto& fqName : packageReferencedTypes) {
592 packageDefinedTypes.erase(fqName);
593 packageImportedTypes.erase(fqName);
594 }
595
596 // A package implicitly imports its own types.hal, only track them in one set.
597 for (const auto& fqName : typesDefinedTypes) {
598 packageImportedTypes.erase(fqName);
599 }
600
601 // defined but not referenced
602 unreferencedDefinitions->insert(packageDefinedTypes.begin(), packageDefinedTypes.end());
603 // imported but not referenced
604 unreferencedImports->insert(packageImportedTypes.begin(), packageImportedTypes.end());
605 return OK;
606 }
607
enforceRestrictionsOnPackage(const FQName & fqName,Enforce enforcement) const608 status_t Coordinator::enforceRestrictionsOnPackage(const FQName& fqName,
609 Enforce enforcement) const {
610 CHECK(enforcement == Enforce::FULL || enforcement == Enforce::NO_HASH ||
611 enforcement == Enforce::NONE);
612
613 // need fqName to be something like android.hardware.foo@1.0.
614 // name and valueName is ignored.
615 if (fqName.package().empty() || fqName.version().empty()) {
616 std::cerr << "ERROR: Cannot enforce restrictions on package " << fqName.string()
617 << ": package or version is missing." << std::endl;
618 return BAD_VALUE;
619 }
620
621 if (enforcement == Enforce::NONE) {
622 return OK;
623 }
624
625 FQName package = fqName.getPackageAndVersion();
626 // look up cache.
627 if (mPackagesEnforced.find(package) != mPackagesEnforced.end()) {
628 return OK;
629 }
630
631 // enforce all rules.
632 status_t err;
633
634 err = enforceMinorVersionUprevs(package, enforcement);
635 if (err != OK) {
636 return err;
637 }
638
639 if (enforcement != Enforce::NO_HASH) {
640 err = enforceHashes(package);
641 if (err != OK) {
642 return err;
643 }
644 }
645
646 // cache it so that it won't need to be enforced again.
647 mPackagesEnforced.insert(package);
648 return OK;
649 }
650
enforceMinorVersionUprevs(const FQName & currentPackage,Enforce enforcement) const651 status_t Coordinator::enforceMinorVersionUprevs(const FQName& currentPackage,
652 Enforce enforcement) const {
653 if(!currentPackage.hasVersion()) {
654 std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
655 << ": missing version." << std::endl;
656 return UNKNOWN_ERROR;
657 }
658
659 if (currentPackage.getPackageMinorVersion() == 0) {
660 return OK; // ignore for @x.0
661 }
662
663 bool hasPrevPackage = false;
664 FQName prevPackage = currentPackage;
665 while (prevPackage.getPackageMinorVersion() > 0) {
666 prevPackage = prevPackage.downRev();
667
668 std::string prevPackagePath;
669 status_t err = getPackagePath(prevPackage, false /* relative */, false /* sanitized */,
670 &prevPackagePath);
671 if (err != OK) return err;
672
673 if (existdir(makeAbsolute(prevPackagePath).c_str())) {
674 hasPrevPackage = true;
675 break;
676 }
677 }
678 if (!hasPrevPackage) {
679 // no @x.z, where z < y, exist.
680 return OK;
681 }
682
683 if (prevPackage != currentPackage.downRev()) {
684 std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
685 << ": Found package " << prevPackage.string() << " but missing "
686 << currentPackage.downRev().string() << "; you cannot skip a minor version."
687 << std::endl;
688 return UNKNOWN_ERROR;
689 }
690
691 bool prevIsTypesOnly;
692 status_t err = isTypesOnlyPackage(prevPackage, &prevIsTypesOnly);
693 if (err != OK) return err;
694
695 if (prevIsTypesOnly) {
696 // A types only package can be extended in any way.
697 return OK;
698 }
699
700 std::vector<FQName> packageInterfaces;
701 err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
702 if (err != OK) {
703 return err;
704 }
705
706 bool extendedInterface = false;
707 for (const FQName ¤tFQName : packageInterfaces) {
708 if (currentFQName.name() == "types") {
709 continue; // ignore types.hal
710 }
711
712 const Interface *iface = nullptr;
713 AST* currentAST = parse(currentFQName, nullptr /* parsedASTs */, enforcement);
714 if (currentAST != nullptr) {
715 iface = currentAST->getInterface();
716 }
717 if (iface == nullptr) {
718 if (currentAST == nullptr) {
719 std::cerr << "WARNING: Skipping " << currentFQName.string()
720 << " because it could not be found or parsed"
721 << " or " << currentPackage.string() << " doesn't pass all requirements."
722 << std::endl;
723 } else {
724 std::cerr << "WARNING: Skipping " << currentFQName.string()
725 << " because the file might contain more than one interface."
726 << std::endl;
727 }
728 continue;
729 }
730
731 if (iface->superType() == nullptr) {
732 CHECK(iface->isIBase());
733 continue;
734 }
735
736 // Assume that currentFQName == android.hardware.foo@2.2::IFoo.
737 FQName lastFQName(prevPackage.package(), prevPackage.version(),
738 currentFQName.name());
739 AST *lastAST = parse(lastFQName);
740
741 for (; lastFQName.getPackageMinorVersion() > 0 &&
742 (lastAST == nullptr || lastAST->getInterface() == nullptr)
743 ; lastFQName = lastFQName.downRev(), lastAST = parse(lastFQName)) {
744 // nothing
745 }
746
747 // Then lastFQName == android.hardware.foo@2.1::IFoo or
748 // lastFQName == android.hardware.foo@2.0::IFoo if 2.1 doesn't exist.
749
750 bool lastFQNameExists = lastAST != nullptr && lastAST->getInterface() != nullptr;
751
752 if (!lastFQNameExists) {
753 continue;
754 }
755
756 if (iface->superType()->fqName() != lastFQName) {
757 std::cerr << "ERROR: Cannot enforce minor version uprevs for "
758 << currentPackage.string() << ": " << iface->fqName().string() << " extends "
759 << iface->superType()->fqName().string()
760 << ", which is not allowed. It must extend " << lastFQName.string()
761 << std::endl;
762 return UNKNOWN_ERROR;
763 }
764
765 // at least one interface must extend the previous version
766 // @2.0::IFoo does not work. It must be @2.1::IFoo for at least one interface.
767 if (lastFQName.getPackageAndVersion() == prevPackage.getPackageAndVersion()) {
768 extendedInterface = true;
769 }
770
771 if (mVerbose) {
772 std::cout << "VERBOSE: EnforceMinorVersionUprevs: " << currentFQName.string()
773 << " passes." << std::endl;
774 }
775 }
776
777 if (!extendedInterface) {
778 // No interface extends the interface with the same name in @x.(y-1).
779 std::cerr << "ERROR: " << currentPackage.string()
780 << " doesn't pass minor version uprev requirement. "
781 << "Requires at least one interface to extend an interface with the same name "
782 << "from " << prevPackage.string() << "." << std::endl;
783 return UNKNOWN_ERROR;
784 }
785
786 return OK;
787 }
788
checkHash(const FQName & fqName) const789 Coordinator::HashStatus Coordinator::checkHash(const FQName& fqName) const {
790 AST* ast = parse(fqName);
791 if (ast == nullptr) return HashStatus::ERROR;
792
793 std::string rootPath;
794 status_t err = getPackageRootPath(fqName, &rootPath);
795 if (err != OK) return HashStatus::ERROR;
796
797 std::string hashPath = makeAbsolute(rootPath) + "/current.txt";
798 std::string error;
799 bool fileExists;
800 std::vector<std::string> frozen =
801 Hash::lookupHash(hashPath, fqName.string(), &error, &fileExists);
802 if (fileExists) onFileAccess(hashPath, "r");
803
804 if (error.size() > 0) {
805 std::cerr << "ERROR: " << error << std::endl;
806 return HashStatus::ERROR;
807 }
808
809 // hash not defined, interface not frozen
810 if (frozen.size() == 0) {
811 // This ensures that it can be detected.
812 Hash::clearHash(ast->getFilename());
813
814 return HashStatus::UNFROZEN;
815 }
816
817 std::string currentHash = ast->getFileHash()->hexString();
818
819 if (std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
820 std::cerr << "ERROR: " << fqName.string() << " has hash " << currentHash
821 << " which does not match hash on record. This interface has "
822 << "been frozen. Do not change it!" << std::endl;
823 return HashStatus::CHANGED;
824 }
825
826 return HashStatus::FROZEN;
827 }
828
getUnfrozenDependencies(const FQName & fqName,std::set<FQName> * result) const829 status_t Coordinator::getUnfrozenDependencies(const FQName& fqName,
830 std::set<FQName>* result) const {
831 CHECK(result != nullptr);
832
833 AST* ast = parse(fqName);
834 if (ast == nullptr) return UNKNOWN_ERROR;
835
836 std::set<FQName> imported;
837 ast->getImportedPackages(&imported);
838
839 // no circular dependency is already guaranteed by parsing
840 // indirect dependencies will be checked when the imported interface frozen checks are done
841 for (const FQName& importedPackage : imported) {
842 std::vector<FQName> packageInterfaces;
843 status_t err = appendPackageInterfacesToVector(importedPackage, &packageInterfaces);
844 if (err != OK) {
845 return err;
846 }
847
848 for (const FQName& importedName : packageInterfaces) {
849 HashStatus status = checkHash(importedName);
850 if (status == HashStatus::ERROR) return UNKNOWN_ERROR;
851 if (status == HashStatus::UNFROZEN) {
852 result->insert(importedName);
853 }
854 }
855 }
856
857 return OK;
858 }
859
enforceHashes(const FQName & currentPackage) const860 status_t Coordinator::enforceHashes(const FQName& currentPackage) const {
861 std::vector<FQName> packageInterfaces;
862 status_t err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
863 if (err != OK) {
864 return err;
865 }
866
867 for (const FQName& currentFQName : packageInterfaces) {
868 HashStatus status = checkHash(currentFQName);
869
870 if (status == HashStatus::ERROR) return UNKNOWN_ERROR;
871 if (status == HashStatus::CHANGED) return UNKNOWN_ERROR;
872
873 // frozen interface can only depend on a frozen interface
874 if (status == HashStatus::FROZEN) {
875 std::set<FQName> unfrozenDependencies;
876 err = getUnfrozenDependencies(currentFQName, &unfrozenDependencies);
877 if (err != OK) return err;
878
879 if (!unfrozenDependencies.empty()) {
880 std::cerr << "ERROR: Frozen interface " << currentFQName.string()
881 << " cannot depend on unfrozen thing(s):" << std::endl;
882 for (const FQName& name : unfrozenDependencies) {
883 std::cerr << " (unfrozen) " << name.string() << std::endl;
884 }
885 return UNKNOWN_ERROR;
886 }
887 }
888
889 // UNFROZEN, ignore
890 }
891
892 return err;
893 }
894
MakeParentHierarchy(const std::string & path)895 bool Coordinator::MakeParentHierarchy(const std::string &path) {
896 static const mode_t kMode = 0755;
897
898 size_t start = 1; // Ignore leading '/'
899 size_t slashPos;
900 while ((slashPos = path.find('/', start)) != std::string::npos) {
901 std::string partial = path.substr(0, slashPos);
902
903 struct stat st;
904 if (stat(partial.c_str(), &st) < 0) {
905 if (errno != ENOENT) {
906 return false;
907 }
908
909 int res = mkdir(partial.c_str(), kMode);
910 if (res < 0) {
911 return false;
912 }
913 } else if (!S_ISDIR(st.st_mode)) {
914 return false;
915 }
916
917 start = slashPos + 1;
918 }
919
920 return true;
921 }
922
923 } // namespace android
924
925