Initial import

This commit is contained in:
Marianne Gagnon 2014-05-29 19:48:42 -04:00
parent 52af408561
commit f5f45f47d7
11 changed files with 1113 additions and 1 deletions

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 auriamg
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

15
makefile Normal file
View File

@ -0,0 +1,15 @@
dylibbundler:
g++ -c -I./src ./src/Settings.cpp -o ./Settings.o
g++ -c -I./src ./src/DylibBundler.cpp -o ./DylibBundler.o
g++ -c -I./src ./src/Dependency.cpp -o ./Dependency.o
g++ -c -I./src ./src/main.cpp -o ./main.o
g++ -c -I./src ./src/Utils.cpp -o ./Utils.o
g++ -o ./dylibbundler ./Settings.o ./DylibBundler.o ./Dependency.o ./main.o ./Utils.o
clean:
rm -f *.o
rm -f ./dylibbundler
install: dylibbundler
cp ./dylibbundler /usr/local/bin/dylibbundler
chmod 775 /usr/local/bin/dylibbundler

270
src/Dependency.cpp Normal file
View File

@ -0,0 +1,270 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "Dependency.h"
#include <iostream>
#include <cstdlib>
#include <sys/param.h>
#include "Utils.h"
#include "Settings.h"
#include <stdlib.h>
#include <sstream>
#include <vector>
std::string stripPrefix(std::string in)
{
return in.substr(in.rfind("/")+1);
}
//the pathes to search for dylibs, store it globally to parse the environment variables only once
std::vector<std::string> pathes;
//initialize the dylib search pathes
void initSearchPathes(){
//Check the same pathes the system would search for dylibs
std::string searchPathes;
char *dyldLibPath = std::getenv("DYLD_LIBRARY_PATH");
if( dyldLibPath!=0 )
searchPathes = dyldLibPath;
dyldLibPath = std::getenv("DYLD_FALLBACK_FRAMEWORK_PATH");
if (dyldLibPath != 0)
{
if (!searchPathes.empty() && searchPathes[ searchPathes.size()-1 ] != ':') searchPathes += ":";
searchPathes += dyldLibPath;
}
dyldLibPath = std::getenv("DYLD_FALLBACK_LIBRARY_PATH");
if (dyldLibPath!=0 )
{
if (!searchPathes.empty() && searchPathes[ searchPathes.size()-1 ] != ':') searchPathes += ":";
searchPathes += dyldLibPath;
}
if (!searchPathes.empty())
{
std::stringstream ss(searchPathes);
std::string item;
while(std::getline(ss, item, ':'))
{
if (item[ item.size()-1 ] != '/') item += "/";
pathes.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)
{
// check if given path is a symlink
std::string cmd = "readlink -n " + path;
const bool is_symlink = system( (cmd+" > /dev/null").c_str())==0;
if (is_symlink)
{
char original_file_buffer[PATH_MAX];
std::string original_file;
if (not realpath(path.c_str(), original_file_buffer))
{
std::cerr << "\n/!\\ WARNING : Cannot resolve symlink '" << path.c_str() << "'" << std::endl;
original_file = path;
}
else
{
original_file = original_file_buffer;
}
//original_file = original_file.substr(0, original_file.find("\n") );
filename = stripPrefix(original_file);
prefix = path.substr(0, path.rfind("/")+1);
addSymlink(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 ) )
{
//the pathes contains at least /usr/lib so if it is empty we have not initilazed it
if( pathes.empty() ) initSearchPathes();
//check if file is contained in one of the pathes
for( size_t i=0; i<pathes.size(); ++i)
{
if (fileExists( pathes[i]+filename ))
{
std::cout << "FOUND " << filename << " in " << pathes[i] << std::endl;
prefix = pathes[i];
missing_prefixes = true; //the prefix was missing
break;
}
}
}
//If the location is still unknown, ask the user for search path
if( prefix.empty() || !fileExists( prefix+filename ) )
{
std::cerr << "\n/!\\ WARNING : Library " << filename << " has an incomplete name (location unknown)" << std::endl;
missing_prefixes = true;
while (true)
{
std::cout << "Please specify now where this library can be found (or write 'quit' to abort): "; fflush(stdout);
char buffer[128];
std::cin >> buffer;
prefix = buffer;
std::cout << std::endl;
if(prefix.compare("quit")==0) exit(1);
if( !prefix.empty() && prefix[ prefix.size()-1 ] != '/' ) prefix += "/";
if( !fileExists( prefix+filename ) )
{
std::cerr << (prefix+filename) << " does not exist. Try again" << std::endl;
continue;
}
else
{
pathes.push_back( prefix );
std::cerr << (prefix+filename) << " was found. /!\\MANUALLY CHECK THE EXECUTABLE WITH 'otool -L', DYLIBBUNDLDER MAY NOT HANDLE CORRECTLY THIS UNSTANDARD/ILL-FORMED DEPENDENCY" << std::endl;
break;
}
}
}
//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;
const int symamount = symlinks.size();
for(int n=0; n<symamount; n++)
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){ symlinks.push_back(stripPrefix(s)); }
// comapres the given Dependency with this one. If both refer to the same file,
// it returns true and merges both entries into one.
bool Dependency::mergeIfSameAs(Dependency& dep2)
{
if(dep2.getOriginalFileName().compare(filename) == 0)
{
const int samount = dep2.getSymlinkAmount();
for(int n=0; n<samount; n++)
addSymlink( dep2.getSymlink(n) ); // FIXME - there may be duplicate symlinks
return true;
}
return false;
}
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::cerr << "\n\nError : An error occured while trying to change identity of library " << getInstallPath() << std::endl;
exit(1);
}
}
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 depencies of " << file_to_fix << std::endl;
exit(1);
}
// for symlinks
const int symamount = symlinks.size();
for(int n=0; n<symamount; n++)
{
std::string command = std::string("install_name_tool -change ") +
prefix+symlinks[n] + " " + getInnerPath() + " " + file_to_fix;
if( systemp( command ) != 0 )
{
std::cerr << "\n\nError : An error occured while trying to fix depencies of " << file_to_fix << std::endl;
exit(1);
}
}
// FIXME - hackish
if(missing_prefixes)
{
// for main lib file
std::string 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 depencies of " << file_to_fix << std::endl;
exit(1);
}
// for symlinks
const int symamount = symlinks.size();
for(int n=0; n<symamount; n++)
{
std::string 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 depencies of " << file_to_fix << std::endl;
exit(1);
}
}//next
}// end if(missing_prefixes)
}

66
src/Dependency.h Normal file
View File

@ -0,0 +1,66 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef _depend_h_
#define _depend_h_
#include <string>
#include <vector>
class Dependency
{
// origin
std::string filename;
std::string prefix;
std::vector<std::string> symlinks;
// installation
std::string new_name;
public:
Dependency(std::string path);
void print();
std::string getOriginalFileName() const{ return filename; }
std::string getOriginalPath() const{ return prefix+filename; }
std::string getInstallPath();
std::string getInnerPath();
void addSymlink(std::string s);
int getSymlinkAmount() const{ return symlinks.size(); }
std::string getSymlink(const int i) const{ return symlinks[i]; }
std::string getPrefix() const{ return prefix; }
void copyYourself();
void fixFileThatDependsOnMe(std::string file);
// comapres the given Dependency with this one. If both refer to the same file,
// it returns true and merges both entries into one.
bool mergeIfSameAs(Dependency& dep2);
};
#endif

202
src/DylibBundler.cpp Normal file
View File

@ -0,0 +1,202 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "DylibBundler.h"
#include <iostream>
#include <cstdlib>
#include "Utils.h"
#include "Settings.h"
#include "Dependency.h"
std::vector<Dependency> deps;
void changeLibPathsOnFile(std::string file_to_fix)
{
std::cout << "\n* Fixing dependencies on " << file_to_fix.c_str() << std::endl;
const int dep_amount = deps.size();
for(int n=0; n<dep_amount; n++)
{
deps[n].fixFileThatDependsOnMe(file_to_fix);
}
}
void addDependency(std::string path)
{
Dependency dep(path);
// we need to check if this library was already added to avoid duplicates
const int dep_amount = deps.size();
for(int n=0; n<dep_amount; n++)
{
if(dep.mergeIfSameAs(deps[n])) return;
}
if(!Settings::isPrefixBundled(dep.getPrefix())) return;
deps.push_back(dep);
}
/*
* Fill vector 'lines' with dependencies of given '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;
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)
{
std::cerr << "Cannot find file " << filename << " to read its dependencies" << std::endl;
exit(1);
}
// split output
tokenize(output, "\n", &lines);
}
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
addDependency( // trim useless info, keep only library name
lines[n].substr(1, lines[n].find(" (") )
);
}
}
void collectSubDependencies()
{
// print status to user
int dep_amount = deps.size();
// recursively collect each dependencie's dependencies
while(true)
{
dep_amount = deps.size();
for(int n=0; n<dep_amount; n++)
{
std::cout << "."; fflush(stdout);
std::vector<std::string> lines;
collectDependencies(deps[n].getOriginalPath(), lines);
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 can not handle them
addDependency( // trim useless info, keep only library name
lines[n].substr(1, lines[n].find(" (") )
);
}//next
}//next
if(deps.size() == dep_amount) break; // no more dependencies were added on this iteration, stop searching
}
}
void createDestDir()
{
std::string dest_folder = Settings::destFolder();
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::string command = std::string("rm -r ") + dest_folder;
if( systemp( command ) != 0)
{
std::cerr << "\n\nError : An error occured while attempting to override dest folder." << std::endl;
exit(1);
}
dest_exists = false;
}
if(!dest_exists)
{
if(Settings::canCreateDir())
{
std::cout << "* Creating output directory " << dest_folder.c_str() << std::endl;
std::string command = std::string("mkdir -p ") + dest_folder;
if( systemp( command ) != 0)
{
std::cerr << "\n\nError : An error occured while creating dest folder." << std::endl;
exit(1);
}
}
else
{
std::cerr << "\n\nError : Dest folder does not exist. Create it or pass the appropriate flag for automatic dest dir creation." << std::endl;
exit(1);
}
}
}
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++)
{
deps[n].print();
}
std::cout << std::endl;
// copy files if requested by user
if(Settings::bundleLibs())
{
createDestDir();
for(int n=0; n<dep_amount; n++)
{
deps[n].copyYourself();
changeLibPathsOnFile(deps[n].getInstallPath());
}
}
const int fileToFixAmount = Settings::fileToFixAmount();
for(int n=0; n<fileToFixAmount; n++)
{
changeLibPathsOnFile(Settings::fileToFix(n));
}
}

34
src/DylibBundler.h Normal file
View File

@ -0,0 +1,34 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef _crawler_
#define _crawler_
#include <string>
void collectDependencies(std::string filename);
void collectSubDependencies();
void doneWithDeps_go();
#endif

94
src/Settings.cpp Normal file
View File

@ -0,0 +1,94 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "Settings.h"
#include <vector>
namespace Settings
{
bool overwrite_files = false;
bool overwrite_dir = false;
bool create_dir = false;
bool canOverwriteFiles(){ return overwrite_files; }
bool canOverwriteDir(){ return overwrite_dir; }
bool canCreateDir(){ return create_dir; }
void canOverwriteFiles(bool permission){ overwrite_files = permission; }
void canOverwriteDir(bool permission){ overwrite_dir = permission; }
void canCreateDir(bool permission){ create_dir = permission; }
bool bundleLibs_bool = false;
bool bundleLibs(){ return bundleLibs_bool; }
void bundleLibs(bool on){ bundleLibs_bool = on; }
std::string dest_folder_str = "./libs/";
std::string destFolder(){ return dest_folder_str; }
void destFolder(std::string path)
{
dest_folder_str = path;
// fix path if needed so it ends with '/'
if( dest_folder_str[ dest_folder_str.size()-1 ] != '/' ) dest_folder_str += "/";
}
std::vector<std::string> files;
void addFileToFix(std::string path){ 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; }
void inside_lib_path(std::string p)
{
inside_path_str = p;
// fix path if needed so it ends with '/'
if( inside_path_str[ inside_path_str.size()-1 ] != '/' ) inside_path_str += "/";
}
std::vector<std::string> prefixes_to_ignore;
void ignore_prefix(std::string prefix)
{
if( prefix[ prefix.size()-1 ] != '/' ) prefix += "/";
prefixes_to_ignore.push_back(prefix);
}
bool isPrefixBundled(std::string prefix)
{
if(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;
const int prefix_amount = prefixes_to_ignore.size();
for(int n=0; n<prefix_amount; n++)
{
if(prefix.compare(prefixes_to_ignore[n]) == 0) return false;
}
return true;
}
}

59
src/Settings.h Normal file
View File

@ -0,0 +1,59 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef _settings_
#define _settings_
#include <string>
namespace Settings
{
bool isPrefixBundled(std::string prefix);
void ignore_prefix(std::string prefix);
bool canOverwriteFiles();
void canOverwriteFiles(bool permission);
bool canOverwriteDir();
void canOverwriteDir(bool permission);
bool canCreateDir();
void canCreateDir(bool permission);
bool bundleLibs();
void bundleLibs(bool on);
std::string destFolder();
void destFolder(std::string path);
void addFileToFix(std::string path);
int fileToFixAmount();
std::string fileToFix(const int n);
std::string inside_lib_path();
void inside_lib_path(std::string p);
}
#endif

178
src/Utils.cpp Normal file
View File

@ -0,0 +1,178 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "Utils.h"
#include "Dependency.h"
#include "Settings.h"
#include <cstdlib>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
/*
void setInstallPath(string loc)
{
path_to_libs_folder = loc;
}*/
void tokenize(const string& str, const char* delim, vector<string>* vectorarg)
{
vector<string>& tokens = *vectorarg;
string delimiters(delim);
// skip delimiters at beginning.
string::size_type lastPos = str.find_first_not_of( delimiters , 0);
// find first "non-delimiter".
string::size_type pos = str.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos)
{
// found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(delimiters, pos);
// find next "non-delimiter"
pos = str.find_first_of(delimiters, lastPos);
}
}
bool fileExists( std::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
}
}
}
void fixLibDependency(string old_lib_path, string new_lib_name, string target_file_name)
{
string command = string("install_name_tool -change ") + old_lib_path + string(" ") + Settings::inside_lib_path() + new_lib_name + string(" ") + target_file_name;
if( systemp( command ) != 0 )
{
cerr << "\n\nError : An error occured while trying to fix depency of " << old_lib_path << " in " << target_file_name << endl;
exit(1);
}
}
void copyFile(string from, string to)
{
bool override = Settings::canOverwriteFiles();
if(!override)
{
if(fileExists( to ))
{
cerr << "\n\nError : File " << to.c_str() << " already exists. Remove it or enable overriding." << endl;
exit(1);
}
}
string override_permission = string(override ? "-f " : "-n ");
// copy file to local directory
string command = string("cp ") + override_permission + from + string(" ") + to;
if( 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;
if( systemp( command2 ) != 0 )
{
cerr << "\n\nError : An error occured while trying to set write permissions on file " << to << endl;
exit(1);
}
}
std::string system_get_output(std::string cmd)
{
FILE * command_output;
char output[128];
int amount_read = 1;
std::string full_output;
try
{
command_output = popen(cmd.c_str(), "r");
if(command_output == NULL) throw;
while(amount_read > 0)
{
amount_read = fread(output, 1, 127, command_output);
if(amount_read <= 0) break;
else
{
output[amount_read] = '\0';
full_output += output;
}
}
}
catch(...)
{
std::cerr << "An error occured while executing command " << cmd.c_str() << std::endl;
pclose(command_output);
return "";
}
int return_value = pclose(command_output);
if(return_value != 0) return "";
return full_output;
}
int systemp(std::string& cmd)
{
std::cout << " " << cmd.c_str() << std::endl;
return system(cmd.c_str());
}

45
src/Utils.h Normal file
View File

@ -0,0 +1,45 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef _utils_h_
#define _utils_h_
#include <string>
#include <vector>
class Library;
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);
// 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);
#endif

149
src/main.cpp Normal file
View File

@ -0,0 +1,149 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Marianne Gagnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <vector>
#include "Settings.h"
#include "Utils.h"
#include "DylibBundler.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?
*/
const std::string VERSION = "0.4.1";
// FIXME - no memory management is done at all (anyway the program closes immediately so who cares?)
std::string installPath = "";
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 << "-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;
}
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)
{
i++;
Settings::addFileToFix(argv[i]);
continue;
}
else if(strcmp(argv[i],"-b")==0 or strcmp(argv[i],"--bundle-deps")==0)
{
Settings::bundleLibs(true);
continue;
}
else if(strcmp(argv[i],"-p")==0 or strcmp(argv[i],"--install-path")==0)
{
i++;
Settings::inside_lib_path(argv[i]);
continue;
}
else if(strcmp(argv[i],"-i")==0 or strcmp(argv[i],"--ignore")==0)
{
i++;
Settings::ignore_prefix(argv[i]);
continue;
}
else if(strcmp(argv[i],"-d")==0 or strcmp(argv[i],"--dest-dir")==0)
{
i++;
Settings::destFolder(argv[i]);
continue;
}
else if(strcmp(argv[i],"-of")==0 or strcmp(argv[i],"--overwrite-files")==0)
{
Settings::canOverwriteFiles(true);
continue;
}
else if(strcmp(argv[i],"-od")==0 or strcmp(argv[i],"--overwrite-dir")==0)
{
Settings::canOverwriteDir(true);
Settings::canCreateDir(true);
continue;
}
else if(strcmp(argv[i],"-cd")==0 or strcmp(argv[i],"--create-dir")==0)
{
Settings::canCreateDir(true);
continue;
}
else if(strcmp(argv[i],"-h")==0 or strcmp(argv[i],"--help")==0)
{
showHelp();
exit(0);
}
else if(i>0)
{
// if we meet an 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);
}
}
if(not Settings::bundleLibs() and Settings::fileToFixAmount()<1)
{
showHelp();
exit(0);
}
std::cout << "* Collecting dependencies"; fflush(stdout);
const int amount = Settings::fileToFixAmount();
for(int n=0; n<amount; n++)
collectDependencies(Settings::fileToFix(n));
collectSubDependencies();
doneWithDeps_go();
return 0;
}