From 8f7e98ef09085011e92b3dfeb8aaf60f31bc9270 Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Thu, 30 Sep 2021 15:36:18 -0400 Subject: cmInstallTargetGenerator: optimize rpath adjustments With builds that have many internal library directories or many external libraries, rpaths can be quite large. The cost of calling install_name_tool thousands of times can add up to minutes on a build, especially if virus scanning software is there to help you out. With this change, instead of deleting and then re-adding an rpath, we ignore it. Likewise we batch all the rpath adjustment calls into a single command. Before, installing SCALE (some libraries have 70+ build-time RPATHs that get deleted, plus up to a dozen external RPATHs from upstream dependencies that should remain in the binary) would take almost 9 minutes on my laptop, and after this change the installation takes only 30 second. --- Source/cmInstallTargetGenerator.cxx | 65 ++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 2f974c4..ae11afc 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -2,11 +2,13 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmInstallTargetGenerator.h" +#include #include #include #include #include #include +#include #include "cmComputeLinkInformation.h" #include "cmGeneratorExpression.h" @@ -680,33 +682,52 @@ void cmInstallTargetGenerator::AddChrpathPatchRule( " this limitation."; mf->IssueMessage(MessageType::WARNING, msg.str()); } else { - // Note: These paths are kept unique to avoid - // install_name_tool corruption. - std::set runpaths; - for (std::string const& i : oldRuntimeDirs) { - std::string runpath = - mf->GetGlobalGenerator()->ExpandCFGIntDir(i, config); - - if (runpaths.find(runpath) == runpaths.end()) { - runpaths.insert(runpath); - os << indent << "execute_process(COMMAND " << installNameTool - << "\n"; - os << indent << " -delete_rpath \"" << runpath << "\"\n"; - os << indent << " \"" << toDestDirPath << "\")\n"; + // To be consistent with older versions, runpath changes must be ordered, + // deleted first, then added, *and* the same path must only appear once. + std::map runpath_change; + std::vector ordered; + for (std::string const& dir : oldRuntimeDirs) { + // Normalize path and add to map of changes to make + auto iter_inserted = runpath_change.insert( + { mf->GetGlobalGenerator()->ExpandCFGIntDir(dir, config), + "delete" }); + if (iter_inserted.second) { + // Add path to ordered list of changes + ordered.push_back(iter_inserted.first->first); } } - runpaths.clear(); - for (std::string const& i : newRuntimeDirs) { - std::string runpath = - mf->GetGlobalGenerator()->ExpandCFGIntDir(i, config); + for (std::string const& dir : newRuntimeDirs) { + // Normalize path and add to map of changes to make + auto iter_inserted = runpath_change.insert( + { mf->GetGlobalGenerator()->ExpandCFGIntDir(dir, config), "add" }); + if (iter_inserted.second) { + // Add path to ordered list of changes + ordered.push_back(iter_inserted.first->first); + } else if (iter_inserted.first->second != "add") { + // Rpath was requested to be deleted and then later re-added. Drop it + // from the list by marking as an empty value. + iter_inserted.first->second.clear(); + } + } - if (runpaths.find(runpath) == runpaths.end()) { - os << indent << "execute_process(COMMAND " << installNameTool - << "\n"; - os << indent << " -add_rpath \"" << runpath << "\"\n"; - os << indent << " \"" << toDestDirPath << "\")\n"; + // Remove rpaths that are unchanged (value was set to empty) + ordered.erase( + std::remove_if(ordered.begin(), ordered.end(), + [&runpath_change](const std::string& runpath) { + return runpath_change.find(runpath)->second.empty(); + }), + ordered.end()); + + if (!ordered.empty()) { + os << indent << "execute_process(COMMAND " << installNameTool << "\n"; + for (std::string const& runpath : ordered) { + // Either 'add_rpath' or 'delete_rpath' since we've removed empty + // entries + os << indent << " -" << runpath_change.find(runpath)->second + << "_rpath \"" << runpath << "\"\n"; } + os << indent << " \"" << toDestDirPath << "\")\n"; } } } else { -- cgit v0.12