From: 김상욱/Common Platform Lab(SR)/Engineer/삼성전자 Date: Mon, 13 Apr 2020 03:02:36 +0000 (+0900) Subject: Rework dotnet executable (#204) X-Git-Tag: accepted/tizen/5.5/unified/20200414.140218~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1528704f8927d175ee339857076c336c6edbc0fb;p=platform%2Fcore%2Fdotnet%2Flauncher.git Rework dotnet executable (#204) * Re-implement dotnet binary * Implement additional switches * Remove corerun-related code from dotnet_launcher.cc * Update code formatting * Fix addAssembliesFromDirectories logic and refactor * Fix the usage of depth parameter * Organize command line options * Minor fixes --- diff --git a/NativeLauncher/inc/utils.h b/NativeLauncher/inc/utils.h index b5c11b8..b8b065f 100644 --- a/NativeLauncher/inc/utils.h +++ b/NativeLauncher/inc/utils.h @@ -143,24 +143,28 @@ bool isManagedAssembly(const std::string& filePath); bool isNativeImage(const std::string& filePath); /** - * @brief find assembly files in the directories - * @param[in] directories - * @param[out] ":" seperated assembly path list + * @brief Resolve assembly files from directories and append their paths to the given list. + * @remark If a native image exists for an assembly in the same directory, it will be used. + * If multiple assemblies of the same name exist, the first one will be used. + * @param[in] directories list of directories + * @param[out] list colon-separated string of assembly paths */ -void assembliesInDirectory(const std::vector& directories, std::string& tpaList); +void addAssembliesFromDirectories(const std::vector& directories, std::string& list); /** - * @brief function pointer for file reader + * @brief File search callback + * @param[in] path full path to a resolved file + * @param[in] filename file name */ -typedef std::function FileReader; +typedef std::function FileReader; /** - * @brief scan files in the given directory and run file reader function for that - * @param[in] directory - * @param[in] file reader function - * @param[in] scan depth + * @brief Scan all files in the given directory. + * @param[in] directory path to a root directory + * @param[in] reader callback for iteration + * @param[in] depth recursive search depth */ -void scanFilesInDir(const std::string& directory, FileReader reader, unsigned int depth); +void scanFilesInDirectory(const std::string& directory, FileReader reader, unsigned int depth); /** * @brief copy smack and ownership. @@ -220,13 +224,14 @@ bool removeAll(const bf::path& path); * @brief change command name of the current process for access via ps command * @param[in] name */ -void setCmdName(const char* name); +void setCmdName(const std::string& name); /** - * @brief get file name from the path - * @param[in] file path - * @return return file name if exist otherwise null. + * @brief Get the file name from the specified path. + * @remark An empty string will be returned if the path string ends with a path separator character. + * @param[in] path path to a file + * @return a substring after the path separator character */ -char* getFileNameFromPath(char* path); +std::string getFileName(const std::string& path); #endif /* __UTILS_H__ */ diff --git a/NativeLauncher/launcher/exec/corerun.cc b/NativeLauncher/launcher/exec/corerun.cc index df6cf1f..4842867 100644 --- a/NativeLauncher/launcher/exec/corerun.cc +++ b/NativeLauncher/launcher/exec/corerun.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -14,54 +14,241 @@ * limitations under the License. */ -#include "dotnet_launcher.h" +#include +#include +#include "coreclr_host.h" #include "utils.h" -#include "log.h" -using tizen::runtime::dotnetcore::CoreRuntime; +static const char* CLR_PATH = "/usr/share/dotnet.tizen/netcoreapp"; +static const char* TOOL_PATH = "/home/owner/share/.dotnet/tools"; -int main(int argc, char *argv[]) -{ - _INFO("##### Run in corerun mode #####"); +void DisplayUsage() { + fprintf(stdout, + "Execute a .NET application or command.\n\n" + "Usage: dotnet [options] [path-to-executable] [arguments]\n" + "Usage: dotnet [command] [arguments]\n\n" + "Options:\n" + "-h, --help show this help message\n" + "--clr-path path to libcoreclr.so and runtime assemblies\n" + "--tool-path path to the tool installation directory\n" + "--additionalprobingpath path containing assemblies to probe for\n" + "--globalizationinvariant run in globalization invariant mode\n\n" + "Commands:\n" + "counters monitor or collect performance counters\n" + "dump capture or analyze a coredump\n" + "gcdump capture a heapdump\n" + "trace collect or convert a diagnostic event trace\n"); +} + +int main(int argc, const char* argv[]) { +#ifdef __arm__ + // libunwind library is used to unwind stack frame, but libunwind for ARM + // does not support ARM vfpv3/NEON registers in DWARF format correctly. + // Therefore let's disable stack unwinding using DWARF information + // See https://github.com/dotnet/runtime/issues/6479 + // + // libunwind use following methods to unwind stack frame. + // UNW_ARM_METHOD_ALL 0xFF + // UNW_ARM_METHOD_DWARF 0x01 + // UNW_ARM_METHOD_FRAME 0x02 + // UNW_ARM_METHOD_EXIDX 0x04 + putenv(const_cast("UNW_ARM_UNWIND_METHOD=6")); +#endif // __arm__ + + argv++; + argc--; + + if (argc <= 0) { + DisplayUsage(); + return -1; + } + + std::string clrFilesPath(CLR_PATH); + std::string toolDllsPath(TOOL_PATH); + + std::string managedAssemblyPath; + std::string additionalProbingPath; + bool globalizationInvariant = false; + + while (argc > 0) { + std::string arg(argv[0]); + + if (arg == "-?" || arg == "-h" || arg == "--help") { + DisplayUsage(); + return 0; + } else if (arg == "--clr-path" && argc > 1) { + clrFilesPath = argv[1]; + argc--; + argv++; + } else if (arg == "--tool-path" && argc > 1) { + toolDllsPath = argv[1]; + argc--; + argv++; + } else if (arg == "--additionalprobingpath" && argc > 1) { + additionalProbingPath = absolutePath(argv[1]); + argc--; + argv++; + } else if (arg == "--globalizationinvariant") { + globalizationInvariant = true; + } else if ((arg == "--runtimeconfig" || arg == "--depsfile") && argc > 1) { + // Just for compatibility with corefx tests. + // See ParseArguments() in coreclr/hosts/unixcorerun/corerun.cpp. + argc--; + argv++; + } else if (arg.at(0) == '-') { + fprintf(stderr, "Unknown option %s.\n", argv[0]); + DisplayUsage(); + return -1; + } else if (isManagedAssembly(arg) || isNativeImage(arg)) { + if (!isFileExist(arg)) { + fprintf(stderr, "The specified file does not exist.\n"); + return -1; + } + managedAssemblyPath = arg; + argc--; + argv++; + break; + } else if (arg == "exec") { + // 'dotnet' and 'dotnet exec' can be alternatively used. + } else if (arg == "tool") { + fprintf(stderr, "This command is not currently supported.\n"); + return -1; + } else { + managedAssemblyPath = toolDllsPath + "/dotnet-" + arg; + + if (isFileExist(managedAssemblyPath + ".ni.dll")) { + managedAssemblyPath += ".ni.dll"; + } else if (isFileExist(managedAssemblyPath + ".dll")) { + managedAssemblyPath += ".dll"; + } else { + fprintf(stderr, + "Could not execute because dotnet-%s does not exist.\n" + "Follow the instructions for tool installation: https://github.com/Samsung/diagnostic-tools\n", argv[0]); + return -1; + } + + // Implicit compatibility mode for System.CommandLine. + std::string termValue(getenv("TERM")); + if (termValue == "linux") { + setenv("TERM", "xterm", 1); + } + + argc--; + argv++; + break; + } + + argc--; + argv++; + } + + std::string currentExeAbsolutePath = absolutePath("/proc/self/exe"); + if (currentExeAbsolutePath.empty()) { + fprintf(stderr, "Failed to get the current executable's absolute path.\n"); + return -1; + } - if (argc < 2) { - _SOUT("No parameter"); + std::string clrFilesAbsolutePath = absolutePath(clrFilesPath); + if (clrFilesAbsolutePath.empty()) { + fprintf(stderr, "Failed to resolve the full path to the CLR files.\n"); return -1; } - if (!isManagedAssembly(argv[1]) && !isNativeImage(argv[1])) { - _SOUT("first parameter should be assembly file"); + std::string managedAssemblyAbsolutePath = absolutePath(managedAssemblyPath); + if (managedAssemblyAbsolutePath.empty()) { + fprintf(stderr, "Failed to get the managed assembly's absolute path.\n"); return -1; } - // remove executable and assembly path form the arguments and pass that to managed code - int vargc = argc - 2; - std::vector vargs; - for (int i = 0; i < vargc; i++) { - vargs.push_back(argv[2 + i]); + std::string coreclrLibPath(clrFilesAbsolutePath + "/libcoreclr.so"); + std::string appPath = baseName(managedAssemblyAbsolutePath); + std::string nativeDllSearchDirs(appPath); + nativeDllSearchDirs += ":" + additionalProbingPath; + nativeDllSearchDirs += ":" + clrFilesAbsolutePath; + + std::string tpaList(managedAssemblyAbsolutePath); + // For now we don't parse .deps.json file but let application DLLs can override runtime DLLs. + std::vector tpaDirs = { appPath, additionalProbingPath, clrFilesAbsolutePath }; + addAssembliesFromDirectories(tpaDirs, tpaList); + + void* coreclrLib = dlopen(coreclrLibPath.c_str(), RTLD_NOW | RTLD_LOCAL); + if (coreclrLib == nullptr) { + const char* error = dlerror(); + fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error); + return -1; } - // set command name to assembly file - char* fileName = getFileNameFromPath(argv[1]); - setCmdName(fileName); - - CoreRuntime* runtime = new CoreRuntime("corerun"); + coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)dlsym(coreclrLib, "coreclr_initialize"); + coreclr_execute_assembly_ptr executeAssembly = (coreclr_execute_assembly_ptr)dlsym(coreclrLib, "coreclr_execute_assembly"); + coreclr_shutdown_ptr shutdownCoreCLR = (coreclr_shutdown_ptr)dlsym(coreclrLib, "coreclr_shutdown"); + + if (initializeCoreCLR == nullptr) { + fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n"); + return -1; + } else if (executeAssembly == nullptr) { + fprintf(stderr, "Function coreclr_execute_assembly not found in the libcoreclr.so\n"); + return -1; + } else if (shutdownCoreCLR == nullptr) { + fprintf(stderr, "Function coreclr_shutdown not found in the libcoreclr.so\n"); + return -1; + } - // get absolute path of input dll file - std::string absoluteDllPath = absolutePath(argv[1]); - std::string absoluteRootPath = baseName(absoluteDllPath); + const char* propertyKeys[] = { + "TRUSTED_PLATFORM_ASSEMBLIES", + "APP_PATHS", + "APP_NI_PATHS", + "NATIVE_DLL_SEARCH_DIRECTORIES", + "System.Globalization.Invariant", + }; + const char* propertyValues[] = { + tpaList.c_str(), + appPath.c_str(), + appPath.c_str(), + nativeDllSearchDirs.c_str(), + globalizationInvariant ? "true" : "false", + }; - if (runtime->initialize(LaunchMode::corerun, false, absoluteRootPath.c_str()) != 0) { - _SOUT("Failed to initialize"); + void* hostHandle; + unsigned int domainId; + + int st = initializeCoreCLR( + currentExeAbsolutePath.c_str(), + "dotnet", + sizeof(propertyKeys) / sizeof(propertyKeys[0]), + propertyKeys, + propertyValues, + &hostHandle, + &domainId); + + if (st < 0) { + fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st); return -1; } - // launch application - if (runtime->launch(fileName, absoluteRootPath.c_str(), absoluteDllPath.c_str(), vargc, &vargs[0])) { - _SOUT("Failed to launch"); + // Set the current process's command name. + std::string managedAssemblyName = getFileName(managedAssemblyAbsolutePath); + setCmdName(managedAssemblyName); + + int exitCode = -1; + + st = executeAssembly( + hostHandle, + domainId, + argc, + argv, + managedAssemblyAbsolutePath.c_str(), + (unsigned int*)&exitCode); + + if (st < 0) { + fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st); + exitCode = -1; + } + + st = shutdownCoreCLR(hostHandle, domainId); + if (st < 0) { + fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st); return -1; } - return 0; + return exitCode; } - diff --git a/NativeLauncher/launcher/exec/launcher.cc b/NativeLauncher/launcher/exec/launcher.cc index 5a4ff57..84afd91 100644 --- a/NativeLauncher/launcher/exec/launcher.cc +++ b/NativeLauncher/launcher/exec/launcher.cc @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) } // set command name to assembly file - setCmdName(getFileNameFromPath(standalonePath)); + setCmdName(getFileName(standalonePath)); // change cmdline from dotnet-launcher to executable path int cmdlineSize = paddingExist ? APPID_MAX_LENGTH : APPID_MAX_LENGTH - PaddingOption.length(); @@ -91,7 +91,7 @@ int main(int argc, char *argv[]) snprintf(argv[0], cmdlineSize - 1, "%s", standalonePath); // initialize CoreRuntime (standalone mode enable, dlog redirection enable, root path NULL) - if (runtime->initialize(LaunchMode::launcher, true, appRootPath) != 0) { + if (runtime->initialize(LaunchMode::launcher) != 0) { _ERR("Failed to initialize"); return -1; } diff --git a/NativeLauncher/launcher/exec/loader.cc b/NativeLauncher/launcher/exec/loader.cc index c090fd9..dda9368 100644 --- a/NativeLauncher/launcher/exec/loader.cc +++ b/NativeLauncher/launcher/exec/loader.cc @@ -121,7 +121,7 @@ static void __loader_create_cb(bundle *extra, int type, void *user_data) } // initialize CoreRuntime (launchmode, dlog redirection enable, root path NULL) - if (runtime->initialize(LaunchMode::loader, true, NULL) != 0) { + if (runtime->initialize(LaunchMode::loader) != 0) { _ERR("Failed to initialized"); } else { _INFO("Success to initialized"); diff --git a/NativeLauncher/launcher/lib/dotnet_launcher.cc b/NativeLauncher/launcher/lib/dotnet_launcher.cc index 5dc2304..7ed7f39 100644 --- a/NativeLauncher/launcher/lib/dotnet_launcher.cc +++ b/NativeLauncher/launcher/lib/dotnet_launcher.cc @@ -347,7 +347,7 @@ CoreRuntime::~CoreRuntime() dispose(); } -int CoreRuntime::initialize(LaunchMode launchMode, bool useDlog, const char* rootPath) +int CoreRuntime::initialize(LaunchMode launchMode) { // checkInjection checks dotnet-launcher run mode // At the moment, this mechanism is used only when the Memory Profiler is started. @@ -399,7 +399,7 @@ int CoreRuntime::initialize(LaunchMode launchMode, bool useDlog, const char* roo return -1; } - if (useDlog && !pluginHasLogControl()) { + if (!pluginHasLogControl()) { if (initializeLogManager() < 0) { _ERR("Failed to initnialize LogManager"); return -1; @@ -449,31 +449,22 @@ int CoreRuntime::initialize(LaunchMode launchMode, bool useDlog, const char* roo // To avoid gdbus blocking issue, below function should be called after fork() initEnvForSpecialFolder(); + __fd = open("/proc/self", O_DIRECTORY); + if (__fd < 0) { + _ERR("Failed to open /proc/self"); + return -1; + } + std::string tpa = getTPA(); std::string runtimeDir = getRuntimeDir(); std::string appName = std::string("dotnet-launcher-") + std::to_string(getpid()); - std::string probePath; - std::string NIprobePath; - std::string nativeLibPath; - - if (launchMode == LaunchMode::corerun) { - probePath = rootPath; - NIprobePath = rootPath; - nativeLibPath = rootPath; - } else { - __fd = open("/proc/self", O_DIRECTORY); - if (__fd < 0) { - _ERR("Failed to open /proc/self"); - return -1; - } - std::string appRoot = std::string("/proc/self/fd/") + std::to_string(__fd); - std::string appBin = concatPath(appRoot, "bin"); - std::string appLib = concatPath(appRoot, "lib"); - std::string appTac = concatPath(appBin, TAC_SYMLINK_SUB_DIR); - probePath = appRoot + ":" + appBin + ":" + appLib + ":" + appTac; - NIprobePath = concatPath(appBin, APP_NI_SUB_DIR) + ":" + concatPath(appLib, APP_NI_SUB_DIR) + ":" + appTac; - nativeLibPath = getExtraNativeLibDirs(appRoot) + ":" + appBin + ":" + appLib + ":" + __nativeLibDirectory + ":" + runtimeDir; - } + std::string appRoot = std::string("/proc/self/fd/") + std::to_string(__fd); + std::string appBin = concatPath(appRoot, "bin"); + std::string appLib = concatPath(appRoot, "lib"); + std::string appTac = concatPath(appBin, TAC_SYMLINK_SUB_DIR); + std::string probePath = appRoot + ":" + appBin + ":" + appLib + ":" + appTac; + std::string NIprobePath = concatPath(appBin, APP_NI_SUB_DIR) + ":" + concatPath(appLib, APP_NI_SUB_DIR) + ":" + appTac; + std::string nativeLibPath = getExtraNativeLibDirs(appRoot) + ":" + appBin + ":" + appLib + ":" + __nativeLibDirectory + ":" + runtimeDir; if (!initializeCoreClr(appName.c_str(), probePath.c_str(), NIprobePath.c_str(), nativeLibPath.c_str(), tpa.c_str())) { _ERR("Failed to initialize coreclr"); diff --git a/NativeLauncher/launcher/lib/dotnet_launcher.h b/NativeLauncher/launcher/lib/dotnet_launcher.h index 8f6c22a..53c8bfb 100644 --- a/NativeLauncher/launcher/lib/dotnet_launcher.h +++ b/NativeLauncher/launcher/lib/dotnet_launcher.h @@ -37,7 +37,7 @@ class CoreRuntime public: CoreRuntime(const char* mode); ~CoreRuntime(); - int initialize(LaunchMode mode, bool useDlog, const char* rootPath); + int initialize(LaunchMode mode); void dispose(); int launch(const char* appId, const char* root, const char* path, int argc, char* argv[]); diff --git a/NativeLauncher/tool/ni_common.cc b/NativeLauncher/tool/ni_common.cc index 05b0cfc..88cc6a1 100644 --- a/NativeLauncher/tool/ni_common.cc +++ b/NativeLauncher/tool/ni_common.cc @@ -477,7 +477,7 @@ void createNiUnderTAC(std::vector nugets, DWORD flags) appPaths.pop_back(); } - auto convert = [&appPaths, flags](const std::string& path, const char* name) { + auto convert = [&appPaths, flags](const std::string& path, const std::string& filename) { if (strstr(path.c_str(), TAC_SHA_256_INFO) != NULL) return; if (!crossgen(path, appPaths.c_str(), flags)) { @@ -485,7 +485,7 @@ void createNiUnderTAC(std::vector nugets, DWORD flags) } }; for (auto& nuget : nugets) { - scanFilesInDir(concatPath(__DOTNET_DIR, nuget), convert, -1); + scanFilesInDirectory(concatPath(__DOTNET_DIR, nuget), convert, -1); } } @@ -554,12 +554,12 @@ void createNiUnderDirs(const std::string rootPaths[], int count, DWORD flags) std::vector tpaAssemblies; splitPath(__tpa, tpaAssemblies); - auto convert = [&appPaths, flags, tpaAssemblies](const std::string& path, const char* name) { + auto convert = [&appPaths, flags, tpaAssemblies](const std::string& path, const std::string& filename) { bool isAppNI = flags & NI_FLAGS_APPNI; if (isAppNI) { for (auto& tpa : tpaAssemblies) { - if (!strcmp(replaceAll(tpa.substr(tpa.rfind('/') + 1), ".ni.dll", ".dll").c_str(), name)) { - fprintf(stderr, "%s present in the TPA list skips generation of NI file.\n", name); + if (!strcmp(replaceAll(tpa.substr(tpa.rfind('/') + 1), ".ni.dll", ".dll").c_str(), filename.c_str())) { + fprintf(stderr, "%s present in the TPA list skips generation of NI file.\n", filename.c_str()); return; } } @@ -570,7 +570,7 @@ void createNiUnderDirs(const std::string rootPaths[], int count, DWORD flags) }; for (int i = 0; i < count; i++) { - scanFilesInDir(rootPaths[i], convert, 1); + scanFilesInDirectory(rootPaths[i], convert, 0); } tpaAssemblies.clear(); @@ -680,7 +680,7 @@ void removeNiPlatform() void removeNiUnderDirs(const std::string rootPaths[], int count) { - 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()); @@ -689,7 +689,7 @@ void removeNiUnderDirs(const std::string rootPaths[], int count) }; for (int i = 0; i < count; i++) { - scanFilesInDir(rootPaths[i], convert, -1); + scanFilesInDirectory(rootPaths[i], convert, -1); } } diff --git a/NativeLauncher/util/path_manager.cc b/NativeLauncher/util/path_manager.cc index 5df09ba..a00e247 100644 --- a/NativeLauncher/util/path_manager.cc +++ b/NativeLauncher/util/path_manager.cc @@ -136,11 +136,8 @@ static std::string getPlatformTPA() std::getline(cacheFile, platform_tpa); cacheFile.close(); } else { - std::vector tpaDir; - tpaDir.push_back(getRuntimeDir()); - tpaDir.push_back(getTizenFXDir()); - tpaDir.push_back(getTizenFXRefDir()); - assembliesInDirectory(tpaDir, platform_tpa); + std::vector tpaDirs = { getRuntimeDir(), getTizenFXDir(), getTizenFXRefDir() }; + addAssembliesFromDirectories(tpaDirs, platform_tpa); } return platform_tpa; @@ -156,7 +153,7 @@ static std::string getPluginTPA() plugin_tpa = plugin_tpa_list; } else if (!__dllPath->extra_dirs.empty()){ _INFO("plugin extra directory found. use plugin extra directroy for TPA"); - assembliesInDirectory(__dllPath->extra_dirs, plugin_tpa); + addAssembliesFromDirectories(__dllPath->extra_dirs, plugin_tpa); } return plugin_tpa; diff --git a/NativeLauncher/util/utils.cc b/NativeLauncher/util/utils.cc index 014f760..59a70f3 100644 --- a/NativeLauncher/util/utils.cc +++ b/NativeLauncher/util/utils.cc @@ -258,54 +258,54 @@ uintptr_t getFileSize(const std::string& path) return 0; } -std::string stripNiDLL(const std::string& path) +std::string getAssemblyNameFromPath(const std::string& path) { - std::string niPath(path); - if (path.size() < 5) return niPath; - if (!strncasecmp(path.c_str() + path.size() - 4, ".dll", 4)) - niPath = path.substr(0, path.size()-4); - else if (!strncasecmp(path.c_str() + path.size() - 4, ".exe", 4)) - niPath = path.substr(0, path.size()-4); + std::string ret(getFileName(path)); - if (!strncasecmp(niPath.c_str() + niPath.size() - 3, ".ni", 3)) - return niPath.substr(0, niPath.size()-3); + if (ret.find_last_of(".") == std::string::npos) + return ret; + ret.erase(ret.find_last_of(".")); - return niPath; + if (ret.size() > 3 && std::equal(ret.begin() + ret.size() - 3, ret.end(), ".ni")) + ret.erase(ret.size() - 3); + + return ret; } -void assembliesInDirectory(const std::vector& directories, std::string& tpaList) -{ - std::map assemblyList; - std::map tmpList; - - auto reader = [&assemblyList, &tmpList] (const std::string& path, const char* name) { - if (isManagedAssembly(path) || isNativeImage(path)) { - std::string dllName = stripNiDLL(name); - std::pair::iterator, bool> ret; - ret = tmpList.insert(std::pair(dllName, path)); - if (ret.second == false) { - if (isNativeImage(path)) - tmpList[dllName] = path; +void addAssembliesFromDirectories(const std::vector& directories, std::string& list) { + std::vector assems; + std::unordered_map assemPaths; + + auto reader = [&assems, &assemPaths](const std::string& path, const std::string& filename) { + if (isManagedAssembly(filename) || isNativeImage(filename)) { + std::string assem = getAssemblyNameFromPath(filename); + + if (assemPaths.count(assem) == 0) { + assems.push_back(assem); + assemPaths[assem] = path; + } else if (isManagedAssembly(assemPaths[assem]) && isNativeImage(filename)) { + // Update only if a native image is found in the same directory. + // For example, if we have two directories = { X, Y } where X contains A.dll and + // Y contains both A.dll and A.ni.dll, always A.dll in X will be used. + if (baseName(assemPaths[assem]).compare(baseName(path)) == 0) + assemPaths[assem] = path; } } }; + for (auto& directory : directories) + scanFilesInDirectory(directory, reader, 0); - for (auto directory : directories) { - scanFilesInDir(directory.c_str(), reader, 1); - // merge scaned dll list to tpa list. - // if the dll is already exist in the list, that is skipped. - assemblyList.insert(tmpList.begin(), tmpList.end()); - } + if (!list.empty() && list.back() != ':') + list.push_back(':'); - std::map::iterator it; - for (it = assemblyList.begin(); it != assemblyList.end(); it++) - tpaList += it->second + ':'; + for (auto& assem : assems) + list += assemPaths[assem] + ":"; - if (tpaList.back() == ':') - tpaList.pop_back(); + if (list.back() == ':') + list.pop_back(); } -void scanFilesInDir(const std::string& directory, FileReader reader, unsigned int depth) +void scanFilesInDirectory(const std::string& directory, FileReader reader, unsigned int depth) { DIR *dir; struct dirent* entry; @@ -342,13 +342,13 @@ void scanFilesInDir(const std::string& directory, FileReader reader, unsigned in } if (!isDir) reader(path, entry->d_name); - else if (depth > 1 && strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) + else if (depth > 0 && strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) innerDirectories.push_back(path); } - if (depth != 0) + if (depth > 0) for (auto& d : innerDirectories) - scanFilesInDir(d.c_str(), reader, depth - 1); + scanFilesInDirectory(d, reader, depth - 1); closedir(dir); } @@ -598,28 +598,23 @@ bool removeAll(const bf::path& path) { return true; } -void setCmdName(const char* name) +void setCmdName(const std::string& name) { #define PRC_NAME_LENGTH 16 char processName[PRC_NAME_LENGTH] = {0, }; - if (name == NULL || *name == '\0') { + if (name.empty()) return; - } memset(processName, '\0', PRC_NAME_LENGTH); - snprintf(processName, PRC_NAME_LENGTH, "%s", name); + snprintf(processName, PRC_NAME_LENGTH, "%s", name.c_str()); prctl(PR_SET_NAME, processName); } -char* getFileNameFromPath(char* path) +std::string getFileName(const std::string& path) { - char* fileName = strrchr(path, PATH_SEPARATOR); - if (fileName != NULL && *fileName != '\0') { - return ++fileName; - } - - return NULL; + std::string ret(path); + size_t index = ret.find_last_of(PATH_SEPARATOR); + return index == std::string::npos ? ret : ret.substr(index + 1); } -