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