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 = 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 (sourceFiles.insert(realPath).second == false) {
167 Logger::E(TAG, "this idl file has been add:%s", sourceFile.c_str());
168 return;
169 }
170 doCompile = true;
171 }
172
AddSourcesByDir(const std::string & dir)173 void Options::AddSourcesByDir(const std::string &dir)
174 {
175 std::set<std::string> files = File::FindFiles(dir);
176 if (!files.empty()) {
177 doCompile = true;
178 sourceFiles.insert(files.begin(), files.end());
179 }
180 }
181
AddPackagePath(const std::string & packagePath)182 bool Options::AddPackagePath(const std::string &packagePath)
183 {
184 size_t index = packagePath.find(":");
185 if (index == std::string::npos || index == packagePath.size() - 1) {
186 Logger::E(TAG, "invalid option parameters '%s'.", packagePath.c_str());
187 return false;
188 }
189
190 std::string package = packagePath.substr(0, index);
191 std::string path = File::AdapterRealPath(packagePath.substr(index + 1));
192 if (path.empty()) {
193 Logger::E(TAG, "invalid path '%s'.", packagePath.substr(index + 1).c_str());
194 return false;
195 }
196
197 auto it = packagePathMap.find(package);
198 if (it != packagePathMap.end()) {
199 Logger::E(TAG, "The '%s:%s' has been set.", package.c_str(), path.c_str());
200 return false;
201 }
202
203 packagePathMap[package] = path;
204 return true;
205 }
206
SetOutDir(const std::string & dir)207 void Options::SetOutDir(const std::string &dir)
208 {
209 doOutDir = true;
210 genDir = dir;
211 }
212
CheckOptions()213 bool Options::CheckOptions()
214 {
215 if (doShowUsage || doShowVersion) {
216 return true;
217 }
218
219 if (doCompile) {
220 if (!DoGetHashKey() && !doDumpAST && !doGenerateCode && !doOutDir) {
221 Logger::E(TAG, "nothing to do.");
222 return false;
223 }
224
225 if (!doGenerateCode && doOutDir) {
226 Logger::E(TAG, "no target language.");
227 return false;
228 }
229
230 if (doGenerateCode && !doOutDir) {
231 Logger::E(TAG, "no out directory.");
232 return false;
233 }
234 } else {
235 if (DoGetHashKey() || doDumpAST || doGenerateCode || doOutDir) {
236 Logger::E(TAG, "no idl files.");
237 return false;
238 }
239 }
240 return true;
241 }
242
ShowVersion() const243 void Options::ShowVersion() const
244 {
245 printf("HDI-GEN: %d.%d\n"
246 "Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.\n\n",
247 VERSION_MAJOR, VERSION_MINOR);
248 }
249
ShowUsage() const250 void Options::ShowUsage() const
251 {
252 printf("Compile a .idl file and generate C/C++ and Java codes.\n"
253 "Usage: idl [options] file\n"
254 "Options:\n"
255 " -h, --help Display command line options\n"
256 " -v, --version Display toolchain version information\n"
257 " -s, --system <value> Set system level 'mini','lite' or 'full', the default value is 'full'\n"
258 " -m, --mode <value> Set generate code mode 'low', 'passthrough', 'ipc' or 'kernel',"
259 " the default value is 'ipc'\n"
260 " -l, --language <value> Set language of generate code 'c','cpp','java' or 'hash',"
261 " the default value is 'cpp'\n"
262 " -p, --package <package name> Set package of idl files\n"
263 " --dump-ast Display the AST of the compiled file\n"
264 " --hash Generate hash info of idl files\n"
265 " -r <rootPackage>:<rootPath> Set root path of root package\n"
266 " -c <*.idl> Compile the .idl file\n"
267 " -D <directory> Directory of the idl file\n"
268 " -d <directory> Place generated codes into <directory>\n"
269 " -o <file> Place the output into <file>\n");
270 }
271
272 /*
273 * -r option: -r ohos.hdi:./drivers/interface
274 * package:ohos.hdi.foo.v1_0
275 * rootPackage:ohos.hdi
276 */
GetRootPackage(const std::string & package) const277 std::string Options::GetRootPackage(const std::string &package) const
278 {
279 const auto &packagePaths = GetPackagePathMap();
280 for (const auto &packageRoot : packagePaths) {
281 if (StringHelper::StartWith(package, packageRoot.first)) {
282 return packageRoot.first;
283 }
284 }
285
286 return "";
287 }
288
289 /*
290 * -r option: -r ohos.hdi:./drivers/interface
291 * package:ohos.hdi.foo.v1_0
292 * rootPath:./drivers/interface
293 */
GetRootPath(const std::string & package) const294 std::string Options::GetRootPath(const std::string &package) const
295 {
296 const auto &packagePaths = GetPackagePathMap();
297 for (const auto &packageRoot : packagePaths) {
298 if (StringHelper::StartWith(package, packageRoot.first)) {
299 return packageRoot.second;
300 }
301 }
302
303 return "";
304 }
305
306 /*
307 * -r option: -r ohos.hdi:./drivers/interface
308 * package:ohos.hdi.foo.v1_0
309 * subPackage:foo.v1_0
310 */
GetSubPackage(const std::string & package) const311 std::string Options::GetSubPackage(const std::string &package) const
312 {
313 std::string rootPackage = GetRootPackage(package);
314 if (rootPackage.empty()) {
315 return package;
316 }
317
318 return package.substr(rootPackage.size() + 1);
319 }
320
321 /*
322 * -r option: -r ohos.hdi:./drivers/interface
323 * package:ohos.hdi.foo.v1_0
324 * packagePath:./drivers/interface/foo/v1_0
325 */
GetPackagePath(const std::string & package) const326 std::string Options::GetPackagePath(const std::string &package) const
327 {
328 std::string rootPackage = "";
329 std::string rootPath = "";
330 const auto &packagePaths = GetPackagePathMap();
331 for (const auto &packageRoot : packagePaths) {
332 if (StringHelper::StartWith(package, packageRoot.first)) {
333 rootPackage = packageRoot.first;
334 rootPath = packageRoot.second;
335 }
336 }
337
338 if (rootPackage.empty()) {
339 // The current path is the root path
340 std::string curPath = File::AdapterPath(StringHelper::Replace(package, '.', SEPARATOR));
341 return File::AdapterRealPath(curPath);
342 }
343
344 if (StringHelper::EndWith(rootPath, SEPARATOR)) {
345 rootPath.pop_back();
346 }
347
348 std::string subPath = StringHelper::Replace(package.substr(rootPackage.size() + 1), '.', SEPARATOR);
349 return File::AdapterPath(rootPath + "/" + subPath);
350 }
351
352 /*
353 * -r option: -r ohos.hdi:./drivers/interface
354 * import: ohos.hdi.foo.v1_0.MyTypes
355 * packagePath:./drivers/interface/foo/v1_0/MyTypes.idl
356 */
GetImportFilePath(const std::string & import) const357 std::string Options::GetImportFilePath(const std::string &import) const
358 {
359 size_t index = import.rfind('.');
360 if (index == std::string::npos) {
361 return import;
362 }
363
364 std::string dir = GetPackagePath(StringHelper::SubStr(import, 0, index));
365 std::string className = import.substr(index + 1);
366 return StringHelper::Format("%s%c%s.idl", dir.c_str(), SEPARATOR, className.c_str());
367 }
368 } // namespace HDI
369 } // namespace OHOS