1 /* 2 * Copyright (C) 2019 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 <android-base/logging.h> 18 #include <android-base/strings.h> 19 #include <hidl-util/FQName.h> 20 #include <hidl-util/Formatter.h> 21 22 #include <algorithm> 23 #include <iostream> 24 #include <vector> 25 26 #include "AST.h" 27 #include "AidlHelper.h" 28 #include "Coordinator.h" 29 #include "DocComment.h" 30 31 using namespace android; 32 33 static void usage(const char* me) { 34 Formatter out(stderr); 35 36 out << "Usage: " << me << " [-o <output path>] "; 37 Coordinator::emitOptionsUsageString(out); 38 out << " FQNAME...\n\n"; 39 40 out << "Converts FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)? to an aidl " 41 "equivalent.\n\n"; 42 43 out.indent(); 44 out.indent(); 45 46 out << "-o <output path>: Location to output files.\n"; 47 out << "-h: Prints this menu.\n"; 48 Coordinator::emitOptionsDetailString(out); 49 50 out.unindent(); 51 out.unindent(); 52 } 53 54 static const FQName& getNewerFQName(const FQName& lhs, const FQName& rhs) { 55 CHECK(lhs.package() == rhs.package()); 56 CHECK(lhs.name() == rhs.name()); 57 58 if (lhs.getPackageMajorVersion() > rhs.getPackageMajorVersion()) return lhs; 59 if (lhs.getPackageMajorVersion() < rhs.getPackageMajorVersion()) return rhs; 60 61 if (lhs.getPackageMinorVersion() > rhs.getPackageMinorVersion()) return lhs; 62 return rhs; 63 } 64 65 // If similar FQName is not found, the same one is returned 66 static FQName getLatestMinorVersionFQNameFromList(const FQName& fqName, 67 const std::vector<FQName>& list) { 68 FQName currentCandidate = fqName; 69 bool found = false; 70 for (const FQName& current : list) { 71 if (current.package() == currentCandidate.package() && 72 current.name() == currentCandidate.name() && 73 current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) { 74 // Prioritize elements in the list over the provided fqName 75 currentCandidate = found ? getNewerFQName(current, currentCandidate) : current; 76 found = true; 77 } 78 } 79 80 return currentCandidate; 81 } 82 83 static FQName getLatestMinorVersionNamedTypeFromList(const FQName& fqName, 84 const std::vector<const NamedType*>& list) { 85 FQName currentCandidate = fqName; 86 bool found = false; 87 for (const NamedType* currentNamedType : list) { 88 const FQName& current = currentNamedType->fqName(); 89 if (current.package() == currentCandidate.package() && 90 current.name() == currentCandidate.name() && 91 current.getPackageMajorVersion() == currentCandidate.getPackageMajorVersion()) { 92 // Prioritize elements in the list over the provided fqName 93 currentCandidate = found ? getNewerFQName(current, currentCandidate) : current; 94 found = true; 95 } 96 } 97 98 return currentCandidate; 99 } 100 101 static bool packageExists(const Coordinator& coordinator, const FQName& fqName) { 102 bool result; 103 status_t err = coordinator.packageExists(fqName, &result); 104 if (err != OK) { 105 std::cerr << "Error trying to find package " << fqName.string() << std::endl; 106 exit(1); 107 } 108 109 return result; 110 } 111 112 static AST* parse(const Coordinator& coordinator, const FQName& target) { 113 AST* ast = coordinator.parse(target); 114 if (ast == nullptr) { 115 std::cerr << "ERROR: Could not parse " << target.name() << ". Aborting." << std::endl; 116 exit(1); 117 } 118 119 if (!ast->getUnhandledComments().empty()) { 120 AidlHelper::notes() 121 << "Unhandled comments from " << target.string() 122 << " follow. Consider using hidl-lint to locate these and fixup as many " 123 << "as possible.\n"; 124 for (const DocComment* docComment : ast->getUnhandledComments()) { 125 docComment->emit(AidlHelper::notes()); 126 } 127 AidlHelper::notes() << "\n"; 128 } 129 130 return ast; 131 } 132 133 // hidl is intentionally leaky. Turn off LeakSanitizer by default. 134 extern "C" const char* __asan_default_options() { 135 return "detect_leaks=0"; 136 } 137 138 int main(int argc, char** argv) { 139 const char* me = argv[0]; 140 if (argc == 1) { 141 usage(me); 142 std::cerr << "ERROR: no fqname specified." << std::endl; 143 exit(1); 144 } 145 146 Coordinator coordinator; 147 std::string outputPath; 148 coordinator.parseOptions(argc, argv, "ho:", [&](int res, char* arg) { 149 switch (res) { 150 case 'o': { 151 if (!outputPath.empty()) { 152 fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n"); 153 exit(1); 154 } 155 outputPath = arg; 156 break; 157 } 158 case 'h': 159 case '?': 160 default: { 161 usage(me); 162 exit(1); 163 break; 164 } 165 } 166 }); 167 168 if (!outputPath.empty() && outputPath.back() != '/') { 169 outputPath += "/"; 170 } 171 coordinator.setOutputPath(outputPath); 172 173 argc -= optind; 174 argv += optind; 175 176 if (argc == 0) { 177 usage(me); 178 std::cerr << "ERROR: no fqname specified." << std::endl; 179 exit(1); 180 } 181 182 for (int i = 0; i < argc; ++i) { 183 const char* arg = argv[i]; 184 185 FQName fqName; 186 if (!FQName::parse(arg, &fqName)) { 187 std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "." 188 << std::endl; 189 exit(1); 190 } 191 192 if (!packageExists(coordinator, fqName)) { 193 std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl; 194 exit(1); 195 } 196 197 FQName currentFqName(fqName); 198 while (currentFqName.getPackageMinorVersion() != 0) { 199 if (!packageExists(coordinator, currentFqName.downRev())) break; 200 201 currentFqName = currentFqName.downRev(); 202 } 203 204 std::vector<FQName> targets; 205 while (packageExists(coordinator, currentFqName)) { 206 std::vector<FQName> newTargets; 207 status_t err = coordinator.appendPackageInterfacesToVector(currentFqName, &newTargets); 208 if (err != OK) break; 209 210 targets.insert(targets.end(), newTargets.begin(), newTargets.end()); 211 212 currentFqName = currentFqName.upRev(); 213 } 214 215 // targets should not contain duplicates since appendPackageInterfaces is only called once 216 // per version. now remove all the elements that are not the "newest" 217 const auto& newEnd = 218 std::remove_if(targets.begin(), targets.end(), [&](const FQName& fqName) -> bool { 219 if (fqName.name() == "types") return false; 220 221 return getLatestMinorVersionFQNameFromList(fqName, targets) != fqName; 222 }); 223 targets.erase(newEnd, targets.end()); 224 225 if (fqName.isFullyQualified()) { 226 // Ensure that this fqName exists in the list. 227 // If not then there is a more recent version 228 if (std::find(targets.begin(), targets.end(), fqName) == targets.end()) { 229 // Not found. Error. 230 std::cerr << "ERROR: A newer minor version of " << fqName.string() 231 << " exists. Compile that instead." << std::endl; 232 exit(1); 233 } else { 234 targets.clear(); 235 targets.push_back(fqName); 236 } 237 } 238 239 // Set up AIDL conversion log 240 std::string aidlPackage = AidlHelper::getAidlPackage(fqName); 241 std::string aidlName = AidlHelper::getAidlName(fqName); 242 Formatter err = coordinator.getFormatter( 243 fqName, Coordinator::Location::DIRECT, 244 base::Join(base::Split(aidlPackage, "."), "/") + "/" + 245 (aidlName.empty() ? "" : (aidlName + "-")) + "conversion.log"); 246 AidlHelper::setNotes(&err); 247 248 std::vector<const NamedType*> namedTypesInPackage; 249 for (const FQName& target : targets) { 250 if (target.name() != "types") continue; 251 252 AST* ast = parse(coordinator, target); 253 254 CHECK(!ast->isInterface()); 255 256 std::vector<const NamedType*> types = ast->getRootScope().getSortedDefinedTypes(); 257 namedTypesInPackage.insert(namedTypesInPackage.end(), types.begin(), types.end()); 258 } 259 260 const auto& endNamedTypes = std::remove_if( 261 namedTypesInPackage.begin(), namedTypesInPackage.end(), 262 [&](const NamedType* namedType) -> bool { 263 return getLatestMinorVersionNamedTypeFromList( 264 namedType->fqName(), namedTypesInPackage) != namedType->fqName(); 265 }); 266 namedTypesInPackage.erase(endNamedTypes, namedTypesInPackage.end()); 267 268 for (const NamedType* namedType : namedTypesInPackage) { 269 AidlHelper::emitAidl(*namedType, coordinator); 270 } 271 272 for (const FQName& target : targets) { 273 if (target.name() == "types") continue; 274 275 AST* ast = parse(coordinator, target); 276 277 const Interface* iface = ast->getInterface(); 278 CHECK(iface); 279 280 AidlHelper::emitAidl(*iface, coordinator); 281 } 282 } 283 284 return 0; 285 } 286