diff --git a/src/Dependency.cpp b/src/Dependency.cpp index 87a38d5..9f222eb 100644 --- a/src/Dependency.cpp +++ b/src/Dependency.cpp @@ -15,35 +15,33 @@ Dependency::Dependency(std::string path, const std::string& dependent_file) : is_framework(false) { - char original_file_buffer[PATH_MAX]; + char buffer[PATH_MAX]; + rtrim_in_place(path); 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(path.c_str(), original_file_buffer)) { - original_file = original_file_buffer; - if (Settings::verboseOutput()) - std::cout << " original_file: " << original_file << std::endl; + else if (realpath(path.c_str(), buffer)) { + original_file = buffer; } else { warning_msg = "\n/!\\ WARNING: Cannot resolve path '" + path + "'\n"; original_file = 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; + std::cout << " original_file: " << original_file << std::endl; + } + // check if given path is a symlink if (original_file != path) - addSymlink(path); + AddSymlink(path); prefix = filePrefix(original_file); filename = stripPrefix(original_file); @@ -72,10 +70,8 @@ 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(); - // the paths contains at least /usr/lib so if it is empty we have not initialized it if (search_paths.empty()) initSearchPaths(); - // check if file is contained in one of the paths for (const auto& search_path : search_paths) { if (fileExists(search_path+filename)) { @@ -103,45 +99,37 @@ Dependency::Dependency(std::string path, const std::string& dependent_file) : is new_name = filename; } -std::string Dependency::getInstallPath() const -{ - return Settings::destFolder() + new_name; -} - -std::string Dependency::getInnerPath() const +std::string Dependency::InnerPath() const { return Settings::insideLibPath() + new_name; } -void Dependency::print() const +std::string Dependency::InstallPath() const { - std::cout << "\n* " << filename << " from " << prefix << std::endl; - for (const auto& symlink : symlinks) { - std::cout << " symlink --> " << symlink << std::endl; - } + return Settings::destFolder() + new_name; } -void Dependency::addSymlink(const std::string& s) +void Dependency::AddSymlink(const std::string& path) { - if (std::find(symlinks.begin(), symlinks.end(), s) == symlinks.end()) - symlinks.push_back(s); + if (std::find(symlinks.begin(), symlinks.end(), path) == symlinks.end()) + symlinks.push_back(path); } -bool Dependency::mergeIfSameAs(Dependency& dep2) +bool Dependency::MergeIfIdentical(Dependency& dependency) { - if (dep2.getOriginalFileName() == filename) { + if (dependency.OriginalFilename() == filename) { for (const auto& symlink : symlinks) { - dep2.addSymlink(symlink); + dependency.AddSymlink(symlink); } return true; } return false; } -void Dependency::copyToAppBundle() const +void Dependency::CopyToBundle() const { - std::string original_path = getOriginalPath(); - std::string dest_path = getInstallPath(); + std::string original_path = OriginalPath(); + std::string dest_path = InstallPath(); if (is_framework) { original_path = getFrameworkRoot(original_path); @@ -149,11 +137,11 @@ void Dependency::copyToAppBundle() const } if (Settings::verboseOutput()) { - std::string inner_path = getInnerPath(); + std::string inner_path = InnerPath(); std::cout << " - original path: " << original_path << std::endl; std::cout << " - inner path: " << inner_path << std::endl; std::cout << " - dest_path: " << dest_path << std::endl; - std::cout << " - install path: " << getInstallPath() << std::endl; + std::cout << " - install path: " << InstallPath() << std::endl; } copyFile(original_path, dest_path); @@ -167,20 +155,28 @@ void Dependency::copyToAppBundle() const deleteFile(dest_path + "/*.prl"); } - changeId(getInstallPath(), "@rpath/"+new_name); + changeId(InstallPath(), "@rpath/" + new_name); } -void Dependency::fixDependentFiles(const std::string& file) const +void Dependency::FixDependentFile(const std::string& dependent_file) const { - changeInstallName(file, getOriginalPath(), getInnerPath()); + changeInstallName(dependent_file, OriginalPath(), InnerPath()); for (const auto& symlink : symlinks) { - changeInstallName(file, symlink, getInnerPath()); + changeInstallName(dependent_file, symlink, InnerPath()); } if (Settings::missingPrefixes()) { - changeInstallName(file, filename, getInnerPath()); + changeInstallName(dependent_file, filename, InnerPath()); for (const auto& symlink : symlinks) { - changeInstallName(file, symlink, getInnerPath()); + changeInstallName(dependent_file, symlink, InnerPath()); } } } + +void Dependency::Print() const +{ + std::cout << "\n* " << filename << " from " << prefix << std::endl; + for (const auto& symlink : symlinks) { + std::cout << " symlink --> " << symlink << std::endl; + } +} diff --git a/src/Dependency.h b/src/Dependency.h index 2b058b8..5ff4b6d 100644 --- a/src/Dependency.h +++ b/src/Dependency.h @@ -1,7 +1,7 @@ #pragma once -#ifndef _depend_h_ -#define _depend_h_ +#ifndef DYLIBBUNDLER_DEPENDENCY_H +#define DYLIBBUNDLER_DEPENDENCY_H #include #include @@ -10,25 +10,25 @@ class Dependency { public: Dependency(std::string path, const std::string& dependent_file); - [[nodiscard]] bool isFramework() const { return is_framework; } + [[nodiscard]] bool IsFramework() const { return is_framework; } - [[nodiscard]] std::string getPrefix() const { return prefix; } - [[nodiscard]] std::string getOriginalFileName() const { return filename; } - [[nodiscard]] std::string getOriginalPath() const { return prefix + filename; } + [[nodiscard]] std::string Prefix() const { return prefix; } + [[nodiscard]] std::string OriginalFilename() const { return filename; } + [[nodiscard]] std::string OriginalPath() const { return prefix + filename; } - [[nodiscard]] std::string getInstallPath() const; - [[nodiscard]] std::string getInnerPath() const; + [[nodiscard]] std::string InnerPath() const; + [[nodiscard]] std::string InstallPath() const; - void print() const; - - void addSymlink(const std::string& s); + void AddSymlink(const std::string& path); // 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); + bool MergeIfIdentical(Dependency& dependency); - void copyToAppBundle() const; - void fixDependentFiles(const std::string& file) const; + void CopyToBundle() const; + void FixDependentFile(const std::string& dependent_file) const; + + void Print() const; private: bool is_framework; diff --git a/src/DylibBundler.cpp b/src/DylibBundler.cpp index 86c4e5e..0549dda 100644 --- a/src/DylibBundler.cpp +++ b/src/DylibBundler.cpp @@ -23,6 +23,7 @@ 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) @@ -32,71 +33,98 @@ void addDependency(const std::string& path, const std::string& dependent_file) // check if this library was already added to |deps| to avoid duplicates bool in_deps = false; for (auto& dep : deps) { - if (dependency.mergeIfSameAs(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.mergeIfSameAs(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.getPrefix())) + if (!Settings::isPrefixBundled(dependency.Prefix())) return; - if (!in_deps && dependency.isFramework()) - frameworks.insert(dependency.getOriginalPath()); + 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, std::vector& lines) +void collectDependencies(const std::string& dependent_file) { - parseLoadCommands(dependent_file, std::string("LC_LOAD_DYLIB"), std::string("name"), lines); -} + if (deps_collected.find(dependent_file) != deps_collected.end() && Settings::fileHasRpath(dependent_file)) + return; -void collectDependenciesForFile(const std::string& file, std::vector& lines) -{ - if (deps_collected.find(file) == deps_collected.end()) - collectDependencies(file, lines); -} + std::map cmds_values; + std::string dylib = "LC_LOAD_DYLIB"; + std::string rpath = "LC_RPATH"; + cmds_values[dylib] = "name"; + cmds_values[rpath] = "path"; + std::map> cmds_results; -void collectDependenciesForFile(const std::string& dependent_file) -{ - std::vector lines; - collectDependenciesForFile(dependent_file, lines); - collectRpathsForFilename(dependent_file); + parseLoadCommands(dependent_file, cmds_values, cmds_results); - for (const auto& line : lines) { - if (!Settings::isPrefixBundled(line)) - continue; // skip system/ignored prefixes - addDependency(line, dependent_file); + if (rpaths_collected.find(dependent_file) == rpaths_collected.end()) { + 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()) + std::cout << " rpath: " << rpath_result << std::endl; + } + rpaths_collected[dependent_file] = true; } - deps_collected[dependent_file] = true; -} -void collectRpaths(const std::string& 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; + if (deps_collected.find(dependent_file) == deps_collected.end()) { + auto dylib_results = cmds_results[dylib]; + for (const auto& dylib_result : dylib_results) { + // skip system/ignored prefixes + if (Settings::isPrefixBundled(dylib_result)) + addDependency(dylib_result, dependent_file); + } + deps_collected[dependent_file] = true; } } -void collectRpathsForFilename(const std::string& filename) -{ - if (!Settings::fileHasRpath(filename)) - collectRpaths(filename); -} +//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() { @@ -110,21 +138,23 @@ void collectSubDependencies() while (true) { deps_size = deps.size(); for (size_t n=0; n lines; - collectDependenciesForFile(original_path, lines); - collectRpathsForFilename(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); +// } - 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 if (deps.size() == deps_size) @@ -143,28 +173,30 @@ void collectSubDependencies() void changeLibPathsOnFile(const std::string& file_to_fix) { - if (deps_collected.find(file_to_fix) == deps_collected.end()) - collectDependenciesForFile(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"; - const size_t dep_amount = deps_per_file[file_to_fix].size(); - for (size_t n=0; n dependencies = deps_per_file[file_to_fix]; + for (auto& dependency : dependencies) { + dependency.FixDependentFile(file_to_fix); } } void fixRpathsOnFile(const std::string& original_file, const std::string& file_to_fix) { std::vector rpaths_to_fix; - if (Settings::fileHasRpath(original_file)) - rpaths_to_fix = Settings::getRpathsForFile(original_file); + if (!Settings::fileHasRpath(original_file)) + return; - for (const auto& i : rpaths_to_fix) { + rpaths_to_fix = Settings::getRpathsForFile(original_file); + for (const auto& rpath_to_fix : rpaths_to_fix) { std::string command = std::string("install_name_tool -rpath "); - command += i + " " + Settings::insideLibPath() + " " + file_to_fix; + command.append(rpath_to_fix).append(" ").append(Settings::insideLibPath()); + command.append(" ").append(file_to_fix); if (systemp(command) != 0) { - std::cerr << "\n\n/!\\ ERROR: An error occured while trying to fix dependencies of " << file_to_fix << "\n"; + std::cerr << "\n\n/!\\ ERROR: An error occured while trying to fix rpath " << rpath_to_fix << " of " << file_to_fix << std::endl; exit(1); } } @@ -173,21 +205,22 @@ void fixRpathsOnFile(const std::string& original_file, const std::string& file_t void bundleDependencies() { for (const auto& dep : deps) { - dep.print(); + dep.Print(); } std::cout << "\n"; if (Settings::verboseOutput()) { + std::cout << "rpaths:" << std::endl; for (const auto& rpath : rpaths) { - std::cout << "rpaths: " << rpath << std::endl; + std::cout << "* " << rpath << std::endl; } } // copy & fix up dependencies if (Settings::bundleLibs()) { createDestDir(); for (const auto& dep : deps) { - dep.copyToAppBundle(); - changeLibPathsOnFile(dep.getInstallPath()); - fixRpathsOnFile(dep.getOriginalPath(), dep.getInstallPath()); + dep.CopyToBundle(); + changeLibPathsOnFile(dep.InstallPath()); + fixRpathsOnFile(dep.OriginalPath(), dep.InstallPath()); } } // fix up selected files @@ -261,7 +294,7 @@ void bundleQtPlugins() std::vector files = lsDir(dest + plugin+"/"); for (const auto& file : files) { Settings::addFileToFix(dest + plugin+"/"+file); - collectDependenciesForFile(dest + plugin+"/"+file); + collectDependencies(dest + plugin + "/" + file); changeId(dest + plugin+"/"+file, "@rpath/" + plugin+"/"+file); } } @@ -276,7 +309,7 @@ void bundleQtPlugins() mkdir(dest + "platforms"); copyFile(qt_plugins_prefix + "platforms/libqcocoa.dylib", dest + "platforms"); Settings::addFileToFix(dest + "platforms/libqcocoa.dylib"); - collectDependenciesForFile(dest + "platforms/libqcocoa.dylib"); + collectDependencies(dest + "platforms/libqcocoa.dylib"); fixupPlugin("printsupport"); fixupPlugin("styles"); diff --git a/src/DylibBundler.h b/src/DylibBundler.h index 15f4578..f67243b 100644 --- a/src/DylibBundler.h +++ b/src/DylibBundler.h @@ -1,23 +1,18 @@ #pragma once -#ifndef _DYLIB_BUNDLER_H_ -#define _DYLIB_BUNDLER_H_ +#ifndef DYLIBBUNDLER_DYLIBBUNDLER_H +#define DYLIBBUNDLER_DYLIBBUNDLER_H #include #include void addDependency(const std::string& path, 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); - // fill |lines| with dependencies of |dependent_file| -void collectDependencies(const std::string& dependent_file, std::vector& lines); -void collectDependenciesForFile(const std::string& dependent_file, std::vector& lines); -void collectDependenciesForFile(const std::string& dependent_file); +//void collectDependencies(const std::string& dependent_file, std::vector& lines); +void collectDependencies(const std::string& dependent_file); -void collectRpaths(const std::string& filename); -void collectRpathsForFilename(const std::string& filename); +//void collectRpaths(const std::string& filename); // recursively collect each dependency's dependencies void collectSubDependencies(); diff --git a/src/Settings.h b/src/Settings.h index f4b480c..d24676d 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -1,7 +1,7 @@ #pragma once -#ifndef _settings_ -#define _settings_ +#ifndef DYLIBBUNDLER_SETTINGS_H +#define DYLIBBUNDLER_SETTINGS_H #include #include diff --git a/src/Utils.cpp b/src/Utils.cpp index 6bde190..35c3388 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -299,27 +299,32 @@ std::string getUserInputDirForFile(const std::string& filename, const std::strin } } -void parseLoadCommands(const std::string& file, const std::string& cmd, const std::string& value, std::vector& lines) +void otool(const std::string& flags, const std::string& file, std::vector& lines) { - std::string command = "otool -l " + file; + std::string command = "otool " + flags + " " + file; std::string output = systemOutput(command); if (output.find("can't open file") != std::string::npos - || output.find("No such file") != std::string::npos - || output.find("at least one file must be specified") != std::string::npos - || output.empty()) { + || output.find("No such file") != std::string::npos + || output.find("at least one file must be specified") != std::string::npos + || output.empty()) { std::cerr << "\n\n/!\\ ERROR: Cannot find file " << file << " to read its load commands\n"; exit(1); } + tokenize(output, "\n", &lines); +} + +void parseLoadCommands(const std::string& file, const std::string& cmd, const std::string& value, std::vector& lines) +{ std::vector raw_lines; - tokenize(output, "\n", &raw_lines); + 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(" ")); - for (const auto& line : raw_lines) { - if (line.find(cmd_line) != std::string::npos) { + for (const auto& raw_line : raw_lines) { + if (raw_line.find(cmd_line) != std::string::npos) { if (searching) { std::cerr << "\n\n/!\\ ERROR: Failed to find " << value << " before next cmd\n"; exit(1); @@ -327,23 +332,62 @@ void parseLoadCommands(const std::string& file, const std::string& cmd, const st searching = true; } else if (searching) { - size_t start_pos = line.find(value_line); + size_t start_pos = raw_line.find(value_line); if (start_pos == std::string::npos) continue; size_t start = start_pos + value.size() + 1; // exclude data label "|value| " size_t end = std::string::npos; if (value == "name" || value == "path") { - size_t end_pos = line.find(" ("); + size_t end_pos = raw_line.find(" ("); if (end_pos == std::string::npos) continue; end = end_pos - start; } - lines.push_back(line.substr(start, end)); + lines.push_back(raw_line.substr(start, end)); searching = false; } } } +void parseLoadCommands(const std::string& file, const std::map& cmds_values, std::map>& cmds_results) +{ + std::vector raw_lines; + otool("-l", file, raw_lines); + + for (const auto& cmd_value : cmds_values) { + std::vector 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(" "); + bool searching = false; + for (const auto& raw_line : raw_lines) { + if (raw_line.find(cmd_line) != std::string::npos) { + if (searching) { + std::cerr << "\n\n/!\\ ERROR: Failed to find " << value << " before next cmd\n"; + exit(1); + } + searching = true; + } else if (searching) { + size_t start_pos = raw_line.find(value_line); + if (start_pos == std::string::npos) + continue; + size_t start = start_pos + value.size() + 1; // exclude data label "|value| " + size_t end = std::string::npos; + if (value == "name" || value == "path") { + size_t end_pos = raw_line.find(" ("); + if (end_pos == std::string::npos) + continue; + end = end_pos - start; + } + lines.push_back(raw_line.substr(start, end)); + searching = false; + } + } + cmds_results[cmd] = lines; + } +} + std::string searchFilenameInRpaths(const std::string& rpath_file, const std::string& dependent_file) { if (Settings::verboseOutput()) { diff --git a/src/Utils.h b/src/Utils.h index 866b280..bde0bba 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -1,8 +1,9 @@ #pragma once -#ifndef _utils_h_ -#define _utils_h_ +#ifndef DYLIBBUNDLER_UTILS_H +#define DYLIBBUNDLER_UTILS_H +#include #include #include @@ -44,7 +45,9 @@ void createDestDir(); std::string getUserInputDirForFile(const std::string& filename, const std::string& dependent_file); +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); diff --git a/src/main.cpp b/src/main.cpp index 8e62b1e..203be8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -126,7 +126,7 @@ int main(int argc, const char* argv[]) const std::vector files_to_fix = Settings::filesToFix(); for (const auto& file_to_fix : files_to_fix) { - collectDependenciesForFile(file_to_fix); + collectDependencies(file_to_fix); } collectSubDependencies(); bundleDependencies();