From 9ae91a71ce3681bff7657a216bf726a81a534cd6 Mon Sep 17 00:00:00 2001 From: SCG82 Date: Sun, 29 Dec 2019 09:07:35 -0800 Subject: [PATCH] use rpaths to locate dependencies --- src/Dependency.cpp | 35 ++++++++------- src/Dependency.h | 6 ++- src/DylibBundler.cpp | 102 +++++++++++++++++++++++++++++++------------ src/Utils.cpp | 4 +- 4 files changed, 101 insertions(+), 46 deletions(-) diff --git a/src/Dependency.cpp b/src/Dependency.cpp index 5f29651..74d17f2 100644 --- a/src/Dependency.cpp +++ b/src/Dependency.cpp @@ -64,11 +64,22 @@ Dependency::Dependency(std::string path, std::string dependent_file) : is_framew std::string original_file; std::string warning_msg; + rtrim_in_place(path); + + if (Settings::verboseOutput()) { + std::cout<< "** Dependency ctor **" << std::endl; + if (path != dependent_file) + std::cout << " dependent file: " << dependent_file << std::endl; + std::cout << " dependency path: " << path << std::endl; + } + if (isRpath(path)) { original_file = searchFilenameInRpaths(path, dependent_file); } - else if (realpath(rtrim(path).c_str(), original_file_buffer)) { + else if (realpath(path.c_str(), original_file_buffer)) { original_file = original_file_buffer; + if (Settings::verboseOutput()) + std::cout << " original_file: " << original_file << std::endl; } else { warning_msg = "\n/!\\ WARNING: Cannot resolve path '" + path + "'\n"; @@ -76,21 +87,17 @@ Dependency::Dependency(std::string path, std::string dependent_file) : is_framew } // check if given path is a symlink - if (original_file != rtrim(path)) { - filename = stripPrefix(original_file); - prefix = filePrefix(original_file); + if (original_file != path) addSymlink(path); - } - else { - filename = stripPrefix(path); - prefix = filePrefix(path); - } + + prefix = filePrefix(original_file); + filename = stripPrefix(original_file); // check if this dependency is in /usr/lib, /System/Library, or in ignored list if (!Settings::isPrefixBundled(prefix)) return; - if (getOriginalPath().find(".framework") != std::string::npos) { + if (original_file.find(".framework") != std::string::npos) { is_framework = true; std::string framework_root = getFrameworkRoot(original_file); std::string framework_path = getFrameworkPath(original_file); @@ -130,11 +137,9 @@ Dependency::Dependency(std::string path, std::string dependent_file) : is_framew // if the location is still unknown, ask the user for search path if (!Settings::isPrefixIgnored(prefix) && (prefix.empty() || !fileExists(prefix+filename))) { if (!Settings::quietOutput()) - std::cerr << "\n/!\\ WARNING: Library " << filename << " has an incomplete name (location unknown)\n"; - if (Settings::verboseOutput()) { - std::cout << "path: " << (prefix+filename) << std::endl; - std::cout << "prefix: " << prefix << std::endl; - } + std::cerr << "\n/!\\ WARNING: Dependency " << filename << " of " << dependent_file << " not found\n"; + if (Settings::verboseOutput()) + std::cout << " path: " << (prefix+filename) << std::endl; missing_prefixes = true; paths.push_back(getUserInputDirForFile(filename)); } diff --git a/src/Dependency.h b/src/Dependency.h index e69b7ba..1c719a3 100644 --- a/src/Dependency.h +++ b/src/Dependency.h @@ -22,8 +22,8 @@ public: std::string getSymlink(int i) const { return symlinks[i]; } std::string getPrefix() const { return prefix; } - // Compares the given dependency with this one. If both refer to the same file, - // it returns true and merges both entries into one. + // Compare the given dependency with this one. If both refer to the same file, + // merge both entries into one and return true. bool mergeIfSameAs(Dependency& dep2); void print(); @@ -33,10 +33,12 @@ public: private: bool is_framework; + // origin std::string filename; std::string prefix; std::vector symlinks; + // installation std::string new_name; }; diff --git a/src/DylibBundler.cpp b/src/DylibBundler.cpp index 7eda60b..5d67145 100644 --- a/src/DylibBundler.cpp +++ b/src/DylibBundler.cpp @@ -22,6 +22,7 @@ std::map deps_collected; std::set frameworks; std::set rpaths; std::map> rpaths_per_file; +std::map rpath_to_fullpath; void initRpaths() { @@ -57,6 +58,7 @@ void collectRpaths(const std::string& filename) std::vector lc_lines; tokenize(output, "\n", &lc_lines); + bool rpath_found = false; if (Settings::verboseOutput()) std::cout << "collecting rpaths for: " << filename << std::endl; @@ -100,28 +102,28 @@ void collectRpathsForFilename(const std::string& filename) std::string searchFilenameInRpaths(const std::string& rpath_file, const std::string& dependent_file) { - char buffer[PATH_MAX]; std::string fullpath; - std::string file_prefix = filePrefix(dependent_file); std::string suffix = rpath_file.substr(rpath_file.rfind("/")+1); + char fullpath_buffer[PATH_MAX]; - for (std::set::iterator it = rpaths.begin(); it != rpaths.end(); ++it) { - std::string rpath = *it; - if (rpath[rpath.size()-1] != '/') - rpath += "/"; - std::string path = rpath + suffix; + if (dependent_file != rpath_file) std::cout << " dependent file: " << dependent_file << std::endl; - std::cout << " dependency: " << rpath_file << std::endl; - std::cout << " rpath: " << path << std::endl; + std::cout << " dependency: " << rpath_file << std::endl; + + auto check_path = [&](std::string path) { + char buffer[PATH_MAX]; + std::string file_prefix = filePrefix(dependent_file); if (path.find("@executable_path") != std::string::npos || path.find("@loader_path") != std::string::npos) { if (path.find("@executable_path") != std::string::npos) path = std::regex_replace(path, std::regex("@executable_path/"), Settings::executableFolder()); - else if (path.find("@loader_path") != std::string::npos) - path = std::regex_replace(path, std::regex("@loader_path/"), file_prefix); + if (dependent_file != rpath_file) + if (path.find("@loader_path") != std::string::npos) + path = std::regex_replace(path, std::regex("@loader_path/"), file_prefix); std::cout << " path to search: " << path << std::endl; if (realpath(path.c_str(), buffer)) { fullpath = buffer; - break; + rpath_to_fullpath[rpath_file] = fullpath; + return true; } } else if (path.find("@rpath") != std::string::npos) { @@ -130,12 +132,35 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str std::cout << " path to search: " << pathE << std::endl; if (realpath(pathE.c_str(), buffer)) { fullpath = buffer; - break; + rpath_to_fullpath[rpath_file] = fullpath; + return true; } - std::cout << " path to search: " << pathL << std::endl; - if (realpath(pathL.c_str(), buffer)) { - fullpath = buffer; - break; + if (dependent_file != rpath_file) { + std::cout << " path to search: " << pathL << std::endl; + if (realpath(pathL.c_str(), buffer)) { + fullpath = buffer; + rpath_to_fullpath[rpath_file] = fullpath; + return true; + } + } + } + return false; + }; + + if (rpath_to_fullpath.find(rpath_file) != rpath_to_fullpath.end()) { + fullpath = rpath_to_fullpath[rpath_file]; + } + else { + if (!check_path(rpath_file)) { + for (auto it = rpaths_per_file[dependent_file].begin(); it != rpaths_per_file[dependent_file].end(); ++it) { + std::string rpath = *it; + if (rpath[rpath.size()-1] != '/') + rpath += "/"; + std::string path = rpath + suffix; + std::cout << " trying rpath: " << path << std::endl; + if (check_path(path)) { + break; + } } } } @@ -143,7 +168,10 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str if (Settings::verboseOutput()) { // std::cout << "rpath file: " << rpath_file << std::endl; // std::cout << "suffix: " << suffix << std::endl; - std::cout << "rpath fullpath: " << fullpath << std::endl; + if (!fullpath.empty()) + std::cout << " ** rpath fullpath: " << fullpath << std::endl; + else + std::cout << " ** rpath fullpath: not found" << std::endl; } if (fullpath.empty()) { @@ -152,8 +180,8 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str fullpath = getUserInputDirForFile(suffix) + suffix; if (Settings::quietOutput() && fullpath.empty()) std::cerr << "\n/!\\ WARNING: Can't get path for '" << rpath_file << "'\n"; - if (realpath(fullpath.c_str(), buffer)) - fullpath = buffer; + if (realpath(fullpath.c_str(), fullpath_buffer)) + fullpath = fullpath_buffer; } return fullpath; @@ -217,7 +245,7 @@ void addDependency(std::string path, std::string dependent_file) // Fill |lines| with dependencies of given |filename| void collectDependencies(std::string dependent_file, std::vector& lines) { - std::string cmd = "otool -L " + dependent_file; + std::string cmd = "otool -l " + dependent_file; std::string output = systemOutput(cmd); if (output.find("can't open file") != std::string::npos @@ -226,8 +254,27 @@ void collectDependencies(std::string dependent_file, std::vector& l std::cerr << "\n\n/!\\ ERROR: Cannot find file " << dependent_file << " to read its dependencies\n"; exit(1); } - // split output - tokenize(output, "\n", &lines); + + std::vector raw_lines; + tokenize(output, "\n", &raw_lines); + + bool searching = false; + for (const auto& line : raw_lines) { + if (line.find("cmd LC_LOAD_DYLIB") != std::string::npos) { + if (searching) { + std::cerr << "Failed to find name before next cmd" << std::endl; + exit(1); + } + searching = true; + } + else if (searching) { + size_t found = line.find("name "); + if (found != std::string::npos) { + lines.push_back('\t' + line.substr(found+5, std::string::npos)); + searching = false; + } + } + } } void collectDependencies(std::string dependent_file) @@ -264,7 +311,7 @@ void collectSubDependencies() for (size_t n=0; n