From cbfcd534d154c2e6e0f927c15565b505d42afa53 Mon Sep 17 00:00:00 2001 From: SCG82 Date: Tue, 7 Jan 2020 00:44:58 -0800 Subject: [PATCH] create DylibBundler class --- CMakeLists.txt | 2 +- src/Dependency.cpp | 67 +++---- src/Dependency.h | 8 +- src/DylibBundler.cpp | 450 +++++++++++++++++++++++++++++++++---------- src/DylibBundler.h | 53 +++-- src/Settings.cpp | 112 +++-------- src/Settings.h | 125 +++++++----- src/Utils.cpp | 425 +++++++--------------------------------- src/Utils.h | 32 +-- src/main.cpp | 41 ++-- 10 files changed, 632 insertions(+), 683 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e40f231..4460b7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,9 @@ if(NOT CMAKE_BUILD_TYPE) endif() string(REPLACE "-O3" "-O2" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) -#string(REPLACE "-std=c++1z" "-std=c++2a" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) add_compile_options(-pipe -Wall -Wextra -Wpedantic) +add_link_options(-Wl,-dead_strip) include_directories(src) diff --git a/src/Dependency.cpp b/src/Dependency.cpp index 9f222eb..38f9986 100644 --- a/src/Dependency.cpp +++ b/src/Dependency.cpp @@ -10,10 +10,12 @@ #include #endif -#include "Settings.h" #include "Utils.h" -Dependency::Dependency(std::string path, const std::string& dependent_file) : is_framework(false) +Dependency::Dependency(std::string path, const std::string& dependent_file, DylibBundler* db) + : is_framework(false), + db_(db) + { char buffer[PATH_MAX]; rtrim_in_place(path); @@ -21,7 +23,7 @@ Dependency::Dependency(std::string path, const std::string& dependent_file) : is std::string warning_msg; if (isRpath(path)) { - original_file = searchFilenameInRpaths(path, dependent_file); + original_file = db_->searchFilenameInRpaths(path, dependent_file); } else if (realpath(path.c_str(), buffer)) { original_file = buffer; @@ -31,7 +33,7 @@ Dependency::Dependency(std::string path, const std::string& dependent_file) : is original_file = path; } - if (Settings::verboseOutput()) { + if (db_->verboseOutput()) { std::cout<< "** Dependency ctor **" << std::endl; if (path != dependent_file) std::cout << " dependent file: " << dependent_file << std::endl; @@ -50,7 +52,7 @@ Dependency::Dependency(std::string path, const std::string& dependent_file) : is prefix += "/"; // check if this dependency is in /usr/lib, /System/Library, or in ignored list - if (!Settings::isPrefixBundled(prefix)) + if (!db_->isPrefixBundled(prefix)) return; if (original_file.find(".framework") != std::string::npos) { @@ -60,7 +62,7 @@ Dependency::Dependency(std::string path, const std::string& dependent_file) : is std::string framework_name = stripPrefix(framework_root); prefix = filePrefix(framework_root); filename = framework_name + "/" + framework_path; - if (Settings::verboseOutput()) { + if (db_->verboseOutput()) { std::cout << " framework root: " << framework_root << std::endl; std::cout << " framework path: " << framework_path << std::endl; std::cout << " framework name: " << framework_name << std::endl; @@ -69,31 +71,31 @@ Dependency::Dependency(std::string path, const std::string& dependent_file) : is // check if the lib is in a known location if (prefix.empty() || !fileExists(prefix+filename)) { - std::vector search_paths = Settings::searchPaths(); + std::vector search_paths = db_->searchPaths(); if (search_paths.empty()) - initSearchPaths(); + db_->initSearchPaths(); // check if file is contained in one of the paths for (const auto& search_path : search_paths) { if (fileExists(search_path+filename)) { warning_msg += "FOUND " + filename + " in " + search_path + "\n"; prefix = search_path; - Settings::missingPrefixes(true); + db_->missingPrefixes(true); break; } } } - if (!Settings::quietOutput()) + if (!db_->quietOutput()) std::cout << warning_msg; // if the location is still unknown, ask the user for search path - if (!Settings::isPrefixIgnored(prefix) && (prefix.empty() || !fileExists(prefix+filename))) { - if (!Settings::quietOutput()) + if (!db_->isPrefixIgnored(prefix) && (prefix.empty() || !fileExists(prefix+filename))) { + if (!db_->quietOutput()) std::cerr << "\n/!\\ WARNING: Dependency " << filename << " of " << dependent_file << " not found\n"; - if (Settings::verboseOutput()) + if (db_->verboseOutput()) std::cout << " path: " << (prefix+filename) << std::endl; - Settings::missingPrefixes(true); - Settings::addSearchPath(getUserInputDirForFile(filename, dependent_file)); + db_->missingPrefixes(true); + db_->addSearchPath(db_->getUserInputDirForFile(filename, dependent_file)); } new_name = filename; @@ -101,12 +103,12 @@ Dependency::Dependency(std::string path, const std::string& dependent_file) : is std::string Dependency::InnerPath() const { - return Settings::insideLibPath() + new_name; + return db_->insideLibPath() + new_name; } std::string Dependency::InstallPath() const { - return Settings::destFolder() + new_name; + return db_->destFolder() + new_name; } void Dependency::AddSymlink(const std::string& path) @@ -115,12 +117,11 @@ void Dependency::AddSymlink(const std::string& path) symlinks.push_back(path); } -bool Dependency::MergeIfIdentical(Dependency& dependency) +bool Dependency::MergeIfIdentical(Dependency* dependency) { - if (dependency.OriginalFilename() == filename) { - for (const auto& symlink : symlinks) { - dependency.AddSymlink(symlink); - } + if (dependency->OriginalFilename() == filename) { + for (const auto& symlink : symlinks) + dependency->AddSymlink(symlink); return true; } return false; @@ -133,10 +134,10 @@ void Dependency::CopyToBundle() const if (is_framework) { original_path = getFrameworkRoot(original_path); - dest_path = Settings::destFolder() + stripPrefix(original_path); + dest_path = db_->destFolder() + stripPrefix(original_path); } - if (Settings::verboseOutput()) { + if (db_->verboseOutput()) { std::string inner_path = InnerPath(); std::cout << " - original path: " << original_path << std::endl; std::cout << " - inner path: " << inner_path << std::endl; @@ -144,31 +145,31 @@ void Dependency::CopyToBundle() const std::cout << " - install path: " << InstallPath() << std::endl; } - copyFile(original_path, dest_path); + db_->copyFile(original_path, dest_path); if (is_framework) { std::string headers_path = dest_path + std::string("/Headers"); char buffer[PATH_MAX]; if (realpath(rtrim(headers_path).c_str(), buffer)) headers_path = buffer; - deleteFile(headers_path, true); - deleteFile(dest_path + "/*.prl"); + db_->deleteFile(headers_path, true); + db_->deleteFile(dest_path + "/*.prl"); } - changeId(InstallPath(), "@rpath/" + new_name); + db_->changeId(InstallPath(), "@rpath/" + new_name); } void Dependency::FixDependentFile(const std::string& dependent_file) const { - changeInstallName(dependent_file, OriginalPath(), InnerPath()); + db_->changeInstallName(dependent_file, OriginalPath(), InnerPath()); for (const auto& symlink : symlinks) { - changeInstallName(dependent_file, symlink, InnerPath()); + db_->changeInstallName(dependent_file, symlink, InnerPath()); } - if (Settings::missingPrefixes()) { - changeInstallName(dependent_file, filename, InnerPath()); + if (db_->missingPrefixes()) { + db_->changeInstallName(dependent_file, filename, InnerPath()); for (const auto& symlink : symlinks) { - changeInstallName(dependent_file, symlink, InnerPath()); + db_->changeInstallName(dependent_file, symlink, InnerPath()); } } } diff --git a/src/Dependency.h b/src/Dependency.h index 5ff4b6d..73f5b0f 100644 --- a/src/Dependency.h +++ b/src/Dependency.h @@ -6,9 +6,11 @@ #include #include +#include "DylibBundler.h" + class Dependency { public: - Dependency(std::string path, const std::string& dependent_file); + Dependency(std::string path, const std::string& dependent_file, DylibBundler* db); [[nodiscard]] bool IsFramework() const { return is_framework; } @@ -23,7 +25,7 @@ public: // Compare the given dependency with this one. If both refer to the same file, // merge both entries into one and return true. - bool MergeIfIdentical(Dependency& dependency); + bool MergeIfIdentical(Dependency* dependency); void CopyToBundle() const; void FixDependentFile(const std::string& dependent_file) const; @@ -40,6 +42,8 @@ private: // installation std::string new_name; + + DylibBundler* db_; }; #endif diff --git a/src/DylibBundler.cpp b/src/DylibBundler.cpp index 0549dda..3a3ed3f 100644 --- a/src/DylibBundler.cpp +++ b/src/DylibBundler.cpp @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include #ifdef __linux @@ -15,50 +17,48 @@ #endif #include "Dependency.h" -#include "Settings.h" #include "Utils.h" -std::vector deps; -std::map> deps_per_file; -std::map deps_collected; -std::set frameworks; -std::set rpaths; -std::map rpaths_collected; -bool qt_plugins_called = false; - -void addDependency(const std::string& path, const std::string& dependent_file) +DylibBundler::DylibBundler() : qt_plugins_called(false) { - Dependency dependency(path, dependent_file); + +} + +DylibBundler::~DylibBundler() = default; + +void DylibBundler::addDependency(const std::string& path, const std::string& dependent_file) +{ + Dependency* dependency = new Dependency(path, dependent_file, this); // check if this library was already added to |deps| to avoid duplicates bool in_deps = false; for (auto& dep : deps) { - if (dependency.MergeIfIdentical(dep)) + if (dependency->MergeIfIdentical(dep)) in_deps = true; } // check if this library was already added to |deps_per_file[dependent_file]| to avoid duplicates bool in_deps_per_file = false; for (auto& dep : deps_per_file[dependent_file]) { - if (dependency.MergeIfIdentical(dep)) + if (dependency->MergeIfIdentical(dep)) in_deps_per_file = true; } // check if this library is in /usr/lib, /System/Library, or in ignored list - if (!Settings::isPrefixBundled(dependency.Prefix())) + if (!this->isPrefixBundled(dependency->Prefix())) return; - if (!in_deps && dependency.IsFramework()) - frameworks.insert(dependency.OriginalPath()); + if (!in_deps && dependency->IsFramework()) + frameworks.insert(dependency->OriginalPath()); if (!in_deps) deps.push_back(dependency); if (!in_deps_per_file) deps_per_file[dependent_file].push_back(dependency); } -void collectDependencies(const std::string& dependent_file) +void DylibBundler::collectDependencies(const std::string& dependent_file) { - if (deps_collected.find(dependent_file) != deps_collected.end() && Settings::fileHasRpath(dependent_file)) + if (deps_collected.find(dependent_file) != deps_collected.end() && fileHasRpath(dependent_file)) return; std::map cmds_values; @@ -74,8 +74,8 @@ void collectDependencies(const std::string& dependent_file) auto rpath_results = cmds_results[rpath]; for (const auto& rpath_result : rpath_results) { rpaths.insert(rpath_result); - Settings::addRpathForFile(dependent_file, rpath_result); - if (Settings::verboseOutput()) + addRpathForFile(dependent_file, rpath_result); + if (verboseOutput()) std::cout << " rpath: " << rpath_result << std::endl; } rpaths_collected[dependent_file] = true; @@ -85,52 +85,18 @@ void collectDependencies(const std::string& dependent_file) auto dylib_results = cmds_results[dylib]; for (const auto& dylib_result : dylib_results) { // skip system/ignored prefixes - if (Settings::isPrefixBundled(dylib_result)) + if (isPrefixBundled(dylib_result)) addDependency(dylib_result, dependent_file); } deps_collected[dependent_file] = true; } } -//void collectDependencies(const std::string& dependent_file, std::vector& lines) -//{ -// if (deps_collected.find(dependent_file) == deps_collected.end()) -// parseLoadCommands(dependent_file, std::string("LC_LOAD_DYLIB"), std::string("name"), lines); -//} - -//void collectDependencies(const std::string& dependent_file) -//{ -// std::vector lines; -// collectDependencies(dependent_file, lines); -// collectRpaths(dependent_file); -// -// for (const auto& line : lines) { -// if (!Settings::isPrefixBundled(line)) -// continue; // skip system/ignored prefixes -// addDependency(line, dependent_file); -// } -// deps_collected[dependent_file] = true; -//} - -//void collectRpaths(const std::string& filename) -//{ -// if (!Settings::fileHasRpath(filename)) { -// std::vector lines; -// parseLoadCommands(filename, std::string("LC_RPATH"), std::string("path"), lines); -// for (const auto &line : lines) { -// rpaths.insert(line); -// Settings::addRpathForFile(filename, line); -// if (Settings::verboseOutput()) -// std::cout << " rpath: " << line << std::endl; -// } -// } -//} - -void collectSubDependencies() +void DylibBundler::collectSubDependencies() { size_t dep_counter = deps.size(); - if (Settings::verboseOutput()) { - std::cout << "(pre sub) # OF FILES: " << Settings::filesToFixCount() << std::endl; + if (verboseOutput()) { + std::cout << "(pre sub) # OF FILES: " << filesToFixCount() << std::endl; std::cout << "(pre sub) # OF DEPS: " << deps.size() << std::endl; } @@ -138,22 +104,12 @@ void collectSubDependencies() while (true) { deps_size = deps.size(); for (size_t n=0; nOriginalPath(); + if (verboseOutput()) std::cout << " (collect sub deps) original path: " << original_path << std::endl; if (isRpath(original_path)) original_path = searchFilenameInRpaths(original_path); -// std::vector lines; -// collectDependencies(original_path, lines); -// collectRpaths(original_path); -// -// for (const auto& line : lines) { -// if (!Settings::isPrefixBundled(line)) -// continue; // skip system/ignored prefixes -// addDependency(line, original_path); -// } - collectDependencies(original_path); } // if no more dependencies were added on this iteration, stop searching @@ -161,39 +117,39 @@ void collectSubDependencies() break; } - if (Settings::verboseOutput()) { - std::cout << "(post sub) # OF FILES: " << Settings::filesToFixCount() << std::endl; + if (verboseOutput()) { + std::cout << "(post sub) # OF FILES: " << filesToFixCount() << std::endl; std::cout << "(post sub) # OF DEPS: " << deps.size() << std::endl; } - if (Settings::bundleLibs() && Settings::bundleFrameworks()) { + if (bundleLibs() && bundleFrameworks()) { if (!qt_plugins_called || (deps.size() != dep_counter)) bundleQtPlugins(); } } -void changeLibPathsOnFile(const std::string& file_to_fix) +void DylibBundler::changeLibPathsOnFile(const std::string& file_to_fix) { if (deps_collected.find(file_to_fix) == deps_collected.end() || rpaths_collected.find(file_to_fix) == rpaths_collected.end()) collectDependencies(file_to_fix); std::cout << "* Fixing dependencies on " << file_to_fix << "\n"; - std::vector dependencies = deps_per_file[file_to_fix]; + std::vector dependencies = deps_per_file[file_to_fix]; for (auto& dependency : dependencies) { - dependency.FixDependentFile(file_to_fix); + dependency->FixDependentFile(file_to_fix); } } -void fixRpathsOnFile(const std::string& original_file, const std::string& file_to_fix) +void DylibBundler::fixRpathsOnFile(const std::string& original_file, const std::string& file_to_fix) { std::vector rpaths_to_fix; - if (!Settings::fileHasRpath(original_file)) + if (!fileHasRpath(original_file)) return; - rpaths_to_fix = Settings::getRpathsForFile(original_file); + rpaths_to_fix = getRpathsForFile(original_file); for (const auto& rpath_to_fix : rpaths_to_fix) { std::string command = std::string("install_name_tool -rpath "); - command.append(rpath_to_fix).append(" ").append(Settings::insideLibPath()); + command.append(rpath_to_fix).append(" ").append(insideLibPath()); command.append(" ").append(file_to_fix); if (systemp(command) != 0) { std::cerr << "\n\n/!\\ ERROR: An error occured while trying to fix rpath " << rpath_to_fix << " of " << file_to_fix << std::endl; @@ -202,36 +158,36 @@ void fixRpathsOnFile(const std::string& original_file, const std::string& file_t } } -void bundleDependencies() +void DylibBundler::bundleDependencies() { for (const auto& dep : deps) { - dep.Print(); + dep->Print(); } std::cout << "\n"; - if (Settings::verboseOutput()) { + if (verboseOutput()) { std::cout << "rpaths:" << std::endl; for (const auto& rpath : rpaths) { std::cout << "* " << rpath << std::endl; } } // copy & fix up dependencies - if (Settings::bundleLibs()) { + if (bundleLibs()) { createDestDir(); for (const auto& dep : deps) { - dep.CopyToBundle(); - changeLibPathsOnFile(dep.InstallPath()); - fixRpathsOnFile(dep.OriginalPath(), dep.InstallPath()); + dep->CopyToBundle(); + changeLibPathsOnFile(dep->InstallPath()); + fixRpathsOnFile(dep->OriginalPath(), dep->InstallPath()); } } // fix up selected files - const auto files = Settings::filesToFix(); + const auto files = filesToFix(); for (const auto& file : files) { changeLibPathsOnFile(file); fixRpathsOnFile(file, file); } } -void bundleQtPlugins() +void DylibBundler::bundleQtPlugins() { bool qtCoreFound = false; bool qtGuiFound = false; @@ -279,11 +235,11 @@ void bundleQtPlugins() if (!qtCoreFound) return; if (!qt_plugins_called) - createQtConf(Settings::resourcesFolder()); + createQtConf(resourcesFolder()); qt_plugins_called = true; - const auto fixupPlugin = [original_file](const std::string& plugin) { - std::string dest = Settings::pluginsFolder(); + const auto fixupPlugin = [&](const std::string& plugin) { + std::string dest = pluginsFolder(); std::string framework_root = getFrameworkRoot(original_file); std::string prefix = filePrefix(framework_root); std::string qt_prefix = filePrefix(prefix.substr(0, prefix.size()-1)); @@ -293,9 +249,12 @@ void bundleQtPlugins() copyFile(qt_plugins_prefix + plugin, dest); std::vector files = lsDir(dest + plugin+"/"); for (const auto& file : files) { - Settings::addFileToFix(dest + plugin+"/"+file); - collectDependencies(dest + plugin + "/" + file); - changeId(dest + plugin+"/"+file, "@rpath/" + plugin+"/"+file); + std::string suffix = std::string("").append(plugin).append("/").append(file); + std::string file_to_fix = dest.append(suffix); + std::string new_rpath = std::string("@rpath/").append(suffix); + addFileToFix(file_to_fix); + collectDependencies(file_to_fix); + changeId(file_to_fix, new_rpath); } } }; @@ -305,10 +264,10 @@ void bundleQtPlugins() std::string qt_prefix = filePrefix(prefix.substr(0, prefix.size()-1)); std::string qt_plugins_prefix = qt_prefix + "plugins/"; - std::string dest = Settings::pluginsFolder(); + std::string dest = pluginsFolder(); mkdir(dest + "platforms"); copyFile(qt_plugins_prefix + "platforms/libqcocoa.dylib", dest + "platforms"); - Settings::addFileToFix(dest + "platforms/libqcocoa.dylib"); + addFileToFix(dest + "platforms/libqcocoa.dylib"); collectDependencies(dest + "platforms/libqcocoa.dylib"); fixupPlugin("printsupport"); @@ -346,3 +305,300 @@ void bundleQtPlugins() collectSubDependencies(); } + + +std::string DylibBundler::getUserInputDirForFile(const std::string& filename, const std::string& dependent_file) +{ + std::vector search_paths = userSearchPaths(); + for (auto& search_path : search_paths) { + if (!search_path.empty() && search_path[search_path.size()-1] != '/') + search_path += "/"; + if (fileExists(search_path + filename)) { + if (!quietOutput()) { + std::cerr << (search_path + filename) << " was found\n" + << "/!\\ WARNING: dylibbundler MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Check the executable with 'otool -L'\n"; + } + return search_path; + } + } + + while (true) { + if (quietOutput()) + std::cerr << "\n/!\\ WARNING: Dependency " << filename << " of " << dependent_file << " not found\n"; + std::cout << "\nPlease specify the directory where this file is located (or enter 'quit' to abort): "; + fflush(stdout); + + std::string prefix; + std::cin >> prefix; + std::cout << std::endl; + + if (prefix == "quit" || prefix == "exit" || prefix == "abort") + exit(1); + + if (!prefix.empty() && prefix[prefix.size()-1] != '/') + prefix += "/"; + + if (!fileExists(prefix+filename)) { + std::cerr << (prefix+filename) << " does not exist. Try again...\n"; + continue; + } + else { + std::cerr << (prefix+filename) << " was found\n" + << "/!\\ WARNING: dylibbundler MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Check the executable with 'otool -L'\n"; + addUserSearchPath(prefix); + return prefix; + } + } +} + +std::string DylibBundler::searchFilenameInRpaths(const std::string& rpath_file, const std::string& dependent_file) +{ + if (verboseOutput()) { + if (dependent_file != rpath_file) + std::cout << " dependent file: " << dependent_file << std::endl; + std::cout << " dependency: " << rpath_file << std::endl; + } + + std::string fullpath; + std::string suffix = rpath_file.substr(rpath_file.rfind('/')+1); + char fullpath_buffer[PATH_MAX]; + + const 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) { + if (appBundleProvided()) + path = std::regex_replace(path, std::regex("@executable_path/"), executableFolder()); + } + if (dependent_file != rpath_file) { + if (path.find("@loader_path") != std::string::npos) + path = std::regex_replace(path, std::regex("@loader_path/"), file_prefix); + } + if (verboseOutput()) + std::cout << " path to search: " << path << std::endl; + if (realpath(path.c_str(), buffer)) { + fullpath = buffer; + rpathToFullPath(rpath_file, fullpath); + return true; + } + } + else if (path.find("@rpath") != std::string::npos) { + if (appBundleProvided()) { + std::string pathE = std::regex_replace(path, std::regex("@rpath/"), executableFolder()); + if (verboseOutput()) + std::cout << " path to search: " << pathE << std::endl; + if (realpath(pathE.c_str(), buffer)) { + fullpath = buffer; + rpathToFullPath(rpath_file, fullpath); + return true; + } + } + if (dependent_file != rpath_file) { + std::string pathL = std::regex_replace(path, std::regex("@rpath/"), file_prefix); + if (verboseOutput()) + std::cout << " path to search: " << pathL << std::endl; + if (realpath(pathL.c_str(), buffer)) { + fullpath = buffer; + rpathToFullPath(rpath_file, fullpath); + return true; + } + } + } + return false; + }; + + // fullpath previously stored + if (rpathFound(rpath_file)) { + fullpath = getFullPath(rpath_file); + } + else if (!check_path(rpath_file)) { + auto rpaths_for_file = getRpathsForFile(dependent_file); + for (auto rpath : rpaths_for_file) { + if (rpath[rpath.size()-1] != '/') + rpath += "/"; + std::string path = rpath + suffix; + if (verboseOutput()) + std::cout << " trying rpath: " << path << std::endl; + if (check_path(path)) + break; + } + } + + if (fullpath.empty()) { + std::vector search_paths = searchPaths(); + for (const auto& search_path : search_paths) { + if (fileExists(search_path+suffix)) { + if (verboseOutput()) + std::cout << "FOUND " << suffix << " in " << search_path << std::endl; + fullpath = search_path + suffix; + break; + } + } + if (fullpath.empty()) { + if (verboseOutput()) + std::cout << " ** rpath fullpath: not found" << std::endl; + if (!quietOutput()) + std::cerr << "\n/!\\ WARNING: Can't get path for '" << rpath_file << "'\n"; + fullpath = getUserInputDirForFile(suffix, dependent_file) + suffix; + if (quietOutput() && fullpath.empty()) + std::cerr << "\n/!\\ WARNING: Can't get path for '" << rpath_file << "'\n"; + if (realpath(fullpath.c_str(), fullpath_buffer)) + fullpath = fullpath_buffer; + } + else if (verboseOutput()) { + std::cout << " ** rpath fullpath: " << fullpath << std::endl; + } + } + else if (verboseOutput()) { + std::cout << " ** rpath fullpath: " << fullpath << std::endl; + } + + return fullpath; +} + +std::string DylibBundler::searchFilenameInRpaths(const std::string& rpath_file) +{ + return searchFilenameInRpaths(rpath_file, rpath_file); +} + +void DylibBundler::initSearchPaths() +{ + std::string searchPaths; + char *dyldLibPath = std::getenv("DYLD_LIBRARY_PATH"); + if (dyldLibPath != nullptr) + searchPaths = dyldLibPath; + dyldLibPath = std::getenv("DYLD_FALLBACK_FRAMEWORK_PATH"); + if (dyldLibPath != nullptr) { + if (!searchPaths.empty() && searchPaths[searchPaths.size()-1] != ':') + searchPaths += ":"; + searchPaths += dyldLibPath; + } + dyldLibPath = std::getenv("DYLD_FALLBACK_LIBRARY_PATH"); + if (dyldLibPath != nullptr) { + if (!searchPaths.empty() && searchPaths[searchPaths.size()-1] != ':') + searchPaths += ":"; + searchPaths += dyldLibPath; + } + if (!searchPaths.empty()) { + std::stringstream ss(searchPaths); + std::string item; + while (std::getline(ss, item, ':')) { + if (item[item.size()-1] != '/') + item += "/"; + addSearchPath(item); + } + } +} + +void DylibBundler::createDestDir() +{ + std::string dest_folder = destFolder(); + if (verboseOutput()) + std::cout << "Checking output directory " << dest_folder << "\n"; + + bool dest_exists = fileExists(dest_folder); + + if (dest_exists && canOverwriteDir()) { + std::cout << "Erasing old output directory " << dest_folder << "\n"; + std::string command = std::string("rm -r ").append(dest_folder); + if (systemp(command) != 0) { + std::cerr << "\n\n/!\\ ERROR: An error occured while attempting to overwrite destination folder\n"; + exit(1); + } + dest_exists = false; + } + + if (!dest_exists) { + if (canCreateDir()) { + std::cout << "Creating output directory " << dest_folder << "\n\n"; + if (!mkdir(dest_folder)) { + std::cerr << "\n/!\\ ERROR: An error occured while creating " << dest_folder << std::endl; + exit(1); + } + } + else { + std::cerr << "\n\n/!\\ ERROR: Destination folder does not exist. Create it or pass the '-cd' or '-od' flag\n"; + exit(1); + } + } +} + +void DylibBundler::changeId(const std::string& binary_file, const std::string& new_id) +{ + std::string command = std::string("install_name_tool -id ").append(new_id).append(" ").append(binary_file); + if (systemp(command) != 0) { + std::cerr << "\n\nError: An error occured while trying to change identity of library " << binary_file << std::endl; + exit(1); + } +} + +void DylibBundler::changeInstallName(const std::string& binary_file, const std::string& old_name, const std::string& new_name) +{ + std::string command = std::string("install_name_tool -change ").append(old_name).append(" "); + command.append(new_name).append(" ").append(binary_file); + if (systemp(command) != 0) { + std::cerr << "\n\nError: An error occured while trying to fix dependencies of " << binary_file << std::endl; + exit(1); + } +} + +void DylibBundler::copyFile(const std::string& from, const std::string& to) +{ + bool overwrite = canOverwriteFiles(); + if (fileExists(to) && !overwrite) { + std::cerr << "\n\nError: File " << to << " already exists. Remove it or enable overwriting (-of)\n"; + exit(1); + } + + // copy file/directory + std::string overwrite_permission = std::string(overwrite ? "-f " : "-n "); + std::string command = std::string("cp -R ").append(overwrite_permission); + command.append(from).append(" ").append(to); + if (from != to && systemp(command) != 0) { + std::cerr << "\n\nError: An error occured while trying to copy file " << from << " to " << to << std::endl; + exit(1); + } + + // give file/directory write permission + std::string command2 = std::string("chmod -R +w ").append(to); + if (systemp(command2) != 0) { + std::cerr << "\n\nError: An error occured while trying to set write permissions on file " << to << std::endl; + exit(1); + } +} + +void DylibBundler::deleteFile(const std::string& path, bool overwrite) +{ + std::string overwrite_permission = std::string(overwrite ? "-f " : " "); + std::string command = std::string("rm -r ").append(overwrite_permission).append(path); + if (systemp(command) != 0) { + std::cerr << "\n\nError: An error occured while trying to delete " << path << std::endl; + exit(1); + } +} + +void DylibBundler::deleteFile(const std::string& path) +{ + bool overwrite = canOverwriteFiles(); + deleteFile(path, overwrite); +} + +bool DylibBundler::mkdir(const std::string& path) +{ + if (verboseOutput()) + std::cout << "Creating directory " << path << std::endl; + std::string command = std::string("mkdir -p ").append(path); + if (systemp(command) != 0) { + std::cerr << "\n/!\\ ERROR: An error occured while creating " << path << std::endl; + return false; + } + return true; +} + +int DylibBundler::systemp(const std::string& cmd) +{ + if (!quietOutput()) + std::cout << " " << cmd << "\n"; + return system(cmd.c_str()); +} diff --git a/src/DylibBundler.h b/src/DylibBundler.h index f67243b..56b3885 100644 --- a/src/DylibBundler.h +++ b/src/DylibBundler.h @@ -3,25 +3,56 @@ #ifndef DYLIBBUNDLER_DYLIBBUNDLER_H #define DYLIBBUNDLER_DYLIBBUNDLER_H +#include +#include #include #include -void addDependency(const std::string& path, const std::string& dependent_file); +//#include "Dependency.h" +#include "Settings.h" -// fill |lines| with dependencies of |dependent_file| -//void collectDependencies(const std::string& dependent_file, std::vector& lines); -void collectDependencies(const std::string& dependent_file); +class Dependency; -//void collectRpaths(const std::string& filename); +class DylibBundler : public Settings { +public: + DylibBundler(); + ~DylibBundler() override; -// recursively collect each dependency's dependencies -void collectSubDependencies(); + void addDependency(const std::string &path, const std::string &dependent_file); + void collectDependencies(const std::string &dependent_file); + void collectSubDependencies(); + void changeLibPathsOnFile(const std::string &file_to_fix); + void fixRpathsOnFile(const std::string &original_file, const std::string &file_to_fix); + void bundleDependencies(); + void bundleQtPlugins(); -void changeLibPathsOnFile(const std::string& file_to_fix); -void fixRpathsOnFile(const std::string& original_file, const std::string& file_to_fix); + std::string getUserInputDirForFile(const std::string& filename, const std::string& dependent_file); + std::string searchFilenameInRpaths(const std::string& rpath_file, const std::string& dependent_file); + std::string searchFilenameInRpaths(const std::string& rpath_file); -void bundleDependencies(); + // check the same paths the system would search for dylibs + void initSearchPaths(); + void createDestDir(); -void bundleQtPlugins(); + void changeId(const std::string& binary_file, const std::string& new_id); + void changeInstallName(const std::string& binary_file, const std::string& old_name, const std::string& new_name); + + void copyFile(const std::string& from, const std::string& to); + void deleteFile(const std::string& path, bool overwrite); + void deleteFile(const std::string& path); + bool mkdir(const std::string& path); + + // run a command in the system shell (like 'system') but also print the command to stdout + int systemp(const std::string& cmd); + +private: + std::vector deps; + std::map> deps_per_file; + std::map deps_collected; + std::set frameworks; + std::set rpaths; + std::map rpaths_collected; + bool qt_plugins_called; +}; #endif diff --git a/src/Settings.cpp b/src/Settings.cpp index d1804a1..2292c95 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -8,28 +8,27 @@ #include "Utils.h" -namespace Settings { +Settings::Settings() : overwrite_files(false), + overwrite_dir(false), + create_dir(false), + quiet_output(false), + verbose_output(false), + bundle_libs(true), + bundle_frameworks(false), + missing_prefixes(false), + dest_folder_str("./libs/"), + dest_folder_str_app("./Frameworks/"), + inside_path_str("@executable_path/../libs/"), + inside_path_str_app("@executable_path/../Frameworks/") +{ + dest_folder = dest_folder_str; + dest_path = dest_folder; + inside_path = inside_path_str; +} -bool overwrite_files = false; -bool overwrite_dir = false; -bool create_dir = false; -bool quiet_output = false; -bool verbose_output = false; -bool bundle_libs = true; -bool bundle_frameworks = false; +Settings::~Settings() = default; -std::string dest_folder_str = "./libs/"; -std::string dest_folder_str_app = "./Frameworks/"; -std::string dest_folder = dest_folder_str; -std::string dest_path = dest_folder; - -std::string inside_path_str = "@executable_path/../libs/"; -std::string inside_path_str_app = "@executable_path/../Frameworks/"; -std::string inside_path = inside_path_str; - -std::string app_bundle; -std::string appBundle() { return app_bundle; } -void appBundle(std::string path) +void Settings::appBundle(std::string path) { app_bundle = std::move(path); char buffer[PATH_MAX]; @@ -55,10 +54,8 @@ void appBundle(std::string path) if (dest_path[dest_path.size()-1] != '/') dest_path += "/"; } -bool appBundleProvided() { return !app_bundle.empty(); } -std::string destFolder() { return dest_path; } -void destFolder(std::string path) +void Settings::destFolder(std::string path) { dest_path = std::move(path); if (appBundleProvided()) @@ -70,21 +67,15 @@ void destFolder(std::string path) dest_path += "/"; } -std::string insideLibPath() { return inside_path; } -void insideLibPath(std::string p) + +void Settings::insideLibPath(std::string p) { inside_path = std::move(p); if (inside_path[inside_path.size()-1] != '/') inside_path += "/"; } -std::string executableFolder() { return app_bundle + "Contents/MacOS/"; } -std::string frameworksFolder() { return app_bundle + "Contents/Frameworks/"; } -std::string pluginsFolder() { return app_bundle + "Contents/PlugIns/"; } -std::string resourcesFolder() { return app_bundle + "Contents/Resources/"; } - -std::vector files; -void addFileToFix(std::string path) +void Settings::addFileToFix(std::string path) { char buffer[PATH_MAX]; if (realpath(path.c_str(), buffer)) @@ -92,17 +83,14 @@ void addFileToFix(std::string path) files.push_back(path); } -std::vector filesToFix() { return files; } -size_t filesToFixCount() { return files.size(); } - -std::vector prefixes_to_ignore; -void ignorePrefix(std::string prefix) +void Settings::ignorePrefix(std::string prefix) { if (prefix[prefix.size()-1] != '/') prefix += "/"; prefixes_to_ignore.push_back(prefix); } -bool isPrefixIgnored(const std::string& prefix) + +bool Settings::isPrefixIgnored(const std::string& prefix) { for (const auto& prefix_to_ignore : prefixes_to_ignore) { if (prefix == prefix_to_ignore) @@ -111,7 +99,7 @@ bool isPrefixIgnored(const std::string& prefix) return false; } -bool isPrefixBundled(const std::string& prefix) +bool Settings::isPrefixBundled(const std::string& prefix) { if (!bundle_frameworks && prefix.find(".framework") != std::string::npos) return false; @@ -125,49 +113,3 @@ bool isPrefixBundled(const std::string& prefix) return false; return true; } - -std::vector search_paths; -std::vector searchPaths() { return search_paths; } -void addSearchPath(const std::string& path) { search_paths.push_back(path); } - -std::vector user_search_paths; -std::vector userSearchPaths() { return user_search_paths; } -void addUserSearchPath(const std::string& path) { user_search_paths.push_back(path); } - -bool canCreateDir() { return create_dir; } -void canCreateDir(bool permission) { create_dir = permission; } - -bool canOverwriteDir() { return overwrite_dir; } -void canOverwriteDir(bool permission) { overwrite_dir = permission; } - -bool canOverwriteFiles() { return overwrite_files; } -void canOverwriteFiles(bool permission) { overwrite_files = permission; } - -bool bundleLibs() { return bundle_libs; } -void bundleLibs(bool status) { bundle_libs = status; } - -bool bundleFrameworks() { return bundle_frameworks; } -void bundleFrameworks(bool status) { bundle_frameworks = status; } - -bool quietOutput() { return quiet_output; } -void quietOutput(bool status) { quiet_output = status; } - -bool verboseOutput() { return verbose_output; } -void verboseOutput(bool status) { verbose_output = status; } - -// if some libs are missing prefixes, then more stuff will be necessary to do -bool missing_prefixes = false; -bool missingPrefixes() { return missing_prefixes; } -void missingPrefixes(bool status) { missing_prefixes = status; } - -std::map rpath_to_fullpath; -std::string getFullPath(const std::string& rpath) { return rpath_to_fullpath[rpath]; } -void rpathToFullPath(const std::string& rpath, const std::string& fullpath) { rpath_to_fullpath[rpath] = fullpath; } -bool rpathFound(const std::string& rpath) { return rpath_to_fullpath.find(rpath) != rpath_to_fullpath.end(); } - -std::map> rpaths_per_file; -std::vector getRpathsForFile(const std::string& file) { return rpaths_per_file[file]; } -void addRpathForFile(const std::string& file, const std::string& rpath) { rpaths_per_file[file].push_back(rpath); } -bool fileHasRpath(const std::string& file) { return rpaths_per_file.find(file) != rpaths_per_file.end(); } - -} // namespace Settings diff --git a/src/Settings.h b/src/Settings.h index d24676d..6478125 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -3,6 +3,11 @@ #ifndef DYLIBBUNDLER_SETTINGS_H #define DYLIBBUNDLER_SETTINGS_H +#include +#include +#include + +#include #include #include @@ -10,69 +15,97 @@ #include #endif -namespace Settings { +class Settings { +public: + Settings(); + virtual ~Settings(); -bool isPrefixBundled(const std::string& prefix); -bool isPrefixIgnored(const std::string& prefix); -void ignorePrefix(std::string prefix); + virtual bool isPrefixBundled(const std::string &prefix); + virtual bool isPrefixIgnored(const std::string &prefix); + virtual void ignorePrefix(std::string prefix); + virtual std::string appBundle() { return app_bundle; } + virtual void appBundle(std::string path); + virtual bool appBundleProvided() { return !app_bundle.empty(); } + + virtual std::string destFolder() { return dest_path; } + virtual void destFolder(std::string path); + virtual std::string insideLibPath() { return inside_path; } + virtual void insideLibPath(std::string p); + + virtual std::string executableFolder() { return app_bundle + "Contents/MacOS/"; } + virtual std::string frameworksFolder() { return app_bundle + "Contents/Frameworks/"; } + virtual std::string pluginsFolder() { return app_bundle + "Contents/PlugIns/"; } + virtual std::string resourcesFolder() { return app_bundle + "Contents/Resources/"; } + + virtual std::vector filesToFix() { return files; } + virtual void addFileToFix(std::string path); + virtual size_t filesToFixCount() { return files.size(); } -std::string appBundle(); -void appBundle(std::string path); -bool appBundleProvided(); + virtual std::vector searchPaths() { return search_paths; } + virtual void addSearchPath(const std::string& path) { search_paths.push_back(path); } -std::string destFolder(); -void destFolder(std::string path); + virtual std::vector userSearchPaths() { return user_search_paths; } + virtual void addUserSearchPath(const std::string& path) { user_search_paths.push_back(path); } -std::string insideLibPath(); -void insideLibPath(std::string p); + virtual bool canCreateDir() { return create_dir; } + virtual void canCreateDir(bool permission) { create_dir = permission; } -std::string executableFolder(); -std::string frameworksFolder(); -std::string pluginsFolder(); -std::string resourcesFolder(); + virtual bool canOverwriteDir() { return overwrite_dir; } + virtual void canOverwriteDir(bool permission) { overwrite_dir = permission; } -std::vector filesToFix(); -void addFileToFix(std::string path); -size_t filesToFixCount(); + virtual bool canOverwriteFiles() { return overwrite_files; } + virtual void canOverwriteFiles(bool permission) { overwrite_files = permission; } -std::vector searchPaths(); -void addSearchPath(const std::string& path); + virtual bool bundleLibs() { return bundle_libs; } + virtual void bundleLibs(bool status) { bundle_libs = status; } -std::vector userSearchPaths(); -void addUserSearchPath(const std::string& path); + virtual bool bundleFrameworks() { return bundle_frameworks; } + virtual void bundleFrameworks(bool status) { bundle_frameworks = status; } -bool canCreateDir(); -void canCreateDir(bool permission); + virtual bool quietOutput() { return quiet_output; } + virtual void quietOutput(bool status) { quiet_output = status; } -bool canOverwriteDir(); -void canOverwriteDir(bool permission); + virtual bool verboseOutput() { return verbose_output; } + virtual void verboseOutput(bool status) { verbose_output = status; } -bool canOverwriteFiles(); -void canOverwriteFiles(bool permission); + virtual bool missingPrefixes() { return missing_prefixes; } + virtual void missingPrefixes(bool status) { missing_prefixes = status; } -bool bundleLibs(); -void bundleLibs(bool status); + virtual std::string getFullPath(const std::string& rpath) { return rpath_to_fullpath[rpath]; } + virtual void rpathToFullPath(const std::string& rpath, const std::string& fullpath) { rpath_to_fullpath[rpath] = fullpath; } + virtual bool rpathFound(const std::string& rpath) { return rpath_to_fullpath.find(rpath) != rpath_to_fullpath.end(); } -bool bundleFrameworks(); -void bundleFrameworks(bool status); + virtual std::vector getRpathsForFile(const std::string& file) { return rpaths_per_file[file]; } + virtual void addRpathForFile(const std::string& file, const std::string& rpath) { rpaths_per_file[file].push_back(rpath); } + virtual bool fileHasRpath(const std::string& file) { return rpaths_per_file.find(file) != rpaths_per_file.end(); } -bool quietOutput(); -void quietOutput(bool status); +protected: + bool overwrite_files; + bool overwrite_dir; + bool create_dir; + bool quiet_output; + bool verbose_output; + bool bundle_libs; + bool bundle_frameworks; + bool missing_prefixes; -bool verboseOutput(); -void verboseOutput(bool status); + std::string dest_folder_str; + std::string dest_folder_str_app; + std::string dest_folder; + std::string dest_path; -bool missingPrefixes(); -void missingPrefixes(bool status); + std::string inside_path_str; + std::string inside_path_str_app; + std::string inside_path; -std::string getFullPath(const std::string& rpath); -void rpathToFullPath(const std::string& rpath, const std::string& fullpath); -bool rpathFound(const std::string& rpath); + std::string app_bundle; + std::vector prefixes_to_ignore; + std::vector search_paths; + std::vector files; -std::vector getRpathsForFile(const std::string& file); -void addRpathForFile(const std::string& file, const std::string& rpath); -bool fileHasRpath(const std::string& file); - -} // namespace Settings + std::vector user_search_paths; + std::map rpath_to_fullpath; + std::map> rpaths_per_file; +}; #endif diff --git a/src/Utils.cpp b/src/Utils.cpp index 35c3388..66f24b4 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -7,9 +7,7 @@ #include #include -// #include #include -// #include #ifndef __clang__ #include #endif @@ -27,6 +25,13 @@ std::string stripPrefix(const std::string& in) return in.substr(in.rfind('/')+1); } +std::string stripLSlash(const std::string& in) +{ + if (in[0] == '.' && in[1] == '/') + return in.substr(2, in.size()); + return in; +} + std::string getFrameworkRoot(const std::string& in) { return in.substr(0, in.find(".framework")+10); @@ -37,13 +42,6 @@ std::string getFrameworkPath(const std::string& in) return in.substr(in.rfind(".framework/")+11); } -std::string stripLSlash(const std::string& in) -{ - if (in[0] == '.' && in[1] == '/') - return in.substr(2, in.size()); - return in; -} - void rtrim_in_place(std::string& s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char c) { @@ -57,6 +55,59 @@ std::string rtrim(std::string s) return s; } +void tokenize(const std::string& str, const char* delim, std::vector* tokens) +{ + std::vector& out = *tokens; + std::string delimiters(delim); + + // skip delimiters at beginning + std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); + // find first non-delimiter + std::string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (pos != std::string::npos || lastPos != std::string::npos) { + // found a token, add it to the vector + out.push_back(str.substr(lastPos, pos - lastPos)); + // skip delimiters + lastPos = str.find_first_not_of(delimiters, pos); + // find next non-delimiter + pos = str.find_first_of(delimiters, lastPos); + } +} + +std::vector lsDir(const std::string& path) +{ + std::string cmd = "ls " + path; + std::string output = systemOutput(cmd); + std::vector files; + tokenize(output, "\n", &files); + return files; +} + +bool fileExists(const std::string& filename) +{ + if (access(filename.c_str(), F_OK) != -1) + return true; + const std::string delims = " \f\n\r\t\v"; + std::string rtrimmed = filename.substr(0, filename.find_last_not_of(delims)+1); + std::string ftrimmed = rtrimmed.substr(rtrimmed.find_first_not_of(delims)); + if (access(ftrimmed.c_str(), F_OK) != -1) + return true; + return false; +} + +bool isRpath(const std::string& path) +{ + return path.find("@rpath") != std::string::npos || path.find("@loader_path") != std::string::npos; +} + +std::string bundleExecutableName(const std::string& app_bundle_path) +{ + std::string cmd = std::string("/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "); + cmd.append(app_bundle_path).append("Contents/Info.plist"); + return rtrim(systemOutput(cmd)); +} + std::string systemOutput(const std::string& cmd) { FILE *command_output = nullptr; @@ -93,215 +144,9 @@ std::string systemOutput(const std::string& cmd) return full_output; } -int systemp(const std::string& cmd) -{ - if (!Settings::quietOutput()) - std::cout << " " << cmd << "\n"; - return system(cmd.c_str()); -} - -void tokenize(const std::string& str, const char* delim, std::vector* vectorarg) -{ - std::vector& tokens = *vectorarg; - std::string delimiters(delim); - - // skip delimiters at beginning - std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); - // find first non-delimiter - std::string::size_type pos = str.find_first_of(delimiters, lastPos); - - while (pos != std::string::npos || lastPos != std::string::npos) { - // found a token, add it to the vector - tokens.push_back(str.substr(lastPos, pos - lastPos)); - // skip delimiters - lastPos = str.find_first_not_of(delimiters, pos); - // find next non-delimiter - pos = str.find_first_of(delimiters, lastPos); - } -} - -std::vector lsDir(const std::string& path) -{ - std::string cmd = "ls " + path; - std::string output = systemOutput(cmd); - std::vector files; - tokenize(output, "\n", &files); - return files; -} - -bool fileExists(const std::string& filename) -{ - if (access(filename.c_str(), F_OK) != -1) - return true; - std::string delims = " \f\n\r\t\v"; - std::string rtrimmed = filename.substr(0, filename.find_last_not_of(delims)+1); - std::string ftrimmed = rtrimmed.substr(rtrimmed.find_first_not_of(delims)); - if (access(ftrimmed.c_str(), F_OK) != -1) - return true; - return false; -} - -bool isRpath(const std::string& path) -{ - return path.find("@rpath") != std::string::npos || path.find("@loader_path") != std::string::npos; -} - -std::string bundleExecutableName(const std::string& app_bundle_path) -{ - std::string cmd = "/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' " + app_bundle_path + "Contents/Info.plist"; - return rtrim(systemOutput(cmd)); -} - -void changeId(const std::string& binary_file, const std::string& new_id) -{ - std::string command = std::string("install_name_tool -id ") + new_id + " " + binary_file; - if (systemp(command) != 0) { - std::cerr << "\n\nError: An error occured while trying to change identity of library " << binary_file << std::endl; - exit(1); - } -} - -void changeInstallName(const std::string& binary_file, const std::string& old_name, const std::string& new_name) -{ - std::string command = std::string("install_name_tool -change ") + old_name + " " + new_name + " " + binary_file; - if (systemp(command) != 0) { - std::cerr << "\n\nError: An error occured while trying to fix dependencies of " << binary_file << std::endl; - exit(1); - } -} - -void copyFile(const std::string& from, const std::string& to) -{ - bool overwrite = Settings::canOverwriteFiles(); - if (fileExists(to) && !overwrite) { - std::cerr << "\n\nError: File " << to << " already exists. Remove it or enable overwriting (-of)\n"; - exit(1); - } - - // copy file/directory - std::string overwrite_permission = std::string(overwrite ? "-f " : "-n "); - std::string command = std::string("cp -R ") + overwrite_permission + from + std::string(" ") + to; - if (from != to && systemp(command) != 0) { - std::cerr << "\n\nError: An error occured while trying to copy file " << from << " to " << to << std::endl; - exit(1); - } - - // give file/directory write permission - std::string command2 = std::string("chmod -R +w ") + to; - if (systemp(command2) != 0) { - std::cerr << "\n\nError: An error occured while trying to set write permissions on file " << to << std::endl; - exit(1); - } -} - -void deleteFile(const std::string& path, bool overwrite) -{ - std::string overwrite_permission = std::string(overwrite ? "-f " : " "); - std::string command = std::string("rm -r ") + overwrite_permission + path; - if (systemp(command) != 0) { - std::cerr << "\n\nError: An error occured while trying to delete " << path << std::endl; - exit(1); - } -} - -void deleteFile(const std::string& path) -{ - bool overwrite = Settings::canOverwriteFiles(); - deleteFile(path, overwrite); -} - -bool mkdir(const std::string& path) -{ - if (Settings::verboseOutput()) - std::cout << "Creating directory " << path << std::endl; - std::string command = std::string("mkdir -p ") + path; - if (systemp(command) != 0) { - std::cerr << "\n/!\\ ERROR: An error occured while creating " << path << std::endl; - return false; - } - return true; -} - -void createDestDir() -{ - std::string dest_folder = Settings::destFolder(); - if (Settings::verboseOutput()) - std::cout << "Checking output directory " << dest_folder << "\n"; - - bool dest_exists = fileExists(dest_folder); - - if (dest_exists && Settings::canOverwriteDir()) { - std::cout << "Erasing old output directory " << dest_folder << "\n"; - std::string command = std::string("rm -r ") + dest_folder; - if (systemp(command) != 0) { - std::cerr << "\n\n/!\\ ERROR: An error occured while attempting to overwrite destination folder\n"; - exit(1); - } - dest_exists = false; - } - - if (!dest_exists) { - if (Settings::canCreateDir()) { - std::cout << "Creating output directory " << dest_folder << "\n\n"; - if (!mkdir(dest_folder)) { - std::cerr << "\n/!\\ ERROR: An error occured while creating " << dest_folder << std::endl; - exit(1); - } - } - else { - std::cerr << "\n\n/!\\ ERROR: Destination folder does not exist. Create it or pass the '-cd' or '-od' flag\n"; - exit(1); - } - } -} - -std::string getUserInputDirForFile(const std::string& filename, const std::string& dependent_file) -{ - std::vector search_paths = Settings::userSearchPaths(); - for (auto& search_path : search_paths) { - if (!search_path.empty() && search_path[search_path.size() - 1] != '/') - search_path += "/"; - if (fileExists(search_path + filename)) { - if (!Settings::quietOutput()) { - std::cerr << (search_path + filename) << " was found\n" - << "/!\\ WARNING: dylibbundler MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Check the executable with 'otool -L'\n"; - } - return search_path; - } - } - - while (true) { - if (Settings::quietOutput()) - std::cerr << "\n/!\\ WARNING: Dependency " << filename << " of " << dependent_file << " not found\n"; - std::cout << "\nPlease specify the directory where this file is located (or enter 'quit' to abort): "; - fflush(stdout); - - std::string prefix; - std::cin >> prefix; - std::cout << std::endl; - - if (prefix == "quit" || prefix == "exit" || prefix == "abort") - exit(1); - - if (!prefix.empty() && prefix[prefix.size()-1] != '/') - prefix += "/"; - - if (!fileExists(prefix+filename)) { - std::cerr << (prefix+filename) << " does not exist. Try again...\n"; - continue; - } - else { - std::cerr << (prefix+filename) << " was found\n" - << "/!\\ WARNING: dylibbundler MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Check the executable with 'otool -L'\n"; - Settings::addUserSearchPath(prefix); - return prefix; - } - } -} - void otool(const std::string& flags, const std::string& file, std::vector& lines) { - std::string command = "otool " + flags + " " + file; + std::string command = std::string("otool ").append(flags).append(" ").append(file); std::string output = systemOutput(command); if (output.find("can't open file") != std::string::npos @@ -321,8 +166,8 @@ void parseLoadCommands(const std::string& file, const std::string& cmd, const st otool("-l", file, raw_lines); bool searching = false; - std::string cmd_line = std::string("cmd ") + cmd; - std::string value_line = std::string(value + std::string(" ")); + std::string cmd_line = std::string("cmd ").append(cmd); + std::string value_line = std::string(value).append(" "); for (const auto& raw_line : raw_lines) { if (raw_line.find(cmd_line) != std::string::npos) { if (searching) { @@ -358,8 +203,8 @@ void parseLoadCommands(const std::string& file, const std::map lines; std::string cmd = cmd_value.first; std::string value = cmd_value.second; - std::string cmd_line = std::string("cmd ") + cmd; - std::string value_line = std::string(value) + std::string(" "); + std::string cmd_line = std::string("cmd ").append(cmd); + std::string value_line = std::string(value).append(" "); bool searching = false; for (const auto& raw_line : raw_lines) { if (raw_line.find(cmd_line) != std::string::npos) { @@ -388,146 +233,6 @@ void parseLoadCommands(const std::string& file, const std::map search_paths = Settings::searchPaths(); - for (const auto& search_path : search_paths) { - if (fileExists(search_path+suffix)) { - if (Settings::verboseOutput()) - std::cout << "FOUND " << suffix << " in " << search_path << std::endl; - fullpath = search_path + suffix; - break; - } - } - if (fullpath.empty()) { - if (Settings::verboseOutput()) - std::cout << " ** rpath fullpath: not found" << std::endl; - if (!Settings::quietOutput()) - std::cerr << "\n/!\\ WARNING: Can't get path for '" << rpath_file << "'\n"; - fullpath = getUserInputDirForFile(suffix, dependent_file) + suffix; - if (Settings::quietOutput() && fullpath.empty()) - std::cerr << "\n/!\\ WARNING: Can't get path for '" << rpath_file << "'\n"; - if (realpath(fullpath.c_str(), fullpath_buffer)) - fullpath = fullpath_buffer; - } - else if (Settings::verboseOutput()) { - std::cout << " ** rpath fullpath: " << fullpath << std::endl; - } - } - else if (Settings::verboseOutput()) { - std::cout << " ** rpath fullpath: " << fullpath << std::endl; - } - - return fullpath; -} - -std::string searchFilenameInRpaths(const std::string& rpath_file) -{ - return searchFilenameInRpaths(rpath_file, rpath_file); -} - -void initSearchPaths() -{ - std::string searchPaths; - char *dyldLibPath = std::getenv("DYLD_LIBRARY_PATH"); - if (dyldLibPath != nullptr) - searchPaths = dyldLibPath; - dyldLibPath = std::getenv("DYLD_FALLBACK_FRAMEWORK_PATH"); - if (dyldLibPath != nullptr) { - if (!searchPaths.empty() && searchPaths[searchPaths.size()-1] != ':') - searchPaths += ":"; - searchPaths += dyldLibPath; - } - dyldLibPath = std::getenv("DYLD_FALLBACK_LIBRARY_PATH"); - if (dyldLibPath != nullptr) { - if (!searchPaths.empty() && searchPaths[searchPaths.size()-1] != ':') - searchPaths += ":"; - searchPaths += dyldLibPath; - } - if (!searchPaths.empty()) { - std::stringstream ss(searchPaths); - std::string item; - while (std::getline(ss, item, ':')) { - if (item[item.size()-1] != '/') - item += "/"; - Settings::addSearchPath(item); - } - } -} - void createQtConf(std::string directory) { std::string contents = "[Paths]\n" diff --git a/src/Utils.h b/src/Utils.h index bde0bba..d6b2020 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -6,55 +6,33 @@ #include #include #include +#include "DylibBundler.h" std::string filePrefix(const std::string& in); std::string stripPrefix(const std::string& in); +std::string stripLSlash(const std::string& in); std::string getFrameworkRoot(const std::string& in); std::string getFrameworkPath(const std::string& in); -std::string stripLSlash(const std::string& in); - // trim from end (in place) void rtrim_in_place(std::string& s); // trim from end (copying) std::string rtrim(std::string s); - -// execute a command in the native shell and return output in string -std::string systemOutput(const std::string& cmd); -// run a command in the system shell (like 'system') but also print the command to stdout -int systemp(const std::string& cmd); - -void tokenize(const std::string& str, const char* delimiters, std::vector*); +void tokenize(const std::string& str, const char* delimiters, std::vector* tokens); std::vector lsDir(const std::string& path); bool fileExists(const std::string& filename); bool isRpath(const std::string& path); - std::string bundleExecutableName(const std::string& app_bundle_path); -void changeId(const std::string& binary_file, const std::string& new_id); -void changeInstallName(const std::string& binary_file, const std::string& old_name, const std::string& new_name); - -void copyFile(const std::string& from, const std::string& to); -void deleteFile(const std::string& path, bool overwrite); -void deleteFile(const std::string& path); -bool mkdir(const std::string& path); - -void createDestDir(); - -std::string getUserInputDirForFile(const std::string& filename, const std::string& dependent_file); +// execute a command in the native shell and return output in string +std::string systemOutput(const std::string& cmd); void otool(const std::string& flags, const std::string& file, std::vector& lines); void parseLoadCommands(const std::string& file, const std::string& cmd, const std::string& value, std::vector& lines); void parseLoadCommands(const std::string& file, const std::map& cmds_values, std::map>& cmds_results); -std::string searchFilenameInRpaths(const std::string& rpath_file, const std::string& dependent_file); -std::string searchFilenameInRpaths(const std::string& rpath_file); - -// check the same paths the system would search for dylibs -void initSearchPaths(); - void createQtConf(std::string directory); #endif diff --git a/src/main.cpp b/src/main.cpp index 203be8d..d15c891 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,7 +8,6 @@ #endif #include "DylibBundler.h" -#include "Settings.h" const std::string VERSION = "2.1.0 (2020-01-04)"; @@ -36,65 +34,66 @@ void showHelp() int main(int argc, const char* argv[]) { + DylibBundler* db = new DylibBundler(); // parse arguments for (int i=0; iappBundle(argv[i]); continue; } else if (strcmp(argv[i],"-x") == 0 || strcmp(argv[i],"--fix-file") == 0) { i++; - Settings::addFileToFix(argv[i]); + db->addFileToFix(argv[i]); continue; } else if (strcmp(argv[i],"-f") == 0 || strcmp(argv[i],"--bundle-frameworks") == 0) { - Settings::bundleFrameworks(true); + db->bundleFrameworks(true); continue; } else if (strcmp(argv[i],"-d") == 0 || strcmp(argv[i],"--dest-dir") == 0) { i++; - Settings::destFolder(argv[i]); + db->destFolder(argv[i]); continue; } else if (strcmp(argv[i],"-p") == 0 || strcmp(argv[i],"--install-path") == 0) { i++; - Settings::insideLibPath(argv[i]); + db->insideLibPath(argv[i]); continue; } else if (strcmp(argv[i],"-s") == 0 || strcmp(argv[i],"--search-path") == 0) { i++; - Settings::addUserSearchPath(argv[i]); + db->addUserSearchPath(argv[i]); continue; } else if (strcmp(argv[i],"-i") == 0 || strcmp(argv[i],"--ignore") == 0) { i++; - Settings::ignorePrefix(argv[i]); + db->ignorePrefix(argv[i]); continue; } else if (strcmp(argv[i],"-of") == 0 || strcmp(argv[i],"--overwrite-files") == 0) { - Settings::canOverwriteFiles(true); + db->canOverwriteFiles(true); continue; } else if (strcmp(argv[i],"-cd") == 0 || strcmp(argv[i],"--create-dir") == 0) { - Settings::canCreateDir(true); + db->canCreateDir(true); continue; } else if (strcmp(argv[i],"-od") == 0 || strcmp(argv[i],"--overwrite-dir") == 0) { - Settings::canOverwriteDir(true); - Settings::canCreateDir(true); + db->canOverwriteDir(true); + db->canCreateDir(true); continue; } else if (strcmp(argv[i],"-n") == 0 || strcmp(argv[i],"--just-print") == 0) { - Settings::bundleLibs(false); + db->bundleLibs(false); continue; } else if (strcmp(argv[i],"-q") == 0 || strcmp(argv[i],"--quiet") == 0) { - Settings::quietOutput(true); + db->quietOutput(true); continue; } else if (strcmp(argv[i],"-v") == 0 || strcmp(argv[i],"--verbose") == 0) { - Settings::verboseOutput(true); + db->verboseOutput(true); continue; } else if (strcmp(argv[i],"-b") == 0 || strcmp(argv[i],"--bundle-libs") == 0) { @@ -117,19 +116,19 @@ int main(int argc, const char* argv[]) } } - if (Settings::filesToFixCount() < 1) { + if (db->filesToFixCount() < 1) { showHelp(); exit(0); } std::cout << "Collecting dependencies...\n"; - const std::vector files_to_fix = Settings::filesToFix(); + const std::vector files_to_fix = db->filesToFix(); for (const auto& file_to_fix : files_to_fix) { - collectDependencies(file_to_fix); + db->collectDependencies(file_to_fix); } - collectSubDependencies(); - bundleDependencies(); + db->collectSubDependencies(); + db->bundleDependencies(); return 0; }