X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=NativeLauncher%2Ftool%2Fni_common.cc;h=54e1c0be5ee15eb5bf1f6b60b4a11c9c14b25397;hb=496e6363ac6bd4bdc9bf4cd9f4313d87aa5b6acf;hp=666f7520a2fd163409d1183029c8188cfcc5f695;hpb=39234061dd1c6bf1eb3f2688ce03eb6f8b3f3fa0;p=platform%2Fcore%2Fdotnet%2Flauncher.git diff --git a/NativeLauncher/tool/ni_common.cc b/NativeLauncher/tool/ni_common.cc index 666f752..54e1c0b 100644 --- a/NativeLauncher/tool/ni_common.cc +++ b/NativeLauncher/tool/ni_common.cc @@ -28,72 +28,211 @@ #include #include +#include +#include -#include #include -#include #include #include - -#include -#include +#include +#include +#include #include "ni_common.h" +#include "db_manager.h" +#include "tac_common.h" #include "path_manager.h" #include "plugin_manager.h" +#include "r2r_checker.h" #ifdef LOG_TAG #undef LOG_TAG #endif -#define LOG_TAG "NETCORE_INSTALLER_PLUGIN" - -#ifndef CROSSGEN_PATH -#error "CROSSGEN_PATH is missed" -#endif +#define LOG_TAG "DOTNET_INSTALLER_PLUGIN" #define __XSTR(x) #x #define __STR(x) __XSTR(x) -static const char* __CROSSGEN_PATH = __STR(CROSSGEN_PATH); +#if defined(__arm__) || defined(__aarch64__) +static const char* __NATIVE_LIB_DIR = __STR(NATIVE_LIB_DIR); +#endif +static const char* __DOTNET_DIR = __STR(DOTNET_DIR); +static const char* __READ_ONLY_APP_UPDATE_DIR = __STR(READ_ONLY_APP_UPDATE_DIR); + +#ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT +static const char* __SYSTEM_BASE_FILE = __STR(SYSTEM_BASE_FILE); +#endif + #undef __STR #undef __XSTR +static std::string CORERUN_CMD = "/usr/share/dotnet.tizen/netcoreapp/corerun"; +static std::string CROSSGEN2_PATH = "/usr/share/dotnet.tizen/netcoreapp/crossgen2/crossgen2.dll"; +static std::string CLRJIT_PATH = "/usr/share/dotnet.tizen/netcoreapp/libclrjit.so"; +static const char* CROSSGEN_OPT_JITPATH = "--jitpath"; +static const char* CROSSGEN_OPT_TARGET_ARCH = "--targetarch"; +static const char* CROSSGEN_OPT_OUT_NEAR_INPUT = "--out-near-input"; +static const char* CROSSGEN_OPT_SINGLE_FILE_COMPILATION = "--single-file-compilation"; +//static const char* CROSSGEN_OPT_PARALLELISM = "--parallelism"; +//static const char* CROSSGEN_OPT_PARALLELISM_COUNT = "5"; +static const char* CROSSGEN_OPT_RESILIENT = "--resilient"; +//static const char* CROSSGEN_OPT_OPTIMIZE = "-O"; +static const char* CROSSGEN_OPT_OPTIMIZE_TIME = "--Ot"; +static const char* CROSSGEN_OPT_INPUTBUBBLE = "--inputbubble"; +static const char* CROSSGEN_OPT_COMPILE_BUBBLE_GENERICS = "--compilebubblegenerics"; +static const char* CROSSGEN_OPT_VERBOSE = "--verbose"; +static std::vector REF_VECTOR; +static std::vector INPUTBUBBLE_REF_VECTOR; +static std::vector MIBC_VECTOR; + static int __interval = 0; -static std::string __tpa; +static PathManager* __pm = nullptr; + +static NIOption* __ni_option = nullptr; + +// singleton +NIOption* getNIOption() +{ + if (__ni_option == nullptr) { + __ni_option = (NIOption*)calloc(sizeof(NIOption), 1); + if (__ni_option == nullptr) { + _SERR("Fail to create NIOption"); + } + } + return __ni_option; +} static void waitInterval() { // by the recommand, ignore small value for performance. if (__interval > 10000) { - fprintf(stderr, "sleep %d usec\n", __interval); + _SOUT("sleep %d usec", __interval); usleep(__interval); } } -static void updateNiFileInfo(const std::string& dllPath, const std::string& niPath) +#ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT +// Get next base address to be used for system ni image from file +// __SYSTEM_BASE_FILE should be checked for existance before calling this function +static uintptr_t getNextBaseAddrFromFile() +{ + FILE *pFile = fopen(__SYSTEM_BASE_FILE, "r"); + if (pFile == NULL) { + _SERR("Failed to open %s", __SYSTEM_BASE_FILE); + return 0; + } + + uintptr_t addr = 0; + uintptr_t size = 0; + + while (fscanf(pFile, "%" SCNxPTR " %" SCNuPTR "", &addr, &size) != EOF) { + } + + fclose(pFile); + + return addr + size; +} + +// Get next base address to be used for system ni image +static uintptr_t getNextBaseAddr() { - char* label = NULL; + uintptr_t baseAddr = 0; + + if (!isFile(__SYSTEM_BASE_FILE)) { + // This is the starting address for all default base addresses + baseAddr = DEFAULT_BASE_ADDR_START; + } else { + baseAddr = getNextBaseAddrFromFile(); + + // Round to a multple of 64K (see ZapImage::CalculateZapBaseAddress in CoreCLR) + uintptr_t BASE_ADDRESS_ALIGNMENT = 0xffff; + baseAddr = (baseAddr + BASE_ADDRESS_ALIGNMENT) & ~BASE_ADDRESS_ALIGNMENT; + } + + return baseAddr; +} - // change smack label - if (smack_getlabel(dllPath.c_str(), &label, SMACK_LABEL_ACCESS) == 0) { - if (smack_setlabel(niPath.c_str(), label, SMACK_LABEL_ACCESS) < 0) { - fprintf(stderr, "Fail to set smack label\n"); +// Save base address of system ni image to file +static void updateBaseAddrFile(const std::string& absNIPath, uintptr_t baseAddr) +{ + uintptr_t niSize = getSizeOfImage(absNIPath); + if (niSize == 0) { + _SERR("File %s doesn't exist", absNIPath.c_str()); + return; + } + + // Write new entry to the file + FILE *pFile = fopen(__SYSTEM_BASE_FILE, "a"); + if (pFile == NULL) { + _SERR("Failed to open %s", __SYSTEM_BASE_FILE); + return; + } + + fprintf(pFile, "%" PRIxPTR " %" PRIuPTR "\n", baseAddr, niSize); + fclose(pFile); +} + +// check if dll is listed in TPA +static bool isTPADll(const std::string& dllPath) +{ + std::string absPath = getBaseName(getAbsolutePath(dllPath)); + + std::vector paths = __pm->getPlatformAssembliesPaths(); + for (unsigned int i = 0; i < paths.size(); i++) { + if (paths[i].find(getBaseName(absPath)) != std::string::npos) { + return true; } - free(label); } - // change owner and groups for generated ni file. - struct stat info; - if (!stat(dllPath.c_str(), &info)) { - if (chown(niPath.c_str(), info.st_uid, info.st_gid) == -1) - fprintf(stderr, "Failed to change owner and group name\n"); + return false; +} +#endif + +/** + * @brief create the directory including parents directory, and + * copy ownership and smack labels to the created directory. + * @param[in] target directory path + * @param[in] source directory path to get ownership and smack label + * @return if directory created successfully, return true otherwise false + */ +static bool createDirsAndCopyOwnerShip(std::string& target_path, const std::string& source) +{ + struct stat st; + mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + + for (std::string::iterator iter = target_path.begin(); iter != target_path.end();) { + std::string::iterator newIter = std::find(iter, target_path.end(), '/'); + std::string newPath = std::string(target_path.begin(), newIter); + + if (!newPath.empty()) { + if (stat(newPath.c_str(), &st) != 0) { + if (mkdir(newPath.c_str(), mode) != 0 && errno != EEXIST) { + _SERR("Fail to create app ni directory (%s)", newPath.c_str()); + return false; + } + if (!source.empty()) { + copySmackAndOwnership(source, newPath); + } + } else { + if (!S_ISDIR(st.st_mode)) { + _SERR("Fail. path is not a dir (%s)", newPath.c_str()); + return false; + } + } + } + iter = newIter; + if(newIter != target_path.end()) { + ++iter; + } } + + return true; } -static std::string getNiFilePath(const std::string& dllPath) +static std::string getNIFilePath(const std::string& dllPath) { size_t index = dllPath.find_last_of("."); if (index == std::string::npos) { - fprintf(stderr, "File doesnot contain extension. fail to get NI file name\n"); + _SERR("File doesnot contain extension. fail to get NI file name"); return ""; } std::string fName = dllPath.substr(0, index); @@ -106,85 +245,269 @@ static std::string getNiFilePath(const std::string& dllPath) return niPath; } -static std::string getAppNIPath(const std::string& niPath) +static std::string getAppNIFilePath(const std::string& absDllPath, NIOption* opt) { - std::string fileName; std::string niDirPath; std::string prevPath; - size_t index = niPath.find_last_of("/"); - if (index != std::string::npos) { - prevPath = niPath.substr(0, index); - fileName = niPath.substr(index + 1, niPath.length()); - } else { - prevPath = "."; - fileName = niPath; - } - + prevPath = getBaseName(absDllPath); niDirPath = concatPath(prevPath, APP_NI_SUB_DIR); - if (!isFileExist(niDirPath)) { - if (mkdir(niDirPath.c_str(), 0755) == 0) { - updateNiFileInfo(prevPath, niDirPath); - } else { - fprintf(stderr, "Fail to create app ni directory (%s)\n", niDirPath.c_str()); + if (opt->flags & NI_FLAGS_APP_UNDER_RO_AREA) { + niDirPath = replaceAll(niDirPath, getBaseName(__pm->getAppRootPath()), __READ_ONLY_APP_UPDATE_DIR); + _SERR("App is installed in RO area. Change NI path to RW area(%s).", niDirPath.c_str()); + _ERR("App is installed in RO area. Change NI path to RW area(%s).", niDirPath.c_str()); + } + + if (!isDirectory(niDirPath)) { + if (!createDirsAndCopyOwnerShip(niDirPath, prevPath)) { + niDirPath = prevPath; + _SERR("fail to create dir (%s)", niDirPath.c_str()); } } - return concatPath(niDirPath, fileName); + return getNIFilePath(concatPath(niDirPath, getFileName(absDllPath))); } -static bool niExist(const std::string& path) +static bool checkNIExistence(const std::string& absDllPath) { - std::string f = getNiFilePath(path); - if (f.empty()) { + std::string absNIPath = getNIFilePath(absDllPath); + if (absNIPath.empty()) { return false; } - if (isFileExist(f)) { + if (isFile(absNIPath)) { return true; } // native image of System.Private.CoreLib.dll should have to overwrite // original file to support new coreclr - if (path.find("System.Private.CoreLib.dll") != std::string::npos) { - std::string coreLibBackup = path + ".Backup"; - if (isFileExist(coreLibBackup)) { - return true; - } + if (absDllPath.find("System.Private.CoreLib.dll") != std::string::npos) { + return isR2RImage(absDllPath); } return false; } -static ni_error_e crossgen(const std::string& dllPath, const std::string& appPath, bool enableR2R, bool isAppNI = false) +static bool checkAppNIExistence(const std::string& absDllPath, NIOption* opt) { - if (!isFileExist(dllPath)) { - fprintf(stderr, "dll file is not exist : %s\n", dllPath.c_str()); - return NI_ERROR_NO_SUCH_FILE; + std::string absNIPath = getAppNIFilePath(absDllPath, opt); + if (absNIPath.empty()) { + return false; } - if (!isManagedAssembly(dllPath)) { - fprintf(stderr, "Input file is not a dll file : %s\n", dllPath.c_str()); + if (isFile(absNIPath)) { + return true; + } + + return false; +} + +static bool checkDllExistInDir(const std::string& path) +{ + bool ret = false; + auto func = [&ret](const std::string& f_path, const std::string& f_name) { + if (isManagedAssembly(f_name) || isNativeImage(f_name)) { + ret = true; + } + }; + + scanFilesInDirectory(path, func, 0); + + return ret; +} + +/* + * Get the list of managed files in the specific directory + * Absolute paths of managed files are stored at the result list. + * If native image already exist in the same directory, managed file is ignored. + */ +static ni_error_e getTargetDllList(const std::string& path, std::vector& fileList) +{ + if (!isDirectory(path)) { return NI_ERROR_INVALID_PARAMETER; } - if (niExist(dllPath)) { - fprintf(stderr, "Already ni file is exist for %s\n", dllPath.c_str()); - return NI_ERROR_ALREADY_EXIST; + auto func = [&fileList](const std::string& f_path, const std::string& f_name) { + if (isManagedAssembly(f_path) && !checkNIExistence(f_path)) { + fileList.push_back(getAbsolutePath(f_path)); + } + }; + + scanFilesInDirectory(path, func, 0); + + return NI_ERROR_NONE; +} + +/* + * Get the list of managed files in the specific directory of Application + * Absolute paths of managed files are stored at the result list. + * If native image already exist in the .native_image directory, managed file is ignored. + * + */ +static ni_error_e getAppTargetDllList(const std::string& path, std::vector& fileList, NIOption *opt) +{ + if (!isDirectory(path)) { + return NI_ERROR_INVALID_PARAMETER; + } + + auto func = [&fileList, opt](const std::string& f_path, const std::string& f_name) { + if (isManagedAssembly(f_path) && !checkAppNIExistence(f_path, opt)) { + fileList.push_back(getAbsolutePath(f_path)); + } + }; + + scanFilesInDirectory(path, func, 0); + + return NI_ERROR_NONE; +} + +static void makeArgs(std::vector& args, const std::vector& refPaths, NIOption* opt) +{ + args.push_back(CORERUN_CMD.c_str()); + if (CROSSGEN2_PATH != "") { + args.push_back(CROSSGEN2_PATH.c_str()); + } + args.push_back(CROSSGEN_OPT_JITPATH); + args.push_back(CLRJIT_PATH.c_str()); + args.push_back(CROSSGEN_OPT_TARGET_ARCH); + args.push_back(ARCHITECTURE_IDENTIFIER); + + //args.push_back(OPT_PARALLELISM); + //args.push_back(OPT_PARALLELISM_COUNT); + args.push_back(CROSSGEN_OPT_RESILIENT); + + args.push_back(CROSSGEN_OPT_OPTIMIZE_TIME); + + if (opt->flags & NI_FLAGS_INPUT_BUBBLE) { + args.push_back(CROSSGEN_OPT_INPUTBUBBLE); + args.push_back(CROSSGEN_OPT_COMPILE_BUBBLE_GENERICS); + + INPUTBUBBLE_REF_VECTOR.clear(); + for (const auto &path : opt->inputBubbleRefFiles) { + INPUTBUBBLE_REF_VECTOR.push_back("--inputbubbleref:" + path); + } + for (const auto &path : INPUTBUBBLE_REF_VECTOR) { + if (find(args.begin(), args.end(), path) == args.end()) { + args.push_back(path.c_str()); + } + } + } + + if (opt->flags & NI_FLAGS_MIBC) { + MIBC_VECTOR.clear(); + for (const auto &path : opt->mibcPath) { + MIBC_VECTOR.push_back("--mibc:" + path); + } + for (const auto &path : MIBC_VECTOR) { + if (find(args.begin(), args.end(), path) == args.end()) { + args.push_back(path.c_str()); + } + } + } + + if (opt->flags & NI_FLAGS_VERBOSE) { + args.push_back(CROSSGEN_OPT_VERBOSE); + } + + REF_VECTOR.clear(); + + // set reference path + for (const auto &path : opt->refFiles) { + REF_VECTOR.push_back("-r:" + path); + } + + std::vector paths = __pm->getPlatformAssembliesPaths(); + for (const auto &path : paths) { + if (checkDllExistInDir(path)) { + REF_VECTOR.push_back("-r:" + path + "/*.dll"); + } } - std::string absDllPath = absolutePath(dllPath); - std::string absNiPath = getNiFilePath(dllPath); - if (absNiPath.empty()) { - fprintf(stderr, "Fail to get ni file name\n"); + if (opt->flags & NI_FLAGS_EXTRA_REF) { + for (const auto &erPath : opt->extraRefPath) { + std::string path = getAbsolutePath(erPath); + if (checkDllExistInDir(path)) { + REF_VECTOR.push_back("-r:" + path + "/*.dll"); + } + } + } + + for (const auto &path : refPaths) { + if (checkDllExistInDir(path)) { + REF_VECTOR.push_back("-r:" + path + "/*.dll"); + } + } + + for (const auto &path : REF_VECTOR) { + if (find(args.begin(), args.end(), path) == args.end()) { + args.push_back(path.c_str()); + } + } +} + +static void clearArgs(std::vector& args) +{ + REF_VECTOR.clear(); + args.clear(); +} + +static ni_error_e makePdbSymlinkForNI(std::string dllPath, std::string niPath) +{ + std::string pdbPath = changeExtension(dllPath, ".dll", ".pdb"); + try { + if (exist(pdbPath)) { + std::string targetPDBPath = changeExtension(niPath, ".ni.dll", ".pdb"); + if (!exist(targetPDBPath)) { + bf::create_symlink(pdbPath, targetPDBPath); + copySmackAndOwnership(pdbPath, targetPDBPath, true); + } + } + } catch (const bf::filesystem_error& error) { + _SERR("Fail to create symlink for %s", pdbPath.c_str()); return NI_ERROR_UNKNOWN; } - if (isAppNI) { - absNiPath = getAppNIPath(absNiPath); + return NI_ERROR_NONE; +} + +static ni_error_e crossgen2PostAction(const std::string& dllPath, const std::string& niPath, NIOption* opt) { + std::string outFile = niPath; + if (!exist(outFile)) { + return NI_ERROR_NO_SUCH_FILE; } + copySmackAndOwnership(dllPath, outFile); + + // if AppNI then move ni.dll file to .native_image and copy pdb to .native_image + if (opt->flags & NI_FLAGS_APPNI) { + outFile = getAppNIFilePath(dllPath, opt); + makePdbSymlinkForNI(dllPath, outFile); + + if (opt->flags & NI_FLAGS_INPUT_BUBBLE && opt->flags & NI_FLAGS_NO_PIPELINE) { + outFile = outFile + ".tmp"; + } + + if (niPath != outFile) { + moveFile(niPath, outFile); + } + } + + if (opt->flags & NI_FLAGS_RM_ORIGIN_AFTER_NI) { + if (!removeFile(dllPath)) { + _SERR("Fail to remove original file : %s", dllPath.c_str()); + } + } + + if (!(opt->flags & NI_FLAGS_INPUT_BUBBLE && opt->flags & NI_FLAGS_NO_PIPELINE)) { + _SOUT("Native image %s generated successfully.", outFile.c_str()); + } + + return NI_ERROR_NONE; +} +static ni_error_e crossgen2PipeLine(const std::vector& dllList, const std::vector& refPaths, NIOption* opt) +{ + // fork crossgen2 pid_t pid = fork(); if (pid == -1) return NI_ERROR_UNKNOWN; @@ -193,127 +516,376 @@ static ni_error_e crossgen(const std::string& dllPath, const std::string& appPat int status; waitpid(pid, &status, 0); if (WIFEXITED(status)) { - // Do not use niExist() function to check whether ni file created or not. - // niEixst() return false for System.Private.Corelib.dll - if (isFileExist(absNiPath)) { - updateNiFileInfo(absDllPath, absNiPath); - return NI_ERROR_NONE; - } else { - fprintf(stderr, "Fail to create native image for %s\n", dllPath.c_str()); - return NI_ERROR_NO_SUCH_FILE; + for (auto& dllPath: dllList) { + ni_error_e ret = crossgen2PostAction(dllPath, changeExtension(dllPath, ".dll", ".ni.dll"), opt); + if (ret != NI_ERROR_NONE) { + return ret; + } } + } else { + _SERR("Failed. Forked process terminated abnormally"); + return NI_ERROR_ABNORMAL_PROCESS_TERMINATION; } } else { - std::string jitPath = getRuntimeDir() + "/libclrjit.so"; - std::vector argv = { - __CROSSGEN_PATH, - "/nologo", - "/Trusted_Platform_Assemblies", __tpa.c_str(), - "/JITPath", jitPath.c_str() - }; - - if (!enableR2R) { - argv.push_back("/FragileNonVersionable"); - } - - argv.push_back("/App_Paths"); - std::string absAppPath; - if (!appPath.empty()) { - absAppPath = appPath; - } else { - absAppPath = baseName(absDllPath); + std::vector argv; + makeArgs(argv, refPaths, opt); + argv.push_back(CROSSGEN_OPT_OUT_NEAR_INPUT); + argv.push_back(CROSSGEN_OPT_SINGLE_FILE_COMPILATION); + + // add input files at the end of parameter + for (const auto &input : dllList) { + argv.push_back(input.c_str()); + _SOUT("+ %s", input.c_str()); } - argv.push_back(absAppPath.c_str()); - - argv.push_back("/out"); - argv.push_back(absNiPath.c_str()); - argv.push_back(absDllPath.c_str()); + // end param argv.push_back(nullptr); - fprintf(stderr, "+ %s (%s)\n", absDllPath.c_str(), enableR2R ? "R2R" : "FNV"); + // print cmd + if (opt->flags & NI_FLAGS_PRINT_CMD) { + _SOUT("==================== NI Commands ========================="); + for (auto &arg: argv) _SOUT("+ %s", arg); + } + + execv(CORERUN_CMD.c_str(), const_cast(argv.data())); - execv(__CROSSGEN_PATH, const_cast(argv.data())); + clearArgs(argv); exit(0); } return NI_ERROR_NONE; } +static ni_error_e crossgen2NoPipeLine(const std::vector& dllList, const std::vector& refPaths, NIOption* opt) +{ + for (auto& dllPath : dllList) { + std::string niPath; + if (opt->flags & NI_FLAGS_APPNI) { + niPath = getAppNIFilePath(dllPath, opt); + } else { + niPath = getNIFilePath(dllPath); + } +#ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT + uintptr_t baseAddr = 0; + if (isTPADll(dllPath)) { + baseAddr = getNextBaseAddr(); + } +#endif + if (opt->flags & NI_FLAGS_INPUT_BUBBLE) { + niPath += ".tmp"; + } + + // fork crossgen2 + pid_t pid = fork(); + if (pid == -1) + return NI_ERROR_UNKNOWN; + + if (pid > 0) { + int status; + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + ni_error_e ret = crossgen2PostAction(dllPath, niPath, opt); + if (ret != NI_ERROR_NONE) { + return ret; + } +#ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT + if (baseAddr != 0) { + updateBaseAddrFile(niPath, baseAddr); + } +#endif + } else { + _SERR("Failed. Forked process terminated abnormally"); + _SERR("Crossgen2 was terminated by the OOM killer. Please check the system."); + removeFile(changeExtension(niPath, ".ni.dll", ".ni.dll.tmp")); + return NI_ERROR_ABNORMAL_PROCESS_TERMINATION; + } + } else { + std::vector argv; + makeArgs(argv, refPaths, opt); + +#ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT + std::string baseAddrString; + if (baseAddr != 0) { + argv.push_back("--imagebase"); + std::stringstream ss; + ss << "0x" << std::hex << baseAddr; + baseAddrString = ss.str(); + argv.push_back(baseAddrString.c_str()); + } +#endif + argv.push_back("-o"); + argv.push_back(niPath.c_str()); + + argv.push_back(dllPath.c_str()); + _SOUT("+ %s", dllPath.c_str()); + + // end param + argv.push_back(nullptr); + + // print cmd + if (opt->flags & NI_FLAGS_PRINT_CMD) { + _SOUT("==================== NI Commands ========================="); + for (auto &arg: argv) _SOUT("+ %s", arg); + } + + execv(CORERUN_CMD.c_str(), const_cast(argv.data())); + + clearArgs(argv); + exit(0); + } + + waitInterval(); + } + + return NI_ERROR_NONE; +} + +static ni_error_e createCoreLibNI(NIOption* opt) +{ + std::string coreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll"); + std::string niCoreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.ni.dll"); + std::string niTmpCoreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.ni.dll.tmp"); + std::string coreLibBackup = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll.Backup"); + + std::vector dllList; + std::vector refPaths; + dllList.push_back(getAbsolutePath(coreLib)); + + if (!isFile(coreLibBackup) && !isR2RImage(coreLib)) { + if (crossgen2NoPipeLine(dllList, refPaths, opt) == NI_ERROR_NONE) { + if (opt->flags & NI_FLAGS_RM_ORIGIN_AFTER_NI) { + std::ofstream output(coreLibBackup); + if (!exist(coreLibBackup)) { + _SERR("Failed to create System.Private.CoreLib.dll.Backup"); + return NI_ERROR_CORE_NI_FILE; + } + copySmackAndOwnership(__pm->getRuntimePath(), coreLibBackup, false); + output.close(); + } else if (rename(coreLib.c_str(), coreLibBackup.c_str())) { + _SERR("Failed to rename from System.Private.CoreLib.dll to System.Private.CoreLib.dll.Backup"); + return NI_ERROR_CORE_NI_FILE; + } + if (opt->flags & NI_FLAGS_INPUT_BUBBLE) { + if (rename(niTmpCoreLib.c_str(), coreLib.c_str())) { + _SERR("Failed to rename from System.Private.CoreLib.ni.dll.tmp to Private.CoreLib.dll"); + return NI_ERROR_CORE_NI_FILE; + } + } else { + if (rename(niCoreLib.c_str(), coreLib.c_str())) { + _SERR("Failed to rename from System.Private.CoreLib.ni.dll to Private.CoreLib.dll"); + return NI_ERROR_CORE_NI_FILE; + } + } + } else { + _SERR("Failed to create native image for %s", coreLib.c_str()); + return NI_ERROR_CORE_NI_FILE; + } + } + return NI_ERROR_NONE; +} + +static ni_error_e doAOTList(std::vector& dllList, const std::string& refPaths, NIOption* opt) +{ + ni_error_e ret = NI_ERROR_NONE; + + if (dllList.empty()) { + return NI_ERROR_INVALID_PARAMETER; + } + // When performing AOT for one Dll, an error is returned when an error occurs. + // However, when processing multiple dlls at once, only the log for errors is output and skipped. + + std::string coreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll"); + bool hasSPC = false; + + for (auto it = dllList.begin(); it != dllList.end(); it++) { + std::string f = *it; + if (!isFile(f)) { + _SERR("dll file is not exist : %s", f.c_str()); + dllList.erase(it--); + } + else if (!isManagedAssembly(f)) { + _SERR("Input file is not a dll file : %s", f.c_str()); + dllList.erase(it--); + } + // handle System.Private.CoreLib.dll separately. + // dllList and path manager contain absolute path. So, there is no need to change path to absolute path + else if (f == coreLib) { + hasSPC = true; + dllList.erase(it--); + } + } + + // In the case of SPC, post-processing is required to change the name of the native image. + // In order to avoid repeatedly checking whether the generated native image is an SPC, + // the SPC native image generation is performed separately. + if (hasSPC) { + ret = createCoreLibNI(opt); + if (ret != NI_ERROR_NONE) { + return ret; + } + } + + // if there is no proper input after processing dll list + if (dllList.empty()) { + if (hasSPC) { + return ret; + } else { + return NI_ERROR_INVALID_PARAMETER; + } + } + + std::vector paths; + splitPath(refPaths, paths); + + if (opt->flags & NI_FLAGS_NO_PIPELINE) { + ret = crossgen2NoPipeLine(dllList, paths, opt); + } else { + std::vector notCompiled; + ret = crossgen2PipeLine(dllList, paths, opt); + if (ret != NI_ERROR_NONE) { + _SERR("Crossgen2 is abnormally terminated. Regenerate native images that failed while running crossgen2."); + for (auto &dll : dllList) { + std::string tFile = changeExtension(dll, ".dll", ".ni.dll"); + if (opt->flags & NI_FLAGS_INPUT_BUBBLE) { + tFile += ".tmp"; + } + if (!exist(tFile)) { + notCompiled.push_back(dll); + } + } + _SERR("Retry running crossgen2 with --no-pipeline mode to avoid termination by OOM."); + ret = crossgen2NoPipeLine(notCompiled, paths, opt); + } + } + + if (ret == NI_ERROR_NONE && opt->flags & NI_FLAGS_INPUT_BUBBLE) { + for (auto &dll : dllList) { + std::string tmpFile; + std::string niFile; + if (opt->flags & NI_FLAGS_APPNI) { + niFile = getAppNIFilePath(dll, opt); + } else { + niFile = getNIFilePath(dll); + } + tmpFile = niFile + ".tmp"; + + if (exist(tmpFile)) { + moveFile(tmpFile, niFile); + _SOUT("Native image %s generated successfully.", niFile.c_str()); + } + } + } + + return ret; +} + +static ni_error_e doAOTFile(const std::string& dllFile, const std::string& refPaths, NIOption* opt) +{ + if (!isFile(dllFile)) { + _SERR("dll file is not exist : %s", dllFile.c_str()); + return NI_ERROR_NO_SUCH_FILE; + } + + if (!isManagedAssembly(dllFile)) { + _SERR("Failed. Input parameter is not managed dll (%s)\n", dllFile.c_str()); + return NI_ERROR_INVALID_PARAMETER; + } + + if (checkNIExistence(dllFile)) { + _SERR("Native image file is already exist : %s", dllFile.c_str()); + return NI_ERROR_ALREADY_EXIST; + } + + std::vector dllList; + dllList.push_back(getAbsolutePath(dllFile)); + return doAOTList(dllList, refPaths, opt); +} + // callback function of "pkgmgrinfo_appinfo_metadata_filter_foreach" static int appAotCb(pkgmgrinfo_appinfo_h handle, void *userData) { char *pkgId = NULL; int ret = 0; - bool* enableR2R = (bool*)userData; + NIOption **pOptions = (NIOption**)userData; + + if ((*pOptions)->flags & NI_FLAGS_SKIP_RO_APP) { + bool isSystem = false; + int ret = pkgmgrinfo_appinfo_is_system(handle, &isSystem); + if (ret != PMINFO_R_OK) { + _SERR("Failed to check that app is System or not\n"); + return -1; + } + if (isSystem) { + return 0; + } + } ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId); if (ret != PMINFO_R_OK) { - fprintf(stderr, "Failed to get pkgid\n"); + _SERR("Failed to get pkgid"); return -1; } - if (removeNiUnderPkgRoot(pkgId) != 0) { - fprintf(stderr, "Failed to remove previous dlls from [%s]\n", pkgId); + if (removeNIUnderPkgRoot(pkgId) != NI_ERROR_NONE) { + _SERR("Failed to remove previous dlls from [%s]", pkgId); return -1; } - // Regenerate ni files with R2R mode forcibiliy. (there is no way to now which option is used) - if (createNiUnderPkgRoot(pkgId, *enableR2R) != 0) { - fprintf(stderr, "Failed to get root path from [%s]\n", pkgId); + if (createNIUnderPkgRoot(pkgId, *pOptions) != NI_ERROR_NONE) { + _SERR("Failed to generate NI file [%s]", pkgId); return -1; } else { - fprintf(stderr, "Complete make application to native image\n"); + _SOUT("Complete make application to native image"); } return 0; } -static void createCoreLibNI(bool enableR2R) +ni_error_e initNICommon() { - std::string coreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll"); - std::string niCoreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.ni.dll"); - std::string coreLibBackup = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll.Backup"); - - if (!isFileExist(coreLibBackup)) { - if (!crossgen(coreLib, std::string(), enableR2R)) { - if (rename(coreLib.c_str(), coreLibBackup.c_str())) { - fprintf(stderr, "Failed to rename System.Private.CoreLib.dll\n"); - } - if (rename(niCoreLib.c_str(), coreLib.c_str())) { - fprintf(stderr, "Failed to rename System.Private.CoreLib.ni.dll\n"); - } - } else { - fprintf(stderr, "Failed to create native image for %s\n", coreLib.c_str()); - } +#if defined(__arm__) || defined(__aarch64__) + + char *env = nullptr; + env = getenv("MIC_CROSSGEN2_ENABLED"); + if (env != nullptr && !strcmp(env, "1")) { + CORERUN_CMD = std::string("/opt/usr/dotnet/mic/crossgen2"); + CROSSGEN2_PATH = ""; + CLRJIT_PATH = std::string("/opt/usr/dotnet/mic/libclrjit_unix_") + ARCHITECTURE_IDENTIFIER + std::string("_x64.so"); } -} -ni_error_e initNICommon(NiCommonOption* option) -{ -#if defined(__arm__) // get interval value - const char* intervalFile = "/usr/share/dotnet.tizen/lib/crossgen_interval.txt"; + const static std::string intervalFile = concatPath(__NATIVE_LIB_DIR, "crossgen_interval.txt"); std::ifstream inFile(intervalFile); if (inFile) { - fprintf(stderr, "crossgen_interval.txt is found\n"); + _SOUT("crossgen_interval.txt is found"); inFile >> __interval; } if (initializePluginManager("normal")) { - fprintf(stderr, "Fail to initialize plugin manager\n"); + _SERR("Fail to initialize PluginManager"); return NI_ERROR_UNKNOWN; } - if (initializePathManager(option->runtimeDir, option->tizenFXDir, option->extraDirs)) { - fprintf(stderr, "Fail to initialize path manager\n"); + + try { + __pm = new PathManager(); + } catch (const std::exception& e) { + _SERR("Failed to create PathManager"); return NI_ERROR_UNKNOWN; } - __tpa = getTPA(); + char* pluginDllPaths = pluginGetDllPath(); + if (pluginDllPaths && pluginDllPaths[0] != '\0') { + __pm->addPlatformAssembliesPaths(pluginDllPaths, true); + } + + char* pluginNativePaths = pluginGetNativeDllSearchingPath(); + if (pluginNativePaths && pluginNativePaths[0] != '\0') { + __pm->addNativeDllSearchingPaths(pluginNativePaths, true); + } return NI_ERROR_NONE; #else - fprintf(stderr, "crossgen supports arm architecture only. skip ni file generation\n"); + _SERR("crossgen supports arm/arm64 architecture only. skip ni file generation"); return NI_ERROR_NOT_SUPPORTED; #endif } @@ -323,156 +895,274 @@ void finalizeNICommon() __interval = 0; finalizePluginManager(); - finalizePathManager(); - __tpa.clear(); -} + delete(__pm); + __pm = nullptr; + if (__ni_option) { + free(__ni_option); + __ni_option = nullptr; + } +} -void createNiPlatform(bool enableR2R) +ni_error_e createNIPlatform(std::string& extraInputs, NIOption* opt) { - const std::string platformDirs[] = {getRuntimeDir(), getTizenFXDir()}; - createNiUnderDirs(platformDirs, 2, enableR2R); + extraInputs += ":" + __pm->getRuntimePath(); + extraInputs += ":" + __pm->getTizenFXPath(); + + return createNIUnderDirs(extraInputs, opt); } -ni_error_e createNiDll(const std::string& dllPath, bool enableR2R) +ni_error_e createNIDll(const std::string& dllPath, NIOption* opt) { - createCoreLibNI(enableR2R); - // System.Private.CoreLib.dll is generated in the createCoreLibNI function. - // Skip if input dll is System.Private.CoreLib.dll - if (dllPath.find("System.Private.CoreLib.dll") != std::string::npos) { - return NI_ERROR_NONE; - } - - return crossgen(dllPath, std::string(), enableR2R); + return doAOTFile(dllPath, std::string(), opt); } -void createNiUnderDirs(const std::string rootPaths[], int count, bool enableR2R, bool isAppNI) +ni_error_e createNIUnderTAC(const std::string& targetPath, const std::string& refPaths, NIOption* opt) { - createCoreLibNI(enableR2R); + ni_error_e ret; + + bool isAppNI = false; + if (opt->flags & NI_FLAGS_APPNI) { + isAppNI = true; + } + + if (opt->flags & NI_FLAGS_INPUT_BUBBLE) { + std::vector refs; + splitPath(refPaths, refs); + for (auto &p: refs) { + if (isDirectory(p) && checkDllExistInDir(p)) { + opt->inputBubbleRefFiles.push_back(p + "/*.dll"); + } + } + } - std::string appPaths; - for (int i = 0; i < count; i++) { - appPaths += rootPaths[i]; - appPaths += ':'; + // get managed file list from targetPath + std::vector dllList; + ret = getTargetDllList(targetPath, dllList); + if (ret != NI_ERROR_NONE) { + return ret; } - if (appPaths.back() == ':') - appPaths.pop_back(); + std::vector needNIList; + std::vector niList; - auto convert = [&appPaths, enableR2R, isAppNI](const std::string& path, const char* name) { - if (!crossgen(path, appPaths.c_str(), enableR2R, isAppNI)) { - waitInterval(); + for (auto &dll : dllList) { + if (!checkNIExistence(dll)) { + needNIList.push_back(dll); } - }; + niList.push_back(getNIFilePath(dll)); + } + + if (!needNIList.empty()) { + // NI fils of TAC-related dlls under /opt/usr/dotnet should not be created under .native_image directory. + // So, unset NI_FLAGS_APPNI temporally and restore it after running AOT. + opt->flags &= ~NI_FLAGS_APPNI; + ret = doAOTList(needNIList, refPaths, opt); + if (isAppNI) { + opt->flags |= NI_FLAGS_APPNI; + } + if (ret != NI_ERROR_NONE) { + return ret; + } + } - for (int i = 0; i < count; i++) { - scanFilesInDir(rootPaths[i], convert, 1); + if (isAppNI) { + for (auto &niPath : niList) { + if (exist(niPath)) { + std::string symNIPath = concatPath(targetPath, getFileName(niPath)); + if (!exist(symNIPath)) { + bf::create_symlink(niPath, symNIPath); + copySmackAndOwnership(targetPath.c_str(), symNIPath.c_str(), true); + _SOUT("%s symbolic link file generated successfully.", symNIPath.c_str()); + _INFO("%s symbolic link file generated successfully.", symNIPath.c_str()); + } + } + } } + + return NI_ERROR_NONE; } -ni_error_e createNiUnderPkgRoot(const std::string& pkgId, bool enableR2R) + +ni_error_e createNIUnderDirs(const std::string& rootPaths, NIOption* opt) { - std::string pkgRoot; - if (getRootPath(pkgId, pkgRoot) != NI_ERROR_NONE) { - fprintf(stderr, "Failed to get root path from [%s]\n", pkgId.c_str()); - return NI_ERROR_INVALID_PACKAGE; + ni_error_e ret = NI_ERROR_NONE; + + std::vector fileList; + std::vector paths; + splitPath(rootPaths, paths); + + if (opt->flags & NI_FLAGS_INPUT_BUBBLE) { + for (auto &p: paths) { + if (isDirectory(p) && checkDllExistInDir(p)) { + opt->inputBubbleRefFiles.push_back(p + "/*.dll"); + } + } } - std::string binDir = concatPath(pkgRoot, "bin"); - std::string libDir = concatPath(pkgRoot, "lib"); - std::string appTAC = concatPath(binDir, ".TAC.Release"); - std::string paths[] = {binDir, libDir, appTAC}; + for (const auto &path : paths) { + if (!exist(path)) { + continue; + } + + if (path.find(TAC_SYMLINK_SUB_DIR) != std::string::npos) { + ret = createNIUnderTAC(path, rootPaths, opt); + if (ret != NI_ERROR_NONE) { + return ret; + } + } else if (opt->flags & NI_FLAGS_APPNI) { + ret = getAppTargetDllList(path, fileList, opt); + if (ret != NI_ERROR_NONE) { + return ret; + } + } else { + ret = getTargetDllList(path, fileList); + if (ret != NI_ERROR_NONE) { + return ret; + } + } + } - createNiUnderDirs(paths, 3, enableR2R, true); + if (fileList.empty()) { + return NI_ERROR_NONE; + } - return NI_ERROR_NONE; + return doAOTList(fileList, rootPaths, opt); } -ni_error_e createNiDllUnderPkgRoot(const std::string& pkgId, const std::string& dllPath, bool enableR2R) +ni_error_e createNIUnderPkgRoot(const std::string& pkgId, NIOption* opt) { - std::string pkgRoot; - if (getRootPath(pkgId, pkgRoot) < 0) { - fprintf(stderr, "Failed to get root path from [%s]\n", pkgId.c_str()); + if (!isR2RImage(concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll"))) { + _SERR("The native image of System.Private.CoreLib does not exist.\n" + "Run the command to create the native image\n" + "# dotnettool --ni-dll /usr/share/dotnet.tizen/netcoreapp/System.Private.CoreLib.dll"); + return NI_ERROR_CORE_NI_FILE; + } + + std::string rootPath = getRootPath(pkgId); + if (rootPath.empty()) { + _SERR("Failed to get root path from [%s]", pkgId.c_str()); return NI_ERROR_INVALID_PACKAGE; } - std::string binDir = concatPath(pkgRoot, "bin"); - std::string libDir = concatPath(pkgRoot, "lib"); - std::string appTAC = concatPath(binDir, ".TAC.Release"); - std::string paths = binDir + ":" + libDir + ":" + appTAC; + __pm->setAppRootPath(rootPath); + + char* extraDllPaths = pluginGetExtraDllPath(); + if (extraDllPaths && extraDllPaths[0] != '\0') { + opt->flags |= NI_FLAGS_EXTRA_REF; + splitPath(extraDllPaths, opt->extraRefPath); + } + + opt->flags |= NI_FLAGS_APPNI; - return crossgen(dllPath, paths, enableR2R, true); + if (isReadOnlyArea(rootPath)) { + opt->flags |= NI_FLAGS_APP_UNDER_RO_AREA; + opt->flags |= NI_FLAGS_NO_PIPELINE; + _SERR("Only no-pipeline mode supported for RO app. Set no-pipeline option forcibly"); + } else { + opt->flags &= ~NI_FLAGS_APP_UNDER_RO_AREA; + } + + // create native image under bin and lib directory + // tac directory is skipped in the createNIUnderDirs. + return createNIUnderDirs(__pm->getAppPaths(), opt); } -void removeNiPlatform() +void removeNIPlatform() { - std::string coreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll"); - std::string coreLibBackup = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll.Backup"); + std::string coreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll"); + std::string coreLibBackup = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll.Backup"); - if (!isFileExist(coreLibBackup)) { - return; - } + if (isR2RImage(coreLib)) { + if (!isFile(coreLibBackup)) { + return; + } - if (remove(coreLib.c_str())) { - fprintf(stderr, "Failed to remove System.Private.CoreLib native image file\n"); + if (remove(coreLib.c_str())) { + _SERR("Failed to remove System.Private.CoreLib native image file"); + } + if (rename(coreLibBackup.c_str(), coreLib.c_str())) { + _SERR("Failed to rename System.Private.CoreLib.Backup to origin"); + } } - if (rename(coreLibBackup.c_str(), coreLib.c_str())) { - fprintf(stderr, "Failed to rename System.Private.CoreLib.Backup to origin\n"); +#ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT + if (isFile(__SYSTEM_BASE_FILE)) { + if (remove(__SYSTEM_BASE_FILE)) { + _SERR("Failed to remove %s", __SYSTEM_BASE_FILE); + } } +#endif - const std::string platformDirs[] = {getRuntimeDir(), getTizenFXDir()}; - - removeNiUnderDirs(platformDirs, 2); + removeNIUnderDirs(__pm->getRuntimePath() + ":" + __pm->getTizenFXPath()); } -void removeNiUnderDirs(const std::string rootPaths[], int count) +void removeNIUnderDirs(const std::string& rootPaths) { - auto convert = [](const std::string& path, std::string name) { + auto convert = [](const std::string& path, const std::string& filename) { if (isNativeImage(path)) { - if (remove(path.c_str())) { - fprintf(stderr, "Failed to remove %s\n", path.c_str()); + std::string assemblyPath = changeExtension(path, ".ni.dll", ".dll"); + if (exist(assemblyPath)) { + if (remove(path.c_str())) { + _SERR("Failed to remove %s", path.c_str()); + } + } else { + _SOUT("%s cannot be removed because there is no %s", path.c_str(), assemblyPath.c_str()); } } }; - for (int i = 0; i < count; i++) - scanFilesInDir(rootPaths[i], convert, -1); + std::vector paths; + splitPath(rootPaths, paths); + for (const auto &path : paths) { + scanFilesInDirectory(path, convert, -1); + } } -ni_error_e removeNiUnderPkgRoot(const std::string& pkgId) +ni_error_e removeNIUnderPkgRoot(const std::string& pkgId) { - std::string pkgRoot; - if (getRootPath(pkgId, pkgRoot) < 0) { - fprintf(stderr, "Failed to get root path from [%s]\n", pkgId.c_str()); + std::string rootPath = getRootPath(pkgId); + if (rootPath.empty()) { + _SERR("Failed to get root path from [%s]", pkgId.c_str()); return NI_ERROR_INVALID_PACKAGE; } - std::string binDir = concatPath(pkgRoot, "bin"); - std::string libDir = concatPath(pkgRoot, "lib"); - std::string paths[] = {binDir, libDir}; - - removeNiUnderDirs(paths, 2); - - std::string binNIDir = concatPath(binDir, APP_NI_SUB_DIR); - if (isFileExist(binNIDir)) { - if (rmdir(binNIDir.c_str()) != 0) { - fprintf(stderr, "Failed to remove app ni dir [%s]\n", binNIDir.c_str()); + __pm->setAppRootPath(rootPath); + + // getAppNIPaths returns bin/.native_image, lib/.native_image and .tac_symlink. + std::string appNIPaths = __pm->getAppNIPaths(); + std::vector paths; + splitPath(appNIPaths, paths); + for (const auto &path : paths) { + if (!isReadOnlyArea(path)) { + // Only the native image inside the TAC should be removed. + if (strstr(path.c_str(), TAC_SYMLINK_SUB_DIR) != NULL) { + removeNIUnderDirs(path); + } else { + if (isDirectory(path)) { + if (!removeAll(path.c_str())) { + _SERR("Failed to remove app ni dir [%s]", path.c_str()); + } + } + } } } - std::string libNIDir = concatPath(libDir, APP_NI_SUB_DIR); - if (isFileExist(libNIDir)) { - if (rmdir(libNIDir.c_str()) != 0) { - fprintf(stderr, "Failed to remove app ni dir [%s]\n", libNIDir.c_str()); + // In special cases, the ni file may exist in the dll location. + // The code below is to avoid this exceptional case. + std::string appPaths = __pm->getAppPaths(); + splitPath(appPaths, paths); + for (const auto &path : paths) { + if (isDirectory(path)) { + removeNIUnderDirs(path); } } return NI_ERROR_NONE; } -ni_error_e regenerateAppNI(bool enableR2R) +ni_error_e regenerateAppNI(NIOption* opt) { int ret = 0; pkgmgrinfo_appinfo_metadata_filter_h handle; @@ -481,21 +1171,89 @@ ni_error_e regenerateAppNI(bool enableR2R) if (ret != PMINFO_R_OK) return NI_ERROR_UNKNOWN; - ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, AOT_METADATA_KEY, AOT_METADATA_VALUE); + ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, AOT_METADATA_KEY, METADATA_VALUE); if (ret != PMINFO_R_OK) { pkgmgrinfo_appinfo_metadata_filter_destroy(handle); return NI_ERROR_UNKNOWN; } - ret = pkgmgrinfo_appinfo_metadata_filter_foreach(handle, appAotCb, &enableR2R); + ret = pkgmgrMDFilterForeach(handle, appAotCb, &opt); + if (ret != 0) { + pkgmgrinfo_appinfo_metadata_filter_destroy(handle); + return NI_ERROR_UNKNOWN; + } + + pkgmgrinfo_appinfo_metadata_filter_destroy(handle); + return NI_ERROR_NONE; +} + +// callback function of "pkgmgrinfo_appinfo_metadata_filter_foreach" +static int regenTacCb(pkgmgrinfo_appinfo_h handle, void *userData) +{ + char *pkgId = NULL; + NIOption **pOpt = (NIOption**)userData; + + int ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId); + if (ret != PMINFO_R_OK || pkgId == NULL) { + _SERR("Failed to get pkgid"); + return -1; + } + + sqlite3 *tac_db = openDB(TAC_APP_LIST_DB); + if (!tac_db) { + _SERR("Sqlite open error"); + return -1; + } + sqlite3_exec(tac_db, "BEGIN;", NULL, NULL, NULL); + + char *sql = sqlite3_mprintf("SELECT * FROM TAC WHERE PKGID = %Q;", pkgId); + std::vector nugets = selectDB(tac_db, sql); + sqlite3_free(sql); + + if (tac_db) { + closeDB(tac_db); + tac_db = NULL; + } + + std::string nugetPaths; + for (const auto &nuget : nugets) { + if (!nugetPaths.empty()) { + nugetPaths += ":"; + } + nugetPaths += concatPath(__DOTNET_DIR, nuget); + } + + for (auto& nuget : nugets) { + createNIUnderTAC(concatPath(__DOTNET_DIR, nuget), nugetPaths, *pOpt); + } + + return 0; +} + +ni_error_e regenerateTACNI(NIOption* opt) +{ + removeNIUnderDirs(__DOTNET_DIR); + + pkgmgrinfo_appinfo_metadata_filter_h handle; + int ret = pkgmgrinfo_appinfo_metadata_filter_create(&handle); + if (ret != PMINFO_R_OK) { + return NI_ERROR_UNKNOWN; + } + + ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, TAC_METADATA_KEY, METADATA_VALUE); if (ret != PMINFO_R_OK) { - fprintf(stderr, "Failed pkgmgrinfo_appinfo_metadata_filter_foreach\n"); pkgmgrinfo_appinfo_metadata_filter_destroy(handle); return NI_ERROR_UNKNOWN; } - fprintf(stderr, "Success pkgmgrinfo_appinfo_metadata_filter_foreach\n"); + ret = pkgmgrMDFilterForeach(handle, regenTacCb, &opt); + if (ret != 0) { + pkgmgrinfo_appinfo_metadata_filter_destroy(handle); + return NI_ERROR_UNKNOWN; + } pkgmgrinfo_appinfo_metadata_filter_destroy(handle); + return NI_ERROR_NONE; } +