blob: 24214273da8d9eaae1eea9cabda8c5cafe0982a8 [file] [log] [blame]
Andreas Huber1aec3972016-08-26 09:26:32 -07001/*
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
Andreas Huber5345ec22016-07-29 13:33:27 -070017#include "Coordinator.h"
18
19#include "AST.h"
Andreas Huber5345ec22016-07-29 13:33:27 -070020
21#include <android-base/logging.h>
Andreas Huberdca261f2016-08-04 13:47:51 -070022#include <iterator>
Andreas Huberd2943e12016-08-05 11:59:31 -070023#include <sys/dir.h>
24#include <sys/stat.h>
Andreas Huber5345ec22016-07-29 13:33:27 -070025
Andreas Huber0d0f9a22016-08-17 10:26:11 -070026extern android::status_t parseFile(android::AST *ast);
Andreas Huber5345ec22016-07-29 13:33:27 -070027
28namespace android {
29
Andreas Huberdca261f2016-08-04 13:47:51 -070030Coordinator::Coordinator(
31 const std::vector<std::string> &packageRootPaths,
32 const std::vector<std::string> &packageRoots)
33 : mPackageRootPaths(packageRootPaths),
34 mPackageRoots(packageRoots) {
35 // empty
Andreas Huberdc981332016-07-29 15:46:54 -070036}
37
Andreas Huberdca261f2016-08-04 13:47:51 -070038Coordinator::~Coordinator() {
39 // empty
40}
Andreas Huber5345ec22016-07-29 13:33:27 -070041
Andreas Huber39fa7182016-08-19 14:27:33 -070042AST *Coordinator::parse(const FQName &fqName, std::set<AST *> *parsedASTs) {
Andreas Huber68f24592016-07-29 14:53:48 -070043 CHECK(fqName.isFullyQualified());
44
Andreas Huber6e584b72016-07-29 16:14:07 -070045 // LOG(INFO) << "parsing " << fqName.string();
46
Andreas Huber68f24592016-07-29 14:53:48 -070047 ssize_t index = mCache.indexOfKey(fqName);
Andreas Huber5345ec22016-07-29 13:33:27 -070048 if (index >= 0) {
49 AST *ast = mCache.valueAt(index);
50
Andreas Huber39fa7182016-08-19 14:27:33 -070051 if (ast != nullptr && parsedASTs != nullptr) {
52 parsedASTs->insert(ast);
53 }
54
Andreas Huber5345ec22016-07-29 13:33:27 -070055 return ast;
56 }
57
Andreas Huber68f24592016-07-29 14:53:48 -070058 // Add this to the cache immediately, so we can discover circular imports.
Andreas Huber39fa7182016-08-19 14:27:33 -070059 mCache.add(fqName, nullptr);
60
61 AST *typesAST = nullptr;
Andreas Huber68f24592016-07-29 14:53:48 -070062
Andreas Huber68f24592016-07-29 14:53:48 -070063 if (fqName.name() != "types") {
64 // Any interface file implicitly imports its package's types.hal.
65 FQName typesName(fqName.package(), fqName.version(), "types");
Andreas Huber39fa7182016-08-19 14:27:33 -070066 typesAST = parse(typesName, parsedASTs);
Andreas Huber68f24592016-07-29 14:53:48 -070067
68 // fall through.
69 }
70
Andreas Huberdca261f2016-08-04 13:47:51 -070071 std::string path = getPackagePath(fqName);
72
Andreas Huber68f24592016-07-29 14:53:48 -070073 path.append(fqName.name());
74 path.append(".hal");
Andreas Huber5345ec22016-07-29 13:33:27 -070075
Andreas Huber0d0f9a22016-08-17 10:26:11 -070076 AST *ast = new AST(this, path);
Andreas Huber39fa7182016-08-19 14:27:33 -070077
78 if (typesAST != NULL) {
79 // If types.hal for this AST's package existed, make it's defined
80 // types available to the (about to be parsed) AST right away.
81 ast->addImportedAST(typesAST);
82 }
83
Andreas Huber0d0f9a22016-08-17 10:26:11 -070084 status_t err = parseFile(ast);
Andreas Huber5345ec22016-07-29 13:33:27 -070085
Andreas Huber68f24592016-07-29 14:53:48 -070086 if (err != OK) {
Andreas Huber6e584b72016-07-29 16:14:07 -070087 // LOG(ERROR) << "parsing '" << path << "' FAILED.";
88
Andreas Huber68f24592016-07-29 14:53:48 -070089 delete ast;
Andreas Huber39fa7182016-08-19 14:27:33 -070090 ast = nullptr;
Andreas Huber68f24592016-07-29 14:53:48 -070091
Andreas Huber39fa7182016-08-19 14:27:33 -070092 return nullptr;
Andreas Huber68f24592016-07-29 14:53:48 -070093 }
94
Andreas Hubera2723d22016-07-29 15:36:07 -070095 if (ast->package().package() != fqName.package()
96 || ast->package().version() != fqName.version()) {
Andreas Huber70a59e12016-08-16 12:57:01 -070097 fprintf(stderr,
98 "ERROR: File at '%s' does not match expected package and/or "
Andreas Huber0d0f9a22016-08-17 10:26:11 -070099 "version.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -0700100 path.c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700101
102 err = UNKNOWN_ERROR;
103 } else {
104 std::string ifaceName;
105 if (ast->isInterface(&ifaceName)) {
106 if (fqName.name() == "types") {
Andreas Huber70a59e12016-08-16 12:57:01 -0700107 fprintf(stderr,
108 "ERROR: File at '%s' declares an interface '%s' "
Andreas Huber0d0f9a22016-08-17 10:26:11 -0700109 "instead of the expected types common to the package.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -0700110 path.c_str(),
111 ifaceName.c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700112
113 err = UNKNOWN_ERROR;
114 } else if (ifaceName != fqName.name()) {
Andreas Huber70a59e12016-08-16 12:57:01 -0700115 fprintf(stderr,
116 "ERROR: File at '%s' does not declare interface type "
Andreas Huber0d0f9a22016-08-17 10:26:11 -0700117 "'%s'.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -0700118 path.c_str(),
119 fqName.name().c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700120
121 err = UNKNOWN_ERROR;
122 }
123 } else if (fqName.name() != "types") {
Andreas Huber70a59e12016-08-16 12:57:01 -0700124 fprintf(stderr,
125 "ERROR: File at '%s' declares types rather than the "
Andreas Huber0d0f9a22016-08-17 10:26:11 -0700126 "expected interface type '%s'.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -0700127 path.c_str(),
128 fqName.name().c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700129
130 err = UNKNOWN_ERROR;
131 }
132 }
133
134 if (err != OK) {
135 delete ast;
Andreas Huber39fa7182016-08-19 14:27:33 -0700136 ast = nullptr;
Andreas Hubera2723d22016-07-29 15:36:07 -0700137
Andreas Huber39fa7182016-08-19 14:27:33 -0700138 return nullptr;
Andreas Hubera2723d22016-07-29 15:36:07 -0700139 }
140
Andreas Huber39fa7182016-08-19 14:27:33 -0700141 if (parsedASTs != nullptr) { parsedASTs->insert(ast); }
142
Andreas Huber68f24592016-07-29 14:53:48 -0700143 mCache.add(fqName, ast);
Andreas Huber5345ec22016-07-29 13:33:27 -0700144
145 return ast;
146}
147
Andreas Huberdca261f2016-08-04 13:47:51 -0700148std::vector<std::string>::const_iterator
149Coordinator::findPackageRoot(const FQName &fqName) const {
Andreas Huber5345ec22016-07-29 13:33:27 -0700150 CHECK(!fqName.package().empty());
151 CHECK(!fqName.version().empty());
Andreas Huber5345ec22016-07-29 13:33:27 -0700152
Andreas Huberdca261f2016-08-04 13:47:51 -0700153 // Find the right package prefix and path for this FQName. For
154 // example, if FQName is "android.hardware.nfc@1.0::INfc", and the
155 // prefix:root is set to [ "android.hardware:hardware/interfaces",
156 // "vendor.qcom.hardware:vendor/qcom"], then we will identify the
157 // prefix "android.hardware" and the package root
158 // "hardware/interfaces".
159
160 // TODO: This now returns on the first match. Throw an error if
161 // there are multiple hits.
162 auto it = mPackageRoots.begin();
163 for (; it != mPackageRoots.end(); it++) {
164 if (fqName.package().find(*it) != std::string::npos) {
165 break;
166 }
167 }
Andreas Huber401cd162016-08-26 10:40:30 -0700168 CHECK(it != mPackageRoots.end())
169 << "Unable to find package root for " << fqName.string();
Andreas Huberdca261f2016-08-04 13:47:51 -0700170
171 return it;
172}
173
174std::string Coordinator::getPackageRoot(const FQName &fqName) const {
175 auto it = findPackageRoot(fqName);
176 auto prefix = *it;
177 return prefix;
178}
179
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700180std::string Coordinator::getPackageRootPath(const FQName &fqName) const {
181 auto it = findPackageRoot(fqName);
182 auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
183 return root;
184}
185
Andreas Huberdca261f2016-08-04 13:47:51 -0700186std::string Coordinator::getPackagePath(
187 const FQName &fqName, bool relative) const {
188
189 auto it = findPackageRoot(fqName);
190 auto prefix = *it;
191 auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
192
193 // Make sure the prefix ends on a '.' and the root path on a '/'
194 if ((*--prefix.end()) != '.') {
195 prefix += '.';
196 }
197
198 if ((*--root.end()) != '/') {
199 root += '/';
200 }
201
202 // Given FQName of "android.hardware.nfc@1.0::IFoo" and a prefix
203 // "android.hardware.", the suffix is "nfc@1.0::IFoo".
204 const std::string packageSuffix = fqName.package().substr(prefix.length());
Andreas Huber5345ec22016-07-29 13:33:27 -0700205
Andreas Huber881227d2016-08-02 14:20:21 -0700206 std::string packagePath;
207 if (!relative) {
Andreas Huberdca261f2016-08-04 13:47:51 -0700208 packagePath = root;
Andreas Huber881227d2016-08-02 14:20:21 -0700209 }
Andreas Huber5345ec22016-07-29 13:33:27 -0700210
211 size_t startPos = 0;
212 size_t dotPos;
213 while ((dotPos = packageSuffix.find('.', startPos)) != std::string::npos) {
214 packagePath.append(packageSuffix.substr(startPos, dotPos - startPos));
215 packagePath.append("/");
216
217 startPos = dotPos + 1;
218 }
219 CHECK_LT(startPos + 1, packageSuffix.length());
220 packagePath.append(packageSuffix.substr(startPos));
221 packagePath.append("/");
222
223 CHECK_EQ(fqName.version().find('@'), 0u);
224 packagePath.append(fqName.version().substr(1));
225 packagePath.append("/");
226
227 return packagePath;
228}
229
Andreas Huberd2943e12016-08-05 11:59:31 -0700230status_t Coordinator::getPackageInterfaceFiles(
231 const FQName &package,
232 std::vector<std::string> *fileNames) const {
233 fileNames->clear();
Andreas Huber6cb08cf2016-08-03 15:44:51 -0700234
Andreas Huberd2943e12016-08-05 11:59:31 -0700235 const std::string packagePath = getPackagePath(package);
236
237 DIR *dir = opendir(packagePath.c_str());
238
239 if (dir == NULL) {
240 return -errno;
241 }
242
243 struct dirent *ent;
244 while ((ent = readdir(dir)) != NULL) {
245 if (ent->d_type != DT_REG) {
Andreas Huber6cb08cf2016-08-03 15:44:51 -0700246 continue;
247 }
248
Andreas Huberd2943e12016-08-05 11:59:31 -0700249 const auto suffix = ".hal";
250 const auto suffix_len = std::strlen(suffix);
251 const auto d_namelen = strlen(ent->d_name);
Andreas Hubere61e3f72016-08-03 10:22:03 -0700252
Andreas Huberd2943e12016-08-05 11:59:31 -0700253 if (d_namelen < suffix_len
254 || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) {
255 continue;
Andreas Hubere61e3f72016-08-03 10:22:03 -0700256 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700257
258 fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len));
259 }
260
261 closedir(dir);
262 dir = NULL;
263
264 return OK;
265}
266
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700267status_t Coordinator::appendPackageInterfacesToSet(
Andreas Huberd2943e12016-08-05 11:59:31 -0700268 const FQName &package,
269 std::vector<FQName> *packageInterfaces) const {
270 packageInterfaces->clear();
271
272 std::vector<std::string> fileNames;
273 status_t err = getPackageInterfaceFiles(package, &fileNames);
274
275 if (err != OK) {
276 return err;
277 }
278
279 for (const auto &fileName : fileNames) {
280 FQName subFQName(
281 package.package() + package.version() + "::" + fileName);
282
283 if (!subFQName.isValid()) {
284 LOG(WARNING)
285 << "Whole-package import encountered invalid filename '"
286 << fileName
287 << "' in package "
288 << package.package()
289 << package.version();
290
291 continue;
292 }
293
294 packageInterfaces->push_back(subFQName);
Andreas Hubere61e3f72016-08-03 10:22:03 -0700295 }
296
297 return OK;
298}
299
Andreas Huberd2943e12016-08-05 11:59:31 -0700300std::string Coordinator::convertPackageRootToPath(const FQName &fqName) const {
301 std::string packageRoot = getPackageRoot(fqName);
302
303 if (*(packageRoot.end()--) != '.') {
304 packageRoot += '.';
305 }
306
307 std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
308
309 return packageRoot; // now converted to a path
310}
311
312// static
313bool Coordinator::MakeParentHierarchy(const std::string &path) {
314 static const mode_t kMode = 0755;
315
316 size_t start = 1; // Ignore leading '/'
317 size_t slashPos;
318 while ((slashPos = path.find("/", start)) != std::string::npos) {
319 std::string partial = path.substr(0, slashPos);
320
321 struct stat st;
322 if (stat(partial.c_str(), &st) < 0) {
323 if (errno != ENOENT) {
324 return false;
325 }
326
327 int res = mkdir(partial.c_str(), kMode);
328 if (res < 0) {
329 return false;
330 }
331 } else if (!S_ISDIR(st.st_mode)) {
332 return false;
333 }
334
335 start = slashPos + 1;
336 }
337
338 return true;
339}
340
Andreas Huber5345ec22016-07-29 13:33:27 -0700341} // namespace android
342