Files
pip/utils/deploy_tool/main.cpp
peri4 caa7880cc4 get rid of piForeach
apply some code analyzer recommendations
ICU flag now check if libicu exists
prepare for more accurate growth of containers (limited PoT, then constantly increase size)
2024-11-20 20:01:47 +03:00

830 lines
28 KiB
C++

/*
PIP - Platform Independent Primitives
Deploy tool
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "picli.h"
#include "pidir.h"
#include "piiostream.h"
#define DELIM "::"
using namespace PICoutManipulators;
PIString cmd_copy, cmd_copydir, cmd_suffix, ign_err_suffix;
PIString qplatforms;
void setCommands() {
#ifdef WINDOWS
cmd_copy = "copy /y ";
cmd_copydir = "copy /y ";
cmd_suffix = " 1> NUL";
ign_err_suffix = " 2> NUL";
qplatforms = "windows";
#else
cmd_copy = "cp -f ";
cmd_copydir = "cp -rf ";
ign_err_suffix = " 2> /dev/null";
# ifdef MAC_OS
qplatforms = "cocoa";
# else
qplatforms = "xcb,wayland";
# endif
#endif
}
void usage() {
piCout << Bold << "Deploy tool";
piCout << Cyan << "Version PIP" << Bold << PIPVersion();
piCout << "";
piCout << "Copy all dependencies of <file> to directory <out_path>";
piCout << "You can specify any file or directory as <file>.";
piCout << "In case of directory it will be scan recursively.";
piCout << "";
piCout << "If some Qt dependency found, copy corresponding Qt plugins.";
piCout << "Styles and platforms selected by -S and -P flags,";
piCout << "any other plugins described by --qt-plugins flag in next format:";
piCout << "\"[*=" DELIM "]<plugins>=<regexp>,<regexp>" DELIM "<plugins>=<regexp>,<regexp>\", e.g.";
piCout << "\"sqldrivers=lite,mysql" DELIM "geoservices=" DELIM "position=nmea\".";
piCout << "If no regexp specified for plugins, nothing will be copied.";
piCout << "Default regexp set by \"*=<regexp>\".";
piCout << "\"*=\" disable optional plugins.";
piCout << "";
piCout << Green << Bold << "Usage:" << Default
<< "\"deploy_tool [-hvfC] [--dependencies [--prefix <text>]] "
"[--qt-plugins-dir <d>] [-s <search_path>] [--ignore <libs>] [-S <styles>] "
"[-l <ldd>] [-D <dpkg>] [--dpkg-workdir <d>] [-L <readelf> | -W <objdump> | -M <otool>] "
"[--name-tool <pathS>] [--rpath] [-d <depth>] [-q <qtdir>] [-a <add_libs>] [-S <styles>] "
"[-P <platforms>] [--qt-plugins <d>] [--qt-modules <l>] [--qt-conf-dir <d>] -o <out_path> <file> [<file2> ...]\""
<< NewLine;
piCout << Green << Bold << "Details:";
piCout << Bold << "Debug control";
piCout << "-h, --help " << Green << "- display this message and exit";
piCout << "-v, --verbose " << Green << "- be verbose";
piCout << "";
piCout << Bold << "Processing control";
piCout << "-f, --fake " << Green << "- don`t copy, only print";
piCout << "-s <search_path> " << Green
<< "- set search pathes for system libraries, may be separated by \"" DELIM "\", default \"/usr/lib\"";
piCout << "--ignore <libs> " << Green << "- ignore libraries names, may be separated by \"" DELIM "\", default \"\"";
piCout << "-l <ldd> " << Green << "- \"ldd\" path, default \"/usr/bin/ldd\"";
piCout << "-L <readelf> " << Green << "- \"readelf\" path, overrides \"ldd\"";
piCout << "-W <objdump> " << Green << "- \"objdump\" path, overrides \"ldd\"";
piCout << "-M <otool> " << Green << "- \"otool\" path, overrides \"ldd\"";
piCout << "-D <dpkg> " << Green << "- \"dpkg\" path, default \"/usr/bin/dpkg\"";
piCout << "--dpkg-workdir <d> " << Green << "- dpkg \"admindir\" path, default \"\"";
piCout << "--name-tool <path> " << Green << "- \"install_name_tool\" path, default \"install_name_tool\"";
piCout << "--strip <path> " << Green << "- \"strip\" path, default \"strip\"";
piCout << "--rpath " << Green << "- set rpath for copied files using \"patchelf\"";
piCout << "-d <depth> " << Green << "- maximum dependepcies depth, default 8";
piCout << "";
piCout << Bold << "Qt control";
piCout << "-q <qtdir> " << Green << "- path where Qt root dir, default takes from \"qmake -v\"";
piCout << "-S <styles> " << Green << "- set Qt styles (e.g. \"oxygen,breeze\"), default \"*\"";
piCout << "-P <platforms> " << Green << "- set Qt platforms (e.g. \"win,mini\"), default by host system";
piCout << "--qt-plugins <d> " << Green << "- set Qt plugins description";
piCout << "--qt-modules <list> " << Green << "- additional Qt modules, may be separated by \"" DELIM "\" (e.g. \"Sql" DELIM "Xml\")";
piCout << "--qml-modules <list> " << Green
<< "- additional Qt QML modules, may be separated by \"" DELIM "\" (e.g. \"Qt" DELIM "QtQuick.2\")";
piCout << "";
piCout << Bold << "Output control";
piCout << "-o <out_path> " << Green << "- path for libraries copy to";
piCout << "--qt-plugins-dir <d> " << Green << "- dir where place Qt plugins, default \"<out_path>\"";
piCout << "--qt-qml-dir <d> " << Green << "- dir where place Qt QML modules, default \"<out_path>\"";
piCout << "--qt-conf-dir <dir> " << Green << "- dir where place \"qt.conf\", default \"<out_path>\"";
piCout << "--dependencies " << Green << "- search dependencies by <dpkg>, print them and copy missing libraries";
piCout << "--prefix <text> " << Green << "- print <text> before dependencies";
piCout << "";
piCout << Bold << "Input control";
piCout << "<file> ... " << Green << "- executable to process";
piCout << "-a <add_libs> " << Green
<< "- additional libs, separated by \"" DELIM "\". Libraries will be searched in <search_path>";
}
struct QtDep {
QtDep(const PIString & l = PIString(), const PIStringList & p = PIStringList()): lib(l), plugins(p) {}
PIString lib;
PIStringList plugins;
};
QtDep qt_deps[] = {QtDep("core", PIStringList() << "platforms"),
QtDep("gui", PIStringList() << "imageformats"),
QtDep("widgets", PIStringList() << "styles"),
QtDep("sql", PIStringList() << "sqldrivers"),
QtDep("positioning", PIStringList() << "position"),
QtDep("location", PIStringList() << "geoservices"),
QtDep("multimedia",
PIStringList() << "audio"
<< "mediaservice"
<< "playlistformats"),
QtDep("printsupport", PIStringList() << "printsupport"),
QtDep("virtualkeyboard", PIStringList() << "platforminputcontexts"),
QtDep("sensors",
PIStringList() << "sensors"
<< "sensorgestures"),
QtDep("texttospeech", PIStringList() << "texttospeech"),
QtDep("serialbus", PIStringList() << "canbus"),
QtDep()};
int depth = 8;
bool fake = false, is_ldd = true, is_deps = false, need_qt = false, make_qt_format = true, rpath = false, win_target = false;
PIString ldd, readelf, objdump, otool, dpkg, nametool, strip, out_dir, qt_dir, dpkg_workdir;
PIString qt_pref, qt_suff, qt_conf_dir, qt_plugins_dir, qt_qml_dir, target_dir;
PIStringList styles, lib_dirs, add_libs, platforms, sqldrivers, input_files, plugin_libs, qt_add_libs, qml_list;
PISet<PIString> all_libs, miss_libs, all_deps, frameworks, framework_libs, miss_frameworks, qt_plugins, ignore_libs, qt_libs;
PIMap<PIString, PIStringList> qt_filters;
PIString findLib(const PIString & l) {
if (PIFile::isExists(l)) return l;
for (const auto & s: lib_dirs) {
if (PIFile::isExists(s + l)) return s + l;
if (win_target || l.toLowerCase().endsWith("dll")) {
PIFile::FileInfo info(l);
PIString fn = info.baseName(), fe = info.extension(), nn;
nn = s + fn + "." + fe.toLowerCase();
if (PIFile::isExists(nn)) return nn;
nn = s + fn + "." + fe.toUpperCase();
if (PIFile::isExists(nn)) return nn;
nn = s + fn.toLowerCase() + "." + fe;
if (PIFile::isExists(nn)) return nn;
nn = s + fn.toLowerCase() + "." + fe.toLowerCase();
if (PIFile::isExists(nn)) return nn;
nn = s + fn.toLowerCase() + "." + fe.toUpperCase();
if (PIFile::isExists(nn)) return nn;
nn = s + fn.toUpperCase() + "." + fe;
if (PIFile::isExists(nn)) return nn;
nn = s + fn.toUpperCase() + "." + fe.toLowerCase();
if (PIFile::isExists(nn)) return nn;
nn = s + fn.toUpperCase() + "." + fe.toUpperCase();
if (PIFile::isExists(nn)) return nn;
}
}
return "";
}
PIString frameworkName(const PIString & l) {
if (!l.contains(".framework/")) return "";
PIStringList ll = l.split("/");
piForeachRC(PIString & _f, ll) {
if (_f.endsWith(".framework")) {
return _f;
}
}
return "";
}
PIString frameworkInternalPath(const PIString & l) {
return l.right(l.size_s() - l.findLast('.') - 11);
}
PIString execute(const PIString & cmd) {
FILE * fp = popen(cmd.dataAscii(), "r");
PIString ret;
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;
ret += PIString(in, r);
}
pclose(fp);
}
return ret;
}
PIStringList filter(const PIString & in, const PIString & f) {
PIStringList ret, lines = in.split("\n");
for (const PIString & l: lines)
if (l.contains(f)) ret << l.trimmed();
return ret;
}
void checkQtLib(PIString lib) {
PIString base = lib.toLowerCase(), pref, suff;
if (base.startsWith("lib")) {
pref += "lib";
base.cutLeft(3);
}
if (base.startsWith("qt5")) {
pref += "Qt5";
base.cutLeft(3);
}
if (base.startsWith("qt6")) {
pref += "Qt6";
base.cutLeft(3);
}
if (base.startsWith("qt")) {
pref += "Qt";
base.cutLeft(2);
}
if (base.find('.') >= 0) {
suff = base.right(base.size_s() - base.find('.'));
base = base.left(base.find('.'));
}
for (int i = 0;; ++i) {
if (qt_deps[i].lib.isEmpty()) break;
if (qt_deps[i].lib == base) {
qt_plugins << PISet<PIString>(qt_deps[i].plugins);
// piCout << "add qt plugins" << qt_deps[i].plugins << "now" << qt_plugins;
need_qt = true;
qt_libs << lib;
if (make_qt_format) {
make_qt_format = false;
qt_pref = pref;
qt_suff = suff;
}
break;
}
}
}
void procLdd(PIString file, bool ext_lib = false, int cur_depth = 0) {
++cur_depth;
if (cur_depth > depth) return;
static PIStringList ignore_ext = {"png", "jpg", "jpeg", "gif", "bmp", "svg", "tif", "tiff", "ico", "pdf",
"txt", "cfg", "conf", "json", "qml", "glsl", "fraq", "vert", "geom", "qmltypes",
"metainfo", "ts", "qm", "ttf", "htm", "html", "md", "sms", "smsee", "blockmodel"};
PIString ext = PIFile::FileInfo(file).extension();
if (ignore_ext.contains(ext.toLowerCase())) return;
piCout << "scan" << file << "...";
PISet<PIString> cur_libs;
if (ext_lib) {
if (!all_libs[file]) {
cur_libs << file;
all_libs << file;
}
}
PIStringList lines;
if (is_ldd) {
lines = execute(ldd + " \"" + file + "\"").split("\n");
} else {
PIString cmd;
if (!readelf.isEmpty()) {
lines = filter(execute(readelf + " -a \"" + file + "\""), "Shared library:");
for (PIString & l: lines) {
l.cutRight(1);
l.cutLeft(l.find('[') + 1);
l.trim(); //.append('.').prepend('.');
}
}
if (!objdump.isEmpty()) {
PIString out = execute(objdump + " -p \"" + file + "\"" + ign_err_suffix);
lines = filter(out, "DLL Name:");
lines << filter(out, "NEEDED");
for (PIString & l: lines) {
if (l.startsWith("DLL"))
l.cutLeft(9);
else
l.cutLeft(6);
l.trim(); //.append('.').prepend('.');
}
}
if (!otool.isEmpty()) {
lines = filter(execute(otool + " -L \"" + file + "\""), "(");
for (auto & l: lines) {
l = l.left(l.find('('));
l.trim(); //.append('.').prepend('.');
}
}
}
for (const auto & sl: lines) {
PIString l = sl.trimmed();
if (!otool.isEmpty()) {
PIString fname = frameworkName(l);
if (!fname.isEmpty()) {
frameworks << fname;
framework_libs << l.cutLeft(1).cutRight(1);
checkQtLib(fname);
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).trim();
if (l.isEmpty()) continue;
if (!otool.isEmpty()) {
if (!l.startsWith("/usr/local/")) {
PIFile::FileInfo fi;
fi.path = l;
l = fi.name();
}
}
PIString flp = findLib(l);
if (flp.isEmpty()) {
// piCout << "Can`t find" << l;
miss_libs << l;
continue;
}
l = flp;
}
if (all_libs[l]) continue;
PIFile::FileInfo fi;
fi.path = l;
PIString lname = fi.baseName();
if (lname.startsWith("lib")) lname.cutLeft(3);
// piCout << "check ignore" << lname << ignore_libs;
if (ignore_libs.contains(lname)) {
continue;
}
checkQtLib(fi.name());
cur_libs << l;
all_libs << l;
}
PIVector<PIString> clibs = cur_libs.toVector();
if (!clibs.isEmpty()) piCout << " new dependencies:\n -" << PIStringList(clibs).join("\n - ");
for (const auto & l: clibs) {
procLdd(l, false, cur_depth);
}
}
void copyWildcard(const PIString & from, const PIString & to) {
piCout << "copy" << from;
if (fake) return;
PIDir(to).make();
#ifdef WINDOWS
PIFile::FileInfo fi(from);
system(("robocopy \"" + fi.dir() + "\" \"" + to + "\" \"" + fi.name() + "\" /E /NJH /NJS /NP /NDL /NS /NC /NFL 1> NUL").data());
#else
system((cmd_copy + from + " \"" + to + "/\"" + cmd_suffix).data());
#endif
}
void procQt() {
// piCout << "qmake ...";
PIString vs;
if (qt_dir.isEmpty()) {
vs = execute("qmake -v");
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 = qt_plugins.toVector();
if (!fake) {
PIDir(qt_plugins_dir).make(true);
PIDir(qt_qml_dir).make(true);
}
for (auto 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;
PIStringList suffixes({".", "..", "qt5", "../qt5", "qt6", "../qt6"});
for (const auto & s: suffixes) {
PIString qd = qloc + "/" + s + "/plugins/";
if (piDebug) PICout(AddSpaces) << "Qt plugins root try" << qd << "...";
if (PIDir::isExists(qd + "platforms")) {
qdir = qloc + "/" + s + "/";
piCout << " yes";
break;
}
piCout << " no";
}
if (qdir.isEmpty()) break;
for (const auto & plugin: pdirs) {
PIStringList filters = qt_filters[plugin];
piCout << "PLUG" << plugin << filters;
for (const auto & f: filters) {
if (f.isEmpty()) continue;
copyWildcard(qdir + "plugins/" + plugin + "/" + f, qt_plugins_dir + plugin);
PIVector<PIFile::FileInfo> copied = PIDir(qt_plugins_dir + plugin).entries();
for (const auto & fi: copied) {
if (fi.isFile()) {
procLdd(fi.path);
plugin_libs << fi.path;
}
}
}
}
for (const auto & q: qml_list) {
if (q.isEmpty()) continue;
copyWildcard(qdir + "qml/" + q + "/*", qt_qml_dir + q);
PIVector<PIFile::FileInfo> copied = PIDir(qt_qml_dir + q).allEntries();
for (const auto & fi: copied) {
if (fi.isFile()) {
procLdd(fi.path);
plugin_libs << fi.path;
}
}
}
break;
}
}
}
bool procDpkg(const PIString & l) {
PIString dpkgdir;
if (!dpkg_workdir.isEmpty()) dpkgdir = " --admindir=" + dpkg_workdir;
PIFile::FileInfo fi;
fi.path = l;
PIString cmd = dpkg + dpkgdir + " -S " + fi.name() + ign_err_suffix;
// PICout(true) << cmd;
PIString vs = execute(cmd);
if (!vs.isEmpty()) {
vs = vs.left(vs.find(":"));
if (!vs.isEmpty() && !vs.endsWith("-cross")) all_deps << vs;
return true;
}
// piCout << "No dep on" << l;
return false;
}
void patchNameTool() {
if (nametool.isEmpty()) return;
PIStringList clibs = all_libs.toVector(), flibs = framework_libs.toVector(), patch_list;
PIStringList dlibs;
PIString libname, cmd;
// PICout(DefaultControls) << "start patch" << clibs;
PIFile::FileInfo fi;
patch_list = input_files;
patch_list << plugin_libs;
for (const auto & l: clibs) {
fi.path = l;
patch_list << (out_dir + fi.name());
}
for (const auto & local_lib: patch_list) {
execute("chmod +w \"" + local_lib + "\"");
fi.path = local_lib;
cmd = nametool + " -id \"@executable_path/../Frameworks/" + fi.name() + "\"";
cmd += " \"" + local_lib + "\"" + ign_err_suffix;
// piCout << " " << cmd;
execute(cmd);
}
for (const auto & f: flibs) {
PIString fl = findLib(frameworkName(f));
if (fl.isEmpty()) continue;
patch_list << (out_dir + frameworkName(f) + "/" + frameworkInternalPath(f));
// PICout(DefaultControls) << "map" << f << "->" << (out_dir + frameworkName(f) + "/" + frameworkInternalPath(f));
}
for (const auto & local_lib: patch_list) {
dlibs = filter(execute(otool + " -L \"" + local_lib + "\""), "(");
for (auto & l: dlibs) {
l = l.left(l.find('('));
l.trim();
}
if (!dlibs.isEmpty()) {
execute("chmod +w \"" + local_lib + "\"");
}
piCout << "patch" << local_lib;
for (auto sys_lib: dlibs) {
sys_lib.cutRight(1).trim();
fi.path = sys_lib;
libname = fi.name();
PIString fl = findLib(libname), fname = frameworkName(sys_lib);
// piCout << " check" << sys_lib << fl;
PIString new_path;
if (all_libs.contains(fl) || all_libs.contains(sys_lib)) {
new_path = "@executable_path/../Frameworks/" + libname;
piCout << " depend on lib" << (fl.isEmpty() ? sys_lib : fl);
} else {
if (frameworks.contains(fname)) {
fl = findLib(fname);
if (fl.isEmpty()) continue;
new_path = "@executable_path/../Frameworks/" + fname + "/" + frameworkInternalPath(sys_lib);
piCout << " depend on framework" << fl;
}
}
if (!new_path.isEmpty() && (sys_lib != new_path)) {
cmd = nametool + " -change \"" + sys_lib + "\"";
cmd += " \"" + new_path + "\"";
cmd += " \"" + local_lib + "\"" + ign_err_suffix;
// piCout << " *" << cmd;
execute(cmd);
}
}
}
for (const auto & bin: input_files) {
cmd = nametool + " -add_rpath \"@executable_path/../Frameworks\"";
cmd += " \"" + bin + "\"" + ign_err_suffix;
execute(cmd);
}
}
void patchRPathFile(const PIFile::FileInfo & file) {
PIString fo = execute("file \"" + file.path + "\"");
if (!fo.contains("ELF")) return;
PIString rp = "\\$ORIGIN:\\$ORIGIN/lib";
PIString arp = PIDir(file.dir()).relative(out_dir);
if (!arp.isEmpty() && arp != "." && arp != "lib") rp.append(":\\$ORIGIN/" + arp);
PIString cmd = "patchelf --set-rpath \"" + rp + "\" \"" + file.path + "\"" + ign_err_suffix;
piCout << "set rpath" << file.path << "to" << rp;
execute(cmd);
}
void patchRPath() {
PIStringList dirs({out_dir, target_dir, qt_plugins_dir, qt_qml_dir});
dirs.removeDuplicates().removeStrings(PIString());
for (const auto & d: dirs) {
PIVector<PIFile::FileInfo> files = PIDir(d).allEntries();
for (const auto & f: files) {
if (f.isDir()) continue;
patchRPathFile(f.path);
}
}
}
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("search_path", true);
cli.addArgument("ignore", PIChar('\0'), true);
cli.addArgument("Styles", true);
cli.addArgument("Platforms", true);
cli.addArgument("qt-plugins", PIChar('\0'), true);
cli.addArgument("qt-modules", PIChar('\0'), true);
cli.addArgument("qml-modules", PIChar('\0'), true);
cli.addArgument("qt-conf-dir", PIChar('\0'), true);
cli.addArgument("qt-plugins-dir", PIChar('\0'), true);
cli.addArgument("qt-qml-dir", PIChar('\0'), true);
cli.addArgument("ldd", true);
cli.addArgument("Lreadelf", true);
cli.addArgument("Wobjdump", true);
cli.addArgument("Motool", true);
cli.addArgument("name-tool", PIChar('\0'), true);
cli.addArgument("rpath", PIChar('\0'));
cli.addArgument("strip", PIChar('\0'), true);
cli.addArgument("Dpkg", true);
cli.addArgument("dpkg-workdir", PIChar('\0'), true);
cli.addArgument("depth", true);
cli.addArgument("qtdir", true);
cli.addArgument("add_libs", true);
if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.optionalArguments().isEmpty()) {
usage();
return 0;
}
setCommands();
fake = cli.hasArgument("fake");
piDebug = cli.hasArgument("verbose");
is_deps = cli.hasArgument("dependencies");
out_dir = cli.argumentValue("output");
lib_dirs = cli.argumentValue("search_path").split(DELIM).removeAll("");
add_libs = cli.argumentValue("add_libs").split(DELIM).removeAll("");
ignore_libs = PISet<PIString>(cli.argumentValue("ignore").split(DELIM).removeAll(""));
qt_dir = cli.argumentValue("qtdir");
ldd = cli.argumentValue("ldd");
readelf = cli.argumentValue("Lreadelf");
objdump = cli.argumentValue("Wobjdump");
otool = cli.argumentValue("Motool");
nametool = cli.argumentValue("name-tool");
strip = cli.argumentValue("strip");
rpath = cli.hasArgument("rpath");
if (nametool.isEmpty()) nametool = "install_name_tool";
if (strip.isEmpty()) strip = "strip";
dpkg = cli.argumentValue("Dpkg");
dpkg_workdir = cli.argumentValue("dpkg-workdir");
#ifdef WINDOWS
readelf.replaceAll("/", "\\");
objdump.replaceAll("/", "\\");
otool.replaceAll("/", "\\");
nametool.replaceAll("/", "\\");
dpkg.replaceAll("/", "\\");
#endif
if (dpkg.isEmpty()) dpkg = "/usr/bin/dpkg";
int etcnt = 0;
if (!readelf.isEmpty()) ++etcnt;
if (!objdump.isEmpty()) ++etcnt;
if (!otool.isEmpty()) ++etcnt;
if (etcnt > 1) {
piCerr << "Can use only one of \"readelf\", \"objdump\" and \"otool\"!";
return 1;
}
if (etcnt > 0) is_ldd = false;
if (!qt_dir.isEmpty()) {
PIString qroot = qt_dir;
if (!qroot.endsWith("/")) qroot.append("/");
lib_dirs << (qroot + "bin") << (qroot + "lib") << (qroot + "Frameworks");
}
for (auto & s: lib_dirs) {
s.trim();
if (!s.endsWith("/")) s += "/";
}
if (out_dir.isEmpty()) out_dir = ".";
if (!out_dir.endsWith("/")) out_dir += "/";
if (ldd.isEmpty()) ldd = "/usr/bin/ldd";
out_dir.replaceAll("//", "/");
for (int i = 0;; ++i) {
if (qt_deps[i].lib.isEmpty()) break;
qt_deps[i].plugins.forEach([](const PIString & p) { qt_filters[p] = PIStringList("*"); });
}
if (!cli.argumentValue("Platforms").isEmpty()) qplatforms = cli.argumentValue("Platforms");
if (platforms.contains("windows")) win_target = true;
platforms = qplatforms.split(",");
styles = cli.argumentValue("Styles").split(",");
if (styles.isEmpty()) styles << "";
PIStringList qpd = cli.argumentValue("qt-plugins").toLowerCase().split(DELIM).removeAll("");
for (const auto & qp: qpd) {
int _i = qp.find('=');
if (_i < 0) continue;
PIString pname = qp.left(_i).trim();
PIStringList pfilt = qp.mid(_i + 1).trim().split(",");
if (pname == "*") {
for (int i = 0;; ++i) {
if (qt_deps[i].lib.isEmpty()) break;
qt_deps[i].plugins.forEach([&pfilt](const PIString & p) { qt_filters[p] = pfilt; });
}
} else {
qt_plugins << pname;
qt_filters[pname] = pfilt;
}
}
qt_filters["platforms"] = platforms;
qt_filters["styles"] = styles;
qt_add_libs = cli.argumentValue("qt-modules").split(DELIM).removeAll("");
auto getQtSubdir = [&cli](const char * arg, PIString & var) {
var = cli.argumentValue(arg).trim();
if (var.isEmpty())
var = out_dir;
else {
if (!PIFile::FileInfo(var).isAbsolute()) var = out_dir + "/" + var;
}
if (!var.endsWith("/")) var += "/";
var.replaceAll("//", "/");
};
getQtSubdir("qt-conf-dir", qt_conf_dir);
getQtSubdir("qt-plugins-dir", qt_plugins_dir);
getQtSubdir("qt-qml-dir", qt_qml_dir);
qml_list = cli.argumentValue("qml-modules").split(DELIM).removeAll("");
need_qt = !qt_add_libs.isEmpty();
auto it = qt_filters.makeIterator();
while (it.next())
it.value().forEach([](PIString & i) {
if (!i.startsWith("*")) i.prepend("*");
if (!i.endsWith("*")) i.append("*");
});
// PICout(PICoutManipulators::DefaultControls) << qt_filters;
if (!fake) PIDir(out_dir).make();
if (!cli.argumentValue("depth").isEmpty()) depth = cli.argumentValue("depth").toInt();
cli.optionalArguments().forEach([](const PIString & a) {
if (PIDir::isExists(a)) {
if (target_dir.isEmpty()) target_dir = a;
PIDir(a).allEntries().forEach([](const PIFile::FileInfo & fi) {
if (fi.isFile()) input_files << fi.path;
});
} else {
if (target_dir.isEmpty()) target_dir = PIFile::FileInfo(a).dir();
if (PIFile::isExists(a)) input_files << a;
}
});
// piCout << files;
if (depth > 0) {
input_files.forEach([](const PIString & f) { procLdd(f); });
qt_add_libs.forEach([](const PIString & f) {
PIString l = findLib(qt_pref + f + qt_suff);
if (l.isEmpty()) return;
procLdd(l, true);
checkQtLib(f);
});
}
for (auto & s: add_libs) {
if (s.isEmpty()) continue;
PIString alib = findLib(s);
if (alib.isEmpty()) continue;
piCout << s << "->" << alib;
procLdd(alib, true);
if (!all_libs[alib]) all_libs << alib;
}
if (need_qt) {
bool need_qt_plugins = true;
if (is_deps) { // if Qt in system, then no plugins copy
PIVector<PIString> qlibs = qt_libs.toVector();
piCout << "check for installed Qt" << qlibs;
for (const auto & l: qlibs) {
if (procDpkg(l)) {
piCout << "system Qt found!";
need_qt_plugins = false;
break;
}
}
}
if (need_qt_plugins) {
procQt();
if (!ldd.isEmpty() || !readelf.isEmpty()) { // qt.conf for Linux and Windows
PIFile qtc(qt_conf_dir + "qt.conf", PIIODevice::ReadWrite);
qtc.clear();
PIString pp = PIDir(qt_conf_dir).relative(qt_plugins_dir), qp = PIDir(qt_conf_dir).relative(qt_qml_dir), lp = "lang/";
if (!pp.isEmpty() && !pp.endsWith('/')) pp.append('/');
if (!qp.isEmpty() && !qp.endsWith('/')) qp.append('/');
if (!otool.isEmpty()) {
pp = "PlugIns";
lp = "Resources/lang";
}
// piCout << pp;
PIIOTextStream ts(&qtc);
ts << "[Paths]\n\tPlugins = " << pp << "\n\tQml2Imports = " << qp << "\n\tTranslations = " << lp << "\n";
}
}
}
#ifdef WINDOWS
out_dir.replaceAll("/", "\\");
#endif
PIVector<PIString> clibs = all_libs.toVector();
for (auto l: clibs) {
PIFile::FileInfo fi;
fi.path = l;
#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).data());
if (!otool.isEmpty()) // Apple
system((strip + " -S \"" + out_dir + fi.name() + "\"").data());
else
system((strip + " --strip-unneeded \"" + out_dir + fi.name() + "\"").data());
}
}
}
PIVector<PIString> fwdirs = frameworks.toVector();
for (const auto & f: fwdirs) {
PIString fd = findLib(f);
if (!fd.isEmpty()) {
piCout << "copy framework" << f;
if (!fake) system((cmd_copydir + "\"" + fd + "\" \"" + out_dir + "\"" + cmd_suffix).data());
} else
miss_frameworks << f;
}
if (!otool.isEmpty()) patchNameTool();
if (rpath) patchRPath();
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;
}