App bundle arg, improved rpath search algorithm, handle frameworks

This commit is contained in:
SCG82 2020-01-02 17:15:19 -08:00
parent c589a4fcaf
commit ba7a43c80c
11 changed files with 728 additions and 266 deletions

View File

@ -1,6 +1,6 @@
DESTDIR=
PREFIX=/usr/local
CXXFLAGS = -O2
CXXFLAGS = -O2 -std=c++17
all: dylibbundler

View File

@ -42,6 +42,11 @@ Here is a list of flags you can pass to dylibbundler on the terminal.
displays a summary of options
</blockquote>
`-a`, `--app` (path to app bundle)
<blockquote>
Application bundle to make self-contained. Fixes the main executable of the app bundle. Add additional binary files to fix up with the `-x` flag.
</blockquote>
`-x`, `--fix-file` (executable or plug-in filepath)
<blockquote>
Fixes given executable or plug-in file (a .dylib can work too. 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.
@ -53,14 +58,19 @@ Copies libaries to a local directory, fixes their internal name so that they are
fixes dependencies where bundled libraries depend on each other. If this option is not passed, no libraries will be prepared for distribution.
</blockquote>
`-f`, `--frameworks`
<blockquote>
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.
</blockquote>
`-i`, `--ignore` (path)
> Dylibs in (path) will be ignored. By default, dylibbundler will ignore libraries installed in `/usr/lib` since they are assumed to be present by default on all OS X 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 `./libs`) For an app bundle, it is often conveniant to set it to something like `./MyApp.app/Contents/libs`.
> Sets the name of the directory in wich distribution-ready dylibs will be placed, relative to the current working directory. (Default is either `./libs` or `./MyApp.app/Contents/Frameworks` if using the `-a` flag for an app bundle.
`-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/../libs/`, which points to a directory named `libs` inside the `Contents` directory of the bundle.)
> Sets the "inner" installation path of libraries, usually inside the bundle and relative to executable. (Default is either `@executable_path/../libs/`, which points to a directory named `libs` inside the `Contents` directory of the bundle, or `@executable_path/../Frameworks/` if using the `-a` flag.)
`-s`, `--search-path` (search path)
> Check for libraries in the specified path
@ -77,7 +87,7 @@ fixes dependencies where bundled libraries depend on each other. If this option
> If the output directory does not exist, create it.
A command may look like
`% dylibbundler -od -b -x ./HelloWorld.app/Contents/MacOS/helloworld -d ./HelloWorld.app/Contents/libs/`
`% dylibbundler -cd -b -f -a ./HelloWorld.app -x ./HelloWorld.app/Contents/PlugIns/printsupport`
If you want to create a universal binary by merging toghether two builds from PPC and Intel machines, you can ease it up by putting the ppc and intel libs in different directories, then to create the universal binary you only have to lipo the executable.

View File

@ -39,66 +39,22 @@ THE SOFTWARE.
#include <sstream>
#include <vector>
std::string stripPrefix(std::string in)
{
return in.substr(in.rfind("/")+1);
}
std::string& rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
//the paths to search for dylibs, store it globally to parse the environment variables only once
std::vector<std::string> paths;
//initialize the dylib search paths
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 )
searchPaths = dyldLibPath;
dyldLibPath = std::getenv("DYLD_FALLBACK_FRAMEWORK_PATH");
if (dyldLibPath != 0)
{
if (!searchPaths.empty() && searchPaths[ searchPaths.size()-1 ] != ':') searchPaths += ":";
searchPaths += dyldLibPath;
}
dyldLibPath = std::getenv("DYLD_FALLBACK_LIBRARY_PATH");
if (dyldLibPath!=0 )
{
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 += "/";
paths.push_back(item);
}
}
}
// if some libs are missing prefixes, this will be set to true
// more stuff will then be necessary to do
bool missing_prefixes = false;
Dependency::Dependency(std::string path)
Dependency::Dependency(std::string path, std::string dependent_file) : is_framework(false)
{
char original_file_buffer[PATH_MAX];
std::string original_file;
std::string warning_msg;
rtrim_in_place(path);
if (isRpath(path))
{
original_file = searchFilenameInRpaths(path);
original_file = searchFilenameInRpaths(path, dependent_file);
}
else if (not realpath(rtrim(path).c_str(), original_file_buffer))
else if (not realpath(path.c_str(), original_file_buffer))
{
std::cerr << "\n/!\\ WARNING : Cannot resolve path '" << path.c_str() << "'" << std::endl;
warning_msg = "\n/!\\ WARNING : Cannot resolve path '" + path + "'\n";
original_file = path;
}
else
@ -107,72 +63,85 @@ Dependency::Dependency(std::string path)
}
// check if given path is a symlink
if (original_file != rtrim(path))
if (original_file != path) addSymlink(path);
prefix = filePrefix(original_file);
filename = stripPrefix(original_file);
if (!prefix.empty() && prefix[ prefix.size()-1 ] != '/') prefix += "/";
// check if this dependency is in /usr/lib, /System/Library, or in ignored list
if (!Settings::isPrefixBundled(prefix)) return;
if (original_file.find(".framework") != std::string::npos)
{
filename = stripPrefix(original_file);
prefix = original_file.substr(0, original_file.rfind("/")+1);
addSymlink(path);
is_framework = true;
std::string framework_root = getFrameworkRoot(original_file);
std::string framework_path = getFrameworkPath(original_file);
std::string framework_name = stripPrefix(framework_root);
prefix = filePrefix(framework_root);
filename = framework_name + "/" + framework_path;
}
else
{
filename = stripPrefix(path);
prefix = path.substr(0, path.rfind("/")+1);
}
//check if the lib is in a known location
if( !prefix.empty() && prefix[ prefix.size()-1 ] != '/' ) prefix += "/";
if( prefix.empty() || !fileExists( prefix+filename ) )
if (prefix.empty() || !fileExists(prefix+filename))
{
//the paths contains at least /usr/lib so if it is empty we have not initialized it
if( paths.empty() ) initSearchPaths();
size_t search_path_count = Settings::searchPathAmount();
if (search_path_count == 0) initSearchPaths();
//check if file is contained in one of the paths
for( size_t i=0; i<paths.size(); ++i)
search_path_count = Settings::searchPathAmount();
for(size_t i=0; i<search_path_count; ++i)
{
if (fileExists( paths[i]+filename ))
std::string search_path = Settings::searchPath(i);
if (fileExists(search_path+filename))
{
std::cout << "FOUND " << filename << " in " << paths[i] << std::endl;
prefix = paths[i];
missing_prefixes = true; //the prefix was missing
warning_msg += "FOUND " + filename + " in " + search_path + "\n";
prefix = search_path;
Settings::missingPrefixes(true); //the prefix was missing
break;
}
}
}
if (!Settings::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::isPrefixIgnored(prefix) && ( prefix.empty() || !fileExists(prefix+filename) ) )
{
std::cerr << "\n/!\\ WARNING : Library " << filename << " has an incomplete name (location unknown)" << std::endl;
missing_prefixes = true;
paths.push_back(getUserInputDirForFile(filename));
if (!Settings::quietOutput())
{
std::cerr << "\n/!\\ WARNING: Dependency " << filename << " of " << dependent_file << " not found\n";
}
Settings::missingPrefixes(true);
Settings::addSearchPath(getUserInputDirForFile(filename, dependent_file));
}
//new_name = filename.substr(0, filename.find(".")) + ".dylib";
new_name = filename;
}
void Dependency::print()
{
std::cout << std::endl;
std::cout << " * " << filename.c_str() << " from " << prefix.c_str() << std::endl;
std::cout << "\n* " << filename.c_str() << " from " << prefix.c_str() << std::endl;
const int symamount = symlinks.size();
for(int n=0; n<symamount; n++)
std::cout << " symlink --> " << symlinks[n].c_str() << std::endl;;
{
std::cout << " symlink --> " << symlinks[n].c_str() << std::endl;
}
}
std::string Dependency::getInstallPath()
{
return Settings::destFolder() + new_name;
}
std::string Dependency::getInnerPath()
{
return Settings::inside_lib_path() + new_name;
}
void Dependency::addSymlink(std::string s)
{
// calling std::find on this vector is not near as slow as an extra invocation of install_name_tool
@ -183,10 +152,11 @@ void Dependency::addSymlink(std::string s)
// it returns true and merges both entries into one.
bool Dependency::mergeIfSameAs(Dependency& dep2)
{
if(dep2.getOriginalFileName().compare(filename) == 0)
if (dep2.getOriginalFileName().compare(filename) == 0)
{
const int samount = getSymlinkAmount();
for(int n=0; n<samount; n++) {
for(int n=0; n<samount; n++)
{
dep2.addSymlink(getSymlink(n));
}
return true;
@ -196,69 +166,51 @@ bool Dependency::mergeIfSameAs(Dependency& dep2)
void Dependency::copyYourself()
{
copyFile(getOriginalPath(), getInstallPath());
// Fix the lib's inner name
std::string command = std::string("install_name_tool -id ") + getInnerPath() + " " + getInstallPath();
if( systemp( command ) != 0 )
std::string original_path = getOriginalPath();
std::string dest_path = getInstallPath();
if (is_framework)
{
std::cerr << "\n\nError : An error occured while trying to change identity of library " << getInstallPath() << std::endl;
exit(1);
original_path = getFrameworkRoot(original_path);
dest_path = Settings::destFolder() + stripPrefix(original_path);
}
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;
// delete headers directory
deleteFile(headers_path, true);
}
// Fix the lib's inner name
changeId(getInstallPath(), "@rpath/"+new_name);
}
void Dependency::fixFileThatDependsOnMe(std::string file_to_fix)
{
// for main lib file
std::string command = std::string("install_name_tool -change ") +
getOriginalPath() + " " + getInnerPath() + " " + file_to_fix;
if( systemp( command ) != 0 )
{
std::cerr << "\n\nError : An error occured while trying to fix dependencies of " << file_to_fix << std::endl;
exit(1);
}
changeInstallName(file_to_fix, getOriginalPath(), getInnerPath());
// for symlinks
const int symamount = symlinks.size();
for(int n=0; n<symamount; n++)
{
command = std::string("install_name_tool -change ") +
symlinks[n] + " " + getInnerPath() + " " + file_to_fix;
if( systemp( command ) != 0 )
{
std::cerr << "\n\nError : An error occured while trying to fix dependencies of " << file_to_fix << std::endl;
exit(1);
}
changeInstallName(file_to_fix, symlinks[n], getInnerPath());
}
// FIXME - hackish
if(missing_prefixes)
if (Settings::missingPrefixes())
{
// for main lib file
command = std::string("install_name_tool -change ") +
filename + " " + getInnerPath() + " " + file_to_fix;
if( systemp( command ) != 0 )
{
std::cerr << "\n\nError : An error occured while trying to fix dependencies of " << file_to_fix << std::endl;
exit(1);
}
changeInstallName(file_to_fix, filename, getInnerPath());
// for symlinks
const int symamount = symlinks.size();
for(int n=0; n<symamount; n++)
{
command = std::string("install_name_tool -change ") +
symlinks[n] + " " + getInnerPath() + " " + file_to_fix;
if( systemp( command ) != 0 )
{
std::cerr << "\n\nError : An error occured while trying to fix dependencies of " << file_to_fix << std::endl;
exit(1);
}
}//next
}// end if(missing_prefixes)
changeInstallName(file_to_fix, symlinks[n], getInnerPath());
}
}
}

View File

@ -38,8 +38,10 @@ class Dependency
// installation
std::string new_name;
bool is_framework;
public:
Dependency(std::string path);
Dependency(std::string path, std::string dependent_file);
void print();
@ -47,7 +49,9 @@ public:
std::string getOriginalPath() const{ return prefix+filename; }
std::string getInstallPath();
std::string getInnerPath();
bool isFramework(){ return is_framework; }
void addSymlink(std::string s);
int getSymlinkAmount() const{ return symlinks.size(); }

View File

@ -26,6 +26,7 @@ THE SOFTWARE.
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <regex>
#include <set>
#include <map>
#ifdef __linux
@ -39,8 +40,11 @@ THE SOFTWARE.
std::vector<Dependency> deps;
std::map<std::string, std::vector<Dependency> > deps_per_file;
std::map<std::string, bool> deps_collected;
std::set<std::string> frameworks;
std::set<std::string> rpaths;
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 changeLibPathsOnFile(std::string file_to_fix)
{
@ -48,13 +52,13 @@ void changeLibPathsOnFile(std::string file_to_fix)
{
collectDependencies(file_to_fix);
}
std::cout << "\n* Fixing dependencies on " << file_to_fix.c_str() << std::endl;
if (!Settings::quietOutput()) std::cout << "\n";
std::cout << "* Fixing dependencies on " << file_to_fix.c_str() << std::endl;
std::vector<Dependency> deps_in_file = deps_per_file[file_to_fix];
const int dep_amount = deps_in_file.size();
const int dep_amount = deps_per_file[file_to_fix].size();
for(int n=0; n<dep_amount; n++)
{
deps_in_file[n].fixFileThatDependsOnMe(file_to_fix);
deps_per_file[file_to_fix][n].fixFileThatDependsOnMe(file_to_fix);
}
}
@ -79,7 +83,7 @@ void collectRpaths(const std::string& filename)
size_t pos = 0;
bool read_rpath = false;
while (pos < lc_lines.size())
while(pos < lc_lines.size())
{
std::string line = lc_lines[pos];
pos++;
@ -117,34 +121,120 @@ void collectRpathsForFilename(const std::string& filename)
}
}
std::string searchFilenameInRpaths(const std::string& rpath_file)
std::string searchFilenameInRpaths(const std::string& rpath_file, const std::string& dependent_file)
{
char buffer[PATH_MAX];
char fullpath_buffer[PATH_MAX];
std::string fullpath;
std::string suffix = rpath_file.substr(rpath_file.rfind("/")+1);
for (std::set<std::string>::iterator it = rpaths.begin(); it != rpaths.end(); ++it)
const auto check_path = [&](std::string path)
{
std::string path = *it + "/" + suffix;
if (realpath(path.c_str(), buffer))
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)
{
fullpath = buffer;
break;
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);
}
}
if (realpath(path.c_str(), buffer))
{
fullpath = buffer;
rpath_to_fullpath[rpath_file] = fullpath;
return true;
}
}
else if (path.find("@rpath") != std::string::npos)
{
if (Settings::appBundleProvided())
{
std::string pathE = std::regex_replace(path, std::regex("@rpath/"), Settings::executableFolder());
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 (realpath(pathL.c_str(), buffer))
{
fullpath = buffer;
rpath_to_fullpath[rpath_file] = fullpath;
return true;
}
}
}
return false;
};
// fullpath previously stored
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 (check_path(path)) break;
}
}
if (fullpath.empty())
{
std::cerr << "\n/!\\ WARNING : can't get path for '" << rpath_file << "'\n";
fullpath = getUserInputDirForFile(suffix) + suffix;
if (realpath(fullpath.c_str(), buffer)) {
fullpath = buffer;
size_t search_path_count = Settings::searchPathAmount();
for(size_t i=0; i<search_path_count; ++i)
{
std::string search_path = Settings::searchPath(i);
if (fileExists(search_path+suffix))
{
fullpath = search_path + suffix;
break;
}
}
if (fullpath.empty())
{
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;
}
}
}
return fullpath;
}
std::string searchFilenameInRpaths(const std::string& rpath_file)
{
return searchFilenameInRpaths(rpath_file, rpath_file);
}
void fixRpathsOnFile(const std::string& original_file, const std::string& file_to_fix)
{
std::vector<std::string> rpaths_to_fix;
@ -168,7 +258,7 @@ void fixRpathsOnFile(const std::string& original_file, const std::string& file_t
void addDependency(std::string path, std::string filename)
{
Dependency dep(path);
Dependency dep(path, filename);
// we need to check if this library was already added to avoid duplicates
bool in_deps = false;
@ -179,15 +269,16 @@ void addDependency(std::string path, std::string filename)
}
// check if this library was already added to |deps_per_file[filename]| to avoid duplicates
std::vector<Dependency> deps_in_file = deps_per_file[filename];
bool in_deps_per_file = false;
const int deps_in_file_amount = deps_in_file.size();
const int deps_in_file_amount = deps_per_file[filename].size();
for(int n=0; n<deps_in_file_amount; n++)
{
if(dep.mergeIfSameAs(deps_in_file[n])) in_deps_per_file = true;
if(dep.mergeIfSameAs(deps_per_file[filename][n])) in_deps_per_file = true;
}
if(!Settings::isPrefixBundled(dep.getPrefix())) return;
if(!in_deps && dep.isFramework()) frameworks.insert(dep.getOriginalPath());
if(!in_deps) deps.push_back(dep);
if(!in_deps_per_file) deps_per_file[filename].push_back(dep);
@ -198,49 +289,63 @@ void addDependency(std::string path, std::string filename)
*/
void collectDependencies(std::string filename, std::vector<std::string>& lines)
{
// execute "otool -L" on the given file and collect the command's output
std::string cmd = "otool -L " + filename;
// execute "otool -l" on the given file and collect the command's output
std::string cmd = "otool -l " + filename;
std::string output = system_get_output(cmd);
if(output.find("can't open file")!=std::string::npos or output.find("No such file")!=std::string::npos or output.size()<1)
if (output.find("can't open file")!=std::string::npos or output.find("No such file")!=std::string::npos or output.size()<1)
{
std::cerr << "Cannot find file " << filename << " to read its dependencies" << std::endl;
exit(1);
}
// split output
tokenize(output, "\n", &lines);
deps_collected[filename] = true;
}
std::vector<std::string> raw_lines;
tokenize(output, "\n", &raw_lines);
bool searching = false;
for(const auto& line : raw_lines) {
if (line.find("cmd LC_LOAD_DYLIB") != std::string::npos)
{
if (searching)
{
std::cerr << "\n\n/!\\ ERROR: Failed to find name before next cmd" << std::endl;
exit(1);
}
searching = true;
}
else if (searching)
{
size_t found = line.find("name ");
if (found != std::string::npos)
{
lines.push_back('\t' + line.substr(found+5, std::string::npos));
searching = false;
}
}
}
}
void collectDependencies(std::string filename)
{
std::vector<std::string> lines;
collectDependencies(filename, lines);
std::cout << "."; fflush(stdout);
const int line_amount = lines.size();
for(int n=0; n<line_amount; n++)
{
std::cout << "."; fflush(stdout);
if(lines[n][0] != '\t') continue; // only lines beginning with a tab interest us
if( lines[n].find(".framework") != std::string::npos ) continue; //Ignore frameworks, we can not handle them
if(!Settings::isPrefixBundled(lines[n])) continue; // skip system/ignored prefixes
// trim useless info, keep only library name
std::string dep_path = lines[n].substr(1, lines[n].rfind(" (") - 1);
if (isRpath(dep_path))
{
collectRpathsForFilename(filename);
}
addDependency(dep_path, filename);
}
deps_collected[filename] = true;
}
void collectSubDependencies()
{
// print status to user
size_t dep_counter = deps.size();
int dep_amount = deps.size();
// recursively collect each dependencie's dependencies
@ -249,7 +354,6 @@ void collectSubDependencies()
dep_amount = deps.size();
for(int n=0; n<dep_amount; n++)
{
std::cout << "."; fflush(stdout);
std::vector<std::string> lines;
std::string original_path = deps[n].getOriginalPath();
if (isRpath(original_path))
@ -262,35 +366,34 @@ void collectSubDependencies()
const int line_amount = lines.size();
for(int n=0; n<line_amount; n++)
{
if(lines[n][0] != '\t') continue; // only lines beginning with a tab interest us
if( lines[n].find(".framework") != std::string::npos ) continue; //Ignore frameworks, we cannot handle them
if (lines[n][0] != '\t') continue; // only lines beginning with a tab interest us
if (!Settings::isPrefixBundled(lines[n])) continue; // skip system/ignored prefixes
// trim useless info, keep only library name
std::string dep_path = lines[n].substr(1, lines[n].rfind(" (") - 1);
if (isRpath(dep_path))
{
collectRpathsForFilename(searchFilenameInRpaths(dep_path));
}
addDependency(dep_path, original_path);
}//next
}//next
if(deps.size() == dep_amount) break; // no more dependencies were added on this iteration, stop searching
if (deps.size() == dep_amount) break; // no more dependencies were added on this iteration, stop searching
}
if (Settings::bundleLibs() && Settings::bundleFrameworks())
{
if ( !qt_plugins_called || (deps.size() != dep_counter) ) copyQtPlugins();
}
}
void createDestDir()
{
std::string dest_folder = Settings::destFolder();
std::cout << "* Checking output directory " << dest_folder.c_str() << std::endl;
std::cout << "Checking output directory " << dest_folder.c_str() << std::endl;
// ----------- check dest folder stuff ----------
bool dest_exists = fileExists(dest_folder);
if(dest_exists and Settings::canOverwriteDir())
{
std::cout << "* Erasing old output directory " << dest_folder.c_str() << std::endl;
std::cout << "Erasing old output directory " << dest_folder.c_str() << std::endl;
std::string command = std::string("rm -r ") + dest_folder;
if( systemp( command ) != 0)
{
@ -305,7 +408,7 @@ void createDestDir()
if(Settings::canCreateDir())
{
std::cout << "* Creating output directory " << dest_folder.c_str() << std::endl;
std::cout << "Creating output directory " << dest_folder.c_str() << "\n\n";
std::string command = std::string("mkdir -p ") + dest_folder;
if( systemp( command ) != 0)
{
@ -324,7 +427,6 @@ void createDestDir()
void doneWithDeps_go()
{
std::cout << std::endl;
const int dep_amount = deps.size();
// print info to user
for(int n=0; n<dep_amount; n++)
@ -353,3 +455,119 @@ void doneWithDeps_go()
fixRpathsOnFile(Settings::fileToFix(n), Settings::fileToFix(n));
}
}
void copyQtPlugins()
{
bool qtCoreFound = false;
bool qtGuiFound = false;
bool qtNetworkFound = false;
bool qtSqlFound = false;
bool qtSvgFound = false;
bool qtMultimediaFound = false;
bool qt3dRenderFound = false;
bool qt3dQuickRenderFound = false;
bool qtPositioningFound = false;
bool qtLocationFound = false;
bool qtTextToSpeechFound = false;
bool qtWebViewFound = false;
std::string original_file;
for (std::set<std::string>::iterator it = frameworks.begin(); it != frameworks.end(); ++it)
{
std::string framework = *it;
if (framework.find("QtCore") != std::string::npos)
{
qtCoreFound = true;
original_file = framework;
}
if (framework.find("QtGui") != std::string::npos)
qtGuiFound = true;
if (framework.find("QtNetwork") != std::string::npos)
qtNetworkFound = true;
if (framework.find("QtSql") != std::string::npos)
qtSqlFound = true;
if (framework.find("QtSvg") != std::string::npos)
qtSvgFound = true;
if (framework.find("QtMultimedia") != std::string::npos)
qtMultimediaFound = true;
if (framework.find("Qt3DRender") != std::string::npos)
qt3dRenderFound = true;
if (framework.find("Qt3DQuickRender") != std::string::npos)
qt3dQuickRenderFound = true;
if (framework.find("QtPositioning") != std::string::npos)
qtPositioningFound = true;
if (framework.find("QtLocation") != std::string::npos)
qtLocationFound = true;
if (framework.find("TextToSpeech") != std::string::npos)
qtTextToSpeechFound = true;
if (framework.find("WebView") != std::string::npos)
qtWebViewFound = true;
}
if (!qtCoreFound) return;
if (!qt_plugins_called) createQtConf(Settings::resourcesFolder());
qt_plugins_called = true;
const auto fixupPlugin = [original_file](std::string plugin)
{
std::string dest = Settings::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));
std::string qt_plugins_prefix = qt_prefix + "plugins/";
if (fileExists(qt_plugins_prefix + plugin))
{
mkdir(dest + plugin);
copyFile(qt_plugins_prefix + plugin, dest);
std::vector<std::string> 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 framework_root = getFrameworkRoot(original_file);
std::string prefix = filePrefix(framework_root);
std::string qt_prefix = filePrefix(prefix.substr(0, prefix.size()-1));
std::string qt_plugins_prefix = qt_prefix + "plugins/";
std::string dest = Settings::pluginsFolder();
mkdir(dest + "platforms");
copyFile(qt_plugins_prefix + "platforms/libqcocoa.dylib", dest + "platforms");
Settings::addFileToFix(dest + "platforms/libqcocoa.dylib");
collectDependencies(dest + "platforms/libqcocoa.dylib");
fixupPlugin("printsupport");
fixupPlugin("styles");
fixupPlugin("imageformats");
fixupPlugin("iconengines");
if (!qtSvgFound) systemp(std::string("rm -f ") + dest + "imageformats/libqsvg.dylib");
if (qtGuiFound)
{
fixupPlugin("platforminputcontexts");
fixupPlugin("virtualkeyboard");
}
if (qtNetworkFound) fixupPlugin("bearer");
if (qtSqlFound) fixupPlugin("sqldrivers");
if (qtMultimediaFound)
{
fixupPlugin("mediaservice");
fixupPlugin("audio");
}
if (qt3dRenderFound)
{
fixupPlugin("sceneparsers");
fixupPlugin("geometryloaders");
}
if (qt3dQuickRenderFound) fixupPlugin("renderplugins");
if (qtPositioningFound) fixupPlugin("position");
if (qtLocationFound) fixupPlugin("geoservices");
if (qtTextToSpeechFound) fixupPlugin("texttospeech");
if (qtWebViewFound) fixupPlugin("webview");
collectSubDependencies();
}

View File

@ -27,10 +27,22 @@ THE SOFTWARE.
#include <string>
void collectDependencies(std::string filename);
void collectSubDependencies();
void doneWithDeps_go();
void changeLibPathsOnFile(std::string file_to_fix);
bool isRpath(const std::string& path);
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_dep);
void fixRpathsOnFile(const std::string& original_file, const std::string& file_to_fix);
void addDependency(std::string path, std::string dependent_file);
void collectDependencies(std::string dependent_file, std::vector<std::string>& lines);
void collectDependencies(std::string dependent_file);
void collectSubDependencies();
void doneWithDeps_go();
void copyQtPlugins();
#endif

View File

@ -23,6 +23,7 @@ THE SOFTWARE.
*/
#include "Settings.h"
#include "Utils.h"
#include <vector>
namespace Settings
@ -31,6 +32,18 @@ namespace Settings
bool overwrite_files = false;
bool overwrite_dir = false;
bool create_dir = false;
bool bundle_libs = false;
bool bundle_frameworks = false;
bool quiet_output = false;
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;
bool canOverwriteFiles(){ return overwrite_files; }
bool canOverwriteDir(){ return overwrite_dir; }
@ -40,33 +53,76 @@ void canOverwriteFiles(bool permission){ overwrite_files = permission; }
void canOverwriteDir(bool permission){ overwrite_dir = permission; }
void canCreateDir(bool permission){ create_dir = permission; }
bool bundleLibs(){ return bundle_libs; }
void bundleLibs(bool on){ bundle_libs = on; }
bool bundleLibs_bool = false;
bool bundleLibs(){ return bundleLibs_bool; }
void bundleLibs(bool on){ bundleLibs_bool = on; }
bool bundleFrameworks(){ return bundle_frameworks; }
void bundleFrameworks(bool on){ bundle_frameworks = on; }
bool quietOutput(){ return quiet_output; }
void quietOutput(bool status){ quiet_output = status; }
std::string dest_folder_str = "./libs/";
std::string destFolder(){ return dest_folder_str; }
void destFolder(std::string path)
std::string app_bundle;
bool appBundleProvided(){ return !app_bundle.empty(); }
std::string appBundle(){ return app_bundle; }
void appBundle(std::string path)
{
dest_folder_str = path;
app_bundle = path;
char buffer[PATH_MAX];
if(realpath(app_bundle.c_str(), buffer))
app_bundle = buffer;
// fix path if needed so it ends with '/'
if( dest_folder_str[ dest_folder_str.size()-1 ] != '/' ) dest_folder_str += "/";
if( app_bundle[ app_bundle.size()-1 ] != '/' )
app_bundle += "/";
std::string bundle_executable_path = app_bundle + "Contents/MacOS/" + bundleExecutableName(app_bundle);
if(realpath(bundle_executable_path.c_str(), buffer))
bundle_executable_path = buffer;
addFileToFix(bundle_executable_path);
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/" + stripLSlash(dest_folder);
if(realpath(dest_path.c_str(), buffer))
dest_path = buffer;
if( dest_path[ dest_path.size()-1 ] != '/' )
dest_path += "/";
}
std::string destFolder(){ return dest_path; }
void destFolder(std::string path)
{
dest_path = path;
if(appBundleProvided()) dest_path = app_bundle + "Contents/" + stripLSlash(path);
char buffer[PATH_MAX];
if(realpath(dest_path.c_str(), buffer)) dest_path = buffer;
if( dest_path[ dest_path.size()-1 ] != '/' ) dest_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<std::string> files;
void addFileToFix(std::string path){ files.push_back(path); }
void addFileToFix(std::string path)
{
char buffer[PATH_MAX];
if(realpath(path.c_str(), buffer)) path = buffer;
files.push_back(path);
}
int fileToFixAmount(){ return files.size(); }
std::string fileToFix(const int n){ return files[n]; }
std::string inside_path_str = "@executable_path/../libs/";
std::string inside_lib_path(){ return inside_path_str; }
std::string inside_lib_path(){ return inside_path; }
void inside_lib_path(std::string p)
{
inside_path_str = p;
inside_path = p;
// fix path if needed so it ends with '/'
if( inside_path_str[ inside_path_str.size()-1 ] != '/' ) inside_path_str += "/";
if( inside_path[ inside_path.size()-1 ] != '/' ) inside_path += "/";
}
std::vector<std::string> prefixes_to_ignore;
@ -89,9 +145,10 @@ bool isPrefixIgnored(std::string prefix)
bool isPrefixBundled(std::string prefix)
{
if(prefix.find(".framework") != std::string::npos) return false;
if(!bundle_frameworks && prefix.find(".framework") != std::string::npos) return false;
if(prefix.find("@executable_path") != std::string::npos) return false;
if(prefix.compare("/usr/lib/") == 0) return false;
if(prefix.find("/usr/lib/") == 0) return false;
if(prefix.find("/System/Library/") != std::string::npos) return false;
if(isPrefixIgnored(prefix)) return false;
return true;
@ -102,4 +159,15 @@ void addSearchPath(std::string path){ searchPaths.push_back(path); }
int searchPathAmount(){ 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 userSearchPathAmount(){ return userSearchPaths.size(); }
std::string userSearchPath(const int n){ return userSearchPaths[n]; }
// if some libs are missing prefixes, this will be set to true
// more stuff will then be necessary to do
bool missing_prefixes = false;
bool missingPrefixes(){ return missing_prefixes; }
void missingPrefixes(bool status){ missing_prefixes = status; }
}

View File

@ -46,9 +46,24 @@ void canCreateDir(bool permission);
bool bundleLibs();
void bundleLibs(bool on);
bool bundleFrameworks();
void bundleFrameworks(bool on);
bool quietOutput();
void quietOutput(bool status);
bool appBundleProvided();
std::string appBundle();
void appBundle(std::string path);
std::string destFolder();
void destFolder(std::string path);
std::string executableFolder();
std::string frameworksFolder();
std::string pluginsFolder();
std::string resourcesFolder();
void addFileToFix(std::string path);
int fileToFixAmount();
std::string fileToFix(const int n);
@ -60,5 +75,12 @@ void addSearchPath(std::string path);
int searchPathAmount();
std::string searchPath(const int n);
void addUserSearchPath(std::string path);
size_t userSearchPathAmount();
std::string userSearchPath(const int n);
bool missingPrefixes();
void missingPrefixes(bool status);
}
#endif

View File

@ -28,18 +28,51 @@ THE SOFTWARE.
#include "Settings.h"
#include <cstdlib>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cstdio>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
/*
void setInstallPath(string loc)
string filePrefix(string in)
{
path_to_libs_folder = loc;
}*/
return in.substr(0, in.rfind("/")+1);
}
string stripPrefix(string in)
{
return in.substr(in.rfind("/")+1);
}
string getFrameworkRoot(string in)
{
return in.substr(0, in.find(".framework")+10);
}
string getFrameworkPath(string in)
{
return in.substr(in.rfind(".framework/")+11);
}
string stripLSlash(string in)
{
if (in[0] == '.' && in[1] == '/') in = in.substr(2, in.size());
return in;
}
void rtrim_in_place(string& s)
{
s.erase(find_if(s.rbegin(), s.rend(), [](unsigned char c){ return !isspace(c); }).base(), s.end());
}
string rtrim(string s)
{
rtrim_in_place(s);
return s;
}
void tokenize(const string& str, const char* delim, vector<string>* vectorarg)
{
@ -67,36 +100,22 @@ void tokenize(const string& str, const char* delim, vector<string>* vectorarg)
}
bool fileExists( std::string filename )
bool fileExists(string filename)
{
if (access( filename.c_str(), F_OK ) != -1)
{
return true; // file exists
}
else
{
//std::cout << "access(filename) returned -1 on filename [" << filename << "] I will try trimming." << std::endl;
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;
}
else
{
//std::cout << "Still failed. Cannot find the specified file." << std::endl;
return false;// file doesn't exist
}
}
if (access( filename.c_str(), F_OK ) != -1) return true; // file exists
string delims = " \f\n\r\t\v";
string rtrimmed = filename.substr(0, filename.find_last_not_of(delims)+1);
string ftrimmed = rtrimmed.substr(rtrimmed.find_first_not_of(delims));
if (access( ftrimmed.c_str(), F_OK ) != -1) return true;
return false; // file doesn't exist
}
void copyFile(string from, string to)
{
bool override = Settings::canOverwriteFiles();
if(!override)
bool overwrite = Settings::canOverwriteFiles();
if(!overwrite)
{
if(fileExists( to ))
{
@ -105,18 +124,17 @@ void copyFile(string from, string to)
}
}
string override_permission = string(override ? "-f " : "-n ");
// copy file to local directory
string command = string("cp ") + override_permission + from + string(" ") + to;
if( from != to && systemp( command ) != 0 )
string overwrite_permission = string(overwrite ? "-f " : "-n ");
string command = string("cp -R ") + overwrite_permission + from + string(" ") + to;
if (from != to && systemp(command) != 0)
{
cerr << "\n\nError : An error occured while trying to copy file " << from << " to " << to << endl;
exit(1);
}
// give it write permission
string command2 = string("chmod +w ") + to;
string command2 = string("chmod -R +w ") + to;
if( systemp( command2 ) != 0 )
{
cerr << "\n\nError : An error occured while trying to set write permissions on file " << to << endl;
@ -124,6 +142,44 @@ void copyFile(string from, string to)
}
}
void deleteFile(string path, bool overwrite)
{
string overwrite_permission = string(overwrite ? "-f " : " ");
string command = string("rm -r ") + overwrite_permission + path;
if( systemp( command ) != 0 )
{
cerr << "\n\nError: An error occured while trying to delete " << path << endl;
exit(1);
}
}
void deleteFile(string path)
{
bool overwrite = Settings::canOverwriteFiles();
deleteFile(path, overwrite);
}
std::vector<std::string> lsDir(const std::string& path)
{
std::string cmd = "ls " + path;
std::string output = system_get_output(cmd);
std::vector<std::string> files;
tokenize(output, "\n", &files);
return files;
}
bool mkdir(const std::string& path)
{
cout << "Creating directory " << path << std::endl;
string command = string("mkdir -p ") + path;
if( systemp( command ) != 0 )
{
cerr << "\n/!\\ ERROR: An error occured while creating " << path << endl;
return false;
}
return true;
}
std::string system_get_output(std::string cmd)
{
FILE * command_output;
@ -161,13 +217,13 @@ std::string system_get_output(std::string cmd)
return full_output;
}
int systemp(std::string& cmd)
int systemp(const std::string& cmd)
{
std::cout << " " << cmd.c_str() << std::endl;
if(!Settings::quietOutput()) std::cout << " " << cmd << "\n";
return system(cmd.c_str());
}
std::string getUserInputDirForFile(const std::string& filename)
std::string getUserInputDirForFile(const std::string& filename, const std::string& dependent_file)
{
const int searchPathAmount = Settings::searchPathAmount();
for(int n=0; n<searchPathAmount; n++)
@ -175,16 +231,21 @@ std::string getUserInputDirForFile(const std::string& filename)
auto searchPath = Settings::searchPath(n);
if( !searchPath.empty() && searchPath[ searchPath.size()-1 ] != '/' ) searchPath += "/";
if( !fileExists( searchPath+filename ) ) {
continue;
} else {
if( !fileExists( searchPath+filename ) ) continue;
if(!Settings::quietOutput())
{
std::cerr << (searchPath+filename) << " was found. /!\\ DYLIBBUNDLER MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Manually check the executable with 'otool -L'" << std::endl;
return searchPath;
}
return searchPath;
}
while (true)
{
if(Settings::quietOutput())
{
std::cerr << "\n/!\\ WARNING: Dependency " << filename << " of " << dependent_file << " not found\n";
}
std::cout << "Please specify the directory where this library is located (or enter 'quit' to abort): "; fflush(stdout);
std::string prefix;
@ -192,8 +253,7 @@ std::string getUserInputDirForFile(const std::string& filename)
std::cout << std::endl;
if(prefix.compare("quit")==0) exit(1);
if( !prefix.empty() && prefix[ prefix.size()-1 ] != '/' ) prefix += "/";
if (!prefix.empty() && prefix[ prefix.size()-1 ] != '/') prefix += "/";
if( !fileExists( prefix+filename ) )
{
@ -203,7 +263,75 @@ std::string getUserInputDirForFile(const std::string& filename)
else
{
std::cerr << (prefix+filename) << " was found. /!\\ DYLIBBUNDLER MAY NOT CORRECTLY HANDLE THIS DEPENDENCY: Manually check the executable with 'otool -L'" << std::endl;
Settings::addSearchPath(prefix);
return prefix;
}
}
}
string bundleExecutableName(const string& app_bundle_path)
{
string cmd = "/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' " + app_bundle_path + "Contents/Info.plist";
return rtrim(system_get_output(cmd));
}
void changeId(string binary_file, string new_id)
{
string command = string("install_name_tool -id ") + new_id + " " + binary_file;
if( systemp( command ) != 0 )
{
cerr << "\n\nError: An error occured while trying to change identity of library " << binary_file << endl;
exit(1);
}
}
void changeInstallName(string binary_file, string old_name, string new_name)
{
string command = string("install_name_tool -change ") + old_name + " " + new_name + " " + binary_file;
if( systemp( command ) != 0 )
{
cerr << "\n\nError: An error occured while trying to fix dependencies of " << binary_file << endl;
exit(1);
}
}
void initSearchPaths()
{
string searchPaths;
char* dyldLibPath = getenv("DYLD_LIBRARY_PATH");
if(dyldLibPath != 0) searchPaths = dyldLibPath;
dyldLibPath = getenv("DYLD_FALLBACK_FRAMEWORK_PATH");
if(dyldLibPath != 0)
{
if( !searchPaths.empty() && searchPaths[ searchPaths.size()-1 ] != ':' ) searchPaths += ":";
searchPaths += dyldLibPath;
}
dyldLibPath = getenv("DYLD_FALLBACK_LIBRARY_PATH");
if(dyldLibPath != 0)
{
if( !searchPaths.empty() && searchPaths[ searchPaths.size()-1 ] != ':' ) searchPaths += ":";
searchPaths += dyldLibPath;
}
if(!searchPaths.empty())
{
stringstream ss(searchPaths);
string item;
while(getline(ss, item, ':'))
{
if( item[ item.size()-1 ] != '/' ) item += "/";
Settings::addSearchPath(item);
}
}
}
void createQtConf(std::string directory)
{
std::string contents = "[Paths]\n"
"Plugins = PlugIns\n"
"Imports = Resources/qml\n"
"Qml2Imports = Resources/qml\n";
if( directory[ directory.size()-1 ] != '/' ) directory += "/";
std::ofstream out(directory + "qt.conf");
out << contents;
out.close();
}

View File

@ -31,16 +31,46 @@ THE SOFTWARE.
class Library;
std::string filePrefix(std::string in);
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)
std::string rtrim(std::string s);
void tokenize(const std::string& str, const char* delimiters, std::vector<std::string>*);
bool fileExists( std::string filename );
void copyFile(std::string from, std::string to);
void deleteFile(std::string path, bool overwrite);
void deleteFile(std::string path);
std::vector<std::string> lsDir(const std::string& path);
bool mkdir(const std::string& path);
// executes a command in the native shell and returns output in string
std::string system_get_output(std::string cmd);
// like 'system', runs a command on the system shell, but also prints the command to stdout.
int systemp(std::string& cmd);
std::string getUserInputDirForFile(const std::string& filename);
int systemp(const std::string& cmd);
std::string getUserInputDirForFile(const std::string& filename, const std::string& dependent_file);
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);
// check the same paths the system would search for dylibs
void initSearchPaths();
void createQtConf(std::string directory);
#endif

View File

@ -53,17 +53,20 @@ void showHelp()
{
std::cout << "dylibbundler " << VERSION << std::endl;
std::cout << "dylibbundler is a utility that helps bundle dynamic libraries inside mac OS X app bundles.\n" << std::endl;
std::cout << "-x, --fix-file <file to fix (executable or app plug-in)>" << std::endl;
std::cout << "-b, --bundle-deps" << std::endl;
std::cout << "-d, --dest-dir <directory to send bundled libraries (relative to cwd)>" << std::endl;
std::cout << "-p, --install-path <'inner' path of bundled libraries (usually relative to executable, by default '@executable_path/../libs/')>" << std::endl;
std::cout << "-s, --search-path <directory to add to list of locations searched>" << std::endl;
std::cout << "-a, --app <application bundle to make self-contained> (fixes the main executable of the app bundle)" << std::endl;
std::cout << "-x, --fix-file <file to fix (executable or app plug-in)> (additional files to fix up inside app)" << std::endl;
std::cout << "-b, --bundle-deps" << std::endl;
std::cout << "-f, --frameworks (copy dependencies that are frameworks)" << std::endl;
std::cout << "-d, --dest-dir <directory to send bundled libraries> (relative to MyApp.app/Contents if '-a' is passed)" << std::endl;
std::cout << "-p, --install-path <'inner' path of bundled libraries> (default: '@executable_path/../libs/' or '@executable_path/../Frameworks/' if '-a' is passed)" << std::endl;
std::cout << "-s, --search-path <directory to add to list of locations searched>" << std::endl;
std::cout << "-of, --overwrite-files (allow overwriting files in output directory)" << std::endl;
std::cout << "-od, --overwrite-dir (totally overwrite output directory if it already exists. implies --create-dir)" << std::endl;
std::cout << "-cd, --create-dir (creates output directory if necessary)" << std::endl;
std::cout << "-i, --ignore <location to ignore> (will ignore libraries in this directory)" << std::endl;
std::cout << "-h, --help" << std::endl;
std::cout << "-i, --ignore <location to ignore> (will ignore libraries in this directory)" << std::endl;
std::cout << "-q, --quiet (less verbose output)" << std::endl;
std::cout << "-h, --help" << std::endl;
}
int main (int argc, char * const argv[])
@ -72,7 +75,13 @@ int main (int argc, char * const argv[])
// parse arguments
for(int i=0; i<argc; i++)
{
if(strcmp(argv[i],"-x")==0 or strcmp(argv[i],"--fix-file")==0)
if(strcmp(argv[i],"-a")==0 || strcmp(argv[i],"--app")==0)
{
i++;
Settings::appBundle(argv[i]);
continue;
}
else if(strcmp(argv[i],"-x")==0 or strcmp(argv[i],"--fix-file")==0)
{
i++;
Settings::addFileToFix(argv[i]);
@ -83,6 +92,11 @@ int main (int argc, char * const argv[])
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 or strcmp(argv[i],"--install-path")==0)
{
i++;
@ -101,6 +115,12 @@ int main (int argc, char * const argv[])
Settings::destFolder(argv[i]);
continue;
}
else if(strcmp(argv[i],"-s")==0 or strcmp(argv[i],"--search-path")==0)
{
i++;
Settings::addUserSearchPath(argv[i]);
continue;
}
else if(strcmp(argv[i],"-of")==0 or strcmp(argv[i],"--overwrite-files")==0)
{
Settings::canOverwriteFiles(true);
@ -117,17 +137,15 @@ int main (int argc, char * const argv[])
Settings::canCreateDir(true);
continue;
}
else if (strcmp(argv[i],"-q") == 0 || strcmp(argv[i],"--quiet") == 0) {
Settings::quietOutput(true);
continue;
}
else if(strcmp(argv[i],"-h")==0 or strcmp(argv[i],"--help")==0)
{
showHelp();
exit(0);
}
if(strcmp(argv[i],"-s")==0 or strcmp(argv[i],"--search-path")==0)
{
i++;
Settings::addSearchPath(argv[i]);
continue;
}
else if(i>0)
{
// if we meet an unknown flag, abort
@ -144,7 +162,7 @@ int main (int argc, char * const argv[])
exit(0);
}
std::cout << "* Collecting dependencies"; fflush(stdout);
std::cout << "Collecting dependencies..." << std::endl;
const int amount = Settings::fileToFixAmount();
for(int n=0; n<amount; n++)