diff --git a/utils/deploy_tool/main.cpp b/utils/deploy_tool/main.cpp index f56cc07d..5a18597e 100644 --- a/utils/deploy_tool/main.cpp +++ b/utils/deploy_tool/main.cpp @@ -26,20 +26,22 @@ using namespace PICoutManipulators; -PIString cmd_copy, cmd_copydir, cmd_suffix; +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"; - qplatforms = "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 "; + cmd_copy = "cp -f "; + cmd_copydir = "cp -rf "; + ign_err_suffix = " 2> /dev/null"; # ifdef MAC_OS - qplatforms = "cocoa"; + qplatforms = "cocoa"; # else qplatforms = "xcb,wayland"; # endif @@ -73,42 +75,45 @@ void usage() { << 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 << "-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 " << Green + piCout << "-f, --fake " << Green << "- don`t copy, only print"; + piCout << "-s " << Green << "- set search pathes for system libraries, may be separated by \"" DELIM "\", default \"/usr/lib\""; - piCout << "--ignore " << Green << "- ignore libraries names, may be separated by \"" DELIM "\", default \"\""; - 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 << "--dpkg-workdir " << Green << "- dpkg \"admindir\" path, default \"\""; - piCout << "--name-tool " << Green << "- \"install_name_tool\" path, default \"install_name_tool\""; - piCout << "--strip " << Green << "- \"strip\" path, default \"strip\""; - piCout << "--rpath " << Green << "- set rpath for copied files using \"patchelf\""; - piCout << "-d " << Green << "- maximum dependepcies depth, default 8"; + piCout << "--ignore " << Green << "- ignore libraries names, may be separated by \"" DELIM "\", default \"\""; + 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 << "--dpkg-workdir " << Green << "- dpkg \"admindir\" path, default \"\""; + piCout << "--name-tool " << Green << "- \"install_name_tool\" path, default \"install_name_tool\""; + piCout << "--strip " << Green << "- \"strip\" path, default \"strip\""; + piCout << "--rpath " << Green << "- set rpath for copied files using \"patchelf\""; + piCout << "-d " << Green << "- maximum dependepcies depth, default 8"; piCout << ""; piCout << Bold << "Qt control"; - piCout << "-q " << Green << "- path where Qt root dir, default takes from \"qmake -v\""; - piCout << "-S " << Green << "- set Qt styles (e.g. \"oxygen,breeze\"), default \"*\""; - piCout << "-P " << Green << "- set Qt platforms (e.g. \"win,mini\"), default by host system"; - piCout << "--qt-plugins " << Green << "- set Qt plugins description"; - piCout << "--qt-modules " << Green << "- additional Qt modules, may be separated by \"" DELIM "\" (e.g. \"Sql" DELIM "Xml\")"; + piCout << "-q " << Green << "- path where Qt root dir, default takes from \"qmake -v\""; + piCout << "-S " << Green << "- set Qt styles (e.g. \"oxygen,breeze\"), default \"*\""; + piCout << "-P " << Green << "- set Qt platforms (e.g. \"win,mini\"), default by host system"; + piCout << "--qt-plugins " << Green << "- set Qt plugins description"; + piCout << "--qt-modules " << Green << "- additional Qt modules, may be separated by \"" DELIM "\" (e.g. \"Sql" DELIM "Xml\")"; + piCout << "--qml-modules " << Green + << "- additional Qt QML modules, may be separated by \"" DELIM "\" (e.g. \"Qt" DELIM "QtQuick.2\")"; piCout << ""; piCout << Bold << "Output control"; - piCout << "-o " << Green << "- path for libraries copy to"; - piCout << "--qt-plugins-dir " << Green << "- dir where place Qt plugins, default \"\""; - piCout << "--qt-conf-dir " << Green << "- dir where place \"qt.conf\", default \"\""; - piCout << "--dependencies " << Green << "- search dependencies by , print them and copy missing libraries"; - piCout << "--prefix " << Green << "- print before dependencies"; + piCout << "-o " << Green << "- path for libraries copy to"; + piCout << "--qt-plugins-dir " << Green << "- dir where place Qt plugins, default \"\""; + piCout << "--qt-qml-dir " << Green << "- dir where place Qt QML modules, default \"\""; + piCout << "--qt-conf-dir " << Green << "- dir where place \"qt.conf\", default \"\""; + 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 << "-a " << Green + piCout << " ... " << Green << "- executable to process"; + piCout << "-a " << Green << "- additional libs, separated by \"" DELIM "\". Libraries will be searched in "; } @@ -140,8 +145,8 @@ QtDep qt_deps[] = {QtDep("core", PIStringList() << "platforms"), 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, target_dir; -PIStringList styles, lib_dirs, add_libs, platforms, sqldrivers, input_files, plugin_libs, qt_add_libs; +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 all_libs, miss_libs, all_deps, frameworks, framework_libs, miss_frameworks, qt_plugins, ignore_libs, qt_libs; PIMap qt_filters; @@ -264,6 +269,10 @@ void checkQtLib(PIString lib) { 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", "ico", "pdf", "txt", "conf", "json", "qml", "glsl", "fraq", "vert", "qmltypes", "metainfo", "qm", "ttf"}; + PIString ext = PIFile::FileInfo(file).extension(); + if (ignore_ext.contains(ext.toLowerCase())) return; piCout << "scan" << file << "..."; PISet cur_libs; if (ext_lib) { @@ -286,7 +295,7 @@ void procLdd(PIString file, bool ext_lib = false, int cur_depth = 0) { } } if (!objdump.isEmpty()) { - PIString out = execute(objdump + " -p \"" + file + "\""); + PIString out = execute(objdump + " -p \"" + file + "\"" + ign_err_suffix); lines = filter(out, "DLL Name:"); lines << filter(out, "NEEDED"); for (PIString & l: lines) { @@ -368,9 +377,8 @@ void copyWildcard(const PIString & from, const PIString & to) { if (fake) 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").data()); + 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 @@ -390,7 +398,10 @@ void procQt() { 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); + if (!fake) { + PIDir(qt_plugins_dir).make(true); + PIDir(qt_qml_dir).make(true); + } piForeach(PIString l, vsl) { if (l.trim().contains("Qt version")) { l.cutLeft(l.find("Qt version") + 10).trim(); @@ -404,7 +415,7 @@ void procQt() { PIString qd = qloc + "/" + s + "/plugins/"; if (piDebug) PICout(AddSpaces) << "Qt plugins root try" << qd << "..."; if (PIDir::isExists(qd + "platforms")) { - qdir = qd; + qdir = qloc + "/" + s + "/"; piCout << " yes"; break; } @@ -413,9 +424,10 @@ void procQt() { if (qdir.isEmpty()) break; piForeachC(PIString & plugin, pdirs) { PIStringList filters = qt_filters[plugin]; + piCout << "PLUG" << plugin << filters; piForeachC(PIString & f, filters) { if (f.isEmpty()) continue; - copyWildcard(qdir + plugin + "/" + f, qt_plugins_dir + plugin); + copyWildcard(qdir + "plugins/" + plugin + "/" + f, qt_plugins_dir + plugin); PIVector copied = PIDir(qt_plugins_dir + plugin).entries(); piForeachC(PIFile::FileInfo & fi, copied) { if (fi.isFile()) { @@ -425,6 +437,17 @@ void procQt() { } } } + for (const auto & q: qml_list) { + if (q.isEmpty()) continue; + copyWildcard(qdir + "qml/" + q + "/*", qt_qml_dir + q); + PIVector copied = PIDir(qt_qml_dir + q).allEntries(); + piForeachC(PIFile::FileInfo & fi, copied) { + if (fi.isFile()) { + procLdd(fi.path); + plugin_libs << fi.path; + } + } + } break; } } @@ -436,7 +459,7 @@ bool procDpkg(const PIString & l) { if (!dpkg_workdir.isEmpty()) dpkgdir = " --admindir=" + dpkg_workdir; PIFile::FileInfo fi; fi.path = l; - PIString cmd = dpkg + dpkgdir + " -S " + fi.name() + " 2> /dev/null"; + PIString cmd = dpkg + dpkgdir + " -S " + fi.name() + ign_err_suffix; // PICout(true) << cmd; PIString vs = execute(cmd); if (!vs.isEmpty()) { @@ -466,7 +489,7 @@ void patchNameTool() { execute("chmod +w \"" + local_lib + "\""); fi.path = local_lib; cmd = nametool + " -id \"@executable_path/../Frameworks/" + fi.name() + "\""; - cmd += " \"" + local_lib + "\" 2> /dev/null"; + cmd += " \"" + local_lib + "\"" + ign_err_suffix; // piCout << " " << cmd; execute(cmd); } @@ -507,7 +530,7 @@ void patchNameTool() { if (!new_path.isEmpty() && (sys_lib != new_path)) { cmd = nametool + " -change \"" + sys_lib + "\""; cmd += " \"" + new_path + "\""; - cmd += " \"" + local_lib + "\" 2> /dev/null"; + cmd += " \"" + local_lib + "\"" + ign_err_suffix; // piCout << " *" << cmd; execute(cmd); } @@ -515,7 +538,7 @@ void patchNameTool() { } piForeach(PIString bin, input_files) { cmd = nametool + " -add_rpath \"@executable_path/../Frameworks\""; - cmd += " \"" + bin + "\" 2> /dev/null"; + cmd += " \"" + bin + "\"" + ign_err_suffix; execute(cmd); } } @@ -527,14 +550,14 @@ void patchRPathFile(const PIFile::FileInfo & file) { 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 + "\" 2> /dev/null"; + 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}); + PIStringList dirs({out_dir, target_dir, qt_plugins_dir, qt_qml_dir}); dirs.removeDuplicates().removeStrings(PIString()); piForeachC(PIString & d, dirs) { PIVector files = PIDir(d).allEntries(); @@ -563,8 +586,10 @@ int main(int argc, char * argv[]) { 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); @@ -663,17 +688,25 @@ int main(int argc, char * argv[]) { qt_filters["styles"] = styles; qt_add_libs = cli.argumentValue("qt-modules").split(DELIM); - qt_conf_dir = cli.argumentValue("qt-conf-dir").trim(); - if (qt_conf_dir.isEmpty()) qt_conf_dir = out_dir; - if (!qt_conf_dir.endsWith("/")) qt_conf_dir += "/"; - qt_plugins_dir = cli.argumentValue("qt-plugins-dir").trim(); - if (qt_plugins_dir.isEmpty()) qt_plugins_dir = out_dir; - if (!qt_plugins_dir.endsWith("/")) qt_plugins_dir += "/"; - qt_conf_dir.replaceAll("//", "/"); - qt_plugins_dir.replaceAll("//", "/"); - need_qt = !qt_add_libs.isEmpty(); + 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); - auto it = qt_filters.makeIterator(); + qml_list = cli.argumentValue("qml-modules").split(DELIM); + + need_qt = !qt_add_libs.isEmpty(); + + auto it = qt_filters.makeIterator(); while (it.next()) it.value().forEach([](PIString & i) { if (!i.startsWith("*")) i.prepend("*"); @@ -729,18 +762,19 @@ int main(int argc, char * argv[]) { } if (need_qt_plugins) { procQt(); - if (!ldd.isEmpty() || !readelf.isEmpty()) { // qt.conf for Linux + 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), lp = "lang/"; + 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\tTranslations = " << lp << "\n"; + ts << "[Paths]\n\tPlugins = " << pp << "\n\tQml2Imports = " << qp << "\n\tTranslations = " << lp << "\n"; } } }