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