#include #include #include using namespace PICoutManipulators; PIString cmd_copy, cmd_copydir, cmd_suffix; PIString qplatforms; void setCommands() { #ifdef WINDOWS cmd_copy = "copy /y "; cmd_copydir = "copy /y "; cmd_suffix = " 1> NUL"; qplatforms = "windows"; #else cmd_copy = "cp -f "; cmd_copydir = "cp -rf "; # ifdef MAC_OS qplatforms = "cocoa"; # else qplatforms = "xcb"; # endif #endif } void usage() { piCout << Bold << "Deploy tool"; piCout << Cyan << "Version PIP" << Bold << PIPVersion(); piCout << ""; piCout << "Copy all dependencies of to directory "; piCout << "If depend on QtCore library, then copy all basic Qt plugins, "; piCout << " and "; piCout << ""; piCout << Green << Bold << "Usage:" << Default << "\"deploy_tool [-hvfC] [--dependencies [--prefix ]] [-p ] [-s ] [-S ] [-l ] [-D ] [-L | -W | -M ] [-d ] [-q ] [-a ] [-S ] [-P ] -o [ ...]\"" << NewLine; piCout << Green << Bold << "Details:"; piCout << Bold << "Debug control"; piCout << "-h " << Green << "- display this message and exit"; piCout << "-v " << Green << "- be verbose"; piCout << ""; piCout << Bold << "Processing control"; piCout << "-f, --fake " << Green << "- don`t copy, only print"; piCout << "-s " << Green << "- set search pathes for system libraries, may be separated by \";\", default \"/usr/lib\""; piCout << "-l " << Green << "- \"ldd\" path, default \"/usr/bin/ldd\""; piCout << "-L " << Green << "- \"readelf\" path, overrides \"ldd\""; piCout << "-W " << Green << "- \"objdump\" path, overrides \"ldd\""; piCout << "-M " << Green << "- \"otool\" path, overrides \"ldd\""; piCout << "-D " << Green << "- \"dpkg\" path, default \"/usr/bin/dpkg\""; piCout << "-d " << Green << "- dependepcies depth, default 1"; piCout << "-q " << Green << "- path where Qt root dir, default takes from \"qmake -v\""; piCout << "-C, --Conf " << Green << "- make \"qt.conf\""; //piCout << "-Q, --Qt-force " << Green << "- force add Qt root dir, default takes from \"qmake -v\""; piCout << ""; piCout << Bold << "Output control"; piCout << "-o " << Green << "- path for libraries copy to"; piCout << "-p " << Green << "- path for Qt plugins, default \"/plugins\""; piCout << "--dependencies " << Green << "- search dependencies by , print them and copy missing libraries"; piCout << "--prefix " << Green << "- print before dependencies"; piCout << ""; piCout << Bold << "Input control"; piCout << " ... " << Green << "- executable to process"; piCout << "-S " << Green << "- add Qt styles (e.g. \"oxygen,breeze\"), default \"\""; piCout << "-P " << Green << "- add Qt platforms (e.g. \"win,mini\"), default by host system"; piCout << "-a " << Green << "- additional libs, separated by \";\""; } int depth = 1; bool need_qt = false, fake = false, is_ldd = true, is_deps = false; PIString ldd, readelf, objdump, otool, dpkg, out_dir, qt_dir, out_plugins_dir; PIStringList styles, lib_dirs, add_libs, platforms; PISet all_libs, miss_libs, all_deps, frameworks, miss_frameworks; PIString findLib(const PIString & l) { if (PIFile::isExists(l)) return l; piForeachC (PIString & s, lib_dirs) { if (PIFile::isExists(s + l)) { return s + l; } } return ""; } void procLdd(PIString file, bool ext_lib = false, int cur_depth = 0) { ++cur_depth; if (cur_depth > depth) return; piCout << "scan" << file << "..."; PISet cur_libs; if (ext_lib) { if (!all_libs[file]) { cur_libs << file; all_libs << file; } } PIStringList lines; if (is_ldd) { PIProcess proc; proc.setGrabOutput(true); proc.exec(ldd, file); proc.waitForFinish(5000); lines = PIString(proc.readOutput()).split("\n"); } else { PIString cmd; if (!readelf.isEmpty()) { cmd = readelf + " -a " + file; cmd += " | grep \"Shared library:\" | grep -oG \"\\[.*\\]\""; } if (!objdump.isEmpty()) { cmd = objdump + " -p " + file; cmd += " | grep \"DLL Name:\""; } if (!otool.isEmpty()) { cmd = otool + " -L " + file; cmd += " | grep -o \".*(\""; } //piCout << cmd; FILE * fp = popen(cmd.dataAscii(), "r"); PIString vs; if (fp) { const int sz = 256; char in[sz]; memset(in, 0, sz); while (true) { int r = fread(in, 1, sz, fp); if (r <= 0) break; vs += PIString(in, r); } pclose(fp); } lines = vs.split("\n"); if (!objdump.isEmpty()) { piForeach (PIString & l, lines) { l.trim().cutLeft(9).trim(); l.append('.').prepend('.'); } } if (!otool.isEmpty()) { piForeach (PIString & l, lines) { l.trim().cutRight(1).trim(); l.append('.').prepend('.'); } } //piCout << "readelf:" << vs; } piForeachC (PIString & sl, lines) { PIString l = sl.trimmed(); if (!otool.isEmpty()) { if (l.contains(".framework/")) { PIStringList ll = l.split("/"); piForeachRC (PIString & _f, ll) { if (_f.endsWith(".framework")) { l = _f; piBreak; } } frameworks << l; if (l.contains("QtCore") || l.contains("Qt5Core")) need_qt = true; continue; } } if (is_ldd) { if (!l.contains("=>")) continue; l.cutLeft(l.find("=>") + 2); if (l.contains("(")) l.cutRight(l.length() - l.find("(")); l.trim(); if (l.toLowerCase() == "not found") { miss_libs << sl.left(sl.find("=>")).trim(); continue; } } else { l.cutLeft(1).cutRight(1); if (l.isEmpty()) continue; if (!otool.isEmpty()) { PIFile::FileInfo fi; fi.path = l; l = fi.name(); } PIString flp = findLib(l); if (flp.isEmpty()) { piCout << "Can`t find" << l; continue; } l = flp; } if (all_libs[l]) continue; if (!need_qt) { PIFile::FileInfo fi; fi.path = l; PIString ln = fi.name(); if (ln.contains("QtCore") || ln.contains("Qt5Core")) need_qt = true; } cur_libs << l; all_libs << l; } PIVector clibs = cur_libs.toVector(); piForeachC (PIString & l, clibs) { procLdd(l, cur_depth); } } void copyWildcard(const PIString & from, const PIString & to) { piCout << "copy" << from; if (fake || is_deps) return; PIDir(to).make(); #ifdef WINDOWS PIFile::FileInfo fi; fi.path = from; system("robocopy \"" + fi.dir() + "\" \"" + to + "\" \"" + fi.name() + "\" /NJH /NJS /NP /NDL /NS /NC /NFL 1> NUL"); #else system(cmd_copy + from + " \"" + to + "/\"" + cmd_suffix); #endif } void procQt() { //piCout << "qmake ..."; PIString vs; if (qt_dir.isEmpty()) { FILE * fp = popen("qmake -v", "r"); if (fp) { char in[1024]; memset(in, 0, 1024); if (fread(in, 1, 1024, fp)) vs = in; pclose(fp); } if (vs.isEmpty()) { piCout << "Can`t exec \"qmake -v\"!"; return; } } else vs = "QMake version ?.?\nUsing Qt version ?.?.? in " + qt_dir; PIStringList vsl = vs.split("\n"); PIStringList pdirs; pdirs << "imageformats" << "sqldrivers"; if (!fake && !is_deps) PIDir(out_plugins_dir).make(true); piForeach (PIString l, vsl) { if (l.trim().contains("Qt version")) { l.cutLeft(l.find("Qt version") + 10).trim(); PIString qv = l.takeWord(); l.takeWord(); PIString qloc = l.trim(); piCout << "Qt" << qv << "in" << qloc; PIString qdir = qloc; #ifdef WINDOWS if (qt_dir.isEmpty()) qdir += "/../plugins/"; else qdir += "/plugins/"; #else if (qt_dir.isEmpty()) qdir += "/qt5/plugins/"; else qdir += "/plugins/"; #endif piForeachC (PIString & p, pdirs) { copyWildcard(qdir + p + "/*", out_plugins_dir + p); } piForeachC (PIString & s, platforms) { copyWildcard(qdir + "platforms/*" + s + "*", out_plugins_dir + "platforms"); } piForeachC (PIString & s, styles) { copyWildcard(qdir + "styles/*" + s + "*", out_plugins_dir + "styles"); } } } pdirs << "platforms" << "styles"; piForeachC (PIString & p, pdirs) { PIVector fil = PIDir(out_plugins_dir + p).entries(); piForeachC (PIFile::FileInfo & f, fil) { if (f.isFile()) procLdd(f.path); } } } bool procDpkg(const PIString & l) { PIString vs, cmd = dpkg + " -S " + l + " 2> /dev/null"; FILE * fp = popen(cmd.dataAscii(), "r"); if (fp) { const int sz = 256; char in[sz]; memset(in, 0, sz); while (true) { int r = fread(in, 1, sz, fp); if (r <= 0) break; vs += PIString(in, r); } pclose(fp); } if (!vs.isEmpty()) { vs = vs.left(vs.find(":")); if (!vs.isEmpty()) all_deps << vs; return true; } //piCout << "No dep on" << l; return false; } int main(int argc, char * argv[]) { PICLI cli(argc, argv); //piCout << cli.rawArguments(); cli.setOptionalArgumentsCount(-1); cli.addArgument("help"); cli.addArgument("verbose"); cli.addArgument("Conf"); cli.addArgument("fake"); cli.addArgument("dependencies", PIChar('\0')); cli.addArgument("prefix", PIChar('\0'), true); cli.addArgument("output", true); cli.addArgument("pqt_out_plugins", true); cli.addArgument("search_path", true); cli.addArgument("Styles", true); cli.addArgument("Platforms", true); cli.addArgument("ldd", true); cli.addArgument("Lreadelf", true); cli.addArgument("Wobjdump", true); cli.addArgument("Motool", true); cli.addArgument("Dpkg", true); cli.addArgument("depth", true); cli.addArgument("qtdir", true); cli.addArgument("add_libs", true); if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || (cli.optionalArguments().size_s() < 1)) { usage(); return 0; } setCommands(); fake = cli.hasArgument("fake"); piDebug = cli.hasArgument("verbose"); is_deps = cli.hasArgument("dependencies"); PIString bin = cli.optionalArguments()[0]; out_dir = cli.argumentValue("output"); lib_dirs = cli.argumentValue("search_path").split(";"); add_libs = cli.argumentValue("add_libs").split(";"); qt_dir = cli.argumentValue("qtdir"); ldd = cli.argumentValue("ldd"); readelf = cli.argumentValue("Lreadelf"); objdump = cli.argumentValue("Wobjdump"); otool = cli.argumentValue("Motool"); dpkg = cli.argumentValue("Dpkg"); if (dpkg.isEmpty()) dpkg = "/usr/bin/dpkg"; out_plugins_dir = out_dir + "plugins/"; if (!cli.argumentValue("pqt_out_plugins").isEmpty()) { out_plugins_dir = cli.argumentValue("pqt_out_plugins"); if (!out_plugins_dir.endsWith("/")) out_plugins_dir.append('/'); } int etcnt = 0; if (!readelf.isEmpty()) ++etcnt; if (!objdump.isEmpty()) ++etcnt; if (!otool.isEmpty()) ++etcnt; if (etcnt > 1) { piCout << "Can use only one of \"readelf\", \"objdump\" and \"otool\"!"; return 1; } if (etcnt > 0) is_ldd = false; if (!cli.argumentValue("Platforms").isEmpty()) qplatforms = cli.argumentValue("Platforms"); platforms = qplatforms.split(","); piForeach (PIString & s, lib_dirs) { if (!s.endsWith("/")) s += "/"; } if (out_dir.isEmpty()) out_dir = "."; if (!out_dir.endsWith("/")) out_dir += "/"; if (ldd.isEmpty()) ldd = "/usr/bin/ldd"; styles = cli.argumentValue("Styles").split(","); if (!fake) PIDir(out_dir).make(); if (!cli.argumentValue("depth").isEmpty()) depth = cli.argumentValue("depth").toInt(); if (depth > 0) procLdd(bin); piForeach (PIString & s, add_libs) { if (s.isEmpty()) continue; piCout << s << "->" << findLib(s); procLdd(findLib(s), true); } if (need_qt && !is_deps) procQt(); #ifdef WINDOWS out_dir.replaceAll("/", "\\"); #endif PIVector clibs = all_libs.toVector(); piForeach (PIString l, clibs) { #ifdef WINDOWS l.replaceAll("/", "\\"); #endif bool need_cp = true; if (is_deps) need_cp = !procDpkg(l); if (need_cp) { piCout << "copy" << l; if (!fake) system(cmd_copy + "\"" + l + "\" \"" + out_dir + "\"" + cmd_suffix); } } PIVector fwdirs = frameworks.toVector(); piForeachC (PIString & f, fwdirs) { PIString fd = findLib(f); if (!fd.isEmpty()) { piCout << "copy framework" << f; if (!fake) system(cmd_copydir + "\"" + fd + "\" \"" + out_dir + "\"" + cmd_suffix); } else miss_frameworks << f; } if (is_deps) { PICout(PICoutManipulators::AddNone) << cli.argumentValue("prefix"); PICout(PICoutManipulators::AddNewLine) << PIStringList(all_deps.toVector()).join(", "); } else { piCout << "copied" << clibs.size_s() << "files"; if (!miss_libs.isEmpty()) piCout << "Missing libraries:\n - " << PIStringList(miss_libs.toVector()).join("\n - "); if (!miss_frameworks.isEmpty()) piCout << "Missing frameworks:\n - " << PIStringList(miss_frameworks.toVector()).join("\n - "); } return 0; }