Bug-fix: fix target dll searching logic
[platform/core/dotnet/launcher.git] / NativeLauncher / tool / dotnettool.cc
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "utils.h"
18 #include "ni_common.h"
19 #include "tac_common.h"
20 #include "profile_common.h"
21 #include "privilege_common.h"
22 #include "multi_target_resolver.h"
23 #include "log.h"
24
25 #include <vector>
26 #include <cstring>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <sys/time.h>
30
31 void DisplayUsage() {
32         _SOUT(
33                 "\n"
34                 "Dotnet Tool Version: 1.0\n"
35                 "\n"
36                 "Commands:\n"
37                 "       -h, --help                - Show this help message\n"
38                 "       --ni-system               - Create NI under System DLLs\n"
39                 "       --ni-dll                  - Create NI for DLL\n"
40                 "       --ni-pkg                  - Create NI for package\n"
41                 "                                   (If package is installed under RO area, NI files are generated under RW area)\n"
42                 "       --ni-dir                  - Create NI for directory\n"
43                 "       --ni-reset-system         - Remove System NI files\n"
44                 "       --ni-reset-pkg            - Remove App NI files\n"
45                 "       --ni-reset-dir            - Remove NI for directory\n"
46                 "       --ni-regen-all-app        - Re-generate All App NI files\n"
47                 "       --tac-regen-all           - Re-generate All TAC files\n"
48                 "       --tac-restore-db          - Restore TAC Database\n"
49                 "       --tac-disable-pkg         - Disable TAC for package\n"
50                 "       --tac-enable-pkg          - Enable TAC for package\n"
51                 "       --resolve-all-app         - Remove unused multi-targeting files of all apps\n"
52                 "                                   (this option is used for FOTA script or test)\n"
53                 "       --rm-app-profile          - Remove application profile of given packages for all users\n"
54                 "                                   (this option should be run as root)\n"
55                 "       --rm-all-app-profile      - Remove application profile of all packages for all users\n"
56                 "                                   (this option should be run as root)\n"
57                 "       --check-all-app-privilege - Chcek application privilege of all package\n"
58                 "                                   (this option should be run as root)\n"
59                 "\n"
60                 "Options:\n"
61                 "       --mibc                    - Specify Mibc files. Sepatated with ':'.\n"
62                 "       --verbose                 - Display verbose information\n"
63                 "       --inputbubble             - Compile input assemblies into one bubble with the assemblies described at --inputbubbleref option\n"
64                 "                                   Note!: If you do not have an accurate understanding of Bubble, do not use this option.\n"
65                 "                                          All assemblies in the bubble must be guaranteed to be compiled to the native image before execution.\n"
66                 "                                          If an uncompiled assembly is included in the bubble during execution, an unknown error may occur.\n"
67                 "                                          If --inputbubbleref option doesnot be set, only input files are included to bubble. \n"
68                 "       --inputbubbleref          - Input bubble reference file(s) to be added to bubble (used with --inputbubble option)\n"
69                 "       --ref                     - Reference file(s) for compilation\n"
70                 "                                   (system paths are set automatically.)\n"
71                 "       --no-pipeline             - Compile the dlls individually\n"
72                 "       --print-cmd               - Print command and options\n"
73                 "       --skip-ro-app             - Skip re-generate NI for apps installed RO area\n"
74                 "                                   (This option works with --ni-regen-all-app only)\n"
75                 "       --rm-origin-after-ni      - Remove original dll after creating native image\n"
76                 "                                   Note!: App ATOC options(--ni-pkg, --ni-regen-all-app, --tac-regen-all) cannot be used with --rm-origin-after-ni option.\n"
77                 "                                   (Use only for assemblies that will not be AOTed again afterward.)\n"
78                 "       --set-priority            - Sets the priority of the process to the specified values from -20(highest priority) to 19(lowest priority).(default : 0)\n"
79
80                 "\n"
81                 "Usage: dotnettool [options] [command] [arguments]\n"
82                 "\n"
83                 "Example:\n"
84                 "1. Create native image for dlls and exes under platform directories\n"
85                 "   # dotnettool --ni-system\n"
86                 "2. Create native image for dll\n"
87                 "   # dotnettool --ni-dll /usr/bin/Tizen.Runtime.dll\n"
88                 "3. Create native image under the package's bin and lib directory\n"
89                 "   # dotnettool --ni-pkg org.tizen.FormsGallery\n"
90                 "4. Regenerate native images for all installed .net packages\n"
91                 "   # dotnettool --ni-regen-all-app\n"
92                 "5. Create native image for dll based on the Mibc data\n"
93                 "   # dotnettool --mibc /tmp/ibcdata/in.mibc --ni-dll /usr/bin/Tizen.Runtime.dll\n"
94                 "6. Remove profile for package\n"
95                 "   # dotnettool --rm-app-profile org.tizen.FormsGallery\n"
96                 "\n");
97 }
98
99 int main(int argc, char* argv[])
100 {
101         argc--;
102         argv++;
103
104         long starttime;
105         long endtime;
106         struct timeval tv;
107         gettimeofday(&tv, NULL);
108         starttime = tv.tv_sec * 1000l + tv.tv_usec / 1000l;
109
110         if (argc <= 0) {
111                 DisplayUsage();
112                 return -1;
113         }
114
115         NIOption* opt = getNIOption();
116         if (opt == nullptr) {
117                 _SERR("Fail to create option structure.");
118                 return -1;
119         }
120
121         std::vector<std::string> args;
122         for (int i = 0; i < argc; ++i) {
123                 std::string arg = argv[i];
124
125                 if ((arg == "-?") || (arg == "-h") || (arg == "--h")) {
126                         DisplayUsage();
127                         return 0;
128                 } else if (arg == "--verbose") {
129                         opt->flags |= NI_FLAGS_VERBOSE;
130                 } else if (arg == "--inputbubble") {
131                         opt->flags |= NI_FLAGS_INPUT_BUBBLE;
132                 } else if (arg == "--no-pipeline") {
133                         opt->flags |= NI_FLAGS_NO_PIPELINE;
134                 } else if (arg == "--print-cmd") {
135                         opt->flags |= NI_FLAGS_PRINT_CMD;
136                 } else if (arg == "--skip-ro-app") {
137                         opt->flags |= NI_FLAGS_SKIP_RO_APP;
138                 }  else if (arg == "--rm-origin-after-ni") {
139                         opt->flags |= NI_FLAGS_RM_ORIGIN_AFTER_NI;
140                 } else if (arg == "--set-priority") {
141                         ++i;
142                         if (i >= argc) {
143                                 _SOUT("Priority value(-20 ~ 19) should be followed for --set-priority option");
144                                 DisplayUsage();
145                                 return 0;
146                         }
147
148                         opt->flags |= NI_FLAGS_SET_PRIORITY;
149                         try {
150                                 opt->priority = std::stoi(argv[i]);
151                                 setPriority(opt);
152                         } catch (...) {
153                                 _SERR("Invalid argument: %s", argv[i]);
154                                 DisplayUsage();
155                                 return 0;
156                         }
157                 } else if (arg == "--mibc") {
158                         ++i;
159                         if (i >= argc) {
160                                 _SOUT("File path containing Mibc files should be followed for --mibc option");
161                                 DisplayUsage();
162                                 return 0;
163                         }
164
165                         opt->flags |= NI_FLAGS_MIBC;
166
167                         std::vector<std::string> paths;
168                         splitPath(std::string(argv[i]), paths);
169                         for (const auto &path : paths) {
170                                 if (!isFile(path) || isDirectory(path)) {
171                                         _SERR("Mibc file path is missing or does not exist");
172                                         return -1;
173                                 }
174                                 opt->mibcPath.push_back(path);
175                         }
176                 } else if (arg == "--inputbubbleref") {
177                         ++i;
178                         if (i >= argc) {
179                                 _SOUT("Path for references should be followed for --inputbubbleref option");
180                                 DisplayUsage();
181                                 return 0;
182                         }
183
184                         opt->flags |= NI_FLAGS_INPUT_BUBBLE_REF;
185
186                         std::vector<std::string> files;
187                         splitPath(std::string(argv[i]), files);
188                         for (const auto &f : files) {
189                                 opt->inputBubbleRefFiles.push_back(f);
190                         }
191                 } else if (arg == "--ref") {
192                         ++i;
193                         if (i >= argc) {
194                                 _SOUT("Path for references should be followed for --ref option");
195                                 DisplayUsage();
196                                 return 0;
197                         }
198
199                         std::vector<std::string> files;
200                         splitPath(std::string(argv[i]), files);
201                         for (const auto &f : files) {
202                                 opt->refFiles.push_back(f);
203                         }
204                 } else {
205                         args.push_back(arg);
206                 }
207         }
208
209         if (args.size() == 0) {
210                 _SERR("The command is missing");
211                 DisplayUsage();
212                 return -1;
213         }
214
215         if (opt->flags & NI_FLAGS_INPUT_BUBBLE_REF && !(opt->flags & NI_FLAGS_INPUT_BUBBLE)) {
216                 _SERR("--inputbubbleref option should be used with --inputbubble option");
217                 DisplayUsage();
218                 return -1;
219         }
220
221         if (initNICommon() != NI_ERROR_NONE) {
222                 return -1;
223         }
224
225         std::vector<std::string>::iterator it = args.begin();
226         std::string cmd = std::string(*it);
227         it = args.erase(it);
228
229         //sh-3.2# dotnettool --ni-system [AssemblyDirectory] [AssemblyDirectory] ...
230         if (cmd == "--ni-system") {
231                 std::string inputs;
232                 while (it != args.end()) {
233                         const std::string dir = std::string(*it);
234                         inputs = inputs + ":" + dir;
235                         it = args.erase(it);
236                 }
237                 int ret = createNIPlatform(inputs, opt);
238                 if (ret != NI_ERROR_NONE) {
239                         _SERR("Failed to generate system NI");
240                 }
241         }
242         //sh-3.2# dotnettool --ni-dll [assemblyPath] [assemblyPath] ...
243         else if (cmd == "--ni-dll") {
244                 if (args.size() < 1) {
245                         _SERR("DLL path is missing");
246                 }
247                 std::vector<std::string> inputs;
248                 while (it != args.end()) {
249                         std::string dll = std::string(*it);
250                         inputs.push_back(dll);
251                         opt->refFiles.push_back(dll);
252                         if (opt->flags & NI_FLAGS_INPUT_BUBBLE) {
253                                 opt->inputBubbleRefFiles.push_back(dll);
254                         }
255                         it = args.erase(it);
256                 }
257
258                 for (auto &dll : inputs) {
259                         int ret = createNIDll(dll, opt);
260                         if (ret != NI_ERROR_NONE && ret != NI_ERROR_ALREADY_EXIST) {
261                                 _SERR("Failed to generate NI file [%s]", dll.c_str());
262                                 break;
263                         }
264                 }
265         }
266         //sh-3.2# dotnettool --ni-pkg [pkgId] [pkgId] ...
267         else if (cmd == "--ni-pkg") {
268                 if (opt->flags & NI_FLAGS_RM_ORIGIN_AFTER_NI) {
269                         _SERR("App AOTC options cannot be used with --rm-origin-after-ni option");
270                         DisplayUsage();
271                         return -1;
272                 }
273
274                 if (args.size() < 1) {
275                         _SERR("Package name is missing");
276                 }
277                 while (it != args.end()) {
278                         std::string pkg = std::string(*it);
279                         int ret = createNIUnderPkgRoot(pkg, opt);
280                         if (ret != NI_ERROR_NONE) {
281                                 _SERR("Failed to generate app NI [%s]", pkg.c_str());
282                                 break;
283                         }
284                         it = args.erase(it);
285                 }
286         }
287         //sh-3.2# dotnettool --ni-dir [AssemblyDirectory] [AssemblyDirectory] ...
288         else if (cmd == "--ni-dir") {
289                 if (args.size() < 1) {
290                         _SERR("Directory path is missing");
291                 }
292                 std::string dir;
293                 while (it != args.end()) {
294                         if (!dir.empty()) {
295                                 dir += std::string(":");
296                         }
297                         dir += std::string(*it);
298                         it = args.erase(it);
299                 }
300                 if (createNIUnderDirs(dir, opt) != NI_ERROR_NONE) {
301                         _SERR("Failed to generate NI directory");
302                 }
303         }
304         //sh-3.2# dotnettool --ni-reset-system
305         else if (cmd == "--ni-reset-system") {
306                 removeNIPlatform();
307         }
308         //sh-3.2# dotnettool --ni-reset-pkg [pkgId] [pkgId] ...
309         else if (cmd == "--ni-reset-pkg") {
310                 if (args.size() < 1) {
311                         _SERR("Package name is missing");
312                 }
313                 while (it != args.end()) {
314                         std::string pkg = std::string(*it);
315                         int ret = removeNIUnderPkgRoot(pkg);
316                         if (ret != NI_ERROR_NONE) {
317                                 _SERR("Failed to remove dlls for given package [%s]", pkg.c_str());
318                                 break;
319                         }
320                         it = args.erase(it);
321                 }
322         }
323         //sh-3.2# dotnettool --ni-reset-dir [AssemblyDirectory] [AssemblyDirectory] ...
324         else if (cmd == "--ni-reset-dir") {
325                 if (args.size() < 1) {
326                         _SERR("Directory path is missing");
327                 }
328                 while (it != args.end()) {
329                         const std::string dir = std::string(*it);
330                         removeNIUnderDirs(dir);
331                         it = args.erase(it);
332                 }
333         }
334         //sh-3.2# dotnettool --ni-regen-all-app
335         else if (cmd == "--ni-regen-all-app") {
336                 if (opt->flags & NI_FLAGS_RM_ORIGIN_AFTER_NI) {
337                         _SERR("App AOTC options cannot be used with --rm-origin-after-ni option");
338                         DisplayUsage();
339                         return -1;
340                 }
341
342                 int ret = regeneratePkgNI(opt);
343                 if (ret != NI_ERROR_NONE) {
344                         _SERR("Failed to regenerate all app NI");
345                 }
346         }
347         //sh-3.2# dotnettool --tac-regen-all
348         else if (cmd == "--tac-regen-all") {
349                 if (opt->flags & NI_FLAGS_RM_ORIGIN_AFTER_NI) {
350                         _SERR("App AOTC options cannot be used with --rm-origin-after-ni option");
351                         DisplayUsage();
352                         return -1;
353                 }
354
355                 int ret = regenerateTACNI(opt);
356                 if (ret != NI_ERROR_NONE) {
357                         _SERR("Failed to regenerate all TAC");
358                 }
359         }
360         //sh-3.2# dotnettool --tac-restore-db
361         else if (cmd == "--tac-restore-db") {
362                 int ret = tac_restoreDB();
363                 if (ret != TAC_ERROR_NONE) {
364                         _SERR("Failed to restore TAC db");
365                 }
366                 ret = tlc_restoreDB();
367                 if (ret != TAC_ERROR_NONE) {
368                         _SERR("Failed to restore TLC db");
369                 }
370         }
371         //sh-3.2# dotnettool --tac-enable-pkg [pkgId] [pkgId] ...
372         else if (cmd == "--tac-enable-pkg") {
373                 if (args.size() < 1) {
374                         _SERR("Package name is missing");
375                 }
376                 while (it != args.end()) {
377                         std::string pkg = std::string(*it);
378                         int ret = enableTACPackage(pkg);
379                         if (ret != TAC_ERROR_NONE) {
380                                 _SERR("Failed to enable tac [%s]", pkg.c_str());
381                                 break;
382                         }
383                         it = args.erase(it);
384                 }
385         }
386         //sh-3.2# dotnettool --tac-disable-pkg [pkgId] [pkgId] ...
387         else if (cmd == "--tac-disable-pkg") {
388                 if (args.size() < 1) {
389                         _SERR("Package name is missing");
390                 }
391                 while (it != args.end()) {
392                         std::string pkg = std::string(*it);
393                         int ret = disableTACPackage(pkg);
394                         if (ret != TAC_ERROR_NONE) {
395                                 _SERR("Failed to disable tac [%s]", pkg.c_str());
396                                 break;
397                         }
398                         it = args.erase(it);
399                 }
400         }
401         //sh-3.2# dotnettool --resolve-all-app
402         else if (cmd == "--resolve-all-app") {
403                 resolveAllApps();
404         }
405         //sh-3.2# dotnettool --rm-app-profile [pkgId] [pkgId] ...
406         else if (cmd == "--rm-app-profile") {
407                 if (args.size() < 1) {
408                         _SERR("Package name is missing");
409                 }
410                 while (it != args.end()) {
411                         std::string pkg = std::string(*it);
412                         if (removeAppProfileData(pkg) != PROFILE_ERROR_NONE) {
413                                 _SERR("Failed to remove [%s] profile data", pkg.c_str());
414                         }
415                         it = args.erase(it);
416                 }
417         }
418         //sh-3.2# dotnettool --rm-all-app-profile
419         else if (cmd == "--rm-all-app-profile") {
420                 removeAllAppProfileData();
421         }
422         //sh-3.2# dotnettool --check-all-app-privilege
423         else if (cmd == "--check-all-app-privilege") {
424                 checkAllAppPrivilege();
425         }
426         else {
427                 _SERR("Unknown option [%s]", cmd.c_str());
428                 DisplayUsage();
429         }
430
431         finalizeNICommon();
432
433         gettimeofday(&tv, NULL);
434         endtime = tv.tv_sec * 1000l + tv.tv_usec / 1000l;
435         int ms = (int)(endtime - starttime);
436         float sec = (float)(ms/1000);
437         _SOUT("\nSpend time for dotnettool is [%d]ms, [%.3f]sec", ms, sec);
438
439         return 0;
440 }