diff --git a/CMakeLists.txt b/CMakeLists.txt index 7916a6e..8c98682 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ if (SomeQtFound) else() message(STATUS "None of Qt found, skip Qt-derived targets") endif() -list(INSERT _DIRS 0 cd_utils) +list(INSERT _DIRS 0 cd_utils deploy_tool) foreach(_D ${_DIRS}) include_directories(${_D}) add_subdirectory(${_D}) diff --git a/deploy_tool/CMakeLists.txt b/deploy_tool/CMakeLists.txt new file mode 100644 index 0000000..d06cd99 --- /dev/null +++ b/deploy_tool/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.0) +project(deploy_tool) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if (NOT LIBPROJECT) + find_package(PIP REQUIRED) + option(LIB "System install" 1) + option(DEBUG "Build with -g3" 0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") + if (DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + endif() +endif() +if (MINGW) + find_package(MinGW REQUIRED) +endif() +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${PIP_INCLUDES}) +file(GLOB SRC "*.h" "*.cpp") +add_executable(${PROJECT_NAME} ${SRC}) +target_link_libraries(${PROJECT_NAME} ${PIP_LIBRARY}) + +if(NOT LIBPROJECT) + if(LIB) + if(WIN32) + if(MINGW) + set(CMAKE_INSTALL_PREFIX ${MINGW_DIR}) + endif() + else() + if (DEFINED ANDROID_PLATFORM) + set(CMAKE_INSTALL_PREFIX ${ANDROID_SYSTEM_LIBRARY_PATH}/usr) + else() + if(CMAKE_CROSSCOMPILING) + set(CMAKE_INSTALL_PREFIX ${CMAKE_STAGING_PREFIX}) + else() + set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX}/usr/local) + endif() + endif() + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + message(STATUS "Install ${PROJECT_NAME} to local \"bin\", \"lib\" and \"include\"") + endif() +endif() + +if(LIB) + list(APPEND _ALL_TARGETS ${PROJECT_NAME}) + set(_ALL_TARGETS ${_ALL_TARGETS} PARENT_SCOPE) + if(WIN32) + install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + install(TARGETS ${PROJECT_NAME} DESTINATION bin) + endif() + #message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + install(TARGETS ${PROJECT_NAME} DESTINATION bin) + #message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() diff --git a/deploy_tool/main.cpp b/deploy_tool/main.cpp new file mode 100644 index 0000000..f0e7471 --- /dev/null +++ b/deploy_tool/main.cpp @@ -0,0 +1,291 @@ +#include +#include +#include + +using namespace PICoutManipulators; + +PIString cmd_copy; +PIString qplatform; + +void setCommands() { +#ifdef WINDOWS + cmd_copy = "copy /y "; + qplatform = "*windows*"; +#else + cmd_copy = "cp -f "; +# ifdef MAC_OS + qplatform = "*cocoa*"; +# else + qplatform = "*xcb*"; +# endif +#endif +} + + +void usage() { + piCout << Bold << "Deploy tool"; + piCout << Cyan << "Version PIP" << Bold << PIPVersion(); + piCout << Green << Bold << "Usage:" << Default << "\"deploy_tool [-hv] -o [-s ] [-S ] [-l ] [-L | -O ] [-d ] [-q ] [-a ] [-S ] [-P ] \"" << 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 << "-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 << "-O " << Green << "- \"*-objdump\" path, overrides \"ldd\""; + piCout << "-d " << Green << "- dependepcies depth, default 1"; + piCout << "-q " << Green << "- path where Qt root dir, default takes from \"qmake -v\""; + piCout << "-S " << Green << "- add Qt styles (e.g. \"oxygen,breeze\"), default \"\""; + piCout << "-P " << Green << "- add Qt platform (e.g. \"windows\"), default by host system"; + piCout << ""; + piCout << Bold << "Output control"; + piCout << "-o " << Green << "- path for libraries copy to"; + piCout << ""; + piCout << Bold << "Input control"; + piCout << " " << Green << "- executable to process"; + piCout << "-a " << Green << "- additional libs, may be separated by \";\""; +} + + +int depth = 1; +PIString ldd, readelf, objdump, out_dir, qt_dir; +PIStringList styles, lib_dirs, add_libs, platforms; +PISet all_libs, miss_libs; + + +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; + PISet cur_libs; + if (ext_lib) { + if (!all_libs[file]) { + cur_libs << file; + all_libs << file; + } + } + PIStringList lines; + if (readelf.isEmpty() && objdump.isEmpty()) { + PIProcess proc; + proc.setGrabOutput(true); + piCout << "scan" << file << "..."; + 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 \"\\[.*\\]\""; + } else { + cmd = objdump + " -p " + file; + cmd += " | grep \"DLL Name:\""; + } + //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('.'); + } + } + //piCout << "readelf:" << vs; + } + piForeachC (PIString & sl, lines) { + PIString l = sl.trimmed(); + if (readelf.isEmpty() && objdump.isEmpty()) { + 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; + PIString flp = findLib(l); + if (flp.isEmpty()) { + piCout << "Can`t find" << l; + continue; + } + l = flp; + } + if (all_libs[l]) continue; + 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; + 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"); +#else + system(cmd_copy + from + " \"" + to + "/\""); +#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; + PIString out_plugins_dir = out_dir + "plugins/"; + pdirs << "imageformats" << "sqldrivers"; + 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); + } + copyWildcard(qdir + "platforms/" + qplatform, 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); + } + } +} + + +int main(int argc, char * argv[]) { + PICLI cli(argc, argv); + //piCout << cli.rawArguments(); + cli.setOptionalArgumentsCount(-1); + cli.addArgument("help"); + cli.addArgument("verbose"); + cli.addArgument("output", true); + cli.addArgument("search_path", true); + cli.addArgument("Styles", true); + cli.addArgument("Platforms", true); + cli.addArgument("ldd", true); + cli.addArgument("Ldd", true); + cli.addArgument("Objdump", 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(); + + piDebug = cli.hasArgument("verbose"); + 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("Ldd"); + objdump = cli.argumentValue("Objdump"); + if (!readelf.isEmpty() && !objdump.isEmpty()) { + piCout << "Can`t you both \"readelf\" and \"objdump\"!"; + return 1; + } + if (!cli.argumentValue("Platforms").isEmpty()) + qplatform = cli.argumentValue("Platforms"); + 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(","); + PIDir(out_dir).make(); + if (!cli.argumentValue("depth").isEmpty()) + depth = cli.argumentValue("depth").toInt(); + if (depth > 0) + procLdd(bin); + piForeach (PIString & s, add_libs) { + piCout << s << "->" << findLib(s); + procLdd(findLib(s), true); + } + procQt(); + + PIVector clibs = all_libs.toVector(); + piForeachC (PIString & l, clibs) { + piCout << "copy" << l; + system(cmd_copy + "\"" + l + "\" \"" + out_dir + "\""); + } + piCout << "copied" << clibs.size_s() << "files"; + if (!miss_libs.isEmpty()) { + piCout << "Missing libraries:\n - " << PIStringList(miss_libs.toVector()).join("\n - "); + } + + return 0; +}