1 /*
2 * Copyright (c) 2021 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 "util/common.h"
19 #include "util/file.h"
20 #include "util/string_helper.h"
21
22 #include "util/logger.h"
23
24 namespace OHOS {
25 namespace HDI {
26 const char *Options::optSupportArgs = "c:d:r:";
27 static struct option g_longOpts[] = {
28 {"help", no_argument, nullptr, 'h'},
29 {"version", no_argument, nullptr, 'v'},
30 {"gen-c", no_argument, nullptr, 'C'},
31 {"gen-cpp", no_argument, nullptr, 'P'},
32 {"gen-java", no_argument, nullptr, 'J'},
33 {"gen-hash", no_argument, nullptr, 'H'},
34 {"build-target", required_argument, nullptr, 'p'},
35 {"module-name", required_argument, nullptr, 'N'},
36 {"passthrough", no_argument, nullptr, 'T'},
37 {"kernel", no_argument, nullptr, 'K'},
38 {"dump-ast", no_argument, nullptr, 'D'},
39 {nullptr, 0, nullptr, 0 }
40 };
41
GetInstance()42 Options &Options::GetInstance()
43 {
44 static Options option;
45 return option;
46 }
47
Parse(int argc,char * argv[])48 Options &Options::Parse(int argc, char *argv[])
49 {
50 program_ = argv[0];
51 opterr = 1;
52 int op = 0;
53 int optIndex = 0;
54
55 while ((op = getopt_long(argc, argv, optSupportArgs, g_longOpts, &optIndex)) != OPT_END) {
56 SetOptionData(op);
57 }
58 CheckOptions();
59
60 return *this;
61 }
62
SetOptionData(char op)63 void Options::SetOptionData(char op)
64 {
65 switch (op) {
66 case 'c':
67 AddSources(optarg);
68 break;
69 case 'd':
70 SetOutDir(optarg);
71 break;
72 case 'h':
73 doShowUsage_ = true;
74 break;
75 case 'v':
76 doShowVersion_ = true;
77 break;
78 case 'r':
79 AddPackagePath(optarg);
80 break;
81 case 'K':
82 doModeKernel_ = true;
83 break;
84 case 'N':
85 SetModuleName(optarg);
86 break;
87 case 'C':
88 SetLanguage(Language::C);
89 break;
90 case 'P':
91 SetLanguage(Language::CPP);
92 break;
93 case 'J':
94 SetLanguage(Language::JAVA);
95 break;
96 case 'p':
97 SetCodePart(optarg);
98 break;
99 case 'T':
100 doPassthrough_ = true;
101 break;
102 case 'H':
103 doGetHashKey_ = true;
104 break;
105 case 'D':
106 doDumpAST_ = true;
107 break;
108 case '?':
109 default:
110 doShowUsage_ = true;
111 break;
112 }
113 }
114
AddPackagePath(const std::string & packagePath)115 void Options::AddPackagePath(const std::string &packagePath)
116 {
117 size_t index = packagePath.find(":");
118 if (index == std::string::npos || index == packagePath.size() - 1) {
119 errors_.push_back(
120 StringHelper::Format("%s: invalid option parameters '%s'.", program_.c_str(), packagePath.c_str()));
121 return;
122 }
123
124 std::string package = packagePath.substr(0, index);
125 std::string path = File::AdapterRealPath(packagePath.substr(index + 1));
126 if (path.empty()) {
127 errors_.push_back(
128 StringHelper::Format("%s: invalid path '%s'.", program_.c_str(), packagePath.substr(index + 1).c_str()));
129 return;
130 }
131
132 auto it = packagePath_.find(package);
133 if (it != packagePath_.end()) {
134 errors_.push_back(
135 StringHelper::Format("%s: The '%s:%s' has been set.", program_.c_str(), package.c_str(), path.c_str()));
136 }
137
138 packagePath_[package] = path;
139 }
140
AddSources(const std::string & sourceFile)141 void Options::AddSources(const std::string &sourceFile)
142 {
143 doCompile_ = true;
144 sourceFiles_.push_back(sourceFile);
145 }
146
SetOutDir(const std::string & dir)147 void Options::SetOutDir(const std::string &dir)
148 {
149 doOutDir_ = true;
150 generationDirectory_ = dir;
151 }
152
SetModuleName(const std::string & moduleName)153 void Options::SetModuleName(const std::string &moduleName)
154 {
155 doSetModuleName_ = true;
156 moduleName_ = moduleName;
157 }
158
SetLanguage(Language kind)159 void Options::SetLanguage(Language kind)
160 {
161 doGenerateCode_ = true;
162 targetLanguage_ = kind;
163 }
164
SetCodePart(const std::string & part)165 void Options::SetCodePart(const std::string &part)
166 {
167 // The default parameter is 'all', and the optional parameters is 'client' or 'server'
168 doGeneratePart_ = true;
169 codePart_ = part;
170 }
171
CheckOptions()172 void Options::CheckOptions()
173 {
174 if (doShowUsage_ || doShowVersion_) {
175 return;
176 }
177
178 if (doCompile_) {
179 if (!doGetHashKey_ && !doDumpAST_ && !doGenerateCode_ && !doOutDir_) {
180 errors_.push_back(StringHelper::Format("%s: nothing to do.", program_.c_str()));
181 return;
182 }
183
184 if (!doGenerateCode_ && doOutDir_) {
185 errors_.push_back(StringHelper::Format("%s: no target language.", program_.c_str()));
186 return;
187 }
188
189 if (doGenerateCode_ && !doOutDir_) {
190 errors_.push_back(StringHelper::Format("%s: no out directory.", program_.c_str()));
191 return;
192 }
193
194 if (doGeneratePart_ && codePart_ != "all" && codePart_ != "client" && codePart_ != "server") {
195 std::string errorLog = "The '--build-target' option parameter must be 'client' 'server' or 'all'.";
196 errors_.push_back(StringHelper::Format("%s: %s", program_.c_str(), errorLog.c_str()));
197 }
198 } else {
199 if (doGetHashKey_ || doDumpAST_ || doGenerateCode_ || doOutDir_) {
200 errors_.push_back(StringHelper::Format("%s: no '-c' option.", program_.c_str()));
201 return;
202 }
203 }
204 }
205
ShowErrors() const206 void Options::ShowErrors() const
207 {
208 for (auto error : errors_) {
209 printf("%s\n", error.c_str());
210 }
211 printf("Use \"--help\" to show usage.\n");
212 }
213
ShowVersion() const214 void Options::ShowVersion() const
215 {
216 printf("HDI-GEN: %d.%d\n"
217 "Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.\n\n",
218 VERSION_MAJOR, VERSION_MINOR);
219 }
220
ShowUsage() const221 void Options::ShowUsage() const
222 {
223 printf("Compile a .idl file and generate C/C++ and Java codes.\n"
224 "Usage: idl [options] file\n"
225 "Options:\n"
226 " --help Display command line options\n"
227 " --version Display toolchain version information\n"
228 " --dump-ast Display the AST of the compiled file\n"
229 " -r <rootPackage>:<rootPath> set root path of root package\n"
230 " -c <*.idl> Compile the .idl file\n"
231 " --gen-hash Generate hash key of the idl file\n"
232 " --gen-c Generate C code\n"
233 " --gen-cpp Generate C++ code\n"
234 " --gen-java Generate Java code\n"
235 " --kernel Generate kernel-mode ioservice stub code,"
236 "default user-mode ioservice stub code\n"
237 " --passthrough Generate code that only supports pass through mode"
238 " --module-name <module name> Set driver module name\n"
239 " --build-target <target name> Generate client code, server code or all code\n"
240 " -d <directory> Place generated codes into <directory>\n");
241 }
242
243 /*
244 * -r option: -r ohos.hdi:./drivers/interface
245 * package:ohos.hdi.foo.v1_0
246 * rootPackage:ohos.hdi
247 */
GetRootPackage(const std::string & package)248 std::string Options::GetRootPackage(const std::string &package)
249 {
250 const auto &packagePaths = GetPackagePathMap();
251 for (const auto &packageRoot : packagePaths) {
252 if (StringHelper::StartWith(package, packageRoot.first)) {
253 return packageRoot.first;
254 }
255 }
256
257 return "";
258 }
259
260 /*
261 * -r option: -r ohos.hdi:./drivers/interface
262 * package:ohos.hdi.foo.v1_0
263 * rootPath:./drivers/interface
264 */
GetRootPath(const std::string & package)265 std::string Options::GetRootPath(const std::string &package)
266 {
267 const auto &packagePaths = GetPackagePathMap();
268 for (const auto &packageRoot : packagePaths) {
269 if (StringHelper::StartWith(package, packageRoot.first)) {
270 return packageRoot.second;
271 }
272 }
273
274 return "";
275 }
276
277 /*
278 * -r option: -r ohos.hdi:./drivers/interface
279 * package:ohos.hdi.foo.v1_0
280 * subPackage:foo.v1_0
281 */
GetSubPackage(const std::string & package)282 std::string Options::GetSubPackage(const std::string &package)
283 {
284 std::string rootPackage = GetRootPackage(package);
285 if (rootPackage.empty()) {
286 return package;
287 }
288
289 return package.substr(rootPackage.size() + 1);
290 }
291
292 /*
293 * -r option: -r ohos.hdi:./drivers/interface
294 * package:ohos.hdi.foo.v1_0
295 * packagePath:./drivers/interface/foo/v1_0
296 */
GetPackagePath(const std::string & package)297 std::string Options::GetPackagePath(const std::string &package)
298 {
299 std::string rootPackage = "";
300 std::string rootPath = "";
301 const auto &packagePaths = GetPackagePathMap();
302 for (const auto &packageRoot : packagePaths) {
303 if (StringHelper::StartWith(package, packageRoot.first)) {
304 rootPackage = packageRoot.first;
305 rootPath = packageRoot.second;
306 }
307 }
308
309 if (rootPackage.empty()) {
310 // The current path is the root path
311 std::string curPath = File::AdapterPath(StringHelper::Replace(package, '.', SEPARATOR));
312 return File::AdapterRealPath(curPath);
313 }
314
315 if (StringHelper::EndWith(rootPath, SEPARATOR)) {
316 rootPath.pop_back();
317 }
318
319 std::string subPath = StringHelper::Replace(package.substr(rootPackage.size() + 1), '.', SEPARATOR);
320 return File::AdapterPath(rootPath + "/" + subPath);
321 }
322
323 /*
324 * -r option: -r ohos.hdi:./drivers/interface
325 * import: ohos.hdi.foo.v1_0.MyTypes
326 * packagePath:./drivers/interface/foo/v1_0/MyTypes.idl
327 */
GetImportFilePath(const std::string & import)328 std::string Options::GetImportFilePath(const std::string &import)
329 {
330 size_t index = import.rfind('.');
331 if (index == std::string::npos) {
332 return import;
333 }
334
335 std::string dir = GetPackagePath(StringHelper::SubStr(import, 0, index));
336 std::string ClassName = import.substr(index + 1);
337 return StringHelper::Format("%s%c%s.idl", dir.c_str(), SEPARATOR, ClassName.c_str());
338 }
339 } // namespace HDI
340 } // namespace OHOS