blob: d549fcf108769c3a277ffb61d89aa4aaa2f504db [file] [log] [blame]
Andreas Huber5345ec22016-07-29 13:33:27 -07001#include "Coordinator.h"
2
3#include "AST.h"
Andreas Huber5345ec22016-07-29 13:33:27 -07004
5#include <android-base/logging.h>
Andreas Huberdca261f2016-08-04 13:47:51 -07006#include <iterator>
Andreas Huberd2943e12016-08-05 11:59:31 -07007#include <sys/dir.h>
8#include <sys/stat.h>
Andreas Huber5345ec22016-07-29 13:33:27 -07009
Andreas Huber68f24592016-07-29 14:53:48 -070010extern android::status_t parseFile(android::AST *ast, const char *path);
Andreas Huber5345ec22016-07-29 13:33:27 -070011
12namespace android {
13
Andreas Huberdca261f2016-08-04 13:47:51 -070014Coordinator::Coordinator(
15 const std::vector<std::string> &packageRootPaths,
16 const std::vector<std::string> &packageRoots)
17 : mPackageRootPaths(packageRootPaths),
18 mPackageRoots(packageRoots) {
19 // empty
Andreas Huberdc981332016-07-29 15:46:54 -070020}
21
Andreas Huberdca261f2016-08-04 13:47:51 -070022Coordinator::~Coordinator() {
23 // empty
24}
Andreas Huber5345ec22016-07-29 13:33:27 -070025
Andreas Huber68f24592016-07-29 14:53:48 -070026AST *Coordinator::parse(const FQName &fqName) {
27 CHECK(fqName.isFullyQualified());
28
Andreas Huber6e584b72016-07-29 16:14:07 -070029 // LOG(INFO) << "parsing " << fqName.string();
30
Andreas Huber68f24592016-07-29 14:53:48 -070031 ssize_t index = mCache.indexOfKey(fqName);
Andreas Huber5345ec22016-07-29 13:33:27 -070032 if (index >= 0) {
33 AST *ast = mCache.valueAt(index);
34
35 return ast;
36 }
37
Andreas Huber68f24592016-07-29 14:53:48 -070038 // Add this to the cache immediately, so we can discover circular imports.
39 mCache.add(fqName, NULL);
40
Andreas Huber68f24592016-07-29 14:53:48 -070041 if (fqName.name() != "types") {
42 // Any interface file implicitly imports its package's types.hal.
43 FQName typesName(fqName.package(), fqName.version(), "types");
44 (void)parse(typesName);
45
46 // fall through.
47 }
48
Andreas Huberdca261f2016-08-04 13:47:51 -070049 std::string path = getPackagePath(fqName);
50
Andreas Huber68f24592016-07-29 14:53:48 -070051 path.append(fqName.name());
52 path.append(".hal");
Andreas Huber5345ec22016-07-29 13:33:27 -070053
54 AST *ast = new AST(this);
Andreas Huber68f24592016-07-29 14:53:48 -070055 status_t err = parseFile(ast, path.c_str());
Andreas Huber5345ec22016-07-29 13:33:27 -070056
Andreas Huber68f24592016-07-29 14:53:48 -070057 if (err != OK) {
Andreas Huber6e584b72016-07-29 16:14:07 -070058 // LOG(ERROR) << "parsing '" << path << "' FAILED.";
59
Andreas Huber68f24592016-07-29 14:53:48 -070060 delete ast;
61 ast = NULL;
62
63 return NULL;
64 }
65
Andreas Hubera2723d22016-07-29 15:36:07 -070066 if (ast->package().package() != fqName.package()
67 || ast->package().version() != fqName.version()) {
68 LOG(ERROR)
69 << "File at '" << path << "' does not match expected package "
70 << "and/or version.";
71
72 err = UNKNOWN_ERROR;
73 } else {
74 std::string ifaceName;
75 if (ast->isInterface(&ifaceName)) {
76 if (fqName.name() == "types") {
77 LOG(ERROR)
78 << "File at '" << path << "' declares an interface '"
79 << ifaceName
80 << "' instead of the expected types common to the package.";
81
82 err = UNKNOWN_ERROR;
83 } else if (ifaceName != fqName.name()) {
84 LOG(ERROR)
85 << "File at '" << path << "' does not declare interface type '"
86 << fqName.name()
87 << "'.";
88
89 err = UNKNOWN_ERROR;
90 }
91 } else if (fqName.name() != "types") {
92 LOG(ERROR)
93 << "File at '" << path << "' declares types rather than the "
94 << "expected interface type '" << fqName.name() << "'.";
95
96 err = UNKNOWN_ERROR;
97 }
98 }
99
100 if (err != OK) {
101 delete ast;
102 ast = NULL;
103
104 return NULL;
105 }
106
Andreas Huber68f24592016-07-29 14:53:48 -0700107 mCache.add(fqName, ast);
Andreas Huber5345ec22016-07-29 13:33:27 -0700108
109 return ast;
110}
111
Andreas Huberdca261f2016-08-04 13:47:51 -0700112std::vector<std::string>::const_iterator
113Coordinator::findPackageRoot(const FQName &fqName) const {
Andreas Huber5345ec22016-07-29 13:33:27 -0700114 CHECK(!fqName.package().empty());
115 CHECK(!fqName.version().empty());
Andreas Huber5345ec22016-07-29 13:33:27 -0700116
Andreas Huberdca261f2016-08-04 13:47:51 -0700117 // Find the right package prefix and path for this FQName. For
118 // example, if FQName is "android.hardware.nfc@1.0::INfc", and the
119 // prefix:root is set to [ "android.hardware:hardware/interfaces",
120 // "vendor.qcom.hardware:vendor/qcom"], then we will identify the
121 // prefix "android.hardware" and the package root
122 // "hardware/interfaces".
123
124 // TODO: This now returns on the first match. Throw an error if
125 // there are multiple hits.
126 auto it = mPackageRoots.begin();
127 for (; it != mPackageRoots.end(); it++) {
128 if (fqName.package().find(*it) != std::string::npos) {
129 break;
130 }
131 }
132 CHECK(it != mPackageRoots.end());
133
134 return it;
135}
136
137std::string Coordinator::getPackageRoot(const FQName &fqName) const {
138 auto it = findPackageRoot(fqName);
139 auto prefix = *it;
140 return prefix;
141}
142
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700143std::string Coordinator::getPackageRootPath(const FQName &fqName) const {
144 auto it = findPackageRoot(fqName);
145 auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
146 return root;
147}
148
Andreas Huberdca261f2016-08-04 13:47:51 -0700149std::string Coordinator::getPackagePath(
150 const FQName &fqName, bool relative) const {
151
152 auto it = findPackageRoot(fqName);
153 auto prefix = *it;
154 auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
155
156 // Make sure the prefix ends on a '.' and the root path on a '/'
157 if ((*--prefix.end()) != '.') {
158 prefix += '.';
159 }
160
161 if ((*--root.end()) != '/') {
162 root += '/';
163 }
164
165 // Given FQName of "android.hardware.nfc@1.0::IFoo" and a prefix
166 // "android.hardware.", the suffix is "nfc@1.0::IFoo".
167 const std::string packageSuffix = fqName.package().substr(prefix.length());
Andreas Huber5345ec22016-07-29 13:33:27 -0700168
Andreas Huber881227d2016-08-02 14:20:21 -0700169 std::string packagePath;
170 if (!relative) {
Andreas Huberdca261f2016-08-04 13:47:51 -0700171 packagePath = root;
Andreas Huber881227d2016-08-02 14:20:21 -0700172 }
Andreas Huber5345ec22016-07-29 13:33:27 -0700173
174 size_t startPos = 0;
175 size_t dotPos;
176 while ((dotPos = packageSuffix.find('.', startPos)) != std::string::npos) {
177 packagePath.append(packageSuffix.substr(startPos, dotPos - startPos));
178 packagePath.append("/");
179
180 startPos = dotPos + 1;
181 }
182 CHECK_LT(startPos + 1, packageSuffix.length());
183 packagePath.append(packageSuffix.substr(startPos));
184 packagePath.append("/");
185
186 CHECK_EQ(fqName.version().find('@'), 0u);
187 packagePath.append(fqName.version().substr(1));
188 packagePath.append("/");
189
190 return packagePath;
191}
192
Andreas Huberfd4afab2016-08-03 13:02:57 -0700193Type *Coordinator::lookupType(const FQName &fqName) const {
Andreas Huber5345ec22016-07-29 13:33:27 -0700194 // Fully qualified.
Andreas Huber68f24592016-07-29 14:53:48 -0700195 CHECK(fqName.isFullyQualified());
Andreas Huber5345ec22016-07-29 13:33:27 -0700196
197 std::string topType;
198 size_t dotPos = fqName.name().find('.');
199 if (dotPos == std::string::npos) {
200 topType = fqName.name();
201 } else {
202 topType = fqName.name().substr(0, dotPos);
203 }
204
Andreas Huber68f24592016-07-29 14:53:48 -0700205 // Assuming {topType} is the name of an interface type, let's see if the
206 // associated {topType}.hal file was imported.
207 FQName ifaceName(fqName.package(), fqName.version(), topType);
208 ssize_t index = mCache.indexOfKey(ifaceName);
Andreas Huber5345ec22016-07-29 13:33:27 -0700209 if (index >= 0) {
210 AST *ast = mCache.valueAt(index);
Andreas Huber6e584b72016-07-29 16:14:07 -0700211 CHECK(ast != NULL);
212
Andreas Huber5345ec22016-07-29 13:33:27 -0700213 Type *type = ast->lookupTypeInternal(fqName.name());
214
215 if (type != NULL) {
Andreas Huberfd4afab2016-08-03 13:02:57 -0700216 return type->ref();
Andreas Huber5345ec22016-07-29 13:33:27 -0700217 }
218 }
219
Andreas Huber68f24592016-07-29 14:53:48 -0700220 FQName typesName(fqName.package(), fqName.version(), "types");
221 index = mCache.indexOfKey(typesName);
Andreas Huber5345ec22016-07-29 13:33:27 -0700222 if (index >= 0) {
223 AST *ast = mCache.valueAt(index);
Andreas Huber6e584b72016-07-29 16:14:07 -0700224 if (ast != NULL) {
225 // ast could be NULL if types.hal didn't exist, which is valid.
226 Type *type = ast->lookupTypeInternal(fqName.name());
Andreas Huber5345ec22016-07-29 13:33:27 -0700227
Andreas Huber6e584b72016-07-29 16:14:07 -0700228 if (type != NULL) {
Andreas Huberfd4afab2016-08-03 13:02:57 -0700229 return type->ref();
Andreas Huber6e584b72016-07-29 16:14:07 -0700230 }
Andreas Huber5345ec22016-07-29 13:33:27 -0700231 }
232 }
233
234 return NULL;
235}
236
Andreas Huberd2943e12016-08-05 11:59:31 -0700237status_t Coordinator::getPackageInterfaceFiles(
238 const FQName &package,
239 std::vector<std::string> *fileNames) const {
240 fileNames->clear();
Andreas Huber6cb08cf2016-08-03 15:44:51 -0700241
Andreas Huberd2943e12016-08-05 11:59:31 -0700242 const std::string packagePath = getPackagePath(package);
243
244 DIR *dir = opendir(packagePath.c_str());
245
246 if (dir == NULL) {
247 return -errno;
248 }
249
250 struct dirent *ent;
251 while ((ent = readdir(dir)) != NULL) {
252 if (ent->d_type != DT_REG) {
Andreas Huber6cb08cf2016-08-03 15:44:51 -0700253 continue;
254 }
255
Andreas Huberd2943e12016-08-05 11:59:31 -0700256 const auto suffix = ".hal";
257 const auto suffix_len = std::strlen(suffix);
258 const auto d_namelen = strlen(ent->d_name);
Andreas Hubere61e3f72016-08-03 10:22:03 -0700259
Andreas Huberd2943e12016-08-05 11:59:31 -0700260 if (d_namelen < suffix_len
261 || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) {
262 continue;
Andreas Hubere61e3f72016-08-03 10:22:03 -0700263 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700264
265 fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len));
266 }
267
268 closedir(dir);
269 dir = NULL;
270
271 return OK;
272}
273
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700274status_t Coordinator::appendPackageInterfacesToSet(
Andreas Huberd2943e12016-08-05 11:59:31 -0700275 const FQName &package,
276 std::vector<FQName> *packageInterfaces) const {
277 packageInterfaces->clear();
278
279 std::vector<std::string> fileNames;
280 status_t err = getPackageInterfaceFiles(package, &fileNames);
281
282 if (err != OK) {
283 return err;
284 }
285
286 for (const auto &fileName : fileNames) {
287 FQName subFQName(
288 package.package() + package.version() + "::" + fileName);
289
290 if (!subFQName.isValid()) {
291 LOG(WARNING)
292 << "Whole-package import encountered invalid filename '"
293 << fileName
294 << "' in package "
295 << package.package()
296 << package.version();
297
298 continue;
299 }
300
301 packageInterfaces->push_back(subFQName);
Andreas Hubere61e3f72016-08-03 10:22:03 -0700302 }
303
304 return OK;
305}
306
Andreas Huberd2943e12016-08-05 11:59:31 -0700307std::string Coordinator::convertPackageRootToPath(const FQName &fqName) const {
308 std::string packageRoot = getPackageRoot(fqName);
309
310 if (*(packageRoot.end()--) != '.') {
311 packageRoot += '.';
312 }
313
314 std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
315
316 return packageRoot; // now converted to a path
317}
318
319// static
320bool Coordinator::MakeParentHierarchy(const std::string &path) {
321 static const mode_t kMode = 0755;
322
323 size_t start = 1; // Ignore leading '/'
324 size_t slashPos;
325 while ((slashPos = path.find("/", start)) != std::string::npos) {
326 std::string partial = path.substr(0, slashPos);
327
328 struct stat st;
329 if (stat(partial.c_str(), &st) < 0) {
330 if (errno != ENOENT) {
331 return false;
332 }
333
334 int res = mkdir(partial.c_str(), kMode);
335 if (res < 0) {
336 return false;
337 }
338 } else if (!S_ISDIR(st.st_mode)) {
339 return false;
340 }
341
342 start = slashPos + 1;
343 }
344
345 return true;
346}
347
Andreas Huber5345ec22016-07-29 13:33:27 -0700348} // namespace android
349