blob: 4dfdfae4f924502ad32c8644615b41159e236303 [file] [log] [blame]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmRuntimeDependencyArchive.h"
#include "cmBinUtilsLinuxELFLinker.h"
#include "cmBinUtilsMacOSMachOLinker.h"
#include "cmBinUtilsWindowsPELinker.h"
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#if defined(_WIN32)
# include "cmGlobalGenerator.h"
# ifndef CMAKE_BOOTSTRAP
# include "cmGlobalVisualStudioVersionedGenerator.h"
# endif
# include "cmsys/Glob.hxx"
# include "cmVSSetupHelper.h"
#endif
#include <algorithm>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <cm/memory>
#if defined(_WIN32)
static void AddVisualStudioPath(std::vector<std::string>& paths,
const std::string& prefix,
unsigned int version, cmGlobalGenerator* gg)
{
// If generating for the VS IDE, use the same instance.
std::string vsloc;
bool found = false;
# ifndef CMAKE_BOOTSTRAP
if (cmHasPrefix(gg->GetName(), prefix)) {
cmGlobalVisualStudioVersionedGenerator* vsgen =
static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
if (vsgen->GetVSInstance(vsloc)) {
found = true;
}
}
# endif
// Otherwise, find a VS instance ourselves.
if (!found) {
cmVSSetupAPIHelper vsSetupAPIHelper(version);
if (vsSetupAPIHelper.GetVSInstanceInfo(vsloc)) {
cmSystemTools::ConvertToUnixSlashes(vsloc);
found = true;
}
}
if (found) {
cmsys::Glob glob;
glob.SetListDirs(true);
glob.FindFiles(vsloc + "/VC/Tools/MSVC/*");
for (auto const& vcdir : glob.GetFiles()) {
paths.push_back(vcdir + "/bin/Hostx64/x64");
paths.push_back(vcdir + "/bin/Hostx86/x64");
paths.push_back(vcdir + "/bin/Hostx64/x86");
paths.push_back(vcdir + "/bin/Hostx86/x86");
}
}
}
static void AddRegistryPath(std::vector<std::string>& paths,
const std::string& path, cmMakefile* mf)
{
// We should view the registry as the target application would view
// it.
cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
if (mf->PlatformIs64Bit()) {
view = cmSystemTools::KeyWOW64_64;
other_view = cmSystemTools::KeyWOW64_32;
}
// Expand using the view of the target application.
std::string expanded = path;
cmSystemTools::ExpandRegistryValues(expanded, view);
cmSystemTools::GlobDirs(expanded, paths);
// Executables can be either 32-bit or 64-bit, so expand using the
// alternative view.
expanded = path;
cmSystemTools::ExpandRegistryValues(expanded, other_view);
cmSystemTools::GlobDirs(expanded, paths);
}
static void AddEnvPath(std::vector<std::string>& paths, const std::string& var,
const std::string& suffix)
{
std::string value;
if (cmSystemTools::GetEnv(var, value)) {
paths.push_back(value + suffix);
}
}
#endif
static cmsys::RegularExpression TransformCompile(const std::string& str)
{
return cmsys::RegularExpression(str);
}
cmRuntimeDependencyArchive::cmRuntimeDependencyArchive(
cmExecutionStatus& status, std::vector<std::string> searchDirectories,
std::string bundleExecutable,
const std::vector<std::string>& preIncludeRegexes,
const std::vector<std::string>& preExcludeRegexes,
const std::vector<std::string>& postIncludeRegexes,
const std::vector<std::string>& postExcludeRegexes,
std::vector<std::string> postIncludeFiles,
std::vector<std::string> postExcludeFiles,
std::vector<std::string> postExcludeFilesStrict)
: Status(status)
, SearchDirectories(std::move(searchDirectories))
, BundleExecutable(std::move(bundleExecutable))
, PreIncludeRegexes(preIncludeRegexes.size())
, PreExcludeRegexes(preExcludeRegexes.size())
, PostIncludeRegexes(postIncludeRegexes.size())
, PostExcludeRegexes(postExcludeRegexes.size())
, PostIncludeFiles(std::move(postIncludeFiles))
, PostExcludeFiles(std::move(postExcludeFiles))
, PostExcludeFilesStrict(std::move(postExcludeFilesStrict))
{
std::transform(preIncludeRegexes.begin(), preIncludeRegexes.end(),
this->PreIncludeRegexes.begin(), TransformCompile);
std::transform(preExcludeRegexes.begin(), preExcludeRegexes.end(),
this->PreExcludeRegexes.begin(), TransformCompile);
std::transform(postIncludeRegexes.begin(), postIncludeRegexes.end(),
this->PostIncludeRegexes.begin(), TransformCompile);
std::transform(postExcludeRegexes.begin(), postExcludeRegexes.end(),
this->PostExcludeRegexes.begin(), TransformCompile);
}
bool cmRuntimeDependencyArchive::Prepare()
{
std::string platform = this->GetMakefile()->GetSafeDefinition(
"CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM");
if (platform.empty()) {
std::string systemName =
this->GetMakefile()->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
if (systemName == "Windows") {
platform = "windows+pe";
} else if (systemName == "Darwin") {
platform = "macos+macho";
} else if (systemName == "Linux") {
platform = "linux+elf";
}
}
if (platform == "linux+elf") {
this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this);
} else if (platform == "windows+pe") {
this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this);
} else if (platform == "macos+macho") {
this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this);
} else {
std::ostringstream e;
e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: "
<< platform;
this->SetError(e.str());
return false;
}
return this->Linker->Prepare();
}
bool cmRuntimeDependencyArchive::GetRuntimeDependencies(
const std::vector<std::string>& executables,
const std::vector<std::string>& libraries,
const std::vector<std::string>& modules)
{
for (auto const& exe : executables) {
if (!this->Linker->ScanDependencies(exe, cmStateEnums::EXECUTABLE)) {
return false;
}
}
for (auto const& lib : libraries) {
if (!this->Linker->ScanDependencies(lib, cmStateEnums::SHARED_LIBRARY)) {
return false;
}
}
return std::all_of(
modules.begin(), modules.end(), [this](std::string const& mod) -> bool {
return this->Linker->ScanDependencies(mod, cmStateEnums::MODULE_LIBRARY);
});
}
void cmRuntimeDependencyArchive::SetError(const std::string& e)
{
this->Status.SetError(e);
}
const std::string& cmRuntimeDependencyArchive::GetBundleExecutable() const
{
return this->BundleExecutable;
}
const std::vector<std::string>&
cmRuntimeDependencyArchive::GetSearchDirectories() const
{
return this->SearchDirectories;
}
const std::string& cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool()
const
{
return this->GetMakefile()->GetSafeDefinition(
"CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL");
}
bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand(
const std::string& search, std::vector<std::string>& command) const
{
// First see if it was supplied by the user
std::string toolCommand = this->GetMakefile()->GetSafeDefinition(
"CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND");
if (toolCommand.empty() && search == "objdump") {
toolCommand = this->GetMakefile()->GetSafeDefinition("CMAKE_OBJDUMP");
}
if (!toolCommand.empty()) {
cmExpandList(toolCommand, command);
return true;
}
// Now go searching for it
std::vector<std::string> paths;
#ifdef _WIN32
cmGlobalGenerator* gg = this->GetMakefile()->GetGlobalGenerator();
// Add newer Visual Studio paths
AddVisualStudioPath(paths, "Visual Studio 17 ", 17, gg);
AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg);
AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg);
// Add older Visual Studio paths
AddRegistryPath(
paths,
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/"
"../../VC/bin",
this->GetMakefile());
AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin");
paths.push_back(
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin");
AddRegistryPath(
paths,
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/"
"../../VC/bin",
this->GetMakefile());
AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin");
paths.push_back(
"C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin");
AddRegistryPath(
paths,
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/"
"../../VC/bin",
this->GetMakefile());
AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin");
paths.push_back(
"C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin");
AddRegistryPath(
paths,
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/"
"../../VC/bin",
this->GetMakefile());
AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin");
paths.push_back(
"C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin");
AddRegistryPath(
paths,
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/"
"../../VC/bin",
this->GetMakefile());
AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin");
paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin");
paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin");
AddRegistryPath(
paths,
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/"
"../../VC/bin",
this->GetMakefile());
AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin");
paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN");
paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN");
AddRegistryPath(
paths,
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/"
"../../VC7/bin",
this->GetMakefile());
AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin");
paths.push_back(
"C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN");
paths.push_back(
"C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN");
#endif
std::string program = cmSystemTools::FindProgram(search, paths);
if (!program.empty()) {
command = { program };
return true;
}
// Couldn't find it
return false;
}
bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name) const
{
cmsys::RegularExpressionMatch match;
auto const regexMatch =
[&match, name](const cmsys::RegularExpression& regex) -> bool {
return regex.find(name.c_str(), match);
};
auto const regexSearch =
[&regexMatch](
const std::vector<cmsys::RegularExpression>& regexes) -> bool {
return std::any_of(regexes.begin(), regexes.end(), regexMatch);
};
return !regexSearch(this->PreIncludeRegexes) &&
regexSearch(this->PreExcludeRegexes);
}
bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) const
{
cmsys::RegularExpressionMatch match;
auto const regexMatch =
[&match, name](const cmsys::RegularExpression& regex) -> bool {
return regex.find(name.c_str(), match);
};
auto const regexSearch =
[&regexMatch](
const std::vector<cmsys::RegularExpression>& regexes) -> bool {
return std::any_of(regexes.begin(), regexes.end(), regexMatch);
};
auto const fileMatch = [name](const std::string& file) -> bool {
return cmSystemTools::SameFile(file, name);
};
auto const fileSearch =
[&fileMatch](const std::vector<std::string>& files) -> bool {
return std::any_of(files.begin(), files.end(), fileMatch);
};
return fileSearch(this->PostExcludeFilesStrict) ||
(!(regexSearch(this->PostIncludeRegexes) ||
fileSearch(this->PostIncludeFiles)) &&
(regexSearch(this->PostExcludeRegexes) ||
fileSearch(this->PostExcludeFiles)));
}
void cmRuntimeDependencyArchive::AddResolvedPath(
const std::string& name, const std::string& path, bool& unique,
std::vector<std::string> rpaths)
{
auto it = this->ResolvedPaths.emplace(name, std::set<std::string>{}).first;
unique = true;
for (auto const& other : it->second) {
if (cmSystemTools::SameFile(path, other)) {
unique = false;
break;
}
}
it->second.insert(path);
this->RPaths[path] = std::move(rpaths);
}
void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name)
{
this->UnresolvedPaths.insert(name);
}
cmMakefile* cmRuntimeDependencyArchive::GetMakefile() const
{
return &this->Status.GetMakefile();
}
const std::map<std::string, std::set<std::string>>&
cmRuntimeDependencyArchive::GetResolvedPaths() const
{
return this->ResolvedPaths;
}
const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths()
const
{
return this->UnresolvedPaths;
}
const std::map<std::string, std::vector<std::string>>&
cmRuntimeDependencyArchive::GetRPaths() const
{
return this->RPaths;
}
bool cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
const std::string& platform)
{
static const std::set<std::string> supportedPlatforms = { "Windows", "Linux",
"Darwin" };
return supportedPlatforms.count(platform);
}