copy dependencies by default

This commit is contained in:
SCG82 2019-12-29 18:26:24 -08:00
parent b6dc294e66
commit 6348de25a8
5 changed files with 105 additions and 119 deletions

View File

@ -1,4 +1,4 @@
mac dylib bundler
mac dylib bundler v2
================
@ -40,26 +40,24 @@ Application bundle to make self-contained. Fixes the main executable of the app
`-x`, `--fix-file` (executable or plug-in filepath)
<blockquote>
Fixes given executable or plug-in file (ex: .dylib, .so) anything on which `otool -L` works is accepted by `-x`. dylibbundler will walk through the dependencies of the specified file to build a dependency list. It will also fix the said files' dependencies so that it expects to find the libraries relative to itself (e.g. in the app bundle) instead of at an absolute path (e.g. /usr/local/lib). To pass multiple files to fix, simply specify multiple `-x` flags.
Fixes given executable or plug-in file (ex: .dylib, .so). Anything on which `otool -L` works is accepted by `-x`. dylibbundler will walk through the dependencies of the specified file to build a dependency list. It will also fix the said files' dependencies so that it expects to find the libraries relative to itself (e.g. in the app bundle) instead of at an absolute path (e.g. /usr/local/lib). To pass multiple files to fix, simply specify multiple `-x` flags.
</blockquote>
<!--
`-b`, `--bundle-deps`
<blockquote>
Copies libaries to a local directory, fixes their internal name so that they are aware of their new location,
fixes dependencies where bundled libraries depend on each other. If this option is not passed, no libraries will be prepared for distribution.
</blockquote>
-->
`-f`, `--bundle-frameworks`
`-f`, `--frameworks`
<blockquote>
Copies frameworks to a local directory, fixes their internal name so that they are aware of their new location,
fixes dependencies where bundled libraries depend on each other. If this option is not passed, dependencies contained in frameworks will be ignored. dylibbundler will also copy any needed Qt plugins and create qt.conf in the `Resources` directory (no need to run macdeployqt).
Copy framework dependencies to app bundle and fix internal names and rpaths. If this option is not passed, dependencies contained in frameworks will be ignored. dylibbundler will also deploy Qt frameworks & plugins, eliminating the need to use `macdeployqt`.
</blockquote>
`-i`, `--ignore` (path)
> Dylibs in (path) will be ignored. By default, dylibbundler will ignore libraries installed in `/usr/lib` & `/System/Library` since they are assumed to be present by default on all macOS installations. *(It is usually recommend not to install additional stuff in `/usr/`, always use ` /usr/local/` or another prefix to avoid confusion between system libs and libs you added yourself)*
`-d`, `--dest-dir` (directory)
> Sets the name of the directory in wich distribution-ready dylibs will be placed, relative to the current working directory. (Default is `./Frameworks`) For an app bundle, it is often conveniant to set it to something like `./MyApp.app/Contents/Frameworks`.
> Sets the name of the directory in wich distribution-ready dylibs will be placed, relative to `./MyApp.app/Contents/`. (Default is `Frameworks`).
`-p`, `--install-path` (libraries install path)
> Sets the "inner" installation path of libraries, usually inside the bundle and relative to executable. (Default is `@executable_path/../Frameworks/`, which points to a directory named `Frameworks` inside the `Contents` directory of the bundle.)
@ -69,20 +67,29 @@ fixes dependencies where bundled libraries depend on each other. If this option
`-s`, `--search-path` (search path)
> Check for libraries in the specified path.
`-i`, `--ignore` (path)
> Dylibs in (path) will be ignored. By default, dylibbundler will ignore libraries installed in `/usr/lib` & `/System/Library` since they are assumed to be present by default on all macOS installations. *(It is usually recommend not to install additional stuff in `/usr/`, always use ` /usr/local/` or another prefix to avoid confusion between system libs and libs you added yourself)*
`-of`, `--overwrite-files`
> When copying libraries to the output directory, allow overwriting files when one with the same name already exists.
`-od`, `--overwrite-dir`
> If the output directory already exists, completely erase its current content before adding anything to it. (This option implies --create-dir)
`-cd`, `--create-dir`
> If the output directory does not exist, create it.
`-od`, `--overwrite-dir`
> If the output directory already exists, completely erase its current content before adding anything to it. (This option implies --create-dir)
`-n`, `--just-print`
> Print the dependencies found (without copying into app bundle).
`-q`, `--quiet`
> Print only summary information.
> Less verbose output.
`-v`, `--verbose`
> Print extra information (useful for debugging).
> More verbose output (only recommended for debugging).
`-V`, `--version`
> Print dylibbundler version number and exit.
A command may look like
`% dylibbundler -q -od -b -x ./HelloWorld.app/Contents/MacOS/helloworld -d ./HelloWorld.app/Contents/Frameworks/`
`% dylibbundler -cd -of -f -q -a ./HelloWorld.app -x ./HelloWorld.app/Contents/PlugIns/printsupport`

View File

@ -25,16 +25,6 @@ std::map<std::string, std::vector<std::string>> rpaths_per_file;
std::map<std::string, std::string> rpath_to_fullpath;
bool qt_plugins_called = false;
void initRpaths()
{
std::string executable_path = Settings::executableFolder();
std::string frameworks_path = Settings::frameworksFolder();
std::string plugins_path = Settings::pluginsFolder();
rpaths.insert(executable_path);
rpaths.insert(frameworks_path);
rpaths.insert(plugins_path);
}
void changeLibPathsOnFile(std::string file_to_fix)
{
if (deps_collected.find(file_to_fix) == deps_collected.end())
@ -42,7 +32,7 @@ void changeLibPathsOnFile(std::string file_to_fix)
std::cout << "* Fixing dependencies on " << file_to_fix << "\n";
const int dep_amount = deps_per_file[file_to_fix].size();
const size_t dep_amount = deps_per_file[file_to_fix].size();
for (size_t n=0; n<dep_amount; ++n) {
deps_per_file[file_to_fix][n].fixFileThatDependsOnMe(file_to_fix);
}
@ -110,7 +100,7 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str
std::string suffix = rpath_file.substr(rpath_file.rfind("/")+1);
char fullpath_buffer[PATH_MAX];
auto check_path = [&](std::string path) {
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) {
@ -155,31 +145,22 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str
if (rpath_to_fullpath.find(rpath_file) != rpath_to_fullpath.end()) {
fullpath = rpath_to_fullpath[rpath_file];
}
else {
if (!check_path(rpath_file)) {
for (auto it = rpaths_per_file[dependent_file].begin(); it != rpaths_per_file[dependent_file].end(); ++it) {
std::string rpath = *it;
if (rpath[rpath.size()-1] != '/')
rpath += "/";
std::string path = rpath + suffix;
if (Settings::verboseOutput())
std::cout << " trying rpath: " << path << std::endl;
if (check_path(path))
break;
}
}
}
if (Settings::verboseOutput()) {
if (!fullpath.empty()) {
std::cout << " ** rpath fullpath: " << fullpath << std::endl;
}
else {
std::cout << " ** rpath fullpath: not found" << std::endl;
else if (!check_path(rpath_file)) {
for (auto it = rpaths_per_file[dependent_file].begin(); it != rpaths_per_file[dependent_file].end(); ++it) {
std::string rpath = *it;
if (rpath[rpath.size()-1] != '/')
rpath += "/";
std::string path = rpath + suffix;
if (Settings::verboseOutput())
std::cout << " trying rpath: " << path << std::endl;
if (check_path(path))
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;
@ -188,6 +169,9 @@ std::string searchFilenameInRpaths(const std::string& rpath_file, const std::str
if (realpath(fullpath.c_str(), fullpath_buffer))
fullpath = fullpath_buffer;
}
else if (Settings::verboseOutput()) {
std::cout << " ** rpath fullpath: " << fullpath << std::endl;
}
return fullpath;
}
@ -289,21 +273,22 @@ void collectDependencies(std::string dependent_file)
std::vector<std::string> lines;
collectDependencies(dependent_file, lines);
for (size_t n=0; n<lines.size(); n++) {
for (size_t i=0; i<lines.size(); ++i) {
if (!Settings::bundleFrameworks()) {
if (lines[n].find(".framework") != std::string::npos)
if (lines[i].find(".framework") != std::string::npos)
continue;
}
// lines containing path begin with a tab
if (lines[n][0] != '\t')
if (lines[i][0] != '\t')
continue;
// trim useless info, keep only library path
std::string dep_path = lines[n].substr(1, lines[n].rfind(" (") - 1);
std::string dep_path = lines[i].substr(1, lines[i].rfind(" (") - 1);
if (isRpath(dep_path))
collectRpathsForFilename(dependent_file);
addDependency(dep_path, dependent_file);
}
deps_collected[dependent_file] = true;
}
@ -320,7 +305,7 @@ void collectSubDependencies()
while (true) {
deps_size = deps.size();
for (size_t n=0; n<deps_size; n++) {
for (size_t n=0; n<deps_size; ++n) {
std::string original_path = deps[n].getOriginalPath();
if (Settings::verboseOutput())
std::cout << " (collect sub deps) original path: " << original_path << std::endl;
@ -332,16 +317,16 @@ void collectSubDependencies()
std::vector<std::string> lines;
collectDependencies(original_path, lines);
for (size_t n=0; n<lines.size(); ++n) {
for (size_t i=0; i<lines.size(); ++i) {
if (!Settings::bundleFrameworks()) {
if (lines[n].find(".framework") != std::string::npos)
if (lines[i].find(".framework") != std::string::npos)
continue;
}
// lines containing path begin with a tab
if (lines[n][0] != '\t')
if (lines[i][0] != '\t')
continue;
// trim useless info, keep only library name
std::string dep_path = lines[n].substr(1, lines[n].rfind(" (") - 1);
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);
@ -374,24 +359,24 @@ void doneWithDeps_go()
const size_t deps_size = deps.size();
for (size_t n=0; n<deps_size; ++n)
for (size_t n=0; n<deps_size; ++n) {
deps[n].print();
}
std::cout << "\n";
// copy files if requested by user
if (Settings::bundleLibs()) {
createDestDir();
for (size_t i=0; i<deps_size; ++i) {
deps[i].copyYourself();
changeLibPathsOnFile(deps[i].getInstallPath());
fixRpathsOnFile(deps[i].getOriginalPath(), deps[i].getInstallPath());
for (size_t n=0; n<deps_size; ++n) {
deps[n].copyYourself();
changeLibPathsOnFile(deps[n].getInstallPath());
fixRpathsOnFile(deps[n].getOriginalPath(), deps[n].getInstallPath());
}
}
const size_t filesToFixSize = Settings::filesToFix().size();
for (size_t n=0; n<filesToFixSize; ++n) {
changeLibPathsOnFile(Settings::fileToFix(n));
fixRpathsOnFile(Settings::fileToFix(n), Settings::fileToFix(n));
for (size_t j=0; j<filesToFixSize; ++j) {
changeLibPathsOnFile(Settings::fileToFix(j));
fixRpathsOnFile(Settings::fileToFix(j), Settings::fileToFix(j));
}
}
@ -460,7 +445,7 @@ void copyQtPlugins()
std::string dest = Settings::pluginsFolder();
auto fixupPlugin = [original_file,dest](std::string plugin) {
const auto fixupPlugin = [original_file,dest] (std::string plugin) {
std::string framework_root = getFrameworkRoot(original_file);
std::string prefix = filePrefix(framework_root);
std::string qt_prefix = filePrefix(prefix.substr(0, prefix.size()-1));

View File

@ -8,7 +8,7 @@ bool overwrite_dir = false;
bool create_dir = false;
bool quiet_output = false;
bool verbose_output = false;
bool bundle_libs = false;
bool bundle_libs = true;
bool bundle_frameworks = false;
std::string dest_folder_str = "Frameworks";

View File

@ -122,7 +122,7 @@ std::vector<std::string> lsDir(std::string path)
bool fileExists(std::string filename)
{
if (access(filename.c_str(), F_OK) != -1) {
return true; // file exists
return true;
}
else {
std::string delims = " \f\n\r\t\v";
@ -131,7 +131,7 @@ bool fileExists(std::string filename)
if (access(ftrimmed.c_str(), F_OK) != -1)
return true;
else
return false; // file doesn't exist
return false;
}
}
@ -168,14 +168,14 @@ void copyFile(std::string from, std::string to)
std::string overwrite_permission = std::string(overwrite ? "-f " : "-n ");
// copy file to local directory
// copy file/directory
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 << "\n";
exit(1);
}
// give it write permission
// 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 << "\n";

View File

@ -8,40 +8,30 @@
#include "Settings.h"
#include "Utils.h"
/*
TODO
- what happens if a library is not remembered by full path but only name? (support improved, still not perfect)
- could get mixed up if symlink and original are not in the same location (won't happen for UNIX prefixes like /usr/, but in random directories?)
FIXME - why does it copy plugins i try to fix to the libs directory?
FIXME - no memory management is done at all (anyway the program closes immediately so who cares?)
*/
const std::string VERSION = "1.0.0 (2019-12-16)";
std::string installPath = "";
const std::string VERSION = "2.0.0 (2019-12-29)";
void showHelp()
{
std::cout << "Usage: dylibbundler [options] -x file" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -a, --app Application bundle to make self-contained" << std::endl;
std::cout << " -x, --fix-file Object file(s) to bundle dependencies (optional)" << std::endl;
std::cout << " -b, --bundle-deps Copy dependencies to app bundle and fix internal names and rpaths" << std::endl;
std::cout << " -f, --bundle-frameworks Copy dependencies that are frameworks (experimental)" << std::endl;
std::cout << " -x, --fix-file Copy file's dependencies to app bundle and fix internal names and rpaths" << std::endl;
std::cout << " -f, --frameworks Copy dependencies that are frameworks (experimental)" << std::endl;
std::cout << " -d, --dest-dir Directory to copy dependencies, relative to <app>/Contents (default: ./Frameworks)" << std::endl;
std::cout << " -p, --install-path Inner path (@rpath) of bundled dependencies (default: @executable_path/../Frameworks/)" << std::endl;
std::cout << " -s, --search-path Directory to add to list of locations searched" << std::endl;
std::cout << " -s, --search-path Add directory to search path" << std::endl;
std::cout << " -i, --ignore Ignore dependencies in this directory (default: /usr/lib & /System/Library)" << std::endl;
std::cout << " -of, --overwrite-files Allow overwriting files in output directory" << std::endl;
std::cout << " -od, --overwrite-dir Overwrite output directory if it exists (implies --create-dir)" << std::endl;
std::cout << " -cd, --create-dir Create output directory if needed" << std::endl;
std::cout << " -i, --ignore Ignore libraries in this directory (default: /usr/lib & /System/Library)" << std::endl;
std::cout << " -od, --overwrite-dir Overwrite (delete) output directory if it exists (implies --create-dir)" << std::endl;
std::cout << " -n, --just-print Print the dependencies found (without copying into app bundle)" << std::endl;
std::cout << " -q, --quiet Less verbose output" << std::endl;
std::cout << " -v, --verbose More verbose output" << std::endl;
std::cout << " -V, --version Print dylibbundler version number and exit" << std::endl;
std::cout << " -h, --help Print this message and exit" << std::endl;
}
int main (int argc, char * const argv[])
int main(int argc, const char* argv[])
{
// parse arguments
for (int i=0; i<argc; i++) {
@ -55,49 +45,45 @@ int main (int argc, char * const argv[])
Settings::addFileToFix(argv[i]);
continue;
}
else if (strcmp(argv[i],"-b") == 0 || strcmp(argv[i],"--bundle-deps") == 0) {
Settings::bundleLibs(true);
continue;
}
else if (strcmp(argv[i],"-f") == 0 || strcmp(argv[i],"--bundle-frameworks") == 0) {
Settings::bundleFrameworks(true);
continue;
}
else if (strcmp(argv[i],"-p") == 0 || strcmp(argv[i],"--install-path") == 0) {
i++;
Settings::insideLibPath(argv[i]);
continue;
}
else if (strcmp(argv[i],"-i") == 0 || strcmp(argv[i],"--ignore") == 0) {
i++;
Settings::ignorePrefix(argv[i]);
continue;
}
else if (strcmp(argv[i],"-d") == 0 || strcmp(argv[i],"--dest-dir") == 0) {
i++;
Settings::destFolder(argv[i]);
continue;
}
else if (strcmp(argv[i],"-p") == 0 || strcmp(argv[i],"--install-path") == 0) {
i++;
Settings::insideLibPath(argv[i]);
continue;
}
else if (strcmp(argv[i],"-s") == 0 || strcmp(argv[i],"--search-path") == 0) {
i++;
Settings::addSearchPath(argv[i]);
continue;
}
else if (strcmp(argv[i],"-i") == 0 || strcmp(argv[i],"--ignore") == 0) {
i++;
Settings::ignorePrefix(argv[i]);
continue;
}
else if (strcmp(argv[i],"-of") == 0 || strcmp(argv[i],"--overwrite-files") == 0) {
Settings::canOverwriteFiles(true);
continue;
}
else if (strcmp(argv[i],"-cd") == 0 || strcmp(argv[i],"--create-dir") == 0) {
Settings::canCreateDir(true);
continue;
}
else if (strcmp(argv[i],"-od") == 0 || strcmp(argv[i],"--overwrite-dir") == 0) {
Settings::canOverwriteDir(true);
Settings::canCreateDir(true);
continue;
}
else if (strcmp(argv[i],"-cd") == 0 || strcmp(argv[i],"--create-dir") == 0) {
Settings::canCreateDir(true);
continue;
}
else if (strcmp(argv[i],"-h") == 0 || strcmp(argv[i],"--help") == 0) {
showHelp();
exit(0);
}
else if (strcmp(argv[i],"-s") == 0 || strcmp(argv[i],"--search-path") == 0) {
i++;
Settings::addSearchPath(argv[i]);
else if (strcmp(argv[i],"-n") == 0 || strcmp(argv[i],"--just-print") == 0) {
Settings::bundleLibs(false);
continue;
}
else if (strcmp(argv[i],"-q") == 0 || strcmp(argv[i],"--quiet") == 0) {
@ -112,9 +98,16 @@ int main (int argc, char * const argv[])
std::cout << "dylibbundler " << VERSION << std::endl;
exit(0);
}
else if (strcmp(argv[i],"-h") == 0 || strcmp(argv[i],"--help") == 0) {
showHelp();
exit(0);
}
else if (strcmp(argv[i],"-b") == 0 || strcmp(argv[i],"--bundle-libs") == 0) {
// old flag, on by default now. ignore.
continue;
}
else if (i > 0) {
// unknown flag, abort
// ignore first one cause it's usually the path to the executable
std::cerr << "Unknown flag " << argv[i] << std::endl << std::endl;
showHelp();
exit(1);
@ -128,9 +121,10 @@ int main (int argc, char * const argv[])
std::cout << "* Collecting dependencies...\n";
const size_t count = Settings::filesToFixCount();
for (size_t n=0; n<count; ++n)
collectDependencies(Settings::fileToFix(n));
const size_t files_count = Settings::filesToFixCount();
for (size_t j=0; j<files_count; ++j)
collectDependencies(Settings::fileToFix(j));
collectSubDependencies();
doneWithDeps_go();