Add 'Tizen Assembly Cache' feature
[platform/core/dotnet/launcher.git] / NativeLauncher / tool / tactool.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 "log.h"
18 #include "utils.h"
19 #include "db_manager.h"
20
21 #include <algorithm>
22 #include <cstdio>
23 #include <cstring>
24 #include <fstream>
25 #include <vector>
26
27 #include <json/json.h>
28 #include <pkgmgr-info.h>
29 #include <pkgmgr_installer_info.h>
30
31 #ifdef  LOG_TAG
32 #undef  LOG_TAG
33 #endif
34 #define LOG_TAG "DOTNET_INSTALLER_PLUGIN"
35
36 const std::string mOptUsrDotnet = "/opt/usr/dotnet";
37 const std::string mTizenNET = "Tizen.NET";
38 const std::string mTizenNETSdk = "Tizen.NET.Sdk";
39 const std::string mNETStandardLibrary = "NETStandard.Library";
40 const std::string mDepsJson = ".deps.json";
41 const std::string tacAppListDB = mOptUsrDotnet + "/.TAC.App.list.db";
42 const std::string tacAppListDBJournal = mOptUsrDotnet + "/.TAC.App.list.db-journal";
43 const std::string tacAppListRestoreDB = mOptUsrDotnet + "/.TAC.App.list.restore.db";
44 const std::string tacAppListRestoreDBJournal = mOptUsrDotnet + "/.TAC.App.list.restore.db-journal";
45 const std::string mdKey = "http://tizen.org/metadata/prefer_nuget_cache";
46 const std::string mdValue = "true";
47
48 static sqlite3 *tac_db = NULL;
49 std::vector<std::string> restoreNuget;
50
51 static void help(const char *argv0)
52 {
53         const char* helpDesc =
54                 "Usage: %s [args] <root paths or pkg name>\n"
55                 "          --help                       - Display this screen\n"
56                 "          --restore-db         - Restore TAC Database\n"
57                 "\n";
58         printf(helpDesc, argv0, argv0, argv0, argv0, argv0);
59 }
60
61 void cleanupDirectory()
62 {
63         std::vector<std::string> removeNuget;
64         for (auto& nuget : bf::recursive_directory_iterator(bf::path(mOptUsrDotnet))) {
65                 bool isExist = false;
66                 for (auto& restore : restoreNuget) {
67                         if (!bf::is_directory(nuget.path())) {
68                                 isExist = true;
69                         }
70                         if (strstr(nuget.path().c_str(), restore.c_str()) != NULL) {
71                                 isExist = true;
72                                 break;
73                         }
74                 }
75                 if (!isExist) {
76                         removeNuget.push_back(nuget.path().string());
77                 }
78         }
79
80         for (auto& rm : removeNuget) {
81                 if (bf::exists(rm)) {
82                         if (!removeAll(rm)) {
83                                 _ERR("Failed to remove of %s", rm.c_str());
84                         }
85                 }
86         }
87         removeNuget.clear();
88 }
89
90 void restoreTACDB(std::string pkgId, std::string depsJsonPath, std::string execName)
91 {
92         std::ifstream ifs(depsJsonPath);
93         Json::CharReaderBuilder reader;
94         Json::Value root;
95         std::string error;
96         if (ifs.is_open()) {
97                 if (!Json::parseFromStream(reader, ifs, &root, &error)) {
98                         _INFO("Failed to parse of deps.json");
99                         ifs.close();
100                         return;
101                 }
102                 const Json::Value runtimeTargetName = root["runtimeTarget"]["name"];
103                 std::string runtimeTarget_name = runtimeTargetName.asString();
104                 const Json::Value nugetPackages = root["targets"][runtimeTarget_name.c_str()];
105                 for (auto& nuget : nugetPackages.getMemberNames()) {
106                         if (strstr(nuget.c_str(), mTizenNET.c_str()) != NULL ||
107                                 strstr(nuget.c_str(), mTizenNETSdk.c_str()) != NULL ||
108                                 strstr(nuget.c_str(), (execName.substr(0, execName.find(".Tizen."))).c_str()) != NULL ||
109                                 strstr(nuget.c_str(), (execName.substr(0, execName.find(".dll"))).c_str()) != NULL) {
110                                 continue;
111                         } else {
112                                 const Json::Value assemblies = nugetPackages[nuget.c_str()]["runtime"];
113                                 if (assemblies != Json::nullValue) {
114                                         const Json::Value dependencies = nugetPackages[nuget.c_str()]["dependencies"];
115                                         bool isDependency = false;
116                                         for (auto& dependency : dependencies.getMemberNames()) {
117                                                 if (strstr(dependency.c_str(), mTizenNET.c_str()) != NULL ||
118                                                         strstr(dependency.c_str(), mNETStandardLibrary.c_str()) != NULL) {
119                                                         continue;
120                                                 } else {
121                                                         isDependency = true;
122                                                 }
123                                         }
124                                         if (!isDependency) {
125                                                 _INFO("PackageId : [%s] / Nuget package : [%s]", pkgId.c_str(), nuget.c_str());
126                                                 std::string name = nuget.substr(0, nuget.find('/'));
127                                                 std::string version = nuget.substr(nuget.rfind('/') + 1);
128                                                 std::string sql = "INSERT INTO TAC (PKGID, NUGET, NAME, VERSION) " \
129                                                                 "VALUES ('" + pkgId + "', '" + nuget + "', '" + name + "', '" + version + "');";
130                                                 dbInsert(tac_db, tacAppListRestoreDB, sql);
131                                                 restoreNuget.push_back(mOptUsrDotnet + "/" + name);
132                                         }
133                                 }
134                         }
135                 }
136                 ifs.close();
137         }
138 }
139
140 static int restoreDBCb(pkgmgrinfo_appinfo_h handle, void *userData)
141 {
142         int ret = 0;
143         char *pkgId = NULL;
144         char *root = NULL;
145         char *exec = NULL;
146         std::string rootPath;
147         std::string execName;
148
149         ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId);
150         if (ret != PMINFO_R_OK) {
151                 fprintf(stderr, "Failed to get pkgid\n");
152                 return -1;
153         }
154
155         ret = pkgmgrinfo_appinfo_get_root_path(handle, &root);
156         if (ret != PMINFO_R_OK) {
157                 fprintf(stderr, "Failed to get root path\n");
158                 return -1;
159         }
160         rootPath = root;
161
162         ret = pkgmgrinfo_appinfo_get_exec(handle, &exec);
163         if (ret != PMINFO_R_OK) {
164                 fprintf(stderr, "Failed to get exec name\n");
165                 return -1;
166         }
167         execName = std::string(exec).substr(std::string(exec).rfind('/') + 1);
168
169         std::string depsJsonName = execName.substr(0, execName.rfind(".dll")) + mDepsJson;
170         std::string depsJsonPath = rootPath + "/" + depsJsonName;
171         if (bf::exists(depsJsonPath)) {
172                 restoreTACDB(pkgId, depsJsonPath, execName);
173         }
174         return 0;
175 }
176
177 int restoreDB()
178 {
179         if (bf::exists(tacAppListRestoreDB)) {
180                 if (!removeFile(tacAppListRestoreDB)) {
181                         _ERR("Failed to remove of %s", tacAppListRestoreDB.c_str());
182                         return -1;
183                 }
184         }
185         if (bf::exists(tacAppListRestoreDBJournal)) {
186                 if (!removeFile(tacAppListRestoreDBJournal)) {
187                         _ERR("Failed to remove of %s", tacAppListRestoreDBJournal.c_str());
188                         return -1;
189                 }
190         }
191
192         tac_db = dbCreate(tacAppListRestoreDB);
193         if (tac_db) {
194                 if (!dbOpen(tac_db, tacAppListRestoreDB)) {
195                         return 0;
196                 }
197         } else {
198                 return 0;
199         }
200
201         int ret = 0;
202         pkgmgrinfo_appinfo_metadata_filter_h handle;
203
204         ret = pkgmgrinfo_appinfo_metadata_filter_create(&handle);
205         if (ret != PMINFO_R_OK) {
206                 return -1;
207         }
208
209         ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, mdKey.c_str(), mdValue.c_str());
210         if (ret != PMINFO_R_OK) {
211                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
212                 return -1;
213         }
214
215         ret = pkgmgrinfo_appinfo_metadata_filter_foreach(handle, restoreDBCb, NULL);
216         if (ret != PMINFO_R_OK) {
217                 fprintf(stderr, "Failed pkgmgrinfo_appinfo_metadata_filter_foreach\n");
218                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
219                 return -1;
220         }
221         pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
222
223         if (tac_db) {
224                 dbClose(tac_db);
225                 tac_db = NULL;
226         }
227
228         if (bf::exists(tacAppListRestoreDB)) {
229                 if (!copyFile(tacAppListRestoreDB, tacAppListDB)) {
230                         _ERR("Failed to move of %s", tacAppListDB.c_str());
231                         return -1;
232                 }
233                 if (!removeFile(tacAppListRestoreDB)) {
234                         _ERR("Failed to remove of %s", tacAppListRestoreDB.c_str());
235                         return -1;
236                 }
237         }
238
239         if (bf::exists(tacAppListRestoreDBJournal)) {
240                 if (!copyFile(tacAppListRestoreDBJournal, tacAppListDBJournal)) {
241                         _ERR("Failed to move of %s", tacAppListDBJournal.c_str());
242                         return -1;
243                 }
244                 if (!removeFile(tacAppListRestoreDBJournal)) {
245                         _ERR("Failed to remove of %s", tacAppListRestoreDBJournal.c_str());
246                         return -1;
247                 }
248         }
249
250     cleanupDirectory();
251         return 0;
252 }
253
254 // step 1. Remove original DB
255 // step 2. Parsing the .deps.json for all apps
256 // step 3. Create new DB
257 // step 4. Cleanup unnecessary TAC directory
258 int main(int argc, char* argv[])
259 {
260         if (cmdOptionExists(argv, argv + argc, "--help")) {
261                 help(argv[0]);
262         } else if (cmdOptionExists(argv, argv + argc, "--restore-db")) {
263                 restoreDB();
264         } else {
265                 help(argv[0]);
266         }
267         return 0;
268 }