1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 *
4 * HDF is dual licensed: you can use it either under the terms of
5 * the GPL, or the BSD license, at your option.
6 * See the LICENSE file in the root of this repository for complete details.
7 */
8
9 #include "util/options.h"
10
11 #include <cstdio>
12 #include <cstring>
13 #include <dirent.h>
14 #include <getopt.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17
18 #include <map>
19
20 #include "util/common.h"
21 #include "util/file.h"
22 #include "util/logger.h"
23 #include "util/string_helper.h"
24
25 namespace OHOS {
26 namespace HDI {
27 const char *Options::optSupportArgs = "hvs:m:l:p:c:d:r:o:D:";
28 static struct option g_longOpts[] = {
29 {"help", no_argument, nullptr, 'h'},
30 {"version", no_argument, nullptr, 'v'},
31 {"system", required_argument, nullptr, 's'},
32 {"mode", required_argument, nullptr, 'm'},
33 {"language", required_argument, nullptr, 'l'},
34 {"package", required_argument, nullptr, 'p'},
35 {"dump-ast", no_argument, nullptr, 'a'},
36 {"hash", no_argument, nullptr, 'H'},
37 {nullptr, 0, nullptr, 0 }
38 };
39
GetInstance()40 Options &Options::GetInstance()
41 {
42 static Options option;
43 return option;
44 }
45
Parse(int argc,char * argv[])46 bool Options::Parse(int argc, char *argv[])
47 {
48 int ret = true;
49 program = argv[0];
50 opterr = 1;
51 int op = 0;
52 int optIndex = 0;
53 while ((op = getopt_long(argc, argv, optSupportArgs, g_longOpts, &optIndex)) != OPT_END) {
54 switch (op) {
55 case 'v':
56 doShowVersion = true;
57 break;
58 case 's':
59 ret = SetSystemLevel(optarg);
60 break;
61 case 'm':
62 ret = SetGenerateMode(optarg);
63 break;
64 case 'l':
65 SetLanguage(optarg);
66 break;
67 case 'p':
68 SetPackage(optarg);
69 break;
70 case 'a':
71 doDumpAST = true;
72 break;
73 case 'H':
74 doHashKey = true;
75 break;
76 case 'c':
77 AddSources(optarg);
78 break;
79 case 'D':
80 AddSourcesByDir(optarg);
81 break;
82 case 'd':
83 SetOutDir(optarg);
84 break;
85 case 'r':
86 ret = AddPackagePath(optarg);
87 break;
88 case 'o':
89 outPutFile = CheckOutPutFile(optarg);
90 break;
91 default:
92 doShowUsage = true;
93 break;
94 }
95 }
96 return ret ? CheckOptions() : ret;
97 }
98
SetSystemLevel(const std::string & system)99 bool Options::SetSystemLevel(const std::string &system)
100 {
101 static std::map<std::string, SystemLevel> systemLevelMap = {
102 {"mini", SystemLevel::MINI},
103 {"lite", SystemLevel::LITE},
104 {"full", SystemLevel::FULL},
105 };
106
107 auto levelIter = systemLevelMap.find(system);
108 if (levelIter == systemLevelMap.end()) {
109 Logger::E(TAG, "invalid system level set: '%s', please input mini/lite/full", system.c_str());
110 return false;
111 }
112 systemLevel = levelIter->second;
113 return true;
114 }
115
SetGenerateMode(const std::string & mode)116 bool Options::SetGenerateMode(const std::string &mode)
117 {
118 static std::map<std::string, GenMode> codeGenMap = {
119 {"low", GenMode::LOW},
120 {"passthrough", GenMode::PASSTHROUGH},
121 {"ipc", GenMode::IPC},
122 {"kernel", GenMode::KERNEL},
123 };
124
125 auto codeGenIter = codeGenMap.find(mode);
126 if (codeGenIter == codeGenMap.end()) {
127 Logger::E(TAG, "invalid generate mode set: '%s', please input low/passthrough/ipc/kernel.", mode.c_str());
128 return false;
129 }
130 genMode = codeGenIter->second;
131 return true;
132 }
133
SetLanguage(const std::string & language)134 bool Options::SetLanguage(const std::string &language)
135 {
136 static const std::map<std::string, Language> languageMap = {
137 {"c", Language::C},
138 {"cpp", Language::CPP},
139 {"java", Language::JAVA},
140 };
141
142 const auto kindIter = languageMap.find(language);
143 if (kindIter == languageMap.end()) {
144 Logger::E(TAG, "invalid language '%s', please input c, cpp or java", language.c_str());
145 return false;
146 }
147
148 doGenerateCode = true;
149 genLanguage = kindIter->second;
150 return true;
151 }
152
SetPackage(const std::string & infPackage)153 void Options::SetPackage(const std::string &infPackage)
154 {
155 idlPackage = infPackage;
156 }
157
AddSources(const std::string & sourceFile)158 void Options::AddSources(const std::string &sourceFile)
159 {
160 std::string realPath = File::AdapterRealPath(sourceFile);
161 if (realPath.empty()) {
162 Logger::E(TAG, "invalid idl file path:%s", sourceFile.c_str());
163 return;
164 }
165
166 if (!File::VerifyRealPath(realPath)) {
167 Logger::E(TAG, "verify path failed, path:%s", realPath.c_str());
168 return;
169 }
170
171 if (sourceFiles.insert(realPath).second == false) {
172 Logger::E(TAG, "this idl file has been add:%s", sourceFile.c_str());
173 return;
174 }
175 doCompile = true;
176 }
177
CheckOutPutFile(const std::string & sourceFile)178 std::string Options::CheckOutPutFile(const std::string &sourceFile)
179 {
180 std::string realPath = File::AdapterRealPath(sourceFile);
181 if (realPath.empty()) {
182 Logger::E(TAG, "invalid idl file path:%s", sourceFile.c_str());
183 return "";
184 }
185
186 if (!File::VerifyRealPath(realPath)) {
187 Logger::E(TAG, "verify path failed, path:%s", realPath.c_str());
188 return "";
189 }
190 return realPath;
191 }
192
AddSourcesByDir(const std::string & dir)193 void Options::AddSourcesByDir(const std::string &dir)
194 {
195 std::set<std::string> files = File::FindFiles(dir);
196 if (!files.empty()) {
197 doCompile = true;
198 sourceFiles.insert(files.begin(), files.end());
199 }
200 }
201
AddPackagePath(const std::string & packagePath)202 bool Options::AddPackagePath(const std::string &packagePath)
203 {
204 size_t index = packagePath.find(":");
205 if (packagePath.size() == 0 || packagePath.size() >= SIZE_MAX) {
206 Logger::E(TAG, "invalid parameters '%s'.", packagePath.c_str());
207 return false;
208 }
209 if (index == std::string::npos || index == packagePath.size() - 1) {
210 Logger::E(TAG, "invalid option parameters '%s'.", packagePath.c_str());
211 return false;
212 }
213
214 std::string package = packagePath.substr(0, index);
215 std::string path = File::AdapterRealPath(packagePath.substr(index + 1));
216 if (path.empty()) {
217 Logger::E(TAG, "invalid path '%s'.", packagePath.substr(index + 1).c_str());
218 return false;
219 }
220
221 auto it = packagePathMap.find(package);
222 if (it != packagePathMap.end()) {
223 Logger::E(TAG, "The '%s:%s' has been set.", package.c_str(), path.c_str());
224 return false;
225 }
226
227 packagePathMap[package] = path;
228 return true;
229 }
230
SetOutDir(const std::string & dir)231 void Options::SetOutDir(const std::string &dir)
232 {
233 doOutDir = true;
234 genDir = dir;
235 }
236
CheckOptions()237 bool Options::CheckOptions()
238 {
239 if (doShowUsage || doShowVersion) {
240 return true;
241 }
242
243 if (doCompile) {
244 if (!DoGetHashKey() && !doDumpAST && !doGenerateCode && !doOutDir) {
245 Logger::E(TAG, "nothing to do.");
246 return false;
247 }
248
249 if (!doGenerateCode && doOutDir) {
250 Logger::E(TAG, "no target language.");
251 return false;
252 }
253
254 if (doGenerateCode && !doOutDir) {
255 Logger::E(TAG, "no out directory.");
256 return false;
257 }
258 } else {
259 if (DoGetHashKey() || doDumpAST || doGenerateCode || doOutDir) {
260 Logger::E(TAG, "no idl files.");
261 return false;
262 }
263 }
264 return true;
265 }
266
ShowVersion() const267 void Options::ShowVersion() const
268 {
269 printf("HDI-GEN: %d.%d\n"
270 "Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.\n\n",
271 VERSION_MAJOR, VERSION_MINOR);
272 }
273
ShowUsage() const274 void Options::ShowUsage() const
275 {
276 printf("Compile a .idl file and generate C/C++ and Java codes.\n"
277 "Usage: idl [options] file\n"
278 "Options:\n"
279 " -h, --help Display command line options\n"
280 " -v, --version Display toolchain version information\n"
281 " -s, --system <value> Set system level 'mini','lite' or 'full', the default value is 'full'\n"
282 " -m, --mode <value> Set generate code mode 'low', 'passthrough', 'ipc' or 'kernel',"
283 " the default value is 'ipc'\n"
284 " -l, --language <value> Set language of generate code 'c','cpp','java' or 'hash',"
285 " the default value is 'cpp'\n"
286 " -p, --package <package name> Set package of idl files\n"
287 " --dump-ast Display the AST of the compiled file\n"
288 " --hash Generate hash info of idl files\n"
289 " -r <rootPackage>:<rootPath> Set root path of root package\n"
290 " -c <*.idl> Compile the .idl file\n"
291 " -D <directory> Directory of the idl file\n"
292 " -d <directory> Place generated codes into <directory>\n"
293 " -o <file> Place the output into <file>\n");
294 }
295
296 /*
297 * -r option: -r ohos.hdi:./drivers/interface
298 * package:ohos.hdi.foo.v1_0
299 * rootPackage:ohos.hdi
300 */
GetRootPackage(const std::string & package) const301 std::string Options::GetRootPackage(const std::string &package) const
302 {
303 const auto &packagePaths = GetPackagePathMap();
304 for (const auto &packageRoot : packagePaths) {
305 if (StringHelper::StartWith(package, packageRoot.first)) {
306 return packageRoot.first;
307 }
308 }
309
310 return "";
311 }
312
313 /*
314 * -r option: -r ohos.hdi:./drivers/interface
315 * package:ohos.hdi.foo.v1_0
316 * rootPath:./drivers/interface
317 */
GetRootPath(const std::string & package) const318 std::string Options::GetRootPath(const std::string &package) const
319 {
320 const auto &packagePaths = GetPackagePathMap();
321 for (const auto &packageRoot : packagePaths) {
322 if (StringHelper::StartWith(package, packageRoot.first)) {
323 return packageRoot.second;
324 }
325 }
326
327 return "";
328 }
329
330 /*
331 * -r option: -r ohos.hdi:./drivers/interface
332 * package:ohos.hdi.foo.v1_0
333 * subPackage:foo.v1_0
334 */
GetSubPackage(const std::string & package) const335 std::string Options::GetSubPackage(const std::string &package) const
336 {
337 std::string rootPackage = GetRootPackage(package);
338 if (rootPackage.empty()) {
339 return package;
340 }
341
342 return package.substr(rootPackage.size() + 1);
343 }
344
345 /*
346 * -r option: -r ohos.hdi:./drivers/interface
347 * package:ohos.hdi.foo.v1_0
348 * packagePath:./drivers/interface/foo/v1_0
349 */
GetPackagePath(const std::string & package) const350 std::string Options::GetPackagePath(const std::string &package) const
351 {
352 std::string rootPackage = "";
353 std::string rootPath = "";
354 const auto &packagePaths = GetPackagePathMap();
355 for (const auto &packageRoot : packagePaths) {
356 if (StringHelper::StartWith(package, packageRoot.first)) {
357 rootPackage = packageRoot.first;
358 rootPath = packageRoot.second;
359 }
360 }
361
362 if (rootPackage.empty()) {
363 // The current path is the root path
364 std::string curPath = File::AdapterPath(StringHelper::Replace(package, '.', SEPARATOR));
365 return File::AdapterRealPath(curPath);
366 }
367
368 if (StringHelper::EndWith(rootPath, SEPARATOR)) {
369 rootPath.pop_back();
370 }
371
372 std::string subPath = StringHelper::Replace(package.substr(rootPackage.size() + 1), '.', SEPARATOR);
373 return File::AdapterPath(rootPath + "/" + subPath);
374 }
375
376 /*
377 * -r option: -r ohos.hdi:./drivers/interface
378 * import: ohos.hdi.foo.v1_0.MyTypes
379 * packagePath:./drivers/interface/foo/v1_0/MyTypes.idl
380 */
GetImportFilePath(const std::string & import) const381 std::string Options::GetImportFilePath(const std::string &import) const
382 {
383 size_t index = import.rfind('.');
384 if (index == std::string::npos) {
385 return import;
386 }
387
388 std::string dir = GetPackagePath(StringHelper::SubStr(import, 0, index));
389 std::string className = import.substr(index + 1);
390 return StringHelper::Format("%s%c%s.idl", dir.c_str(), SEPARATOR, className.c_str());
391 }
392 } // namespace HDI
393 } // namespace OHOS