blob: 6a85240743595ef05aa8bcb4b996f828ba09b103 [file]
// Copyright 2023 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/compiler/Utils/ToolUtils.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#if __linux__ || __APPLE__
#include <dlfcn.h>
#elif defined(WIN32)
#include <Windows.h>
#endif
#define DEBUG_TYPE "iree-tools"
namespace mlir::iree_compiler {
std::string escapeCommandLineComponent(const std::string &component) {
#if defined(_WIN32)
return "\"" + component + "\"";
#else
return component;
#endif // _WIN32
}
StringRef unescapeCommandLineComponent(StringRef component) {
#if defined(_WIN32)
if (component.starts_with("\"") && component.ends_with("\"")) {
return component.drop_front(1).drop_back(1);
}
#endif // _WIN32
return component;
}
static std::string normalizeToolNameForPlatform(const std::string &toolName) {
#if defined(_WIN32)
return toolName + ".exe";
#else
return toolName;
#endif // _WIN32
}
static std::string findToolAtPath(SmallVector<std::string> normalizedToolNames,
const Twine &path) {
LLVM_DEBUG(llvm::dbgs() << "Searching for tool at path '" << path << "'\n");
for (auto toolName : normalizedToolNames) {
SmallString<256> pathStorage;
llvm::sys::path::append(pathStorage, path, toolName);
if (llvm::sys::fs::exists(pathStorage)) {
llvm::sys::fs::make_absolute(pathStorage);
(void)llvm::sys::path::remove_dots(pathStorage, /*remove_dot_dot=*/true);
return escapeCommandLineComponent(std::string(pathStorage));
}
}
return "";
}
static SmallVector<std::string>
normalizeToolNames(SmallVector<std::string> toolNames) {
SmallVector<std::string> normalizedToolNames;
normalizedToolNames.reserve(toolNames.size());
for (auto toolName : toolNames) {
normalizedToolNames.push_back(normalizeToolNameForPlatform(toolName));
}
return normalizedToolNames;
}
std::string findToolFromExecutableDir(SmallVector<std::string> toolNames) {
const auto &normalizedToolNames = normalizeToolNames(toolNames);
std::string mainExecutablePath =
llvm::sys::fs::getMainExecutable(nullptr, nullptr);
SmallString<256> mainExecutableDir(mainExecutablePath);
llvm::sys::path::remove_filename(mainExecutableDir);
LLVM_DEBUG({
llvm::dbgs() << "Searching from the executable directory "
<< mainExecutableDir << " for one of these tools: [";
llvm::interleaveComma(normalizedToolNames, llvm::dbgs());
llvm::dbgs() << "]\n";
});
// First search the current executable's directory. This should find tools
// within the install directory (through CMake or binary distributions).
std::string toolPath = findToolAtPath(normalizedToolNames, mainExecutableDir);
if (!toolPath.empty()) {
LLVM_DEBUG(llvm::dbgs() << "Found tool in executable's directory at path "
<< toolPath << "\n");
return toolPath;
}
// Next search around in the CMake build tree.
toolPath = findToolAtPath(normalizedToolNames,
mainExecutableDir + "/../llvm-project/bin/");
if (!toolPath.empty()) {
LLVM_DEBUG(llvm::dbgs()
<< "Found tool in build tree at path " << toolPath << "\n");
return toolPath;
}
LLVM_DEBUG(llvm::dbgs() << "Tool not found.\n");
return "";
}
static std::string getCurrentDylibPath() {
#if __linux__ || __APPLE__
Dl_info dlInfo;
if (dladdr((void *)getCurrentDylibPath, &dlInfo) == 0)
return {};
return (dlInfo.dli_fname);
#elif defined(WIN32)
HMODULE hm = NULL;
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCSTR)&getCurrentDylibPath, &hm) == 0) {
LLVM_DEBUG(llvm::dbgs() << "GetModuleHandleEx could not find the module\n");
return {};
}
llvm::SmallVector<char, MAX_PATH> dllPath;
dllPath.resize_for_overwrite(dllPath.capacity());
while (1) {
auto size = ::GetModuleFileNameA(hm, dllPath.data(), dllPath.size());
if (size == 0) {
LLVM_DEBUG(llvm::dbgs() << "GetModuleFileNameA failed\n");
return {};
}
if (size == dllPath.size()) {
dllPath.resize_for_overwrite(dllPath.size() + MAX_PATH);
continue;
}
dllPath.truncate(size);
break;
}
return std::string(dllPath.data(), dllPath.size());
#endif
LLVM_DEBUG(
llvm::dbgs() << "Platform cannot resolve its current dylib address\n");
return {};
}
std::string findToolFromDylibDir(SmallVector<std::string> toolNames) {
const auto &normalizedToolNames = normalizeToolNames(toolNames);
std::string dylibPath = getCurrentDylibPath();
if (dylibPath.empty())
return {};
SmallString<256> dylibDir(dylibPath);
llvm::sys::path::remove_filename(dylibDir);
LLVM_DEBUG({
llvm::dbgs() << "Searching from the dylib directory " << dylibDir
<< " for one of these tools: [";
llvm::interleaveComma(normalizedToolNames, llvm::dbgs());
llvm::dbgs() << "]\n";
});
// First search the current dylib's directory. This should find tools
// like:
// libIREECompiler.so
// iree-lld
// Python extensions are always packaged this way, and it happens
// to be how Windows installs are organized (i.e. .exe always next to
// the .dll).
std::string toolPath = findToolAtPath(normalizedToolNames, dylibDir);
if (!toolPath.empty()) {
LLVM_DEBUG(llvm::dbgs() << "Found tool in library's directory at path "
<< toolPath << "\n");
return toolPath;
}
// Then search in an adjacent bin/ directory. Binary installs are
// packaged this way:
// lib/
// libIREECompiler.so
// bin/
// iree-lld
toolPath = findToolAtPath(normalizedToolNames, dylibDir + "/../bin/");
if (!toolPath.empty()) {
LLVM_DEBUG(llvm::dbgs()
<< "Found tool in library's adjacent bin directory at path "
<< toolPath << "\n");
return toolPath;
}
// Then search in an adjacent tools/ directory. Build trees are
// organized this way for reasons:
// lib/
// libIREECompiler.so
// tools/
// iree-lld
toolPath = findToolAtPath(normalizedToolNames, dylibDir + "/../tools/");
if (!toolPath.empty()) {
LLVM_DEBUG(llvm::dbgs()
<< "Found tool in library's adjacent tools directory at path "
<< toolPath << "\n");
return toolPath;
}
// Next search around in the CMake build tree:
// lib/
// libIREECompiler.so
// llvm-project/bin
// lld
toolPath =
findToolAtPath(normalizedToolNames, dylibDir + "/../llvm-project/bin/");
if (!toolPath.empty()) {
LLVM_DEBUG(llvm::dbgs()
<< "Found tool relative to dylib in build tree at path "
<< toolPath << "\n");
return toolPath;
}
return "";
}
std::string findToolInEnvironment(SmallVector<std::string> toolNames) {
const auto &normalizedToolNames = normalizeToolNames(toolNames);
LLVM_DEBUG({
llvm::dbgs() << "Searching environment PATH for one of these tools: [";
llvm::interleaveComma(normalizedToolNames, llvm::dbgs());
llvm::dbgs() << "]\n";
});
for (auto toolName : normalizedToolNames) {
if (auto result = llvm::sys::Process::FindInEnvPath("PATH", toolName)) {
LLVM_DEBUG(llvm::dbgs() << "Found tool on environment PATH at path "
<< result << "\n");
return escapeCommandLineComponent(std::string(*result));
}
}
LLVM_DEBUG(llvm::dbgs() << "Tool not found.\n");
return "";
}
std::string findTool(SmallVector<std::string> toolNames) {
// TODO(benvanik): add a test for IREE_[toolName]_PATH.
std::string dylibDirPath = findToolFromDylibDir(toolNames);
if (!dylibDirPath.empty())
return dylibDirPath;
// Search the install or build dir.
std::string executableDirPath = findToolFromExecutableDir(toolNames);
if (!executableDirPath.empty())
return executableDirPath;
// Currently fall back on searching the environment.
std::string environmentPath = findToolInEnvironment(toolNames);
if (!environmentPath.empty())
return environmentPath;
return "";
}
std::string findTool(std::string toolName) {
SmallVector<std::string> toolNames = {toolName};
return findTool(toolNames);
}
std::string findPlatformLibDirectory(StringRef platformName) {
std::string dylibPath = getCurrentDylibPath();
if (dylibPath.empty())
return {};
SmallString<256> path(dylibPath);
llvm::sys::path::remove_filename(path);
llvm::sys::path::append(path, "iree_platform_libs", platformName);
if (!llvm::sys::fs::is_directory(path))
return {};
llvm::sys::fs::make_absolute(path);
(void)llvm::sys::path::remove_dots(path, /*remove_dot_dot=*/true);
std::string pathStr(path);
return pathStr;
}
} // namespace mlir::iree_compiler