diff options
author | Marianne Gagnon <auria.mg@gmail.com> | 2014-05-30 03:48:42 +0400 |
---|---|---|
committer | Marianne Gagnon <auria.mg@gmail.com> | 2014-05-30 03:48:42 +0400 |
commit | f5f45f47d7c6e9d13544612bdecf089f0e187f76 (patch) | |
tree | 9785e74c539af8b816ff575cf0ec5da332799355 /src | |
parent | 52af408561c30befe49b6ed2a499c7dbe7cddfe9 (diff) |
Initial import
Diffstat (limited to 'src')
-rw-r--r-- | src/Dependency.cpp | 270 | ||||
-rw-r--r-- | src/Dependency.h | 66 | ||||
-rw-r--r-- | src/DylibBundler.cpp | 202 | ||||
-rw-r--r-- | src/DylibBundler.h | 34 | ||||
-rw-r--r-- | src/Settings.cpp | 94 | ||||
-rw-r--r-- | src/Settings.h | 59 | ||||
-rw-r--r-- | src/Utils.cpp | 178 | ||||
-rw-r--r-- | src/Utils.h | 45 | ||||
-rw-r--r-- | src/main.cpp | 149 |
9 files changed, 1097 insertions, 0 deletions
diff --git a/src/Dependency.cpp b/src/Dependency.cpp new file mode 100644 index 0000000..8140c58 --- /dev/null +++ b/src/Dependency.cpp @@ -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) +} diff --git a/src/Dependency.h b/src/Dependency.h new file mode 100644 index 0000000..ca4f153 --- /dev/null +++ b/src/Dependency.h @@ -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
\ No newline at end of file diff --git a/src/DylibBundler.cpp b/src/DylibBundler.cpp new file mode 100644 index 0000000..0fef302 --- /dev/null +++ b/src/DylibBundler.cpp @@ -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)); + } +} diff --git a/src/DylibBundler.h b/src/DylibBundler.h new file mode 100644 index 0000000..e637e96 --- /dev/null +++ b/src/DylibBundler.h @@ -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
\ No newline at end of file diff --git a/src/Settings.cpp b/src/Settings.cpp new file mode 100644 index 0000000..12cdad3 --- /dev/null +++ b/src/Settings.cpp @@ -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; +} + +}
\ No newline at end of file diff --git a/src/Settings.h b/src/Settings.h new file mode 100644 index 0000000..701b7a2 --- /dev/null +++ b/src/Settings.h @@ -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
\ No newline at end of file diff --git a/src/Utils.cpp b/src/Utils.cpp new file mode 100644 index 0000000..9bfc2c6 --- /dev/null +++ b/src/Utils.cpp @@ -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()); +} diff --git a/src/Utils.h b/src/Utils.h new file mode 100644 index 0000000..36ead35 --- /dev/null +++ b/src/Utils.h @@ -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
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..b8dbd46 --- /dev/null +++ b/src/main.cpp @@ -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; +} |