revise algorithm

This commit is contained in:
SCG82 2020-01-03 05:48:01 -08:00
parent ad8e86a7ea
commit 53e56194fc
7 changed files with 179 additions and 78 deletions

View File

@ -28,7 +28,7 @@ bool qt_plugins_called = false;
void changeLibPathsOnFile(std::string file_to_fix)
{
if (deps_collected.find(file_to_fix) == deps_collected.end())
collectDependencies(file_to_fix);
collectDependenciesForFile(file_to_fix);
std::cout << "* Fixing dependencies on " << file_to_fix << "\n";
@ -47,37 +47,41 @@ void collectRpaths(const std::string& filename)
if (Settings::verboseOutput())
std::cout << " collecting rpaths for: " << filename << std::endl;
size_t pos = 0;
bool read_rpath = false;
std::string cmd = "otool -l " + filename;
std::string output = systemOutput(cmd);
std::vector<std::string> lc_lines;
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.size() < 1) {
std::cerr << "\n\n/!\\ ERROR: Cannot find file " << filename << " to read its rpaths\n";
exit(1);
}
std::vector<std::string> lc_lines;
tokenize(output, "\n", &lc_lines);
while (pos < lc_lines.size()) {
std::string line = lc_lines[pos];
pos++;
if (read_rpath) {
bool searching = false;
for (const auto& line : lc_lines) {
if (line.find("cmd LC_RPATH") != std::string::npos) {
if (searching) {
std::cerr << "\n\n/!\\ ERROR: Failed to find path before next cmd" << std::endl;
exit(1);
}
searching = true;
}
else if (searching) {
size_t start_pos = line.find("path ");
size_t end_pos = line.find(" (");
if (start_pos == std::string::npos || end_pos == std::string::npos) {
std::cerr << "\n/!\\ WARNING: Unexpected LC_RPATH format\n";
if (start_pos == std::string::npos || end_pos == std::string::npos)
continue;
}
start_pos += 5; // to exclude "path "
std::string rpath = line.substr(start_pos, end_pos - start_pos);
rpaths.insert(rpath);
rpaths_per_file[filename].push_back(rpath);
read_rpath = false;
searching = false;
if (Settings::verboseOutput())
std::cout << " rpath: " << rpath << std::endl;
continue;
}
if (line.find("LC_RPATH") != std::string::npos) {
read_rpath = true;
pos++;
}
}
}
@ -104,8 +108,10 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str
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());
if (path.find("@executable_path") != std::string::npos) {
if (Settings::appBundleProvided())
path = std::regex_replace(path, std::regex("@executable_path/"), Settings::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);
@ -119,16 +125,18 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str
}
}
else if (path.find("@rpath") != std::string::npos) {
std::string pathE = std::regex_replace(path, std::regex("@rpath/"), Settings::executableFolder());
std::string pathL = std::regex_replace(path, std::regex("@rpath/"), file_prefix);
if (Settings::verboseOutput())
std::cout << " path to search: " << pathE << std::endl;
if (realpath(pathE.c_str(), buffer)) {
fullpath = buffer;
rpath_to_fullpath[rpath_file] = fullpath;
return true;
if (Settings::appBundleProvided()) {
std::string pathE = std::regex_replace(path, std::regex("@rpath/"), Settings::executableFolder());
if (Settings::verboseOutput())
std::cout << " path to search: " << pathE << std::endl;
if (realpath(pathE.c_str(), buffer)) {
fullpath = buffer;
rpath_to_fullpath[rpath_file] = fullpath;
return true;
}
}
if (dependent_file != rpath_file) {
std::string pathL = std::regex_replace(path, std::regex("@rpath/"), file_prefix);
if (Settings::verboseOutput())
std::cout << " path to search: " << pathL << std::endl;
if (realpath(pathL.c_str(), buffer)) {
@ -159,17 +167,31 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str
}
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) + 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()) {
size_t search_path_count = Settings::searchPathCount();
for (size_t i=0; i<search_path_count; ++i) {
std::string search_path = Settings::searchPath(i);
if (fileExists(search_path+suffix)) {
if (Settings::verboseOutput())
std::cout << "FOUND " + suffix + " in " + search_path + "\n";
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) + 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;
}
@ -233,7 +255,50 @@ void addDependency(std::string path, std::string dependent_file)
deps_per_file[dependent_file].push_back(dep);
}
// Fill |lines| with dependencies of given |filename|
void parseLoadCommands(const std::string& file, std::string cmd, std::string value, std::vector<std::string>& lines)
{
std::string command = "otool -l " + 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.size() < 1) {
std::cerr << "\n\n/!\\ ERROR: Cannot find file " << file << " to read its load commands\n";
exit(1);
}
std::vector<std::string> raw_lines;
tokenize(output, "\n", &raw_lines);
bool searching = false;
for (const auto& line : raw_lines) {
if (line.find("cmd " + cmd) != std::string::npos) {
if (searching) {
std::cerr << "\n\n/!\\ ERROR: Failed to find " << value << " before next cmd" << std::endl;
exit(1);
}
searching = true;
}
else if (searching) {
size_t start_pos = line.find(value + " ");
size_t end_pos = std::string::npos;
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") {
end_pos = line.find(" (");
if (end_pos == std::string::npos)
continue;
end = end_pos - start;
}
lines.push_back('\t' + line.substr(start, end));
searching = false;
}
}
}
void collectDependencies(const std::string& dependent_file, std::vector<std::string>& lines)
{
std::string cmd = "otool -l " + dependent_file;
@ -241,6 +306,7 @@ void collectDependencies(const std::string& dependent_file, std::vector<std::str
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.size() < 1) {
std::cerr << "\n\n/!\\ ERROR: Cannot find file " << dependent_file << " to read its dependencies\n";
exit(1);
@ -260,18 +326,28 @@ void collectDependencies(const std::string& dependent_file, std::vector<std::str
}
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;
}
size_t start_pos = line.find("name ");
size_t end_pos = line.find(" (");
if (start_pos == std::string::npos || end_pos == std::string::npos)
continue;
start_pos += 5; // to exclude "name "
lines.push_back('\t' + line.substr(start_pos, end_pos - start_pos));
searching = false;
}
}
}
void collectDependencies(const std::string& dependent_file)
void collectDependenciesForFile(const std::string& file, std::vector<std::string>& lines)
{
if (deps_collected.find(file) == deps_collected.end())
collectDependencies(file, lines);
}
void collectDependenciesForFile(const std::string& dependent_file)
{
std::vector<std::string> lines;
collectDependencies(dependent_file, lines);
collectDependenciesForFile(dependent_file, lines);
collectRpathsForFilename(dependent_file);
for (size_t i=0; i<lines.size(); ++i) {
// lines containing path begin with a tab
@ -281,16 +357,13 @@ void collectDependencies(const std::string& dependent_file)
continue;
// trim useless info, keep only library path
std::string dep_path = lines[i].substr(1, lines[i].rfind(" (") - 1);
if (isRpath(dep_path))
collectRpathsForFilename(dependent_file);
// if (isRpath(dep_path))
// collectRpathsForFilename(searchFilenameInRpaths(dep_path, dependent_file));
addDependency(dep_path, dependent_file);
}
deps_collected[dependent_file] = true;
}
// recursively collect each dependency's dependencies
void collectSubDependencies()
{
size_t dep_counter = deps.size();
@ -312,7 +385,7 @@ void collectSubDependencies()
collectRpathsForFilename(original_path);
std::vector<std::string> lines;
collectDependencies(original_path, lines);
collectDependenciesForFile(original_path, lines);
for (size_t i=0; i<lines.size(); ++i) {
// lines containing path begin with a tab
@ -323,10 +396,8 @@ void collectSubDependencies()
continue;
// trim useless info, keep only library name
std::string dep_path = lines[i].substr(1, lines[i].rfind(" (") - 1);
if (isRpath(dep_path)) {
std::string full_path = searchFilenameInRpaths(dep_path, original_path);
collectRpathsForFilename(full_path);
}
// if (isRpath(dep_path))
// collectRpathsForFilename(searchFilenameInRpaths(dep_path, original_path));
addDependency(dep_path, original_path);
}
}
@ -354,7 +425,6 @@ void doneWithDeps_go()
}
const size_t deps_size = deps.size();
for (size_t n=0; n<deps_size; ++n) {
deps[n].print();
}
@ -452,7 +522,7 @@ void copyQtPlugins()
std::vector<std::string> files = lsDir(dest + plugin+"/");
for (const auto& file : files) {
Settings::addFileToFix(dest + plugin+"/"+file);
collectDependencies(dest + plugin+"/"+file);
collectDependenciesForFile(dest + plugin+"/"+file);
changeId(dest + plugin+"/"+file, "@rpath/" + plugin+"/"+file);
}
}
@ -466,7 +536,7 @@ void copyQtPlugins()
mkdir(dest + "platforms");
copyFile(qt_plugins_prefix + "platforms/libqcocoa.dylib", dest + "platforms");
Settings::addFileToFix(dest + "platforms/libqcocoa.dylib");
collectDependencies(dest + "platforms/libqcocoa.dylib");
collectDependenciesForFile(dest + "platforms/libqcocoa.dylib");
fixupPlugin("printsupport");
fixupPlugin("styles");

View File

@ -3,19 +3,26 @@
#include <string>
void initRpaths();
void changeLibPathsOnFile(std::string file_to_fix);
void parseLoadCommands(const std::string& file, std::string cmd, std::string value, std::vector<std::string>& lines);
void collectRpaths(const std::string& filename);
void collectRpathsForFilename(const std::string& filename);
std::string searchFilenameInRpaths(const std::string& rpath_dep, 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 fixRpathsOnFile(const std::string& original_file, const std::string& file_to_fix);
void addDependency(std::string path, std::string dependent_file);
// fill |lines| with dependencies of |dependent_file|
void collectDependencies(const std::string& dependent_file, std::vector<std::string>& lines);
void collectDependencies(const std::string& dependent_file);
void collectDependenciesForFile(const std::string& dependent_file, std::vector<std::string>& lines);
void collectDependenciesForFile(const std::string& dependent_file);
// recursively collect each dependency's dependencies
void collectSubDependencies();
void doneWithDeps_go();

View File

@ -23,24 +23,21 @@ std::string inside_path = inside_path_str;
std::string app_bundle;
bool appBundleProvided() { return !app_bundle.empty(); }
std::string appBundle() { return app_bundle; }
void appBundle(std::string path) {
void appBundle(std::string path)
{
app_bundle = path;
if (app_bundle[app_bundle.size()-1] != '/')
app_bundle += "/"; // fix path if needed so it ends with '/'
std::string cmd = "/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' ";
cmd += app_bundle + "Contents/Info.plist";
std::string bundle_executable = systemOutput(cmd);
addFileToFix(app_bundle + "Contents/MacOS/" + bundle_executable);
addFileToFix(app_bundle + "Contents/MacOS/" + bundleExecutableName(app_bundle));
if (inside_path == inside_path_str)
inside_path = inside_path_str_app;
if (dest_folder == dest_folder_str)
dest_folder = dest_folder_str_app;
dest_path = app_bundle + "Contents/" + dest_folder;
char buffer[PATH_MAX];
dest_path = app_bundle + "Contents/" + stripLSlash(dest_folder);
if (realpath(dest_path.c_str(), buffer))
dest_path = buffer;
if (dest_path[dest_path.size()-1] != '/')
@ -55,12 +52,11 @@ void destFolder(std::string path)
dest_folder = path;
if (appBundleProvided()) {
char buffer[PATH_MAX];
std::string dest_path = app_bundle + "Contents/" + path;
std::string dest_path = app_bundle + "Contents/" + stripLSlash(path);
if (realpath(dest_path.c_str(), buffer))
dest_path = buffer;
if (dest_path[dest_path.size()-1] != '/')
dest_path += "/";
dest_folder = dest_path;
}
}
@ -120,6 +116,11 @@ void addSearchPath(std::string path) { searchPaths.push_back(path); }
size_t searchPathCount() { return searchPaths.size(); }
std::string searchPath(const int n) { return searchPaths[n]; }
std::vector<std::string> userSearchPaths;
void addUserSearchPath(std::string path) { userSearchPaths.push_back(path); }
size_t userSearchPathCount() { return userSearchPaths.size(); }
std::string userSearchPath(const int n) { return userSearchPaths[n]; }
bool canCreateDir() { return create_dir; }
void canCreateDir(bool permission) { create_dir = permission; }

View File

@ -10,6 +10,7 @@ bool isPrefixBundled(std::string prefix);
bool isPrefixIgnored(std::string prefix);
void ignorePrefix(std::string prefix);
bool appBundleProvided();
std::string appBundle();
void appBundle(std::string path);
@ -33,6 +34,10 @@ void addSearchPath(std::string path);
size_t searchPathCount();
std::string searchPath(int n);
void addUserSearchPath(std::string path);
size_t userSearchPathCount();
std::string userSearchPath(int n);
bool canCreateDir();
void canCreateDir(bool permission);

View File

@ -32,6 +32,13 @@ std::string getFrameworkPath(std::string in)
return in.substr(in.rfind(".framework/")+11);
}
std::string stripLSlash(std::string in)
{
if (in[0] == '.' && in[1] == '/')
in = in.substr(2, in.size());
return in;
}
// trim from end (in place)
void rtrim_in_place(std::string& s)
{
@ -141,6 +148,13 @@ bool isRpath(const std::string& path)
return path.find("@rpath") == 0 || path.find("@loader_path") == 0;
}
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 systemOutput(cmd);
}
void changeId(std::string binary_file, std::string new_id)
{
std::string command = std::string("install_name_tool -id ") + new_id + " " + binary_file;
@ -247,9 +261,9 @@ void createDestDir()
std::string getUserInputDirForFile(const std::string& filename)
{
const size_t searchPathCount = Settings::searchPathCount();
const size_t searchPathCount = Settings::userSearchPathCount();
for (size_t n=0; n<searchPathCount; ++n) {
auto searchPath = Settings::searchPath(n);
auto searchPath = Settings::userSearchPath(n);
if (!searchPath.empty() && searchPath[searchPath.size()-1] != '/')
searchPath += "/";
if (!fileExists(searchPath+filename)) {
@ -287,6 +301,7 @@ std::string getUserInputDirForFile(const std::string& filename)
else {
std::cerr << (prefix+filename) << " was found\n"
<< "/!\\ WARNINGS: dylibbundler MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Check the executable with 'otool -L'\n";
Settings::addUserSearchPath(prefix);
return prefix;
}
}
@ -294,7 +309,6 @@ std::string getUserInputDirForFile(const std::string& filename)
void initSearchPaths()
{
// check the same paths the system would search for dylibs
std::string searchPaths;
char *dyldLibPath = std::getenv("DYLD_LIBRARY_PATH");
if (dyldLibPath != 0)

View File

@ -12,6 +12,8 @@ std::string stripPrefix(std::string in);
std::string getFrameworkRoot(std::string in);
std::string getFrameworkPath(std::string in);
std::string stripLSlash(std::string in);
// trim from end (in place)
void rtrim_in_place(std::string& s);
// trim from end (copying)
@ -28,6 +30,8 @@ std::vector<std::string> lsDir(std::string path);
bool fileExists(std::string filename);
bool isRpath(const std::string& path);
std::string bundleExecutableName(const std::string& app_bundle_path);
void changeId(std::string binary_file, std::string new_id);
void changeInstallName(std::string binary_file, std::string old_name, std::string new_name);

View File

@ -12,7 +12,7 @@ const std::string VERSION = "2.0.0 (2019-12-29)";
void showHelp()
{
std::cout << "Usage: dylibbundler [options] -x file" << std::endl;
std::cout << "Usage: dylibbundler -a file.app [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -a, --app Application bundle to make self-contained" << std::endl;
std::cout << " -x, --fix-file Copy file's dependencies to app bundle and fix internal names and rpaths" << std::endl;
@ -61,7 +61,7 @@ int main(int argc, const char* argv[])
}
else if (strcmp(argv[i],"-s") == 0 || strcmp(argv[i],"--search-path") == 0) {
i++;
Settings::addSearchPath(argv[i]);
Settings::addUserSearchPath(argv[i]);
continue;
}
else if (strcmp(argv[i],"-i") == 0 || strcmp(argv[i],"--ignore") == 0) {
@ -124,7 +124,7 @@ int main(int argc, const char* argv[])
const size_t files_count = Settings::filesToFixCount();
for (size_t j=0; j<files_count; ++j)
collectDependencies(Settings::fileToFix(j));
collectDependenciesForFile(Settings::fileToFix(j));
collectSubDependencies();
doneWithDeps_go();