blob: fb6b9e46ba506168d6002badbab44fc1cb9dccf2 [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 Huber0d0f9a22016-08-17 10:26:11 -070010extern android::status_t parseFile(android::AST *ast);
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
Andreas Huber0d0f9a22016-08-17 10:26:11 -070054 AST *ast = new AST(this, path);
55 status_t err = parseFile(ast);
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()) {
Andreas Huber70a59e12016-08-16 12:57:01 -070068 fprintf(stderr,
69 "ERROR: File at '%s' does not match expected package and/or "
Andreas Huber0d0f9a22016-08-17 10:26:11 -070070 "version.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -070071 path.c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -070072
73 err = UNKNOWN_ERROR;
74 } else {
75 std::string ifaceName;
76 if (ast->isInterface(&ifaceName)) {
77 if (fqName.name() == "types") {
Andreas Huber70a59e12016-08-16 12:57:01 -070078 fprintf(stderr,
79 "ERROR: File at '%s' declares an interface '%s' "
Andreas Huber0d0f9a22016-08-17 10:26:11 -070080 "instead of the expected types common to the package.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -070081 path.c_str(),
82 ifaceName.c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -070083
84 err = UNKNOWN_ERROR;
85 } else if (ifaceName != fqName.name()) {
Andreas Huber70a59e12016-08-16 12:57:01 -070086 fprintf(stderr,
87 "ERROR: File at '%s' does not declare interface type "
Andreas Huber0d0f9a22016-08-17 10:26:11 -070088 "'%s'.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -070089 path.c_str(),
90 fqName.name().c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -070091
92 err = UNKNOWN_ERROR;
93 }
94 } else if (fqName.name() != "types") {
Andreas Huber70a59e12016-08-16 12:57:01 -070095 fprintf(stderr,
96 "ERROR: File at '%s' declares types rather than the "
Andreas Huber0d0f9a22016-08-17 10:26:11 -070097 "expected interface type '%s'.\n",
Andreas Huber70a59e12016-08-16 12:57:01 -070098 path.c_str(),
99 fqName.name().c_str());
Andreas Hubera2723d22016-07-29 15:36:07 -0700100
101 err = UNKNOWN_ERROR;
102 }
103 }
104
105 if (err != OK) {
106 delete ast;
107 ast = NULL;
108
109 return NULL;
110 }
111
Andreas Huber68f24592016-07-29 14:53:48 -0700112 mCache.add(fqName, ast);
Andreas Huber5345ec22016-07-29 13:33:27 -0700113
114 return ast;
115}
116
Andreas Huberdca261f2016-08-04 13:47:51 -0700117std::vector<std::string>::const_iterator
118Coordinator::findPackageRoot(const FQName &fqName) const {
Andreas Huber5345ec22016-07-29 13:33:27 -0700119 CHECK(!fqName.package().empty());
120 CHECK(!fqName.version().empty());
Andreas Huber5345ec22016-07-29 13:33:27 -0700121
Andreas Huberdca261f2016-08-04 13:47:51 -0700122 // Find the right package prefix and path for this FQName. For
123 // example, if FQName is "android.hardware.nfc@1.0::INfc", and the
124 // prefix:root is set to [ "android.hardware:hardware/interfaces",
125 // "vendor.qcom.hardware:vendor/qcom"], then we will identify the
126 // prefix "android.hardware" and the package root
127 // "hardware/interfaces".
128
129 // TODO: This now returns on the first match. Throw an error if
130 // there are multiple hits.
131 auto it = mPackageRoots.begin();
132 for (; it != mPackageRoots.end(); it++) {
133 if (fqName.package().find(*it) != std::string::npos) {
134 break;
135 }
136 }
137 CHECK(it != mPackageRoots.end());
138
139 return it;
140}
141
142std::string Coordinator::getPackageRoot(const FQName &fqName) const {
143 auto it = findPackageRoot(fqName);
144 auto prefix = *it;
145 return prefix;
146}
147
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700148std::string Coordinator::getPackageRootPath(const FQName &fqName) const {
149 auto it = findPackageRoot(fqName);
150 auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
151 return root;
152}
153
Andreas Huberdca261f2016-08-04 13:47:51 -0700154std::string Coordinator::getPackagePath(
155 const FQName &fqName, bool relative) const {
156
157 auto it = findPackageRoot(fqName);
158 auto prefix = *it;
159 auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
160
161 // Make sure the prefix ends on a '.' and the root path on a '/'
162 if ((*--prefix.end()) != '.') {
163 prefix += '.';
164 }
165
166 if ((*--root.end()) != '/') {
167 root += '/';
168 }
169
170 // Given FQName of "android.hardware.nfc@1.0::IFoo" and a prefix
171 // "android.hardware.", the suffix is "nfc@1.0::IFoo".
172 const std::string packageSuffix = fqName.package().substr(prefix.length());
Andreas Huber5345ec22016-07-29 13:33:27 -0700173
Andreas Huber881227d2016-08-02 14:20:21 -0700174 std::string packagePath;
175 if (!relative) {
Andreas Huberdca261f2016-08-04 13:47:51 -0700176 packagePath = root;
Andreas Huber881227d2016-08-02 14:20:21 -0700177 }
Andreas Huber5345ec22016-07-29 13:33:27 -0700178
179 size_t startPos = 0;
180 size_t dotPos;
181 while ((dotPos = packageSuffix.find('.', startPos)) != std::string::npos) {
182 packagePath.append(packageSuffix.substr(startPos, dotPos - startPos));
183 packagePath.append("/");
184
185 startPos = dotPos + 1;
186 }
187 CHECK_LT(startPos + 1, packageSuffix.length());
188 packagePath.append(packageSuffix.substr(startPos));
189 packagePath.append("/");
190
191 CHECK_EQ(fqName.version().find('@'), 0u);
192 packagePath.append(fqName.version().substr(1));
193 packagePath.append("/");
194
195 return packagePath;
196}
197
Andreas Huberfd4afab2016-08-03 13:02:57 -0700198Type *Coordinator::lookupType(const FQName &fqName) const {
Andreas Huber5345ec22016-07-29 13:33:27 -0700199 // Fully qualified.
Andreas Huber68f24592016-07-29 14:53:48 -0700200 CHECK(fqName.isFullyQualified());
Andreas Huber5345ec22016-07-29 13:33:27 -0700201
202 std::string topType;
203 size_t dotPos = fqName.name().find('.');
204 if (dotPos == std::string::npos) {
205 topType = fqName.name();
206 } else {
207 topType = fqName.name().substr(0, dotPos);
208 }
209
Andreas Huber68f24592016-07-29 14:53:48 -0700210 // Assuming {topType} is the name of an interface type, let's see if the
211 // associated {topType}.hal file was imported.
212 FQName ifaceName(fqName.package(), fqName.version(), topType);
213 ssize_t index = mCache.indexOfKey(ifaceName);
Andreas Huber5345ec22016-07-29 13:33:27 -0700214 if (index >= 0) {
215 AST *ast = mCache.valueAt(index);
Andreas Huber6e584b72016-07-29 16:14:07 -0700216 CHECK(ast != NULL);
217
Andreas Huber5345ec22016-07-29 13:33:27 -0700218 Type *type = ast->lookupTypeInternal(fqName.name());
219
220 if (type != NULL) {
Andreas Huberfd4afab2016-08-03 13:02:57 -0700221 return type->ref();
Andreas Huber5345ec22016-07-29 13:33:27 -0700222 }
223 }
224
Andreas Huber68f24592016-07-29 14:53:48 -0700225 FQName typesName(fqName.package(), fqName.version(), "types");
226 index = mCache.indexOfKey(typesName);
Andreas Huber5345ec22016-07-29 13:33:27 -0700227 if (index >= 0) {
228 AST *ast = mCache.valueAt(index);
Andreas Huber6e584b72016-07-29 16:14:07 -0700229 if (ast != NULL) {
230 // ast could be NULL if types.hal didn't exist, which is valid.
231 Type *type = ast->lookupTypeInternal(fqName.name());
Andreas Huber5345ec22016-07-29 13:33:27 -0700232
Andreas Huber6e584b72016-07-29 16:14:07 -0700233 if (type != NULL) {
Andreas Huberfd4afab2016-08-03 13:02:57 -0700234 return type->ref();
Andreas Huber6e584b72016-07-29 16:14:07 -0700235 }
Andreas Huber5345ec22016-07-29 13:33:27 -0700236 }
237 }
238
239 return NULL;
240}
241
Andreas Huberd2943e12016-08-05 11:59:31 -0700242status_t Coordinator::getPackageInterfaceFiles(
243 const FQName &package,
244 std::vector<std::string> *fileNames) const {
245 fileNames->clear();
Andreas Huber6cb08cf2016-08-03 15:44:51 -0700246
Andreas Huberd2943e12016-08-05 11:59:31 -0700247 const std::string packagePath = getPackagePath(package);
248
249 DIR *dir = opendir(packagePath.c_str());
250
251 if (dir == NULL) {
252 return -errno;
253 }
254
255 struct dirent *ent;
256 while ((ent = readdir(dir)) != NULL) {
257 if (ent->d_type != DT_REG) {
Andreas Huber6cb08cf2016-08-03 15:44:51 -0700258 continue;
259 }
260
Andreas Huberd2943e12016-08-05 11:59:31 -0700261 const auto suffix = ".hal";
262 const auto suffix_len = std::strlen(suffix);
263 const auto d_namelen = strlen(ent->d_name);
Andreas Hubere61e3f72016-08-03 10:22:03 -0700264
Andreas Huberd2943e12016-08-05 11:59:31 -0700265 if (d_namelen < suffix_len
266 || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) {
267 continue;
Andreas Hubere61e3f72016-08-03 10:22:03 -0700268 }
Andreas Huberd2943e12016-08-05 11:59:31 -0700269
270 fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len));
271 }
272
273 closedir(dir);
274 dir = NULL;
275
276 return OK;
277}
278
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700279status_t Coordinator::appendPackageInterfacesToSet(
Andreas Huberd2943e12016-08-05 11:59:31 -0700280 const FQName &package,
281 std::vector<FQName> *packageInterfaces) const {
282 packageInterfaces->clear();
283
284 std::vector<std::string> fileNames;
285 status_t err = getPackageInterfaceFiles(package, &fileNames);
286
287 if (err != OK) {
288 return err;
289 }
290
291 for (const auto &fileName : fileNames) {
292 FQName subFQName(
293 package.package() + package.version() + "::" + fileName);
294
295 if (!subFQName.isValid()) {
296 LOG(WARNING)
297 << "Whole-package import encountered invalid filename '"
298 << fileName
299 << "' in package "
300 << package.package()
301 << package.version();
302
303 continue;
304 }
305
306 packageInterfaces->push_back(subFQName);
Andreas Hubere61e3f72016-08-03 10:22:03 -0700307 }
308
309 return OK;
310}
311
Andreas Huberd2943e12016-08-05 11:59:31 -0700312std::string Coordinator::convertPackageRootToPath(const FQName &fqName) const {
313 std::string packageRoot = getPackageRoot(fqName);
314
315 if (*(packageRoot.end()--) != '.') {
316 packageRoot += '.';
317 }
318
319 std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
320
321 return packageRoot; // now converted to a path
322}
323
324// static
325bool Coordinator::MakeParentHierarchy(const std::string &path) {
326 static const mode_t kMode = 0755;
327
328 size_t start = 1; // Ignore leading '/'
329 size_t slashPos;
330 while ((slashPos = path.find("/", start)) != std::string::npos) {
331 std::string partial = path.substr(0, slashPos);
332
333 struct stat st;
334 if (stat(partial.c_str(), &st) < 0) {
335 if (errno != ENOENT) {
336 return false;
337 }
338
339 int res = mkdir(partial.c_str(), kMode);
340 if (res < 0) {
341 return false;
342 }
343 } else if (!S_ISDIR(st.st_mode)) {
344 return false;
345 }
346
347 start = slashPos + 1;
348 }
349
350 return true;
351}
352
Andreas Huber5345ec22016-07-29 13:33:27 -0700353} // namespace android
354