diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 34b18bc159..83e15a8bf7 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,133 +1,164 @@ project (krita-and-all-its-deps) # # Build all dependencies for Krita and finally Krita itself. # Parameters: EXTERNALS_DOWNLOAD_DIR place to download all packages # INSTALL_ROOT place to install everything to # MXE_TOOLCHAIN: the toolchain file to cross-compile using MXE # # Example usage: cmake ..\kritadeposx -DEXTERNALS_DOWNLOAD_DIR=/dev2/d -DINSTALL_ROOT=/dev2/i -DWIN64_BUILD=TRUE -DBOOST_LIBRARYDIR=/dev2/i/lib -G "Visual Studio 11 Win64" cmake_minimum_required(VERSION 2.8.6) if(NOT SUBMAKE_JOBS) set(SUBMAKE_JOBS 1) endif(NOT SUBMAKE_JOBS) if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message(FATAL_ERROR "Compiling in the source directory is not supported. Use for example 'mkdir build; cd build; cmake ..'.") endif (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) # Tools must be obtained to work with: include (ExternalProject) # allow specification of a directory with pre-downloaded # requirements if(NOT IS_DIRECTORY ${EXTERNALS_DOWNLOAD_DIR}) message(FATAL_ERROR "No externals download dir set. Use -DEXTERNALS_DOWNLOAD_DIR") endif() if(NOT IS_DIRECTORY ${INSTALL_ROOT}) message(FATAL_ERROR "No install dir set. Use -DINSTALL_ROOT") endif() set(TOP_INST_DIR ${INSTALL_ROOT}) set(EXTPREFIX "${TOP_INST_DIR}") set(CMAKE_PREFIX_PATH "${EXTPREFIX}") if (${CMAKE_GENERATOR} STREQUAL "Visual Studio 14 2015 Win64") SET(GLOBAL_PROFILE -DCMAKE_MODULE_LINKER_FLAGS=/machine:x64 -DCMAKE_EXE_LINKER_FLAGS=/machine:x64 -DCMAKE_SHARED_LINKER_FLAGS=/machine:x64 -DCMAKE_STATIC_LINKER_FLAGS=/machine:x64 ) endif () message( STATUS "CMAKE_GENERATOR: ${CMAKE_GENERATOR}") message( STATUS "CMAKE_CL_64: ${CMAKE_CL_64}") set(GLOBAL_BUILD_TYPE RelWithDebInfo) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DBUILD_TESTING=false) +if (MINGW) + option(QT_ENABLE_DEBUG_INFO "Build Qt with debug info included" OFF) +endif (MINGW) + +set(SECURITY_EXE_LINKER_FLAGS "") +set(SECURITY_SHARED_LINKER_FLAGS "") +set(SECURITY_MODULE_LINKER_FLAGS "") +if (MINGW) + option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) + if (USE_MINGW_HARDENING_LINKER) + set(SECURITY_EXE_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") + set(SECURITY_SHARED_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") + set(SECURITY_MODULE_LINKER_FLAGS "-Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + # Enable high-entropy ASLR for 64-bit + # The image base has to be >4GB for HEASLR to be enabled. + # The values used here are kind of arbitrary. + set(SECURITY_EXE_LINKER_FLAGS "${SECURITY_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") + set(SECURITY_SHARED_LINKER_FLAGS "${SECURITY_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") + set(SECURITY_MODULE_LINKER_FLAGS "${SECURITY_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") + set(GLOBAL_PROFILE ${GLOBAL_PROFILE} + -DCMAKE_EXE_LINKER_FLAGS=${SECURITY_EXE_LINKER_FLAGS} + -DCMAKE_SHARED_LINKER_FLAGS=${SECURITY_SHARED_LINKER_FLAGS} + -DCMAKE_MODULE_LINKER_FLAGS=${SECURITY_MODULE_LINKER_FLAGS} + ) + endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + else (USE_MINGW_HARDENING_LINKER) + message(WARNING "Linker Security Flags not enabled!") + endif (USE_MINGW_HARDENING_LINKER) +endif (MINGW) + if (DEFINED EP_PREFIX) set_directory_properties(PROPERTIES EP_PREFIX ${EP_PREFIX}) endif (DEFINED EP_PREFIX) if (MSVC) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_EXE_LINKER_FLAGS=/PROFILE -DCMAKE_SHARED_LINKER_FLAGS=/PROFILE) set(PATCH_COMMAND myptch) endif() if (MINGW) set(PATCH_COMMAND myptch) endif() if (MSYS) set(PATCH_COMMAND patch) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_TOOLCHAIN_FILE=${MXE_TOOLCHAIN} -DCMAKE_FIND_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_SYSTEM_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include -DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include -DCMAKE_LIBRARY_PATH=${CMAKE_PREFIX_PATH}/lib -DZLIB_ROOT=${CMAKE_PREFIX_PATH} ) set(GLOBAL_AUTOMAKE_PROFILE --host=i686-pc-mingw32 ) endif() if (APPLE) set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_MACOSX_RPATH=ON -DKDE_SKIP_RPATH_SETTINGS=ON -DBUILD_WITH_INSTALL_RPATH=ON -DAPPLE_SUPPRESS_X11_WARNING=ON) set(PATCH_COMMAND patch) endif () if (UNIX AND NOT APPLE) set(LINUX true) set(PATCH_COMMAND patch) endif () # this list must be dependency-ordered add_subdirectory( ext_python ) if (MSVC) add_subdirectory( ext_patch ) add_subdirectory( ext_png2ico ) endif (MSVC) if (MINGW) add_subdirectory( ext_patch ) add_subdirectory( ext_png2ico ) endif (MINGW) add_subdirectory( ext_iconv ) add_subdirectory( ext_gettext ) add_subdirectory( ext_zlib ) add_subdirectory( ext_libxml2 ) add_subdirectory( ext_libxslt ) add_subdirectory( ext_boost ) add_subdirectory( ext_jpeg ) add_subdirectory( ext_tiff ) add_subdirectory( ext_png ) add_subdirectory( ext_eigen3 ) add_subdirectory( ext_expat ) # for exiv2 add_subdirectory( ext_exiv2 ) add_subdirectory( ext_ilmbase ) add_subdirectory( ext_lcms2 ) add_subdirectory( ext_openexr ) add_subdirectory( ext_vc ) add_subdirectory( ext_gsl ) add_subdirectory( ext_fftw3 ) add_subdirectory( ext_ocio ) if (MSVC) add_subdirectory( ext_pthreads ) endif (MSVC) add_subdirectory( ext_fontconfig) add_subdirectory( ext_freetype) add_subdirectory( ext_qt ) add_subdirectory( ext_poppler ) add_subdirectory( ext_libraw ) add_subdirectory( ext_frameworks ) add_subdirectory( ext_sip ) add_subdirectory( ext_pyqt ) if (MSVC OR MINGW) add_subdirectory( ext_drmingw ) endif (MSVC OR MINGW) diff --git a/3rdparty/README.md b/3rdparty/README.md index 3f563afd75..c1d1ee0d98 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -1,245 +1,241 @@ = CMake external projects to build krita's dependencies on Linux, Windows or OSX = If you need to build Krita's dependencies for the following reasons: * you develop on Windows and aren't using Craft * you develop on OSX and aren't using Homebrew * you want to build a generic, distro-agnostic version of Krita for Linux * you develop on Linux, but some dependencies aren't available for your distribution and you know what you're doing, you can use the following guide to build the dependencies that Krita needs. If you develop on Linux and your distribution has all dependencies available, YOU DO NOT NEED THIS GUIDE AND YOU SHOULD STOP READING NOW Otherwise you risk major confusion. == Prerequisites == Note: on all operating systems the entire procedure is done in a terminal window. 1. git: https://git-scm.com/downloads. Make sure git is in your path -2. cmake 3.3.2 or later: https://cmake.org/download/. Make sure cmake is in your path. +2. CMake 3.3.2 or later: https://cmake.org/download/. Make sure cmake is in your path. + * CMake 3.9 does not build Krita properly at the moment, please use 3.8 instead. 3. Make sure you have a compiler: * Linux: gcc, minimum version 4.8 * OSX: clang, you need to install xcode for this - * Windows: mingw-w64 5.4 (by mingw-builds) - - 32-bit (x86) target: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/5.4.0/threads-posix/dwarf/ - - 64-bit (x64) target: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/5.4.0/threads-posix/seh/ + * Windows: mingw-w64 7.1 (by mingw-builds) + - 32-bit (x86) target: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/7.1.0/threads-posix/dwarf/ + - 64-bit (x64) target: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/7.1.0/threads-posix/seh/ Make sure mingw's bin folder is in your path. It might be a good idea to create a batch file which sets the path and start cmd. MSVC is *not* supported at the moment. -4. On Windows, you will also need Python 3.6: https://www.python.org. Make sure to have python.exe in your path. This version of python will be used for two things: to configure Qt and to build the python scripting module. Make sure the version you download is exactly python-3.6.1. Make sure Python is in your path. +4. On Windows, you will also need Python 3.6.2 (technically any versions of 3.6 is fine, but it's not tested): https://www.python.org. Make sure to have that version of python.exe in your path. This version of Python will be used for two things: to configure Qt and to build the Python scripting module. Make sure the version you download is exactly python-3.6.2. Make sure that specific version of Python comes first in your path. == Setup your environment == == Prepare your directory layout == 1. Make a toplevel build directory, say $HOME/dev or c:\dev. We'll refer to this directory as BUILDROOT. You can use a variable for this, on WINDOWS %BUILDROOT%, on OSX and Linux $BUILDROOT. You will have to replace BUILDROOT with $BUILDROOT or %BUILDROOT whenever you copy and paste a command, depending on your operating system. 2. Checkout krita in BUILDROOT cd BUILDROOT git clone git://anongit.kde.org/krita.git 3. Create the build directory mkdir BUILDROOT/b 4. Create the downloads directory mkdir BUILDROOT/d 5. Create the install directory mkdir BUILDROOT/i == Prepare the externals build == 1. enter the BUILDROOT/b directory 2. run cmake: * Linux: export PATH=$BUILDROOT/i/bin:$PATH export PYTHONHOME=$BUILDROOT/i (only if you want to build your own python) cmake ../krita/3rdparty \ -DINSTALL_ROOT=$BUILDROOT/i \ -DEXTERNALS_DOWNLOAD_DIR=$BUILDROOT/d \ -DCMAKE_INSTALL_PREFIX=BUILDROOT/i * OSX: export PATH=$BUILDROOT/i/bin:$PATH export PYTHONHOME=$BUILDROOT/i (only if you want to build your own python) cmake ../krita/3rdparty/ \ -DCMAKE_INSTALL_PREFIX=$BUILDROOT/i \ -DEXTERNALS_DOWNLOAD_DIR=$BUILDROOT/d \ -DINSTALL_ROOT=$BUILDROOT/i - * Windows 32 bits: - - TODO - - * Windows 64 bits: + * Windows 32-bit / 64-bit: Note that the cmake command needs to point to your BUILDROOT like /dev/d, not c:\dev\d. set PATH=%BUILDROOT%\i\bin\;%BUILDROOT%\i\lib;%PATH% set PYTHONHOME=%BUILDROOT%/i (only if you want to build your own python) set PATH=BUILDROOT\i\bin\;BUILDROOT\i\lib;%PATH% cmake ..\krita\3rdparty -DEXTERNALS_DOWNLOAD_DIR=/dev/d -DINSTALL_ROOT=/dev/i -G "MinGW Makefiles" +- If you want to build Qt and some other dependencies with parallel jobs, add + `-DSUBMAKE_JOBS=` to the cmake command where is the number of jobs to + run (if your PC has 4 CPU cores, you might want to set it to 5). + 3. build the packages: With a judicious application of DEPENDS statements, it's possible to build it all in one go, but in my experience that fails always, so it's better to build the dependencies independently. -If you want to use the included version of Python (can be used on Windows to build Qt instead of installing Python separately): - - cmake --build . --config RelWithDebInfo --target ext_python - On Windows: cmake --build . --config RelWithDebInfo --target ext_patch cmake --build . --config RelWithDebInfo --target ext_png2ico cmake --build . --config RelWithDebInfo --target ext_gettext On OSX: cmake --build . --config RelWithDebInfo --target ext_gettext On all operating systems: cmake --build . --config RelWithDebInfo --target ext_qt cmake --build . --config RelWithDebInfo --target ext_zlib cmake --build . --config RelWithDebInfo --target ext_boost Note about boost: check if the headers are installed into i/include/boost, but not into i/include/boost-1.61/boost cmake --build . --config RelWithDebInfo --target ext_eigen3 cmake --build . --config RelWithDebInfo --target ext_exiv2 cmake --build . --config RelWithDebInfo --target ext_fftw3 - -On Windows: - - set FFTW_LIB_DIR=%BUILDROOT%\i\lib - dlltool.exe -k --output-lib %FFTW_LIB_DIR%\libfftw3-3.a --input-def %FFTW_LIB_DIR%\libfftw3-3.def - dlltool.exe -k --output-lib %FFTW_LIB_DIR%\libfftw3f-3.a --input-def %FFTW_LIB_DIR%\libfftw3f-3.def - dlltool.exe -k --output-lib %FFTW_LIB_DIR%\libfftw3l-3.a --input-def %FFTW_LIB_DIR%\libfftw3l-3.def On all operating systems cmake --build . --config RelWithDebInfo --target ext_ilmbase cmake --build . --config RelWithDebInfo --target ext_jpeg cmake --build . --config RelWithDebInfo --target ext_lcms2 cmake --build . --config RelWithDebInfo --target ext_ocio cmake --build . --config RelWithDebInfo --target ext_openexr Note for OSX: On OSX, you need to first build openexr; that will fail; then you need to set the rpath for the two utilities correctly, then try to build openexr again. install_name_tool -add_rpath $BUILD_ROOT/i/lib $BUILD_ROOT/b/ext_openexr/ext_openexr-prefix/src/ext_openexr-build/IlmImf/./b44ExpLogTable install_name_tool -add_rpath $BUILD_ROOT/i/lib $BUILD_ROOT/b/ext_openexr/ext_openexr-prefix/src/ext_openexr-build/IlmImf/./dwaLookups On All operating systems: cmake --build . --config RelWithDebInfo --target ext_png cmake --build . --config RelWithDebInfo --target ext_tiff cmake --build . --config RelWithDebInfo --target ext_gsl cmake --build . --config RelWithDebInfo --target ext_vc cmake --build . --config RelWithDebInfo --target ext_libraw On Windows cmake --build . --config RelWithDebInfo --target ext_freetype cmake --build . --config RelWithDebInfo --target ext_poppler On Linux cmake --build . --config RelWithDebInfo --target ext_kcrash Everywhere else: cmake --build . --config RelWithDebInfo --target ext_kwindowsystem On Windows, if you want to include DrMingw for dumping backtrace on crash: cmake --build . --config RelWithDebInfo --target ext_drmingw +On Windows, if you want to include Python scripting: + + cmake --build . --config RelWithDebInfo --target ext_python + cmake --build . --config RelWithDebInfo --target ext_sip + cmake --build . --config RelWithDebInfo --target ext_pyqt + Note: poppler should be buildable on Linux as well with a home-built freetype and fontconfig, but I don't know how to make fontconfig find freetype, and on Linux, fontconfig is needed for poppler. Poppler is needed for PDF import. Note 2: libcurl still isn't available. Note 3: if you want to build a release, you need to get the binary gettext archives from files.kde.org/krita/build/dependencies: http://files.kde.org/krita/build/dependencies/gettext0.19.8.1-iconv1.14-shared-32.zip http://files.kde.org/krita/build/dependencies/gettext0.19.8.1-iconv1.14-shared-64.zip Take care, these zips contain a libstdc++-6.dll that you don't want in your path when building. == Build Krita == 1. Make a krita build directory: mkdir BUILDROOT/build 2. Enter the BUILDROOT/build 3. Run On Windows Depending on what you want to use, run this command for MSBuild: cmake ..\krita -G "MinGW Makefiles" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -Wno-dev -DDEFINE_NO_DEPRECATED=1 Or this to use jom (faster compiling, uses all cores, ships with QtCreator/pre-built Qt binaries): cmake ..\krita -G "MinGW Makefiles" -DBoost_DEBUG=OFF -DBOOST_INCLUDEDIR=c:\dev\i\include -DBOOST_DEBUG=ON -DBOOST_ROOT=c:\dev\i -DBOOST_LIBRARYDIR=c:\dev\i\lib -DCMAKE_INSTALL_PREFIX=c:\dev\i -DCMAKE_PREFIX_PATH=c:\dev\i -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DHAVE_MEMORY_LEAK_TRACKER=OFF -Wno-dev -DDEFINE_NO_DEPRECATED=1 On Linux cmake ../krita -DCMAKE_INSTALL_PREFIX=BUILDROOT/i -DDEFINE_NO_DEPRECATED=1 -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfobg On OSX cmake ../krita -DCMAKE_INSTALL_PREFIX=$BUILDROOT/i -DDEFINE_NO_DEPRECATED=1 -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DBUNDLE_INSTALL_DIR=$BUILDROOT/i/bin -DCMAKE_BUILD_TYPE=RelWithDebInfo 4. Run On Linux and OSX make make install On Windows Either use MSBuild to build (-- /m tells msbuild to use all your cores): cmake --build . --config RelWithDebInfo --target INSTALL -- /m Or use jom which should be in a path similar to C:\Qt\Qt5.6.0\Tools\QtCreator\bin\jom.exe. So, from the same folder, instead of running cmake run: "C:\Qt\Qt5.6.0\Tools\QtCreator\bin\jom.exe" install 6. Run krita: On Linux BUILDROOT/i/bin/krita On Windows BUILDROOT\i\bin\krita.exe On OSX BUILDROOT/i/bin/krita.app/Contents/MacOS/krita == Packaging a Windows Build == If you want to create a stripped down version of Krita to distribute, after building everything just copy the package_2.cmd file from the "windows" folder inside krita root source folder to BUILDROOT and run it (most likely C:\dev\). That script will copy the necessary files into the specified folder and leave out developer related files. After the script runs there will be two new ZIP files that contain a small portable version of Krita and a separate portable debug version. diff --git a/3rdparty/ext_boost/CMakeLists.txt b/3rdparty/ext_boost/CMakeLists.txt index 72a580b8bb..e20ea14060 100755 --- a/3rdparty/ext_boost/CMakeLists.txt +++ b/3rdparty/ext_boost/CMakeLists.txt @@ -1,86 +1,86 @@ SET(PREFIX_ext_boost "${EXTPREFIX}" ) if (MSVC) if (${CMAKE_GENERATOR} STREQUAL "Visual Studio 14 2015 Win64") ExternalProject_Add( ext_boost DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/boost_1_61_0.zip URL_MD5 015ae4afa6f3e597232bfe1dab949ace CONFIGURE_COMMAND /bootstrap.bat --prefix=${PREFIX_ext_boost} BUILD_COMMAND /b2.exe -j${SUBMAKE_JOBS} --with-system --build-dir=build-dir --prefix=${PREFIX_ext_boost} toolset=msvc-14.0 variant=release link=shared threading=multi architecture=x86 address-model=64 variant=release install INSTALL_COMMAND "" INSTALL_DIR ${EXTPREFIX_boost} UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) ExternalProject_Add_Step( ext_boost post_install COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/boost_system-vc140-mt-1_61.dll ${PREFIX_ext_boost}/bin/boost_system-vc140-mt-1_61.dll DEPENDEES install ) else() ExternalProject_Add( ext_boost DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/boost_1_61_0.zip URL_MD5 015ae4afa6f3e597232bfe1dab949ace CONFIGURE_COMMAND /bootstrap.bat --prefix=${PREFIX_ext_boost} BUILD_COMMAND /b2.exe -j${SUBMAKE_JOBS} --with-system --build-dir=build-dir --prefix=${PREFIX_ext_boost} toolset=msvc-14.0 variant=release link=shared threading=multi architecture=x86 variant=release install INSTALL_COMMAND "" INSTALL_DIR ${EXTPREFIX_boost} UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) ExternalProject_Add_Step( ext_boost post_install COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/boost_system-vc140-mt-1_61.dll ${PREFIX_ext_boost}/bin/boost_system-vc140-mt-1_61.dll DEPENDEES install ) endif() elseif(MINGW) string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" KRITA_boost_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) ExternalProject_Add( ext_boost DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/boost_1_61_0.zip URL_MD5 015ae4afa6f3e597232bfe1dab949ace CONFIGURE_COMMAND /bootstrap.bat gcc --prefix=${PREFIX_ext_boost} - BUILD_COMMAND /b2.exe -j${SUBMAKE_JOBS} --with-system --build-dir=build-dir --prefix=${PREFIX_ext_boost} toolset=gcc variant=release link=shared threading=multi architecture=x86 variant=release install + BUILD_COMMAND /b2.exe -j${SUBMAKE_JOBS} linkflags=${SECURITY_SHARED_LINKER_FLAGS} --with-system --build-dir=build-dir --prefix=${PREFIX_ext_boost} toolset=gcc variant=release link=shared threading=multi architecture=x86 variant=release install INSTALL_COMMAND "" INSTALL_DIR ${EXTPREFIX_boost} UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) ExternalProject_Add_Step( ext_boost post_install COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_boost}/lib/libboost_system-mgw${KRITA_boost_COMPILER_VERSION}-mt-1_61.dll ${PREFIX_ext_boost}/bin/ DEPENDEES install ) else() ExternalProject_Add( ext_boost DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/boost_1_61_0.tar.bz2 URL_MD5 6095876341956f65f9d35939ccea1a9f CONFIGURE_COMMAND /bootstrap.sh --prefix=${PREFIX_ext_boost} --with-libraries=system BUILD_COMMAND /b2 -j${SUBMAKE_JOBS} install INSTALL_COMMAND "" INSTALL_DIR ${PREFIX_ext_boost} UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) endif() diff --git a/3rdparty/ext_frameworks/CMakeLists.txt b/3rdparty/ext_frameworks/CMakeLists.txt index 4810deb6e2..203ceb9747 100755 --- a/3rdparty/ext_frameworks/CMakeLists.txt +++ b/3rdparty/ext_frameworks/CMakeLists.txt @@ -1,226 +1,227 @@ SET(EXTPREFIX_frameworks "${EXTPREFIX}" ) # # All needed frameworks: # # Archive # Config # WidgetsAddons # Completion # CoreAddons # GuiAddons # I18n # ItemModels # ItemViews # WindowSystem # On Linux: # KCrash ExternalProject_Add( ext_extra_cmake_modules DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/extra-cmake-modules-5.24.0.zip URL_MD5 e0c19ba97ebd964f9bdc9110c64ce96a PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ecm_install_to_share.diff INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" ) ExternalProject_Add( ext_karchive DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/karchive-5.24.0.zip URL_MD5 739843accfe9bd85ab2f1582722cf01e INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_extra_cmake_modules ) ExternalProject_Add( ext_kconfig DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kconfig-5.24.0.zip URL_MD5 f87ecff795eb76e4ec6561758a5baf87 PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kconfig.diff INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_karchive ) ExternalProject_Add( ext_kwidgetsaddons DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kwidgetsaddons-5.24.0.zip URL_MD5 0e399b427814a4814c65a3cf407f9d79 INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kwidgetsaddons.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kconfig ) ExternalProject_Add( ext_kcompletion DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kcompletion-5.24.0.zip URL_MD5 e8764251ab45005aa81dba242852300c INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kwidgetsaddons ) ExternalProject_Add( ext_kcoreaddons DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kcoreaddons-5.24.0.zip URL_MD5 2885878625b19ad0300ef3770b897112 INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/desktoptojson.diff + COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kcoreaddons-compile-fix.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kcompletion ) ExternalProject_Add( ext_kguiaddons DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kguiaddons-5.24.0.zip URL_MD5 9bdadbc57d0634816ef80ee9798c3d6c INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kcoreaddons ) ExternalProject_Add( ext_ki18n DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.17/ki18n-5.17.0.zip URL_MD5 7d60380d09f98defbf878ea9daba0fbb INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ki18n.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/ki18n-appdatalocation.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kguiaddons ) ExternalProject_Add( ext_kitemmodels DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kitemmodels-5.24.0.zip URL_MD5 ff41589f48395fc01d5fc7887593779d INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_ki18n ) ExternalProject_Add( ext_kitemviews DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kitemviews-5.24.0.zip URL_MD5 33f638d027a3011a6a69f7484eee3287 INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kitemmodels ) ExternalProject_Add( ext_kimageformats DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kimageformats-5.24.0.zip URL_MD5 c1964516bcb2bfe882858f0c0913deb5 INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kimageformats.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kitemviews ) ExternalProject_Add( ext_kwindowsystem DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kwindowsystem-5.24.0.zip URL_MD5 5915e4f63ded983af6db7db3a6cbae1a INSTALL_DIR ${EXTPREFIX_frameworks} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/kwindowsystem-x11.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kimageformats ) ExternalProject_Add( ext_kcrash DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://download.kde.org/Attic/frameworks/5.24/kcrash-5.24.0.zip URL_MD5 a2e41e6650105fc3ac8fbd44afbae4fe INSTALL_DIR ${EXTPREFIX_frameworks} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_frameworks} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PREFIX_PATH=${EXTPREFIX} -DBUILD_TESTING=false UPDATE_COMMAND "" DEPENDS ext_kwindowsystem ) diff --git a/3rdparty/ext_frameworks/kcoreaddons-compile-fix.diff b/3rdparty/ext_frameworks/kcoreaddons-compile-fix.diff new file mode 100644 index 0000000000..08fc26605c --- /dev/null +++ b/3rdparty/ext_frameworks/kcoreaddons-compile-fix.diff @@ -0,0 +1,19 @@ +commit e0ea4199bc4da359eb91ab51274785d17a4f2909 +Author: Ralf Habacker +Date: Wed Sep 21 13:38:03 2016 +0200 + + Windows compile fix. + +diff --git a/src/lib/util/kuser_win.cpp b/src/lib/util/kuser_win.cpp +index 1d77f89..5c3fa45 100644 +--- a/src/lib/util/kuser_win.cpp ++++ b/src/lib/util/kuser_win.cpp +@@ -853,7 +853,7 @@ static std::unique_ptr queryProcessInformation(TOKEN_INFORMATION_CLASS t + HANDLE _token; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &_token)) { + qWarning("Failed to get the token for the current process: %d", (int)GetLastError()); +- return false; ++ return nullptr; + } + ScopedHANDLE token(_token, handleCloser); + // query required size diff --git a/3rdparty/ext_pyqt/CMakeLists.txt b/3rdparty/ext_pyqt/CMakeLists.txt index 6b15924903..8e429e960c 100644 --- a/3rdparty/ext_pyqt/CMakeLists.txt +++ b/3rdparty/ext_pyqt/CMakeLists.txt @@ -1,34 +1,47 @@ SET(PREFIX_ext_pyqt "${EXTPREFIX}" ) if (UNIX) ExternalProject_Add( ext_pyqt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/PyQt5_gpl-5.6.tar.gz URL_MD5 dbfc885c0548e024ba5260c4f44e0481 CONFIGURE_COMMAND ${PREFIX_ext_pyqt}/bin/python3 /configure.py --confirm-license BUILD_COMMAND make INSTALL_COMMAND make install BUILD_IN_SOURCE 1 UPDATE_COMMAND "" - ALWAYS 0 ) elseif(MINGW) + list(APPEND _PYQT_conf + --confirm-license + --target-py-version 3.6 + --bindir ${PREFIX_ext_pyqt}/bin + --qt ${PREFIX_ext_pyqt} + --sip ${PREFIX_ext_pyqt}/bin/sip.exe + --sip-incdir ${PREFIX_ext_pyqt}/include + --spec win32-g++ + --verbose + --sipdir ${PREFIX_ext_pyqt}/share/sip + --destdir ${PREFIX_ext_pyqt}/share/krita/pykrita + --no-qml-plugin --no-python-dbus --no-qsci-api --no-tools + --disable QtSql --disable QtTest --disable QtWinExtras + ) ExternalProject_Add( ext_pyqt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/PyQt5_gpl-5.6.zip - URL_MD5 916e79bacda1799f8db7bc034043e450 - - CONFIGURE_COMMAND python.exe /configure.py --confirm-license --target-py-version 3.6 --bindir {PREFIX_ext_pyqt}/bin --qt ${PREFIX_ext_pyqt} --sip ${PREFIX_ext_pyqt}/bin/sip.exe --sip-incdir ${PREFIX_ext_pyqt}/include --target-py-version 3.6 --spec win32-g++ --verbose --sipdir ${PREFIX_ext_pyqt}/share/sip --destdir ${PREFIX_ext_pyqt}/bin --no-qml-plugin --no-python-dbus --no-qsci-api --no-tools - BUILD_COMMAND mingw32-make - INSTALL_COMMAND mingw32-make install + URL https://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-5.9/PyQt5_gpl-5.9.zip + URL_MD5 d978884753df265896eda436d8f4e07b + PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/pyqt-configure-fix.patch + + CONFIGURE_COMMAND python.exe /configure.py ${_PYQT_conf} + BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS} CXXFLAGS=-D_hypot=hypot LDFLAGS=${SECURITY_SHARED_LINKER_FLAGS} + INSTALL_COMMAND mingw32-make -j${SUBMAKE_JOBS} install BUILD_IN_SOURCE 1 UPDATE_COMMAND "" - ALWAYS 0 ) endif() diff --git a/3rdparty/ext_pyqt/pyqt-configure-fix.patch b/3rdparty/ext_pyqt/pyqt-configure-fix.patch new file mode 100644 index 0000000000..86d896664e --- /dev/null +++ b/3rdparty/ext_pyqt/pyqt-configure-fix.patch @@ -0,0 +1,15 @@ +--- a/configure.py 2017-07-03 18:25:14.000000000 +0800 ++++ b/configure.py 2017-07-15 01:19:44.622435500 +0800 +@@ -2168,10 +2168,8 @@ + if source is None: + for disabled in run_test_program(mname, test, verbose): + if disabled: +- inform("Disabled %s features: %s" % (mname, +- ', '.join(disabled))) +- +- target_config.pyqt_disabled_features.extend(disabled) ++ inform("Disabled %s features: %s" % (mname, disabled)) ++ target_config.pyqt_disabled_features.append(disabled) + + # Include the module in the build. + target_config.pyqt_modules.append(mname) diff --git a/3rdparty/ext_python/CMakeLists.txt b/3rdparty/ext_python/CMakeLists.txt index 708924e323..c0e99c2a74 100644 --- a/3rdparty/ext_python/CMakeLists.txt +++ b/3rdparty/ext_python/CMakeLists.txt @@ -1,46 +1,47 @@ SET(PREFIX_ext_python "${EXTPREFIX}" ) if (UNIX) ExternalProject_Add( ext_python DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/Python-3.5.2.tar.gz URL_MD5 ea334d398990037a4b8be324bd475c83 CONFIGURE_COMMAND /configure --prefix=${PREFIX_ext_python} ${GLOBAL_AUTOMAKE_PROFILE} --enable-shared BUILD_COMMAND make INSTALL_COMMAND make install COMMAND ${CMAKE_COMMAND} -E copy ${PREFIX_ext_python}/bin/python3 ${PREFIX_ext_python}/bin/python UPDATE_COMMAND "" ALWAYS 0 ) elseif(MINGW) if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") ExternalProject_Add( ext_python DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/python-3.6.1-embed-amd64.zip - URL_MD5 708496ebbe9a730d19d5d288afd216f1 + URL https://www.python.org/ftp/python/3.6.2/python-3.6.2-embed-amd64.zip + URL_MD5 0fdfe9f79e0991815d6fc1712871c17f INSTALL_DIR ${PREFIX_ext_python} CONFIGURE_COMMAND "" BUILD_COMMAND ${CMAKE_COMMAND} -E echo deploying python3 64-bit binary - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory / ${PREFIX_ext_python}/bin + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory / ${PREFIX_ext_python}/python + COMMAND ${CMAKE_COMMAND} -E copy /python3.dll ${PREFIX_ext_python}/bin + COMMAND ${CMAKE_COMMAND} -E copy /python36.dll ${PREFIX_ext_python}/bin UPDATE_COMMAND "" ) else("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - ExternalProject_Add( ext_python DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/python-3.6.1-embed-win32.zip - URL_MD5 8dff09a1b19b7a7dcb915765328484cf + URL https://www.python.org/ftp/python/3.6.2/python-3.6.2-embed-win32.zip + URL_MD5 2ca4768fdbadf6e670e97857bfab83e8 INSTALL_DIR ${PREFIX_ext_python} CONFIGURE_COMMAND "" BUILD_COMMAND ${CMAKE_COMMAND} -E echo deploying python3 32-bit binary - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory / ${PREFIX_ext_python}/bin - - + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory / ${PREFIX_ext_python}/python + COMMAND ${CMAKE_COMMAND} -E copy /python3.dll ${PREFIX_ext_python}/bin + COMMAND ${CMAKE_COMMAND} -E copy /python36.dll ${PREFIX_ext_python}/bin UPDATE_COMMAND "" ) endif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") endif() diff --git a/3rdparty/ext_qt/CMakeLists.txt b/3rdparty/ext_qt/CMakeLists.txt index ba606e1233..6cca1a11d2 100644 --- a/3rdparty/ext_qt/CMakeLists.txt +++ b/3rdparty/ext_qt/CMakeLists.txt @@ -1,183 +1,206 @@ SET(EXTPREFIX_qt "${EXTPREFIX}") if (WIN32) + list(APPEND _QT_conf -skip qt3d -skip qtactiveqt -skip qtcanvas3d + -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects + -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland + -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview + -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools + -no-compile-examples -no-dbus -no-iconv -no-angle -no-qml-debug -no-ssl + -no-openssl -no-libproxy -no-system-proxies -no-icu -no-mtdev + -skip qtcharts -skip qtdatavis3d -skip qtgamepad -skip qtnetworkauth + -skip qtpurchasing -skip qtremoteobjects -skip qtscxml -skip qtserialbus + -skip qtspeech -skip qtvirtualkeyboard + # + -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg + # + -opensource -confirm-license + # + -release -platform win32-g++ -prefix ${EXTPREFIX_qt} + QMAKE_LFLAGS_APP+=${SECURITY_EXE_LINKER_FLAGS} + QMAKE_LFLAGS_SHLIB+=${SECURITY_SHARED_LINKER_FLAGS} + QMAKE_LFLAGS_SONAME+=${SECURITY_SHARED_LINKER_FLAGS} + ) + if (QT_ENABLE_DEBUG_INFO) + # Set the option to build Qt with debugging info enabled + list(APPEND _QT_conf -force-debug-info) + endif(QT_ENABLE_DEBUG_INFO) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.zip - URL_MD5 9d7ea0cadcec7b5a63e8e83686756978 + URL https://download.qt.io/archive/qt/5.9/5.9.1/single/qt-everywhere-opensource-src-5.9.1.zip + URL_MD5 3f2e538ccc468d28bcfdefac96d1e975 PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/disable-wintab.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qtgui-private-headers.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-Don-t-request-the-MIME-image-every-time-Windows-asks.patch COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0002-Hack-always-return-we-support-DIBV5.patch COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0003-Hack-for-fullscreen-workaround.patch INSTALL_DIR ${EXTPREFIX_qt} - CONFIGURE_COMMAND /configure.bat -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++ - - # use this line for building Qt with debugging info enabled - #CONFIGURE_COMMAND /configure.bat -release -force-debug-info -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtdoc -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtsensors -skip qtserialport -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -no-sql-sqlite -nomake examples -nomake tools -no-compile-examples -no-dbus -no-iconv -no-angle -no-ssl -no-openssl -no-wmf-backend -no-qml-debug -no-libproxy -no-system-proxies -no-nis -no-icu -no-mtdev -opensource -confirm-license -release -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -prefix ${EXTPREFIX_qt} -platform win32-g++ + CONFIGURE_COMMAND /configure.bat ${_QT_conf} BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS} INSTALL_COMMAND mingw32-make -j${SUBMAKE_JOBS} install UPDATE_COMMAND "" - BUILD_IN_SOURCE 1 + # Use a short name to reduce the chance of exceeding path length limit + SOURCE_DIR s + BINARY_DIR b DEPENDS ext_patch ) elseif (NOT APPLE) ExternalProject_Add( ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL https://download.qt.io/official_releases/qt/5.6/5.6.1-1/single/qt-everywhere-opensource-src-5.6.1-1.tar.gz URL_MD5 8fdec6d657bc370bd3183d8fe8e9c47a PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qt-no-motion-compression.diff INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure -prefix ${EXTPREFIX_qt} -opensource -confirm-license -nomake examples -no-sql-sqlite -no-openssl -no-qml-debug -no-mtdev -no-journald -no-syslog -no-nis -no-cups -no-tslib -no-directfb -no-linuxfb -no-libproxy -no-pch -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -qt-harfbuzz -qt-freetype -qt-xcb -qt-xkbcommon-x11 -optimized-qmake -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtenginio -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtandroidextras -skip qtserialport BUILD_COMMAND $(MAKE) INSTALL_COMMAND $(MAKE) install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) else( APPLE ) # XCODE_VERSION is set by CMake when using the Xcode generator, otherwise we need # to detect it manually here. if (NOT XCODE_VERSION) execute_process( COMMAND xcodebuild -version OUTPUT_VARIABLE xcodebuild_version OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_FILE /dev/null ) string(REGEX MATCH "Xcode ([0-9]([.][0-9])+)" version_match ${xcodebuild_version}) if (version_match) message(STATUS "${EXTPREFIX_qt}:Identified Xcode Version: ${CMAKE_MATCH_1}") set(XCODE_VERSION ${CMAKE_MATCH_1}) else() # If detecting Xcode version failed, set a crazy high version so we default # to the newest. set(XCODE_VERSION 99) message(WARNING "${EXTPREFIX_qt}:Failed to detect the version of an installed copy of Xcode, falling back to highest supported version. Set XCODE_VERSION to override.") endif(version_match) endif(NOT XCODE_VERSION) # ------------------------------------------------------------------------------- # Verify the Xcode installation on Mac OS like Qt5.7 does/will # If not stop now, the system isn't configured correctly for Qt. # No reason to even proceed. # ------------------------------------------------------------------------------- set(XCSELECT_OUTPUT) find_program(XCSELECT_PROGRAM "xcode-select") if(XCSELECT_PROGRAM) message(STATUS "${EXTPREFIX_qt}:Found XCSELECT_PROGRAM as ${XCSELECT_PROGRAM}") set(XCSELECT_COMMAND ${XCSELECT_PROGRAM} "--print-path") execute_process( COMMAND ${XCSELECT_COMMAND} RESULT_VARIABLE XCSELECT_COMMAND_RESULT OUTPUT_VARIABLE XCSELECT_COMMAND_OUTPUT ERROR_FILE /dev/null ) if(NOT XCSELECT_COMMAND_RESULT) # returned 0, we're ok. string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" XCSELECT_COMMAND_OUTPUT ${XCSELECT_COMMAND_OUTPUT}) else() string(REPLACE ";" " " XCSELECT_COMMAND_STR "${XCSELECT_COMMAND}") # message(STATUS "${XCSELECT_COMMAND_STR}") message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} test failed with status ${XCSELECT_COMMAND_RESULT}") endif() else() message(FATAL_ERROR "${EXTPREFIX_qt}:${XCSELECT_PROGRAM} not found. No Xcode is selected. Use xcode-select -switch to choose an Xcode version") endif() # Belts and suspenders # Beyond all the Xcode and Qt version checking, the proof of the pudding # lies in the success/failure of this command: xcrun --find xcrun. # On failure a patch is necessary, otherwise we're ok # So hard check xcrun now... set(XCRUN_OUTPUT) find_program(XCRUN_PROGRAM "xcrun") if(XCRUN_PROGRAM) message(STATUS "${EXTPREFIX_qt}:Found XCRUN_PROGRAM as ${XCRUN_PROGRAM}") set(XCRUN_COMMAND ${XCRUN_PROGRAM} "--find xcrun") execute_process( COMMAND ${XCRUN_COMMAND} RESULT_VARIABLE XCRUN_COMMAND_RESULT OUTPUT_VARIABLE XCRUN_COMMAND_OUTPUT ERROR_FILE /dev/null ) if(NOT XCRUN_COMMAND_RESULT) # returned 0, we're ok. string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" XCRUN_COMMAND_OUTPUT ${XCRUN_COMMAND_OUTPUT}) else() string(REPLACE ";" " " XCRUN_COMMAND_STR "${XCRUN_COMMAND}") # message(STATUS "${XCRUN_COMMAND_STR}") message(STATUS "${EXTPREFIX_qt}:xcrun test failed with status ${XCRUN_COMMAND_RESULT}") endif() else() message(STATUS "${EXTPREFIX_qt}:xcrun not found -- ${XCRUN_PROGRAM}") endif() # # Now configure ext_qt accordingly # if ((XCRUN_COMMAND_RESULT) AND (NOT (XCODE_VERSION VERSION_LESS 8.0.0))) # Fix Xcode xcrun related issue. # NOTE: This should be fixed by Qt 5.7.1 see here: http://code.qt.io/cgit/qt/qtbase.git/commit/?h=dev&id=77a71c32c9d19b87f79b208929e71282e8d8b5d9 # NOTE: but no one's holding their breath. set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-166202.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase -i ${CMAKE_CURRENT_SOURCE_DIR}/qtbase-configure.patch COMMAND ${PATCH_COMMAND} -p1 -b -d /qtbase/mkspecs/features/mac -i ${CMAKE_CURRENT_SOURCE_DIR}/mac-default.patch) message(STATUS "${EXTPREFIX_qt}:Additional patches injected.") else() # No extra patches will be applied # NOTE: defaults for some untested scenarios like xcrun fails and xcode_version < 8. # NOTE: that is uncharted territory and (hopefully) a very unlikely scenario... set(ext_qt_PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/gerrit-166202.diff COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/macdeploy-qt.diff) endif() # Qt is big - try and parallelize if at all possible include(ProcessorCount) ProcessorCount(NUM_CORES) if(NOT NUM_CORES EQUAL 0) if (NUM_CORES GREATER 2) # be nice... MATH( EXPR NUM_CORES "${NUM_CORES} - 2" ) endif() set(PARALLEL_MAKE "make;-j${NUM_CORES}") message(STATUS "${EXTPREFIX_qt}:Parallelized make: ${PARALLEL_MAKE}") else() set(PARALLEL_MAKE "make") endif() ExternalProject_Add(ext_qt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} LOG_DOWNLOAD ON LOG_UPDATE ON LOG_CONFIGURE ON LOG_BUILD ON LOG_TEST ON LOG_INSTALL ON BUILD_IN_SOURCE ON URL https://download.qt.io/official_releases/qt/5.7/5.7.0/single/qt-everywhere-opensource-src-5.7.0.tar.gz URL_MD5 9a46cce61fc64c20c3ac0a0e0fa41b42 PATCH_COMMAND ${ext_qt_PATCH_COMMAND} INSTALL_DIR ${EXTPREFIX_qt} CONFIGURE_COMMAND /configure -confirm-license -opensource -nomake examples -no-openssl -no-compile-examples -qt-freetype -qt-harfbuzz -opengl desktop -qt-zlib -qt-pcre -qt-libpng -qt-libjpeg -skip qt3d -skip qtactiveqt -skip qtcanvas3d -skip qtconnectivity -skip qtgraphicaleffects -skip qtlocation -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview -skip qtxmlpatterns -prefix ${EXTPREFIX_qt} BUILD_COMMAND ${PARALLEL_MAKE} INSTALL_COMMAND make install UPDATE_COMMAND "" BUILD_IN_SOURCE 1 ) endif() diff --git a/3rdparty/ext_qt/disable-wintab.diff b/3rdparty/ext_qt/disable-wintab.diff index 6a98378662..8484959597 100644 --- a/3rdparty/ext_qt/disable-wintab.diff +++ b/3rdparty/ext_qt/disable-wintab.diff @@ -1,68 +1,68 @@ diff --git a/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp b/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp index 4934b6c..613e8fe 100644 --- a/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/qtbase/src/plugins/platforms/windows/qwindowscontext.cpp -@@ -299,9 +299,6 @@ struct QWindowsContextPrivate { +@@ -238,9 +238,6 @@ QWindowsMimeConverter m_mimeConverter; QWindowsScreenManager m_screenManager; QSharedPointer m_creationContext; --#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE) +-#if QT_CONFIG(tabletevent) - QScopedPointer m_tabletSupport; -#endif const HRESULT m_oleInitializeResult; const QByteArray m_eventType; - QWindow *m_lastActiveWindow; -@@ -346,17 +343,10 @@ QWindowsContext::QWindowsContext() : + QWindow *m_lastActiveWindow = nullptr; +@@ -279,17 +276,10 @@ const QByteArray bv = qgetenv("QT_QPA_VERBOSE"); if (!bv.isEmpty()) QLoggingCategory::setFilterRules(QString::fromLocal8Bit(bv)); --#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE) +-#if QT_CONFIG(tabletevent) - d->m_tabletSupport.reset(QWindowsTabletSupport::create()); - qCDebug(lcQpaTablet) << "Tablet support: " << (d->m_tabletSupport.isNull() ? QStringLiteral("None") : d->m_tabletSupport->description()); -#endif } QWindowsContext::~QWindowsContext() { --#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE) +-#if QT_CONFIG(tabletevent) - d->m_tabletSupport.reset(); // Destroy internal window before unregistering classes. -#endif unregisterWindowClasses(); if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) OleUninitialize(); -@@ -397,12 +387,7 @@ bool QWindowsContext::initTouch(unsigned integrationOptions) +@@ -335,12 +325,7 @@ void QWindowsContext::setTabletAbsoluteRange(int a) { --#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE) +-#if QT_CONFIG(tabletevent) - if (!d->m_tabletSupport.isNull()) - d->m_tabletSupport->setAbsoluteRange(a); -#else Q_UNUSED(a) -#endif } int QWindowsContext::processDpiAwareness() -@@ -792,11 +777,7 @@ QWindowsScreenManager &QWindowsContext::screenManager() +@@ -700,11 +685,7 @@ QWindowsTabletSupport *QWindowsContext::tabletSupport() const { --#if !defined(QT_NO_TABLETEVENT) && !defined(Q_OS_WINCE) +-#if QT_CONFIG(tabletevent) - return d->m_tabletSupport.data(); -#else return 0; -#endif } /*! -@@ -1166,10 +1147,6 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, +@@ -1077,10 +1058,6 @@ *result = LRESULT(MA_NOACTIVATE); return true; } -#ifndef QT_NO_TABLETEVENT - if (!d->m_tabletSupport.isNull()) - d->m_tabletSupport->notifyActivate(); -#endif // !QT_NO_TABLETEVENT if (platformWindow->testFlag(QWindowsWindow::BlockedByModal)) - if (const QWindow *modalWindow = QGuiApplication::modalWindow()) - QWindowsWindow::baseWindowOf(modalWindow)->alertWindow(); + if (const QWindow *modalWindow = QGuiApplication::modalWindow()) { + QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(modalWindow); diff --git a/3rdparty/ext_sip/CMakeLists.txt b/3rdparty/ext_sip/CMakeLists.txt index 51dc55274f..6faea99d69 100644 --- a/3rdparty/ext_sip/CMakeLists.txt +++ b/3rdparty/ext_sip/CMakeLists.txt @@ -1,33 +1,39 @@ SET(PREFIX_ext_sip "${EXTPREFIX}" ) if (UNIX) ExternalProject_Add( ext_sip DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/sip-4.18.tar.gz URL_MD5 78724bf2a79878201c3bc81a1d8248ea CONFIGURE_COMMAND ${PREFIX_ext_sip}/bin/python3 /configure.py -b ${PREFIX_ext_sip}/bin -d ${PREFIX_ext_sip}/sip -e ${PREFIX_ext_sip}/include --sipdir ${PREFIX_ext_sip}/sip --target-py-version 3.5 BUILD_COMMAND make INSTALL_COMMAND make install BUILD_IN_SOURCE 1 UPDATE_COMMAND "" - ALWAYS 0 ) elseif (MINGW) + list(APPEND _SIP_conf + --platform win32-g++ + -b ${PREFIX_ext_sip}/bin + -d ${PREFIX_ext_sip}/share/krita/pykrita + -e ${PREFIX_ext_sip}/include + --sipdir ${PREFIX_ext_sip}/share/sip + --target-py-version 3.6 + ) ExternalProject_Add( ext_sip DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/sip-4.18.zip - URL_MD5 e860d06782962fa02f81aeecba3d82a7 - - CONFIGURE_COMMAND python.exe /configure.py --platform win32-g++ -b ${PREFIX_ext_sip}/bin -d ${PREFIX_ext_sip}/sip -e ${PREFIX_ext_sip}/include --sipdir ${PREFIX_ext_sip}/sip --target-py-version 3.6 - BUILD_COMMAND mingw32-make - INSTALL_COMMAND mingw32-make install + URL https://sourceforge.net/projects/pyqt/files/sip/sip-4.19.3/sip-4.19.3.zip + URL_MD5 1098da9ee1915354fedf38fd6fbe22ce + + CONFIGURE_COMMAND python.exe /configure.py ${_SIP_conf} + BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS} LDFLAGS=${SECURITY_SHARED_LINKER_FLAGS} + INSTALL_COMMAND mingw32-make -j${SUBMAKE_JOBS} install BUILD_IN_SOURCE 1 UPDATE_COMMAND "" - ALWAYS 0 ) endif() diff --git a/3rdparty/ext_vc/CMakeLists.txt b/3rdparty/ext_vc/CMakeLists.txt index 9c218bbfab..0df09a9670 100755 --- a/3rdparty/ext_vc/CMakeLists.txt +++ b/3rdparty/ext_vc/CMakeLists.txt @@ -1,14 +1,12 @@ SET(PREFIX_ext_vc "${EXTPREFIX}" ) ExternalProject_Add( ext_vc DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL http://files.kde.org/krita/build/dependencies/Vc-1.2.0.tar.gz - URL_MD5 f2a213ae4bad0dcf4ec6469e4dad41c1 + URL https://github.com/VcDevel/Vc/releases/download/1.3.2/Vc-1.3.2.tar.gz + URL_HASH SHA1=2a87ff94dd18836cd3934b54180b15b3505bd50c INSTALL_DIR ${PREFIX_ext_vc} - PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/vc1.2_malloc_free.patch CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_vc} -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DCMAKE_SYSTEM_PROCESSOR=x86 UPDATE_COMMAND "" ) - diff --git a/3rdparty/ext_vc/vc1.2_malloc_free.patch b/3rdparty/ext_vc/vc1.2_malloc_free.patch deleted file mode 100644 index b257bd3dac..0000000000 --- a/3rdparty/ext_vc/vc1.2_malloc_free.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- a/common/malloc.h -+++ b/common/malloc.h -@@ -90,6 +90,12 @@ - { - #ifdef __MIC__ - _mm_free(p); -+#elif defined(_WIN32) -+# ifdef __GNUC__ -+ return __mingw_aligned_free(p); -+# else -+ return _aligned_free(p); -+# endif - #else - std::free(p); - #endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 02cc6c8126..ee45de4506 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,665 +1,673 @@ project(krita) message(STATUS "Using CMake version: ${CMAKE_VERSION}") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(MIN_QT_VERSION 5.6.0) option(OVERRIDE_QT_VERSION "Use this to make it possible to build with Qt < 5.6.0. There will be bugs." OFF) if (OVERRIDE_QT_VERSION) set(MIN_QT_VERSION 5.4.0) endif() set(MIN_FRAMEWORKS_VERSION 5.7.0) if (POLICY CMP0002) cmake_policy(SET CMP0002 OLD) endif() if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () if (POLICY CMP0022) cmake_policy(SET CMP0022 OLD) endif () if (POLICY CMP0026) cmake_policy(SET CMP0026 OLD) endif() if (POLICY CMP0042) cmake_policy(SET CMP0042 NEW) endif() if (POLICY CMP0046) cmake_policy(SET CMP0046 OLD) endif () if (POLICY CMP0059) cmake_policy(SET CMP0059 OLD) endif() if (POLICY CMP0063) cmake_policy(SET CMP0063 OLD) endif() if (POLICY CMP0054) cmake_policy(SET CMP0054 OLD) endif() if (POLICY CMP0064) cmake_policy(SET CMP0064 OLD) endif() if (APPLE) set(APPLE_SUPPRESS_X11_WARNING TRUE) set(KDE_SKIP_RPATH_SETTINGS TRUE) set(CMAKE_MACOSX_RPATH 1) set(BUILD_WITH_INSTALL_RPATH 1) add_definitions(-mmacosx-version-min=10.9 -Wno-macro-redefined -Wno-deprecated-register) endif() if (LINUX) if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 AND NOT WINDOWS) add_definitions(-Werror=delete-incomplete) endif() endif() ###################### ####################### ## Constants defines ## ####################### ###################### # define common versions of Krita applications, used to generate kritaversion.h # update these version for every release: set(KRITA_VERSION_STRING "4.0.0-pre-alpha") set(KRITA_STABLE_VERSION_MAJOR 4) # 3 for 3.x, 4 for 4.x, etc. set(KRITA_STABLE_VERSION_MINOR 0) # 0 for 3.0, 1 for 3.1, etc. set(KRITA_VERSION_RELEASE 0) # 88 for pre-alpha, 89 for Alpha, increase for next test releases, set 0 for first Stable, etc. set(KRITA_ALPHA 1) # uncomment only for Alpha #set(KRITA_BETA 1) # uncomment only for Beta #set(KRITA_RC 1) # uncomment only for RC set(KRITA_YEAR 2017) # update every year if(NOT DEFINED KRITA_ALPHA AND NOT DEFINED KRITA_BETA AND NOT DEFINED KRITA_RC) set(KRITA_STABLE 1) # do not edit endif() message(STATUS "Krita version: ${KRITA_VERSION_STRING}") # Define the generic version of the Krita libraries here # This makes it easy to advance it when the next Krita release comes. # 14 was the last GENERIC_KRITA_LIB_VERSION_MAJOR of the previous Krita series # (2.x) so we're starting with 15 in 3.x series, 16 in 4.x series if(KRITA_STABLE_VERSION_MAJOR EQUAL 4) math(EXPR GENERIC_KRITA_LIB_VERSION_MAJOR "${KRITA_STABLE_VERSION_MINOR} + 16") else() # let's make sure we won't forget to update the "16" message(FATAL_ERROR "Reminder: please update offset == 16 used to compute GENERIC_KRITA_LIB_VERSION_MAJOR to something bigger") endif() set(GENERIC_KRITA_LIB_VERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}.0.0") set(GENERIC_KRITA_LIB_SOVERSION "${GENERIC_KRITA_LIB_VERSION_MAJOR}") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") LIST (APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/kde_macro") # fetch git revision for the current build set(KRITA_GIT_SHA1_STRING "") set(KRITA_GIT_BRANCH_STRING "") include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_SHA1) get_git_branch(GIT_BRANCH) if(GIT_SHA1 AND GIT_BRANCH) string(SUBSTRING ${GIT_SHA1} 0 7 GIT_SHA1) set(KRITA_GIT_SHA1_STRING ${GIT_SHA1}) set(KRITA_GIT_BRANCH_STRING ${GIT_BRANCH}) endif() if(NOT DEFINED RELEASE_BUILD) # estimate mode by CMAKE_BUILD_TYPE content if not set on cmdline string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) set(RELEASE_BUILD_TYPES "release" "relwithdebinfo" "minsizerel") list(FIND RELEASE_BUILD_TYPES "${CMAKE_BUILD_TYPE_TOLOWER}" INDEX) if (INDEX EQUAL -1) set(RELEASE_BUILD FALSE) else() set(RELEASE_BUILD TRUE) endif() endif() message(STATUS "Release build: ${RELEASE_BUILD}") # create test make targets enable_testing() # collect list of broken tests, empty here to start fresh with each cmake run set(KRITA_BROKEN_TESTS "" CACHE INTERNAL "KRITA_BROKEN_TESTS") ############ ############# ## Options ## ############# ############ include(FeatureSummary) if (WIN32) option(USE_DRMINGW "Support the Dr. Mingw crash handler (only on windows)" ON) add_feature_info("Dr. Mingw" USE_DRMINGW "Enable the Dr. Mingw crash handler") if (MINGW) option(USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags (mingw-w64)" ON) add_feature_info("Linker Security Flags" USE_MINGW_HARDENING_LINKER "Enable DEP (NX), ASLR and high-entropy ASLR linker flags") if (USE_MINGW_HARDENING_LINKER) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--dynamicbase -Wl,--nxcompat -Wl,--disable-auto-image-base") if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") # Enable high-entropy ASLR for 64-bit # The image base has to be >4GB for HEASLR to be enabled. # The values used here are kind of arbitrary. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x140000000") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--high-entropy-va -Wl,--image-base,0x180000000") endif ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") else (USE_MINGW_HARDENING_LINKER) message(WARNING "Linker Security Flags not enabled!") endif (USE_MINGW_HARDENING_LINKER) endif (MINGW) endif () option(HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal." ON) configure_file(config-hide-safe-asserts.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-hide-safe-asserts.h) add_feature_info("Safe Asserts" HIDE_SAFE_ASSERTS "Don't show message box for \"safe\" asserts, just ignore them automatically and dump a message to the terminal.") option(FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true." OFF) add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a binary release build that can package some extra things like color themes. Linux distributions that build and install Krita into a default system location should not define this option to true.") option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF) add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).") include(MacroJPEG) ########################################################### ## Look for Python3. It is also searched by KF5, ## ## so we should request the correct version in advance ## ########################################################### -find_package(PythonInterp 3.0) -find_package(PythonLibrary 3.0) - +if(MINGW) + # Special check: Building on Windows and ext_python is used + find_package(PythonInterp 3.6) + find_package(PythonLibrary 3.6) + if(PYTHONLIBS_FOUND) + include("${CMAKE_CURRENT_SOURCE_DIR}/PythonWindowsCheck.cmake") + endif(PYTHONLIBS_FOUND) +else(MINGW) + find_package(PythonInterp 3.0) + find_package(PythonLibrary 3.0) +endif(MINGW) ######################## ######################### ## Look for KDE and Qt ## ######################### ######################## find_package(ECM 5.19 REQUIRED NOMODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMOptionalAddSubdirectory) include(ECMAddAppIcon) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(GenerateExportHeader) include(ECMMarkAsTest) include(ECMInstallIcons) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) # do not reorder to be alphabetical: this is the order in which the frameworks # depend on each other. find_package(KF5 ${MIN_FRAMEWORKS_VERSION} REQUIRED COMPONENTS Archive Config WidgetsAddons Completion CoreAddons GuiAddons I18n ItemModels ItemViews WindowSystem ) # KConfig deprecated authorizeKAction. In order to be warning free, # compile with the updated function when the dependency is new enough. # Remove this (and the uses of the define) when the minimum KF5 # version is >= 5.24.0. if (${KF5Config_VERSION} VERSION_LESS "5.24.0" ) message("Old KConfig (< 5.24.0) found.") add_definitions(-DKCONFIG_BEFORE_5_24) endif() find_package(Qt5 ${MIN_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Xml Network PrintSupport Svg Test Concurrent ) include (MacroAddFileDependencies) include (MacroBoolTo01) include (MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("Compiling Krita inside the source directory is not possible. Please refer to the build instruction https://community.kde.org/Krita#Build_Instructions") # Note: OPTIONAL_COMPONENTS does not seem to be reliable # (as of ECM 5.15.0, CMake 3.2) find_package(Qt5Multimedia ${MIN_QT_VERSION}) set_package_properties(Qt5Multimedia PROPERTIES DESCRIPTION "Qt multimedia integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide sound support for animations") macro_bool_to_01(Qt5Multimedia_FOUND HAVE_QT_MULTIMEDIA) configure_file(config-qtmultimedia.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-qtmultimedia.h ) if (NOT WIN32 AND NOT APPLE) find_package(Qt5 ${MIN_QT_VERSION} REQUIRED X11Extras) find_package(Qt5DBus ${MIN_QT_VERSION}) set(HAVE_DBUS ${Qt5DBus_FOUND}) set_package_properties(Qt5DBus PROPERTIES DESCRIPTION "Qt DBUS integration" URL "http://www.qt.io/" TYPE OPTIONAL PURPOSE "Optionally used to provide a dbus api on Linux") find_package(KF5KIO ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5KIO_FOUND HAVE_KIO) set_package_properties(KF5KIO PROPERTIES DESCRIPTION "KDE's KIO Framework" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kio/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used for recent document handling") find_package(KF5Crash ${MIN_FRAMEWORKS_VERSION}) macro_bool_to_01(KF5Crash_FOUND HAVE_KCRASH) set_package_properties(KF5Crash PROPERTIES DESCRIPTION "KDE's Crash Handler" URL "http://api.kde.org/frameworks-api/frameworks5-apidocs/kcrash/html/index.html" TYPE OPTIONAL PURPOSE "Optionally used to provide crash reporting on Linux") find_package(X11 REQUIRED COMPONENTS Xinput) set(HAVE_X11 TRUE) add_definitions(-DHAVE_X11) find_package(XCB COMPONENTS XCB ATOM) set(HAVE_XCB ${XCB_FOUND}) else() set(HAVE_DBUS FALSE) set(HAVE_X11 FALSE) set(HAVE_XCB FALSE) endif() add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_USE_FAST_OPERATOR_PLUS -DQT_USE_FAST_CONCATENATION -DQT_NO_URL_CAST_FROM_STRING -DQT_DISABLE_DEPRECATED_BEFORE=0 ) add_definitions(-DTRANSLATION_DOMAIN=\"krita\") # # The reason for this mode is that the Debug mode disable inlining # if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS_KRITADEVS "-O3 -g" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() if(UNIX) set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") endif() if(WIN32) if(MSVC) # C4522: 'class' : multiple assignment operators specified set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4522") endif() endif() # enable exceptions globally kde_enable_exceptions() # only with this definition will all the FOO_TEST_EXPORT macro do something # TODO: check if this can be moved to only those places which make use of it, # to reduce global compiler definitions that would trigger a recompile of # everything on a change (like adding/removing tests to/from the build) if(BUILD_TESTING) add_definitions(-DCOMPILING_TESTS) endif() set(KRITA_DEFAULT_TEST_DATA_DIR ${CMAKE_SOURCE_DIR}/sdk/tests/data/) macro(macro_add_unittest_definitions) add_definitions(-DFILES_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/") add_definitions(-DFILES_OUTPUT_DIR="${CMAKE_CURRENT_BINARY_DIR}") add_definitions(-DFILES_DEFAULT_DATA_DIR="${KRITA_DEFAULT_TEST_DATA_DIR}") add_definitions(-DSYSTEM_RESOURCES_DATA_DIR="${CMAKE_SOURCE_DIR}/krita/data/") endmacro() # overcome some platform incompatibilities if(WIN32) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif() # set custom krita plugin installdir set(KRITA_PLUGIN_INSTALL_DIR ${LIB_INSTALL_DIR}/kritaplugins) ########################### ############################ ## Required dependencies ## ############################ ########################### find_package(PNG REQUIRED) if (APPLE) # this is not added correctly on OSX -- see http://forum.kde.org/viewtopic.php?f=139&t=101867&p=221242#p221242 include_directories(SYSTEM ${PNG_INCLUDE_DIR}) endif() add_definitions(-DBOOST_ALL_NO_LIB) find_package(Boost REQUIRED COMPONENTS system) # for pigment and stage include_directories(${Boost_INCLUDE_DIRS}) ## ## Test for GNU Scientific Library ## find_package(GSL) set_package_properties(GSL PROPERTIES URL "http://www.gnu.org/software/gsl" TYPE RECOMMENDED PURPOSE "Required by Krita's Transform tool.") macro_bool_to_01(GSL_FOUND HAVE_GSL) configure_file(config-gsl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gsl.h ) ########################### ############################ ## Optional dependencies ## ############################ ########################### ## ## Check for OpenEXR ## find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Compression library" URL "http://www.zlib.net/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic and the PSD plugins") macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB) find_package(OpenEXR) set_package_properties(OpenEXR PROPERTIES DESCRIPTION "High dynamic-range (HDR) image file format" URL "http://www.openexr.com" TYPE OPTIONAL PURPOSE "Required by the Krita OpenEXR filter") macro_bool_to_01(OPENEXR_FOUND HAVE_OPENEXR) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() find_package(TIFF) set_package_properties(TIFF PROPERTIES DESCRIPTION "TIFF Library and Utilities" URL "http://www.remotesensing.org/libtiff" TYPE OPTIONAL PURPOSE "Required by the Krita TIFF filter") find_package(JPEG) set_package_properties(JPEG PROPERTIES DESCRIPTION "Free library for JPEG image compression. Note: libjpeg8 is NOT supported." URL "http://www.libjpeg-turbo.org" TYPE OPTIONAL PURPOSE "Required by the Krita JPEG filter") set(LIBRAW_MIN_VERSION "0.16") find_package(LibRaw ${LIBRAW_MIN_VERSION}) set_package_properties(LibRaw PROPERTIES DESCRIPTION "Library to decode RAW images" URL "http://www.libraw.org" TYPE OPTIONAL PURPOSE "Required to build the raw import plugin") find_package(FFTW3) set_package_properties(FFTW3 PROPERTIES DESCRIPTION "A fast, free C FFT library" URL "http://www.fftw.org/" TYPE OPTIONAL PURPOSE "Required by the Krita for fast convolution operators and some G'Mic features") macro_bool_to_01(FFTW3_FOUND HAVE_FFTW3) find_package(OCIO) set_package_properties(OCIO PROPERTIES DESCRIPTION "The OpenColorIO Library" URL "http://www.opencolorio.org" TYPE OPTIONAL PURPOSE "Required by the Krita LUT docker") macro_bool_to_01(OCIO_FOUND HAVE_OCIO) ## ## Look for OpenGL ## # TODO: see if there is a better check for QtGui being built with opengl support (and thus the QOpenGL* classes) if(Qt5Gui_OPENGL_IMPLEMENTATION) message(STATUS "Found QtGui OpenGL support") else() message(FATAL_ERROR "Did NOT find QtGui OpenGL support. Check your Qt configuration. You cannot build Krita without Qt OpenGL support.") endif() ## ## Test for eigen3 ## find_package(Eigen3 REQUIRED "3.0") set_package_properties(Eigen3 PROPERTIES DESCRIPTION "C++ template library for linear algebra" URL "http://eigen.tuxfamily.org" TYPE REQUIRED) ## ## Test for exiv2 ## set(EXIV2_MIN_VERSION "0.16") find_package(Exiv2 REQUIRED) set_package_properties(Exiv2 PROPERTIES DESCRIPTION "Image metadata library and tools" URL "http://www.exiv2.org" PURPOSE "Required by Krita") ## ## Test for lcms ## find_package(LCMS2 REQUIRED "2.4") set_package_properties(LCMS2 PROPERTIES DESCRIPTION "LittleCMS Color management engine" URL "http://www.littlecms.com" TYPE REQUIRED PURPOSE "Will be used for color management and is necessary for Krita") if(LCMS2_FOUND) if(NOT ${LCMS2_VERSION} VERSION_LESS 2040 ) set(HAVE_LCMS24 TRUE) endif() set(HAVE_REQUIRED_LCMS_VERSION TRUE) set(HAVE_LCMS2 TRUE) endif() ## ## Test for Vc ## set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ) set(HAVE_VC FALSE) if( NOT MSVC) find_package(Vc 1.1.0) set_package_properties(Vc PROPERTIES DESCRIPTION "Portable, zero-overhead SIMD library for C++" URL "https://github.com/VcDevel/Vc" TYPE OPTIONAL PURPOSE "Required by the Krita for vectorization") macro_bool_to_01(Vc_FOUND HAVE_VC) endif() configure_file(config-vc.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-vc.h ) if(HAVE_VC) message(STATUS "Vc found!") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/vc") include (VcMacros) if(Vc_COMPILER_IS_CLANG) set(ADDITIONAL_VC_FLAGS "-Wabi -ffp-contract=fast -fPIC") elseif (NOT MSVC) set(ADDITIONAL_VC_FLAGS "-Wabi -fabi-version=0 -ffp-contract=fast -fPIC") endif() #Handle Vc master if(Vc_COMPILER_IS_GCC OR Vc_COMPILER_IS_CLANG) AddCompilerFlag("-std=c++11" _ok) if(NOT _ok) AddCompilerFlag("-std=c++0x" _ok) endif() endif() macro(ko_compile_for_all_implementations_no_scalar _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() macro(ko_compile_for_all_implementations _objs _src) vc_compile_for_all_implementations(${_objs} ${_src} FLAGS ${ADDITIONAL_VC_FLAGS} ONLY Scalar SSE2 SSSE3 SSE4_1 AVX AVX2+FMA+BMI2) endmacro() endif() set(CMAKE_MODULE_PATH ${OLD_CMAKE_MODULE_PATH} ) add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) if(WIN32) set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS} ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} ) endif() ## ## Test endianess ## include (TestBigEndian) test_big_endian(CMAKE_WORDS_BIGENDIAN) ## ## Test for qt-poppler ## find_package(Poppler COMPONENTS Qt5) set_package_properties(Poppler PROPERTIES DESCRIPTION "A PDF rendering library" URL "http://poppler.freedesktop.org" TYPE OPTIONAL PURPOSE "Required by the Krita PDF filter.") ## ## Test for pthreads (for G'Mic) ## find_package(Threads) set_package_properties(Threads PROPERTIES DESCRIPTION "PThreads - A low-level threading library" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic plugin") ## ## Test for OpenMP (for G'Mic) ## find_package(OpenMP) set_package_properties(OpenMP PROPERTIES DESCRIPTION "A low-level parallel execution library" URL "http://openmp.org/wp/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic plugin") ## ## Test for Curl (for G'Mic) ## find_package(CURL) set_package_properties(CURL PROPERTIES DESCRIPTION "A tool to fetch remote data" URL "http://curl.haxx.se/" TYPE OPTIONAL PURPOSE "Optionally used by the G'Mic plugin") ############################ ############################# ## Add Krita helper macros ## ############################# ############################ include(MacroKritaAddBenchmark) #################### ##################### ## Define includes ## ##################### #################### # for config.h and includes (if any?) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces ) add_subdirectory(libs) add_subdirectory(plugins) add_subdirectory(benchmarks) add_subdirectory(krita) configure_file(KoConfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/KoConfig.h ) configure_file(config_convolution.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_convolution.h) configure_file(config-ocio.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ocio.h ) check_function_exists(powf HAVE_POWF) configure_file(config-powf.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-powf.h) message("\nBroken tests:") foreach(tst ${KRITA_BROKEN_TESTS}) message(" * ${tst}") endforeach() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/PythonWindowsCheck.cmake b/PythonWindowsCheck.cmake new file mode 100644 index 0000000000..a6f768abaf --- /dev/null +++ b/PythonWindowsCheck.cmake @@ -0,0 +1,23 @@ +# Check whether the found python is the same as ext_python +# HACK: Find pythonxx.dll and compare equality. Probably not the best idea... +# TODO: Check the python version + +set(_check_python_dll "python36.dll") + +if(NOT ${PYTHONLIBS_VERSION_STRING} VERSION_EQUAL "3.6.2") + message(FATAL_ERROR "Windows build with Python requires Python 3.6.2, found version ${PYTHONLIBS_VERSION_STRING} instead.") +else() + if(EXISTS "${CMAKE_INSTALL_PREFIX}/python/${_check_python_dll}") + message(STATUS "python36.dll is found in \"${CMAKE_INSTALL_PREFIX}/python/\".") + file(SHA1 "${CMAKE_INSTALL_PREFIX}/python/${_check_python_dll}" _ext_python_dll_sha1) + get_filename_component(_found_python_dir ${PYTHON_EXECUTABLE} DIRECTORY) + file(SHA1 "${_found_python_dir}/${_check_python_dll}" _found_python_dll_sha1) + if(NOT ${_ext_python_dll_sha1} STREQUAL ${_found_python_dll_sha1}) + message(FATAL_ERROR "The found ${_check_python_dll} is not the same as the ${_check_python_dll} from ext_python.") + endif() + else() + message(FATAL_ERROR "${_check_python_dll} is NOT found in \"${CMAKE_INSTALL_PREFIX}/python/\".") + endif() +endif() + +unset(_check_python_dll) diff --git a/cmake/modules/FindPyQt5.cmake b/cmake/modules/FindPyQt5.cmake index b4a52e156c..f4b142cb4d 100644 --- a/cmake/modules/FindPyQt5.cmake +++ b/cmake/modules/FindPyQt5.cmake @@ -1,53 +1,54 @@ # Find PyQt5 # ~~~~~~~~~~ # Copyright (c) 2014, Simon Edwards # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # PyQt5 website: http://www.riverbankcomputing.co.uk/pyqt/index.php # # Find the installed version of PyQt5. FindPyQt5 should only be called after # Python has been found. # # This file defines the following variables: # # PYQT5_VERSION - The version of PyQt5 found expressed as a 6 digit hex number # suitable for comparison as a string # # PYQT5_VERSION_STR - The version of PyQt5 as a human readable string. # # PYQT5_VERSION_TAG - The PyQt version tag using by PyQt's sip files. # # PYQT5_SIP_DIR - The directory holding the PyQt5 .sip files. # # PYQT5_SIP_FLAGS - The SIP flags used to build PyQt. IF(EXISTS PYQT5_VERSION) # Already in cache, be silent SET(PYQT5_FOUND TRUE) ELSE(EXISTS PYQT5_VERSION) FIND_FILE(_find_pyqt5_py FindPyQt5.py PATHS ${CMAKE_MODULE_PATH}) - EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} ${_find_pyqt5_py} OUTPUT_VARIABLE pyqt5_config) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_PREFIX_PATH}/share/krita/pykrita" ${PYTHON_EXECUTABLE} ${_find_pyqt5_py} OUTPUT_VARIABLE pyqt5_config) + IF(pyqt5_config) STRING(REGEX REPLACE "^pyqt_version:([^\n]+).*$" "\\1" PYQT5_VERSION ${pyqt5_config}) STRING(REGEX REPLACE ".*\npyqt_version_str:([^\n]+).*$" "\\1" PYQT5_VERSION_STR ${pyqt5_config}) STRING(REGEX REPLACE ".*\npyqt_version_tag:([^\n]+).*$" "\\1" PYQT5_VERSION_TAG ${pyqt5_config}) STRING(REGEX REPLACE ".*\npyqt_sip_dir:([^\n]+).*$" "\\1" PYQT5_SIP_DIR ${pyqt5_config}) STRING(REGEX REPLACE ".*\npyqt_sip_flags:([^\n]+).*$" "\\1" PYQT5_SIP_FLAGS ${pyqt5_config}) SET(PYQT5_FOUND TRUE) ENDIF(pyqt5_config) IF(PYQT5_FOUND) IF(NOT PYQT5_FIND_QUIETLY) MESSAGE(STATUS "Found PyQt5 version: ${PYQT5_VERSION_STR}") ENDIF(NOT PYQT5_FIND_QUIETLY) ELSE(PYQT5_FOUND) IF(PYQT5_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find PyQt5.") ENDIF(PYQT5_FIND_REQUIRED) ENDIF(PYQT5_FOUND) ENDIF(EXISTS PYQT5_VERSION) diff --git a/cmake/modules/FindSIP.cmake b/cmake/modules/FindSIP.cmake index 0cbc853ae7..4bb8b40973 100644 --- a/cmake/modules/FindSIP.cmake +++ b/cmake/modules/FindSIP.cmake @@ -1,68 +1,68 @@ # Find SIP # ~~~~~~~~ # # SIP website: http://www.riverbankcomputing.co.uk/sip/index.php # # Find the installed version of SIP. FindSIP should be called after Python # has been found. # # This file defines the following variables: # # SIP_VERSION - The version of SIP found expressed as a 6 digit hex number # suitable for comparison as a string. # # SIP_VERSION_STR - The version of SIP found as a human readable string. # # SIP_EXECUTABLE - Path and filename of the SIP command line executable. # # SIP_INCLUDE_DIR - Directory holding the SIP C++ header file. # # SIP_DEFAULT_SIP_DIR - Default directory where .sip files should be installed # into. # Copyright (c) 2007, Simon Edwards # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. IF(SIP_VERSION) # Already in cache, be silent SET(SIP_FOUND TRUE) ELSE(SIP_VERSION) FIND_FILE(_find_sip_py FindSIP.py PATHS ${CMAKE_MODULE_PATH}) - EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} ${_find_sip_py} OUTPUT_VARIABLE sip_config) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_PREFIX_PATH}/share/krita/pykrita" ${PYTHON_EXECUTABLE} ${_find_sip_py} OUTPUT_VARIABLE sip_config) IF(sip_config) STRING(REGEX REPLACE "^sip_version:([^\n]+).*$" "\\1" SIP_VERSION ${sip_config}) STRING(REGEX REPLACE ".*\nsip_version_str:([^\n]+).*$" "\\1" SIP_VERSION_STR ${sip_config}) STRING(REGEX REPLACE ".*\nsip_bin:([^\n]+).*$" "\\1" SIP_EXECUTABLE ${sip_config}) IF(NOT SIP_DEFAULT_SIP_DIR) STRING(REGEX REPLACE ".*\ndefault_sip_dir:([^\n]+).*$" "\\1" SIP_DEFAULT_SIP_DIR ${sip_config}) ENDIF(NOT SIP_DEFAULT_SIP_DIR) STRING(REGEX REPLACE ".*\nsip_inc_dir:([^\n]+).*$" "\\1" SIP_INCLUDE_DIR ${sip_config}) FILE(TO_CMAKE_PATH ${SIP_DEFAULT_SIP_DIR} SIP_DEFAULT_SIP_DIR) FILE(TO_CMAKE_PATH ${SIP_INCLUDE_DIR} SIP_INCLUDE_DIR) if (WIN32) set(SIP_EXECUTABLE ${SIP_EXECUTABLE}.exe) endif() IF(EXISTS ${SIP_EXECUTABLE}) SET(SIP_FOUND TRUE) ELSE() MESSAGE(STATUS "Found SIP configuration but the sip executable could not be found.") ENDIF() ENDIF(sip_config) IF(SIP_FOUND) IF(NOT SIP_FIND_QUIETLY) MESSAGE(STATUS "Found SIP version: ${SIP_VERSION_STR}") ENDIF(NOT SIP_FIND_QUIETLY) ELSE(SIP_FOUND) IF(SIP_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find SIP") ENDIF(SIP_FIND_REQUIRED) ENDIF(SIP_FOUND) ENDIF(SIP_VERSION) diff --git a/cmake/modules/SIPMacros.cmake b/cmake/modules/SIPMacros.cmake index 29cc394484..895cddc5bb 100644 --- a/cmake/modules/SIPMacros.cmake +++ b/cmake/modules/SIPMacros.cmake @@ -1,128 +1,132 @@ # Macros for SIP # ~~~~~~~~~~~~~~ # Copyright (c) 2007, Simon Edwards # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # SIP website: http://www.riverbankcomputing.co.uk/sip/index.php # # This file defines the following macros: # # ADD_SIP_PYTHON_MODULE (MODULE_NAME MODULE_SIP [library1, libaray2, ...]) # Specifies a SIP file to be built into a Python module and installed. # MODULE_NAME is the name of Python module including any path name. (e.g. # os.sys, Foo.bar etc). MODULE_SIP the path and filename of the .sip file # to process and compile. libraryN are libraries that the Python module, # which is typically a shared library, should be linked to. The built # module will also be install into Python's site-packages directory. # # The behaviour of the ADD_SIP_PYTHON_MODULE macro can be controlled by a # number of variables: # # SIP_INCLUDES - List of directories which SIP will scan through when looking # for included .sip files. (Corresponds to the -I option for SIP.) # # SIP_TAGS - List of tags to define when running SIP. (Corresponds to the -t # option for SIP.) # # SIP_CONCAT_PARTS - An integer which defines the number of parts the C++ code # of each module should be split into. Defaults to 8. (Corresponds to the # -j option for SIP.) # # SIP_DISABLE_FEATURES - List of feature names which should be disabled # running SIP. (Corresponds to the -x option for SIP.) # # SIP_EXTRA_OPTIONS - Extra command line options which should be passed on to # SIP. SET(SIP_INCLUDES) SET(SIP_TAGS) SET(SIP_CONCAT_PARTS 8) SET(SIP_DISABLE_FEATURES) SET(SIP_EXTRA_OPTIONS) MACRO(ADD_SIP_PYTHON_MODULE MODULE_NAME MODULE_SIP) SET(EXTRA_LINK_LIBRARIES ${ARGN}) STRING(REPLACE "." "/" _x ${MODULE_NAME}) GET_FILENAME_COMPONENT(_parent_module_path ${_x} PATH) GET_FILENAME_COMPONENT(_child_module_name ${_x} NAME) GET_FILENAME_COMPONENT(_module_path ${MODULE_SIP} PATH) if(_module_path STREQUAL "") set(CMAKE_CURRENT_SIP_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") else(_module_path STREQUAL "") set(CMAKE_CURRENT_SIP_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${_module_path}") endif(_module_path STREQUAL "") GET_FILENAME_COMPONENT(_abs_module_sip ${MODULE_SIP} ABSOLUTE) # We give this target a long logical target name. # (This is to avoid having the library name clash with any already # install library names. If that happens then cmake dependancy # tracking get confused.) STRING(REPLACE "." "_" _logical_name ${MODULE_NAME}) SET(_logical_name "python_module_${_logical_name}") FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_SIP_OUTPUT_DIR}) # Output goes in this dir. SET(_sip_includes) FOREACH (_inc ${SIP_INCLUDES}) GET_FILENAME_COMPONENT(_abs_inc ${_inc} ABSOLUTE) LIST(APPEND _sip_includes -I ${_abs_inc}) ENDFOREACH (_inc ) SET(_sip_tags) FOREACH (_tag ${SIP_TAGS}) LIST(APPEND _sip_tags -t ${_tag}) ENDFOREACH (_tag) SET(_sip_x) FOREACH (_x ${SIP_DISABLE_FEATURES}) LIST(APPEND _sip_x -x ${_x}) ENDFOREACH (_x ${SIP_DISABLE_FEATURES}) SET(_message "-DMESSAGE=Generating CPP code for module ${MODULE_NAME}") SET(_sip_output_files) FOREACH(CONCAT_NUM RANGE 0 ${SIP_CONCAT_PARTS} ) IF( ${CONCAT_NUM} LESS ${SIP_CONCAT_PARTS} ) SET(_sip_output_files ${_sip_output_files} ${CMAKE_CURRENT_SIP_OUTPUT_DIR}/sip${_child_module_name}part${CONCAT_NUM}.cpp ) ENDIF( ${CONCAT_NUM} LESS ${SIP_CONCAT_PARTS} ) ENDFOREACH(CONCAT_NUM RANGE 0 ${SIP_CONCAT_PARTS} ) IF(NOT WIN32) SET(TOUCH_COMMAND touch) ELSE(NOT WIN32) SET(TOUCH_COMMAND echo) # instead of a touch command, give out the name and append to the files # this is basically what the touch command does. FOREACH(filename ${_sip_output_files}) FILE(APPEND filename "") ENDFOREACH(filename ${_sip_output_files}) ENDIF(NOT WIN32) ADD_CUSTOM_COMMAND( OUTPUT ${_sip_output_files} COMMAND ${CMAKE_COMMAND} -E echo ${message} COMMAND ${TOUCH_COMMAND} ${_sip_output_files} COMMAND ${SIP_EXECUTABLE} ${_sip_tags} ${_sip_x} ${SIP_EXTRA_OPTIONS} -j ${SIP_CONCAT_PARTS} -c ${CMAKE_CURRENT_SIP_OUTPUT_DIR} ${_sip_includes} ${_abs_module_sip} DEPENDS ${_abs_module_sip} ${SIP_EXTRA_FILES_DEPEND} ) # not sure if type MODULE could be uses anywhere, limit to cygwin for now IF (CYGWIN) ADD_LIBRARY(${_logical_name} MODULE ${_sip_output_files} ) ELSE (CYGWIN) ADD_LIBRARY(${_logical_name} SHARED ${_sip_output_files} ) ENDIF (CYGWIN) TARGET_LINK_LIBRARIES(${_logical_name} ${PYTHON_LIBRARY}) TARGET_LINK_LIBRARIES(${_logical_name} ${EXTRA_LINK_LIBRARIES}) SET_TARGET_PROPERTIES(${_logical_name} PROPERTIES PREFIX "" OUTPUT_NAME ${_child_module_name}) + IF (MINGW) + TARGET_COMPILE_DEFINITIONS(${_logical_name} PRIVATE _hypot=hypot) + ENDIF (MINGW) + IF (WIN32) SET_TARGET_PROPERTIES(${_logical_name} PROPERTIES SUFFIX ".pyd") ENDIF (WIN32) INSTALL(TARGETS ${_logical_name} DESTINATION "${PYTHON_SITE_PACKAGES_INSTALL_DIR}/${_parent_module_path}") ENDMACRO(ADD_SIP_PYTHON_MODULE) diff --git a/packaging/windows/package_2.cmd b/packaging/windows/package_2.cmd index 5cbf0da2f4..978a376d55 100644 --- a/packaging/windows/package_2.cmd +++ b/packaging/windows/package_2.cmd @@ -1,331 +1,349 @@ @echo off :: This batch script is meant to prepare a Krita package folder to be zipped or :: to be a base for the installer. :: :: Just drop it next to the "i" install folder where the dependencies and Krita :: binaries are. :: :: Also copy filelist_bin_dll.txt and filelist_lib_dll.txt if you want more :: fine-grained DLL dependencies copying. :: :: You may want to review the following parameters. :: :: Configuration parameters: :: :: MINGW_GCC_BIN: Path to the mingw-w64/bin dir :: SEVENZIP_EXE: Path to 7-Zip executable (either 7z.exe or 7zG.exe) :: BUILDDIR_SRC: Path to krita source dir (for package shortcut) :: BUILDDIR_INSTALL: Path to INSTALL prefix of the build :: :: Note that paths should only contain alphanumeric, _ and -, except for the :: path to 7-Zip, which is fine if quoted. :: set MINGW_GCC_BIN=C:\TDM-GCC-64\bin\ set SEVENZIP_EXE="C:\Program Files\7-Zip\7z.exe" rem set BUILDDIR_SRC=%CD%\krita set BUILDDIR_SRC=%CD%\krita set BUILDDIR_INSTALL=%CD%\i :: -------- set PATH=%MINGW_GCC_BIN%;%PATH% echo Krita Windows packaging script echo. echo Configurations: echo MINGW_GCC_BIN: %MINGW_GCC_BIN% echo SEVENZIP_EXE: %SEVENZIP_EXE% echo BUILDDIR_SRC: %BUILDDIR_SRC% echo BUILDDIR_INSTALL: %BUILDDIR_INSTALL% echo. if "%1" == "" ( set PAUSE=pause ) else ( set "PAUSE= " ) :: Simple checking if not exist %BUILDDIR_INSTALL% ( echo ERROR: Cannot find the install folder! %PAUSE% exit /B 1 ) if not "%BUILDDIR_INSTALL: =%" == "%BUILDDIR_INSTALL%" ( echo ERROR: Install path contains space, which will not work properly! %PAUSE% exit /B 1 ) :: Decide package name to use if "%1" == "" ( echo Please input a package name. It will be used for the output directory, package echo file name and as the top-level directory of the package. echo Alternatively, you can pass it as the first argument to this script. echo You should only use alphanumeric, _ and - echo. set /P pkg_name=Package name^> setlocal if "!pkg_name!" == "" ( echo ERROR: You cannot choose an empty name! %PAUSE% exit /B 1 ) endlocal ) else ( set pkg_name=%1 ) echo Package name is "%pkg_name%" set pkg_root=%CD%\%pkg_name% echo Packaging dir is %pkg_root% echo. if exist %pkg_root% ( echo ERROR: Packaging dir already exists! Please remove or rename it first. %PAUSE% exit /B 1 ) echo. echo Trying to guess GCC version... g++ --version > NUL if errorlevel 1 ( echo ERROR: g++ is not working. %PAUSE% exit /B 1 ) for /f "delims=" %%a in ('g++ --version ^| find "g++"') do set GCC_VERSION_LINE=%%a echo -- %GCC_VERSION_LINE% if "%GCC_VERSION_LINE:tdm64=%" == "%GCC_VERSION_LINE%" ( echo Compiler doesn't look like TDM64-GCC, assuming simple mingw-w64 set IS_TDM= ) else ( echo Compiler looks like TDM64-GCC set IS_TDM=1 ) echo. echo Trying to guess target architecture... objdump --version > NUL if errorlevel 1 ( echo ERROR: objdump is not working. %PAUSE% exit /B 1 ) for /f "delims=, tokens=1" %%a in ('objdump -f %BUILDDIR_INSTALL%\bin\krita.exe ^| find "architecture"') do set TARGET_ARCH_LINE=%%a echo -- %TARGET_ARCH_LINE% if "%TARGET_ARCH_LINE:x86-64=%" == "%TARGET_ARCH_LINE%" ( echo Target looks like x86 set IS_X64= ) else ( echo Target looks like x64 set IS_x64=1 ) echo. echo Testing for objcopy... objcopy --version > NUL if errorlevel 1 ( echo ERROR: objcopy is not working. %PAUSE% exit /B 1 ) echo. echo Testing for strip... strip --version > NUL if errorlevel 1 ( echo ERROR: strip is not working. %PAUSE% exit /B 1 ) echo. echo Creating base directories... mkdir %pkg_root% && ^ mkdir %pkg_root%\bin && ^ mkdir %pkg_root%\lib && ^ mkdir %pkg_root%\share if errorlevel 1 ( echo ERROR: Cannot create packaging dir tree! %PAUSE% exit /B 1 ) echo. echo Copying GCC libraries... if x%IS_TDM% == x ( if x%is_x64% == x ( :: mingw-w64 x86 set "STDLIBS=gcc_s_dw2-1 gomp-1 stdc++-6 winpthread-1" ) else ( :: mingw-w64 x64 set "STDLIBS=gcc_s_seh-1 gomp-1 stdc++-6 winpthread-1" ) ) else ( if x%is_x64% == x ( :: TDM-GCC x86 set "STDLIBS=gomp-1" ) else ( :: TDM-GCC x64 set "STDLIBS=gomp_64-1" ) ) for %%L in (%STDLIBS%) do copy "%MINGW_GCC_BIN%\lib%%L.dll" %pkg_root%\bin echo. echo Copying files... :: krita.exe copy %BUILDDIR_INSTALL%\bin\krita.exe %pkg_root%\bin :: DLLs from bin/ if exist filelist_bin_dll.txt ( for /f %%F in (filelist_bin_dll.txt) do copy %BUILDDIR_INSTALL%\bin\%%F %pkg_root%\bin ) else ( echo INFO: filelist_bin_dll.txt not found, copying all DLLs except Qt5 from bin/ setlocal enableextensions enabledelayedexpansion for /f "delims=" %%F in ('dir /b "%BUILDDIR_INSTALL%\bin\*.dll"') do ( set file=%%F set file=!file:~0,3! if not x!file! == xQt5 copy %BUILDDIR_INSTALL%\bin\%%F %pkg_root%\bin ) endlocal ) :: symsrv.yes for Dr. Mingw copy %BUILDDIR_INSTALL%\bin\symsrv.yes %pkg_root%\bin :: DLLs from lib/ if exist filelist_lib_dll.txt ( for /f %%F in (filelist_lib_dll.txt) do copy %BUILDDIR_INSTALL%\lib\%%F %pkg_root%\bin ) else ( echo INFO: filelist_lib_dll.txt not found, copying all DLLs from lib/ copy %BUILDDIR_INSTALL%\lib\*.dll %pkg_root%\bin ) :: Boost, there might be more than one leftover but we can't really do much copy %BUILDDIR_INSTALL%\bin\libboost_system-*.dll %pkg_root%\bin :: KF5 plugins may be placed at different locations depending on how Qt is built xcopy /S /Y /I %BUILDDIR_INSTALL%\lib\plugins\imageformats %pkg_root%\bin\imageformats xcopy /S /Y /I %BUILDDIR_INSTALL%\plugins\imageformats %pkg_root%\bin\imageformats xcopy /S /Y /I %BUILDDIR_INSTALL%\lib\plugins\kf5 %pkg_root%\bin\kf5 xcopy /S /Y /I %BUILDDIR_INSTALL%\plugins\kf5 %pkg_root%\bin\kf5 :: Qt Translations :: it seems that windeployqt does these, but only *some* of these??? mkdir %pkg_root%\bin\translations setlocal enableextensions enabledelayedexpansion for /f "delims=" %%F in ('dir /b "%BUILDDIR_INSTALL%\translations\qt_*.qm"') do ( :: Exclude qt_help_*.qm set temp=%%F set temp2=!temp:_help=! if x!temp2! == x!temp! copy %BUILDDIR_INSTALL%\translations\!temp! %pkg_root%\bin\translations\!temp! ) endlocal :: Krita plugins xcopy /Y %BUILDDIR_INSTALL%\lib\kritaplugins\*.dll %pkg_root%\lib\kritaplugins\ :: Share xcopy /Y /S /I %BUILDDIR_INSTALL%\share\color %pkg_root%\share\color xcopy /Y /S /I %BUILDDIR_INSTALL%\share\color-schemes %pkg_root%\share\color-schemes xcopy /Y /S /I %BUILDDIR_INSTALL%\share\icons %pkg_root%\share\icons xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kf5 %pkg_root%\share\kf5 xcopy /Y /S /I %BUILDDIR_INSTALL%\share\krita %pkg_root%\share\krita xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kritaplugins %pkg_root%\share\kritaplugins xcopy /Y /S /I %BUILDDIR_INSTALL%\share\mime %pkg_root%\share\mime :: Not useful on Windows it seems rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\appdata %pkg_root%\share\appdata rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\applications %pkg_root%\share\applications rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\doc %pkg_root%\share\doc rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\kservices5 %pkg_root%\share\kservices5 rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\man %pkg_root%\share\man rem xcopy /Y /S /I %BUILDDIR_INSTALL%\share\ocio %pkg_root%\share\ocio :: Copy locale to bin xcopy /Y /S /I %BUILDDIR_INSTALL%\share\locale %pkg_root%\bin\locale :: Copy shortcut link from source (can't create it dynamically) copy %BUILDDIR_SRC%\packaging\windows\krita.lnk %pkg_root% :: windeployqt %BUILDDIR_INSTALL%\bin\windeployqt.exe --release -concurrent -network -printsupport -svg -xml -multimedia %pkg_root%\bin\krita.exe +:: Copy embedded Python +xcopy /Y /S /I %BUILDDIR_INSTALL%\python %pkg_root%\python + :: For chopping relative path :: 512 should be enough :: n+2 to also account for a trailing backslash setlocal enableextensions enabledelayedexpansion for /L %%n in (1 1 512) do if "!pkg_root:~%%n,1!" neq "" set /a "pkg_root_len_plus_one=%%n+2" endlocal & set pkg_root_len_plus_one=%pkg_root_len_plus_one% +echo. +setlocal enableextensions enabledelayedexpansion +:: Remove Python cache files +for /d /r "%pkg_root%" %%F in (__pycache__\) do ( + if EXIST "%%F" ( + set relpath=%%F + set relpath=!relpath:~%pkg_root_len_plus_one%! + echo Deleting Python cache !relpath! + rmdir /S /Q "%%F" + ) +) +endlocal + echo. echo Splitting debug info from binaries... call :split-debug "%pkg_root%\bin\krita.exe" bin\krita.exe setlocal enableextensions enabledelayedexpansion :: Find all DLLs for /r "%pkg_root%" %%F in (*.dll) do ( set relpath=%%F set relpath=!relpath:~%pkg_root_len_plus_one%! call :split-debug "%%F" !relpath! ) endlocal echo. echo Packaging debug info... :: (note that the top-level package dir is not included) %SEVENZIP_EXE% a -tzip %pkg_name%-dbg.zip -r %pkg_root%\*.debug echo -------- echo. echo Packaging stripped binaries... %SEVENZIP_EXE% a -tzip %pkg_name%.zip %pkg_root%\ -xr!.debug echo -------- echo. echo. echo Krita packaged as %pkg_name%.zip if exist %pkg_name%-dbg.zip echo Debug info packaged as %pkg_name%-dbg.zip echo Packaging dir is %pkg_root% -echo NOTE: Do not create installer with packaging dir unless you removed all debug -echo info from it! +echo NOTE: Do not create installer with packaging dir. Extract from +echo %pkg_name%.zip instead, +echo and do _not_ run krita inside the extracted directory because it will +echo create extra unnecessary files. echo. echo Please remember to actually test the package before releasing it. echo. %PAUSE% exit /b :split-debug echo Splitting debug info of %2 objcopy --only-keep-debug %~1 %~1.debug if ERRORLEVEL 1 exit /b %ERRORLEVEL% :: If the debug file is small enough then consider there being no debug info. :: Discard these files since they somehow make gdb crash. call :getfilesize %~1.debug if /i %getfilesize_retval% LEQ 2048 ( echo Discarding %2.debug del %~1.debug exit /b 0 ) if not exist %~dp1.debug mkdir %~dp1.debug move %~1.debug %~dp1.debug\ > NUL strip %~1 :: Add debuglink :: FIXME: There is a problem with gdb that cause it to output this warning :: FIXME: "warning: section .gnu_debuglink not found in xxx.debug" :: FIXME: I tried adding a link to itself but this kills drmingw :( objcopy --add-gnu-debuglink="%~dp1.debug\%~nx1.debug" %~1 exit /b %ERRORLEVEL% :getfilesize set getfilesize_retval=%~z1 goto :eof :relpath_dirpath call :relpath_dirpath_internal "" "%~1" goto :eof :relpath_dirpath_internal for /f "tokens=1* delims=\" %%a in ("%~2") do ( :: If part 2 is empty, it means part 1 is probably the file name if x%%b==x ( set relpath_dirpath_retval=%~1 ) else ( call :relpath_dirpath_internal "%~1%%a\" %%b ) ) goto :eof diff --git a/plugins/extensions/pykrita/CMakeLists.txt b/plugins/extensions/pykrita/CMakeLists.txt index 4994adb102..b955bcde47 100644 --- a/plugins/extensions/pykrita/CMakeLists.txt +++ b/plugins/extensions/pykrita/CMakeLists.txt @@ -1,34 +1,34 @@ -find_package(PythonLibrary) +# PythonLibrary should've been found in the root CMakeLists.txt set_package_properties(PythonLibrary PROPERTIES DESCRIPTION "Python Library" URL "http://www.python.org" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYTHONLIBS_FOUND HAVE_PYTHONLIBS) find_package(SIP "4.18.0") set_package_properties(SIP PROPERTIES DESCRIPTION "Support for generating SIP Python bindings" URL "https://www.riverbankcomputing.com/software/sip/download" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(SIP_FOUND HAVE_SIP) find_package(PyQt5 "5.6.0") set_package_properties(PyQt5 PROPERTIES DESCRIPTION "Python bindings for Qt5." URL "https://www.riverbankcomputing.com/software/pyqt/download5" TYPE OPTIONAL PURPOSE "Required by the Krita PyQt plugin") macro_bool_to_01(PYQT5_FOUND HAVE_PYQT5) if (HAVE_PYQT5 AND HAVE_SIP AND HAVE_PYTHONLIBS) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${SIP_INCLUDE_DIR} ${PYTHON_INCLUDE_PATH}) add_subdirectory(sip) add_subdirectory(plugin) add_subdirectory(kritarunner) endif () diff --git a/plugins/extensions/pykrita/kritarunner/CMakeLists.txt b/plugins/extensions/pykrita/kritarunner/CMakeLists.txt index 2e3443d3ff..eec2049b58 100644 --- a/plugins/extensions/pykrita/kritarunner/CMakeLists.txt +++ b/plugins/extensions/pykrita/kritarunner/CMakeLists.txt @@ -1,30 +1,33 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../plugin ${CMAKE_CURRENT_BINARY_DIR}/../plugin ${CMAKE_CURRENT_SOURCE_DIR}/../libkis ${CMAKE_CURRENT_BINARY_DIR}/../libkis ) set(kritarunner_SRCS main.cpp ../plugin/engine.cpp ../plugin/plugin.cpp ../plugin/pyqtpluginsettings.cpp ../plugin/utilities.cpp ) add_executable(kritarunner ${kritarunner_SRCS}) target_link_libraries(kritarunner PRIVATE ${PYTHON_LIBRARY} kritaui kritalibkis Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml Qt5::Network Qt5::PrintSupport Qt5::Svg Qt5::Concurrent) -install(TARGETS kritarunner ${INSTALL_TARGETS_DEFAULT_ARGS}) +if (MINGW) + target_compile_definitions(kritarunner PRIVATE _hypot=hypot) +endif (MINGW) +install(TARGETS kritarunner ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/plugins/extensions/pykrita/plugin/CMakeLists.txt b/plugins/extensions/pykrita/plugin/CMakeLists.txt index 7ca32184ac..52ac98a2e7 100644 --- a/plugins/extensions/pykrita/plugin/CMakeLists.txt +++ b/plugins/extensions/pykrita/plugin/CMakeLists.txt @@ -1,38 +1,42 @@ # NOTE Disable trivial Qt keywords due conflicts w/ some Python.h header # (at least version 3.3 of it has a member PyType_Spec::slots) add_definitions(-DQT_NO_KEYWORDS) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) set(SOURCES plugin.cpp pyqtpluginsettings.cpp utilities.cpp engine.cpp ) ki18n_wrap_ui(SOURCES info.ui manager.ui ) add_library(kritapykrita MODULE ${SOURCES}) target_link_libraries( kritapykrita ${PYTHON_LIBRARY} kritaui kritalibkis ) +if (MINGW) + target_compile_definitions(kritapykrita PRIVATE _hypot=hypot) +endif (MINGW) + install(TARGETS kritapykrita DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) # Install "built-in" api install( DIRECTORY krita DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" ) add_subdirectory(plugins) #add_subdirectory(test) diff --git a/plugins/extensions/pykrita/plugin/engine.cpp b/plugins/extensions/pykrita/plugin/engine.cpp index b66fbeb244..92ec3cda85 100644 --- a/plugins/extensions/pykrita/plugin/engine.cpp +++ b/plugins/extensions/pykrita/plugin/engine.cpp @@ -1,797 +1,799 @@ // This file is part of PyKrita, Krita' Python scripting plugin. // // Copyright (C) 2006 Paul Giannaros // Copyright (C) 2012, 2013 Shaheed Haque // Copyright (C) 2013 Alex Turbov // // This library 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 2.1 of the License, or (at your option) version 3, or any // later version accepted by the membership of KDE e.V. (or its // successor approved by the membership of KDE e.V.), which shall // act as a proxy defined in Section 6 of version 3 of the license. // // This library 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 library. If not, see . // #include "engine.h" // config.h defines PYKRITA_PYTHON_LIBRARY, the path to libpython.so // on the build system #include "config.h" #include "utilities.h" #include #include #include #include #include #include #include #include //#include #include #include /// Name of the file where per-plugin configuration is stored. #define CONFIG_FILE "kritapykritarc" #if PY_MAJOR_VERSION < 3 # define PYKRITA_INIT initpykrita #else # define PYKRITA_INIT PyInit_pykrita #endif PyMODINIT_FUNC PYKRITA_INIT(); // fwd decl /// \note Namespace name written in uppercase intentionally! /// It will appear in debug output from Python plugins... namespace PYKRITA { PyObject* debug(PyObject* /*self*/, PyObject* args) { const char* text; if (PyArg_ParseTuple(args, "s", &text)) dbgScript << text; Py_INCREF(Py_None); return Py_None; } } // namespace PYKRITA namespace { PyObject* s_pykrita; /** * \attention Krita has embedded Python, so init function \b never will be called * automatically! We can use this fact to initialize a pointer to an instance * of the \c Engine class (which is a part of the \c Plugin), so exported * functions will know it (yep, from Python's side they should be static). */ PyKrita::Engine* s_engine_instance = 0; /** * Wrapper function, called explicitly from \c Engine::Engine * to initialize pointer to the only (by design) instance of the engine, * so exported (to Python) functions get know it... Then invoke * a real initialization sequence... */ void pythonInitwrapper(PyKrita::Engine* const engine) { Q_ASSERT("Sanity check" && !s_engine_instance); s_engine_instance = engine; // Call initialize explicitly to initialize embedded interpreter. PYKRITA_INIT(); } /** * Functions for the Python module called pykrita. * \todo Does it \b REALLY needed? Configuration data will be flushed * on exit anyway! Why to write it (and even allow to plugins to call this) * \b before krita really going to exit? It would be better to \b deprecate * this (harmful) function! */ PyObject* pykritaSaveConfiguration(PyObject* /*self*/, PyObject* /*unused*/) { if (s_engine_instance) s_engine_instance->saveGlobalPluginsConfiguration(); Py_INCREF(Py_None); return Py_None; } PyMethodDef pykritaMethods[] = { { "saveConfiguration" , &pykritaSaveConfiguration , METH_NOARGS , "Save the configuration of the plugin into " CONFIG_FILE } , { "qDebug" , &PYKRITA::debug , METH_VARARGS , "True KDE way to show debug info" } , { 0, 0, 0, 0 } }; } // anonymous namespace //BEGIN Python module registration PyMODINIT_FUNC PYKRITA_INIT() { #if PY_MAJOR_VERSION < 3 s_pykrita = Py_InitModule3("pykrita", pykritaMethods, "The pykrita module"); PyModule_AddStringConstant(s_pykrita, "__file__", __FILE__); #else static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT , "pykrita" , "The pykrita module" , -1 , pykritaMethods , 0 , 0 , 0 , 0 }; s_pykrita = PyModule_Create(&moduledef); PyModule_AddStringConstant(s_pykrita, "__file__", __FILE__); return s_pykrita; #endif } //END Python module registration //BEGIN PyKrita::Engine::PluginState PyKrita::Engine::PluginState::PluginState() : m_enabled(false) , m_broken(false) , m_unstable(false) , m_isDir(false) { } //END PyKrita::Engine::PluginState /** * Just initialize some members. The second (most important) part * is to call \c Engine::tryInitializeGetFailureReason()! * W/o that call instance is invalid and using it lead to UB! */ PyKrita::Engine::Engine() : m_configuration(0) , m_sessionConfiguration(0) , m_engineIsUsable(false) { } /// \todo More accurate shutdown required: /// need to keep track what exactly was broken on /// initialize attempt... PyKrita::Engine::~Engine() { dbgScript << "Going to destroy the Python engine"; // Notify Python that engine going to die { Python py = Python(); py.functionCall("_pykritaUnloading"); } unloadAllModules(); // Clean internal configuration dicts // NOTE Do not need to save anything! It's already done! if (m_configuration) { Py_DECREF(m_configuration); } if (m_sessionConfiguration) { Py_DECREF(m_sessionConfiguration); } + Python::maybeFinalize(); Python::libraryUnload(); s_engine_instance = 0; } void PyKrita::Engine::unloadAllModules() { // Unload all modules for (int i = 0; i < m_plugins.size(); ++i) { if (m_plugins[i].isEnabled() && !m_plugins[i].isBroken()) { unloadModule(i); } } } /** * \todo Make sure noone tries to use uninitialized engine! * (Or enable exceptions for this module, so this case wouldn't even araise?) */ QString PyKrita::Engine::tryInitializeGetFailureReason() { dbgScript << "Construct the Python engine for Python" << PY_MAJOR_VERSION << "," << PY_MINOR_VERSION; - if (0 != PyImport_AppendInittab(Python::PYKRITA_ENGINE, PYKRITA_INIT)) { - return i18nc("@info:tooltip ", "Cannot load built-in pykrita module"); - } - Python::libraryLoad(); - Python py = Python(); + if (!Python::libraryLoad()) { + return i18nc("@info:tooltip ", "Cannot load Python library"); + } // Update PYTHONPATH // 0) custom plugin directories (prefer local dir over systems') // 1) shipped krita module's dir - // 2) w/ site_packages/ dir of the Python QStringList pluginDirectories = KoResourcePaths::findDirs("pythonscripts"); - pluginDirectories << KoResourcePaths::locate("appdata", "plugins/pykrita/") - << QLatin1String(PYKRITA_PYTHON_SITE_PACKAGES_INSTALL_DIR) - ; dbgScript << "Plugin Directories: " << pluginDirectories; - if (!py.prependPythonPaths(pluginDirectories)) { - return i18nc("@info:tooltip ", "Cannot update Python paths"); + if (!Python::setPath(pluginDirectories)) { + return i18nc("@info:tooltip ", "Cannot set Python paths"); } + if (0 != PyImport_AppendInittab(Python::PYKRITA_ENGINE, PYKRITA_INIT)) { + return i18nc("@info:tooltip ", "Cannot load built-in pykrita module"); + } + + Python::ensureInitialized(); + Python py = Python(); + PyRun_SimpleString( "import sip\n" "sip.setapi('QDate', 2)\n" "sip.setapi('QTime', 2)\n" "sip.setapi('QDateTime', 2)\n" "sip.setapi('QUrl', 2)\n" "sip.setapi('QTextStream', 2)\n" "sip.setapi('QString', 2)\n" "sip.setapi('QVariant', 2)\n" ); // Initialize our built-in module. pythonInitwrapper(this); if (!s_pykrita) { return i18nc("@info:tooltip ", "No pykrita built-in module"); } // Setup global configuration m_configuration = PyDict_New(); /// \todo Check \c m_configuration ? // Host the configuration dictionary. py.itemStringSet("configuration", m_configuration); // Setup per session configuration m_sessionConfiguration = PyDict_New(); py.itemStringSet("sessionConfiguration", m_sessionConfiguration); // Initialize 'plugins' dict of module 'pykrita' PyObject* plugins = PyDict_New(); py.itemStringSet("plugins", plugins); // Get plugins available scanPlugins(); // NOTE Empty failure reson string indicates success! m_engineIsUsable = true; return QString(); } int PyKrita::Engine::columnCount(const QModelIndex&) const { return Column::LAST__; } int PyKrita::Engine::rowCount(const QModelIndex&) const { return m_plugins.size(); } QModelIndex PyKrita::Engine::index(const int row, const int column, const QModelIndex& parent) const { if (!parent.isValid() && row < m_plugins.size() && column < Column::LAST__) return createIndex(row, column); return QModelIndex(); } QModelIndex PyKrita::Engine::parent(const QModelIndex&) const { return QModelIndex(); } QVariant PyKrita::Engine::headerData( const int section , const Qt::Orientation orientation , const int role ) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (section) { case Column::NAME: return i18nc("@title:column", "Name"); case Column::COMMENT: return i18nc("@title:column", "Comment"); default: break; } } return QVariant(); } QVariant PyKrita::Engine::data(const QModelIndex& index, const int role) const { Q_ASSERT("Sanity check" && index.row() < m_plugins.size()); Q_ASSERT("Sanity check" && index.column() < Column::LAST__); switch (role) { case Qt::DisplayRole: switch (index.column()) { case Column::NAME: return m_plugins[index.row()].m_pythonPlugin.name(); case Column::COMMENT: return m_plugins[index.row()].m_pythonPlugin.comment(); default: break; } break; case Qt::CheckStateRole: { if (index.column() == Column::NAME) { const bool checked = m_plugins[index.row()].isEnabled(); return checked ? Qt::Checked : Qt::Unchecked; } break; } case Qt::ToolTipRole: if (!m_plugins[index.row()].m_errorReason.isEmpty()) return m_plugins[index.row()].m_errorReason; break; case Qt::ForegroundRole: if (m_plugins[index.row()].isUnstable()) { KColorScheme scheme(QPalette::Inactive, KColorScheme::View); return scheme.foreground(KColorScheme::NegativeText).color(); } default: break; } return QVariant(); } Qt::ItemFlags PyKrita::Engine::flags(const QModelIndex& index) const { Q_ASSERT("Sanity check" && index.row() < m_plugins.size()); Q_ASSERT("Sanity check" && index.column() < Column::LAST__); int result = Qt::ItemIsSelectable; if (index.column() == Column::NAME) result |= Qt::ItemIsUserCheckable; // Disable to select/check broken modules if (!m_plugins[index.row()].isBroken()) result |= Qt::ItemIsEnabled; return static_cast(result); } bool PyKrita::Engine::setData(const QModelIndex& index, const QVariant& value, const int role) { Q_ASSERT("Sanity check" && index.row() < m_plugins.size()); if (role == Qt::CheckStateRole) { Q_ASSERT("Sanity check" && !m_plugins[index.row()].isBroken()); const bool enabled = value.toBool(); m_plugins[index.row()].m_enabled = enabled; if (enabled) loadModule(index.row()); else unloadModule(index.row()); } return true; } QStringList PyKrita::Engine::enabledPlugins() const { /// \todo \c std::transform + lambda or even better to use /// filtered and transformed view from boost QStringList result; Q_FOREACH(const PluginState & plugin, m_plugins) if (plugin.isEnabled()) { result.append(plugin.m_pythonPlugin.name()); } return result; } void PyKrita::Engine::readGlobalPluginsConfiguration() { Python py = Python(); PyDict_Clear(m_configuration); KConfig config(CONFIG_FILE, KConfig::SimpleConfig); config.sync(); py.updateDictionaryFromConfiguration(m_configuration, &config); } void PyKrita::Engine::saveGlobalPluginsConfiguration() { Python py = Python(); KConfig config(CONFIG_FILE, KConfig::SimpleConfig); py.updateConfigurationFromDictionary(&config, m_configuration); config.sync(); } bool PyKrita::Engine::isPythonPluginUsable(const PyPlugin *pythonPlugin) { dbgScript << "Got Krita/PythonPlugin: " << pythonPlugin->name() << ", module-path=" << pythonPlugin->library() ; // Make sure mandatory properties are here if (pythonPlugin->name().isEmpty()) { dbgScript << "Ignore desktop file w/o a name"; return false; } if (pythonPlugin->library().isEmpty()) { dbgScript << "Ignore desktop file w/o a module to import"; return false; } return true; } bool PyKrita::Engine::setModuleProperties(PluginState& plugin) { // Find the module: // 0) try to locate directory based plugin first QString rel_path = plugin.moduleFilePathPart(); rel_path = rel_path + "/" + "__init__.py"; dbgScript << "Finding Pyrhon module with rel_path:" << rel_path; QString module_path = KoResourcePaths::findResource("pythonscripts", rel_path); dbgScript << "module_path:" << module_path; if (module_path.isEmpty()) { // 1) Nothing found, then try file based plugin rel_path = plugin.moduleFilePathPart() + ".py"; dbgScript << "Finding Pyrhon module with rel_path:" << rel_path; module_path = KoResourcePaths::findResource("pythonscripts", rel_path); dbgScript << "module_path:" << module_path; } else { plugin.m_isDir = true; } // Is anything found at all? if (module_path.isEmpty()) { plugin.m_broken = true; plugin.m_errorReason = i18nc( "@info:tooltip" , "Unable to find the module specified %1" , plugin.m_pythonPlugin.library() ); dbgScript << "Cannot load module:" << plugin.m_errorReason; return false; } dbgScript << "Found module path:" << module_path; return true; } QPair PyKrita::Engine::parseDependency(const QString& d) { // Check if dependency has package info attached const int pnfo = d.indexOf('('); if (pnfo != -1) { QString dependency = d.mid(0, pnfo); QString version_str = d.mid(pnfo + 1, d.size() - pnfo - 2).trimmed(); dbgScript << "Desired version spec [" << dependency << "]:" << version_str; version_checker checker = version_checker::fromString(version_str); if (!(checker.isValid() && d.endsWith(')'))) { dbgScript << "Invalid version spec " << d; QString reason = i18nc( "@info:tooltip" , "

Specified version has invalid format for dependency %1: " "%2. Skipped

" , dependency , version_str ); return qMakePair(reason, version_checker()); } return qMakePair(dependency, checker); } return qMakePair(d, version_checker(version_checker::undefined)); } PyKrita::version PyKrita::Engine::tryObtainVersionFromTuple(PyObject* version_obj) { Q_ASSERT("Sanity check" && version_obj); if (PyTuple_Check(version_obj) == 0) return version::invalid(); int version_info[3] = {0, 0, 0}; for (unsigned i = 0; i < PyTuple_Size(version_obj); ++i) { PyObject* v = PyTuple_GetItem(version_obj, i); if (v && PyLong_Check(v)) version_info[i] = PyLong_AsLong(v); else version_info[i] = -1; } if (version_info[0] != -1 && version_info[1] != -1 && version_info[2] != -1) return version(version_info[0], version_info[1], version_info[2]); return version::invalid(); } /** * Try to parse version string as a simple triplet X.Y.Z. * * \todo Some modules has letters in a version string... * For example current \c pytz version is \e "2013d". */ PyKrita::version PyKrita::Engine::tryObtainVersionFromString(PyObject* version_obj) { Q_ASSERT("Sanity check" && version_obj); if (!Python::isUnicode(version_obj)) return version::invalid(); QString version_str = Python::unicode(version_obj); if (version_str.isEmpty()) return version::invalid(); return version::fromString(version_str); } /** * Collect dependencies and check them. To do it * just try to import a module... when unload it ;) * * \c X-Python-Dependencies property of \c .desktop file has the following format: * python-module(version-info), where python-module * a python module name to be imported, version-spec * is a version triplet delimited by dots, possible w/ leading compare * operator: \c =, \c <, \c >, \c <=, \c >= */ void PyKrita::Engine::verifyDependenciesSetStatus(PluginState& plugin) { QStringList dependencies = plugin.m_pythonPlugin.property("X-Python-Dependencies").toStringList(); #if PY_MAJOR_VERSION < 3 { // Try to get Py2 only dependencies QStringList py2_dependencies = plugin.m_service->property("X-Python-2-Dependencies").toStringList(); dependencies.append(py2_dependencies); } #endif Python py = Python(); QString reason = i18nc("@info:tooltip", "Dependency check"); Q_FOREACH(const QString & d, dependencies) { QPair info_pair = parseDependency(d); version_checker& checker = info_pair.second; if (!checker.isValid()) { plugin.m_broken = true; reason += info_pair.first; continue; } dbgScript << "Try to import dependency module/package:" << d; // Try to import a module const QString& dependency = info_pair.first; PyObject* module = py.moduleImport(PQ(dependency)); if (module) { if (checker.isEmpty()) { // Need to check smth? dbgScript << "No version to check, just make sure it's loaded:" << dependency; Py_DECREF(module); continue; } // Try to get __version__ from module // See PEP396: http://www.python.org/dev/peps/pep-0396/ PyObject* version_obj = py.itemString("__version__", PQ(dependency)); if (!version_obj) { dbgScript << "No __version__ for " << dependency << "[" << plugin.m_pythonPlugin.name() << "]:\n" << py.lastTraceback() ; plugin.m_unstable = true; reason += i18nc( "@info:tooltip" , "

Failed to check version of dependency %1: " "Module do not have PEP396 __version__ attribute. " "It is not disabled, but behaviour is unpredictable...

" , dependency ); } // PEP396 require __version__ to tuple of integers... try it! version dep_version = tryObtainVersionFromTuple(version_obj); if (!dep_version.isValid()) // Second attempt: some "bad" modules have it as a string dep_version = tryObtainVersionFromString(version_obj); // Did we get it? if (!dep_version.isValid()) { // Dunno what is this... Giving up! dbgScript << "***: Can't parse module version for" << dependency; plugin.m_unstable = true; reason += i18nc( "@info:tooltip" , "

%1: Unexpected module's version format" , dependency ); } else if (!checker(dep_version)) { dbgScript << "Version requirement check failed [" << plugin.m_pythonPlugin.name() << "] for " << dependency << ": wanted " << checker.operationToString() << QString(checker.required()) << ", but found" << QString(dep_version) ; plugin.m_broken = true; reason += i18nc( "@info:tooltip" , "

%1: No suitable version found. " "Required version %2 %3, but found %4

" , dependency , checker.operationToString() , QString(checker.required()) , QString(dep_version) ); } // Do not need this module anymore... Py_DECREF(module); } else { dbgScript << "Load failure [" << plugin.m_pythonPlugin.name() << "]:\n" << py.lastTraceback(); plugin.m_broken = true; reason += i18nc( "@info:tooltip" , "

Failure on module load %1:

%2
" , dependency , py.lastTraceback() ); } } if (plugin.isBroken() || plugin.isUnstable()) { plugin.m_errorReason = reason; } } void PyKrita::Engine::scanPlugins() { m_plugins.clear(); // Clear current state. QStringList desktopFiles = KoResourcePaths::findAllResources("data", "pykrita/*desktop"); qDebug() << desktopFiles; Q_FOREACH(const QString &desktopFile, desktopFiles) { QSettings s(desktopFile, QSettings::IniFormat); s.beginGroup("Desktop Entry"); if (s.value("ServiceTypes").toString() == "Krita/PythonPlugin") { PyPlugin pyplugin; pyplugin.m_comment = s.value("Comment").toString(); pyplugin.m_name = s.value("Name").toString(); pyplugin.m_libraryPath = s.value("X-KDE-Library").toString(); pyplugin.m_properties["X-Python-2-Compatible"] = s.value("X-Python-2-Compatible", false).toBool(); if (!isPythonPluginUsable(&pyplugin)) { dbgScript << pyplugin.name() << "is not usable"; continue; } PluginState pluginState; pluginState.m_pythonPlugin = pyplugin; if (!setModuleProperties(pluginState)) { dbgScript << "Cannot load" << pyplugin.name() << ": broken" << pluginState.isBroken() << "because:" << pluginState.errorReason(); continue; } verifyDependenciesSetStatus(pluginState); m_plugins.append(pluginState); } } } void PyKrita::Engine::setEnabledPlugins(const QStringList& enabled_plugins) { for (int i = 0; i < m_plugins.size(); ++i) { m_plugins[i].m_enabled = enabled_plugins.indexOf(m_plugins[i].m_pythonPlugin.name()) != -1; } } void PyKrita::Engine::tryLoadEnabledPlugins() { for (int i = 0; i < m_plugins.size(); ++i) { dbgScript << "Trying to load plugin" << m_plugins[i].pythonModuleName() << ". Enabled:" << m_plugins[i].isEnabled() << ". Broken: " << m_plugins[i].isBroken(); if (!m_plugins[i].isBroken()) { m_plugins[i].m_enabled = true; loadModule(i); } } } void PyKrita::Engine::loadModule(const int idx) { Q_ASSERT("Plugin index is out of range!" && 0 <= idx && idx < m_plugins.size()); PluginState& plugin = m_plugins[idx]; Q_ASSERT( "Why to call loadModule() for disabled/broken plugin?" && plugin.isEnabled() && !plugin.isBroken() ); QString module_name = plugin.pythonModuleName(); dbgScript << "Loading module: " << module_name; Python py = Python(); // Get 'plugins' key from 'pykrita' module dictionary. // Every entry has a module name as a key and 2 elements tuple as a value PyObject* plugins = py.itemString("plugins"); Q_ASSERT( "'plugins' dict expected to be alive, otherwise code review required!" && plugins ); PyObject* module = py.moduleImport(PQ(module_name)); if (module) { // Move just loaded module to the dict const int ins_result = PyDict_SetItemString(plugins, PQ(module_name), module); Q_ASSERT("expected successful insertion" && ins_result == 0); Py_DECREF(module); // Handle failure in release mode. if (ins_result == 0) { // Initialize the module from Python's side PyObject* const args = Py_BuildValue("(s)", PQ(module_name)); PyObject* result = py.functionCall("_pluginLoaded", Python::PYKRITA_ENGINE, args); Py_DECREF(args); if (result) { dbgScript << "\t" << "success!"; return; } } plugin.m_errorReason = i18nc("@info:tooltip", "Internal engine failure"); } else { plugin.m_errorReason = i18nc( "@info:tooltip" , "Module not loaded:%1" , py.lastTraceback() ); } plugin.m_broken = true; warnScript << "Error loading plugin" << module_name; } void PyKrita::Engine::unloadModule(int idx) { Q_ASSERT("Plugin index is out of range!" && 0 <= idx && idx < m_plugins.size()); PluginState& plugin = m_plugins[idx]; Q_ASSERT("Why to call unloadModule() for broken plugin?" && !plugin.isBroken()); dbgScript << "Unloading module: " << plugin.pythonModuleName(); Python py = Python(); // Get 'plugins' key from 'pykrita' module dictionary PyObject* plugins = py.itemString("plugins"); Q_ASSERT( "'plugins' dict expected to be alive, otherwise code review required!" && plugins ); PyObject* const args = Py_BuildValue("(s)", PQ(plugin.pythonModuleName())); py.functionCall("_pluginUnloading", Python::PYKRITA_ENGINE, args); Py_DECREF(args); // This will just decrement a reference count for module instance PyDict_DelItemString(plugins, PQ(plugin.pythonModuleName())); // Remove the module also from 'sys.modules' dict to really unload it, // so if reloaded all @init actions will work again! PyObject* sys_modules = py.itemString("modules", "sys"); Q_ASSERT("Sanity check" && sys_modules); PyDict_DelItemString(sys_modules, PQ(plugin.pythonModuleName())); } // krita: space-indent on; indent-width 4; #undef PYKRITA_INIT diff --git a/plugins/extensions/pykrita/plugin/plugin.cpp b/plugins/extensions/pykrita/plugin/plugin.cpp index 0201a53601..c0b08f7981 100644 --- a/plugins/extensions/pykrita/plugin/plugin.cpp +++ b/plugins/extensions/pykrita/plugin/plugin.cpp @@ -1,93 +1,84 @@ /* * Copyright (c) 2014 Boudewijn Rempt (boud@valdyas.org) * * 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; version 2 of the License. * * 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 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plugin.h" #include "engine.h" #include "utilities.h" #include #include #include #include #include #include "pyqtpluginsettings.h" #include #include K_PLUGIN_FACTORY_WITH_JSON(KritaPyQtPluginFactory, "kritapykrita.json", registerPlugin();) KritaPyQtPlugin::KritaPyQtPlugin(QObject *parent, const QVariantList &) : KisViewPlugin(parent) , m_engineFailureReason(m_engine.tryInitializeGetFailureReason()) , m_autoReload(false) { qDebug() << "Loading Python plugin"; KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); PyQtPluginSettingsFactory* settingsFactory = new PyQtPluginSettingsFactory(&m_engine); - QByteArray pythonPath = qgetenv("PYTHONPATH"); - qDebug() << "\tPython path:" << pythonPath; - - QStringList pluginDirectories = KoResourcePaths::findDirs("pythonscripts"); - qDebug() << "\tPython plugin directories" << pluginDirectories; - Q_FOREACH(const QString pluginDir, pluginDirectories) { - pythonPath.prepend(pluginDir.toUtf8() + ":"); - } - qputenv("PYTHONPATH", pythonPath); //load and save preferences //if something in kritarc is missing, then the default from this load function will be used and saved back to kconfig. //this way, cfg.readEntry() in any part won't be able to set its own default KisPreferenceSet* settings = settingsFactory->createPreferenceSet(); Q_ASSERT(settings); settings->loadPreferences(); settings->savePreferences(); delete settings; preferenceSetRegistry->add("PyQtPluginSettingsFactory", settingsFactory); // Try to import the `pykrita` module PyKrita::Python py = PyKrita::Python(); PyObject* pykritaPackage = py.moduleImport("pykrita"); pykritaPackage = py.moduleImport("krita"); if (pykritaPackage) { dbgScript << "Loaded pykrita, now load plugins"; m_engine.tryLoadEnabledPlugins(); //py.functionCall("_pykritaLoaded", PyKrita::Python::PYKRITA_ENGINE); } else { dbgScript << "Cannot load pykrita module"; m_engine.setBroken(); } Q_FOREACH (Extension* ext, Krita::instance()->extensions()) { ext->setup(); } } KritaPyQtPlugin::~KritaPyQtPlugin() { } #include "plugin.moc" diff --git a/plugins/extensions/pykrita/plugin/utilities.cpp b/plugins/extensions/pykrita/plugin/utilities.cpp index 52050d6491..d941604824 100644 --- a/plugins/extensions/pykrita/plugin/utilities.cpp +++ b/plugins/extensions/pykrita/plugin/utilities.cpp @@ -1,546 +1,607 @@ // This file is part of PyKrita, Krita' Python scripting plugin. // // Copyright (C) 2006 Paul Giannaros // Copyright (C) 2012, 2013 Shaheed Haque // // This library 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 2.1 of the License, or (at your option) version 3, or any // later version accepted by the membership of KDE e.V. (or its // successor approved by the membership of KDE e.V.), which shall // act as a proxy defined in Section 6 of version 3 of the license. // // This library 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 library. If not, see . // // config.h defines PYKRITA_PYTHON_LIBRARY, the path to libpython.so // on the build system #include "config.h" #include "utilities.h" #include #include #include +#include #include #include #include +#include #include #include #include +#include #include #define THREADED 1 namespace PyKrita { namespace { #ifndef Q_OS_WIN QLibrary* s_pythonLibrary = 0; #endif PyThreadState* s_pythonThreadState = 0; } // anonymous namespace const char* Python::PYKRITA_ENGINE = "pykrita"; Python::Python() { #if THREADED m_state = PyGILState_Ensure(); #endif } Python::~Python() { #if THREADED PyGILState_Release(m_state); #endif } bool Python::prependStringToList(PyObject* const list, const QString& value) { PyObject* const u = unicode(value); bool result = !PyList_Insert(list, 0, u); Py_DECREF(u); if (!result) traceback(QString("Failed to prepend %1").arg(value)); return result; } bool Python::functionCall(const char* const functionName, const char* const moduleName) { PyObject* const result = functionCall(functionName, moduleName, PyTuple_New(0)); if (result) Py_DECREF(result); return bool(result); } PyObject* Python::functionCall( const char* const functionName , const char* const moduleName , PyObject* const arguments ) { if (!arguments) { errScript << "Missing arguments for" << moduleName << functionName; return 0; } PyObject* const func = itemString(functionName, moduleName); if (!func) { errScript << "Failed to resolve" << moduleName << functionName; return 0; } if (!PyCallable_Check(func)) { traceback(QString("Not callable %1.%2").arg(moduleName).arg(functionName)); return 0; } PyObject* const result = PyObject_CallObject(func, arguments); Py_DECREF(arguments); if (!result) traceback(QString("No result from %1.%2").arg(moduleName).arg(functionName)); return result; } bool Python::itemStringDel(const char* const item, const char* const moduleName) { PyObject* const dict = moduleDict(moduleName); const bool result = dict && PyDict_DelItemString(dict, item); if (!result) traceback(QString("Could not delete item string %1.%2").arg(moduleName).arg(item)); return result; } PyObject* Python::itemString(const char* const item, const char* const moduleName) { if (PyObject* const value = itemString(item, moduleDict(moduleName))) return value; errScript << "Could not get item string" << moduleName << item; return 0; } PyObject* Python::itemString(const char* item, PyObject* dict) { if (dict) if (PyObject* const value = PyDict_GetItemString(dict, item)) return value; traceback(QString("Could not get item string %1").arg(item)); return 0; } bool Python::itemStringSet(const char* const item, PyObject* const value, const char* const moduleName) { PyObject* const dict = moduleDict(moduleName); const bool result = dict && !PyDict_SetItemString(dict, item, value); if (!result) traceback(QString("Could not set item string %1.%2").arg(moduleName).arg(item)); return result; } PyObject* Python::kritaHandler(const char* const moduleName, const char* const handler) { if (PyObject* const module = moduleImport(moduleName)) return functionCall(handler, "krita", Py_BuildValue("(O)", module)); return 0; } QString Python::lastTraceback() const { QString result; result.swap(m_traceback); return result; } -void Python::libraryLoad() +bool Python::libraryLoad() { -#ifdef Q_OS_WIN - if (Py_IsInitialized()) { - dbgScript << "Python interpreter is already initialized"; - } else { - dbgScript << "Initializing Python interpreter"; -#else + // no-op on Windows +#ifndef Q_OS_WIN if (!s_pythonLibrary) { dbgScript << "Creating s_pythonLibrary" << PYKRITA_PYTHON_LIBRARY; s_pythonLibrary = new QLibrary(PYKRITA_PYTHON_LIBRARY); - if (!s_pythonLibrary) + if (!s_pythonLibrary) { errScript << "Could not create" << PYKRITA_PYTHON_LIBRARY; + return false; + } s_pythonLibrary->setLoadHints(QLibrary::ExportExternalSymbolsHint); - if (!s_pythonLibrary->load()) + if (!s_pythonLibrary->load()) { errScript << "Could not load" << PYKRITA_PYTHON_LIBRARY; + return false; + } + } #endif + return true; +} - Py_InitializeEx(0); - if (!Py_IsInitialized()) { +bool Python::setPath(const QStringList& paths) +{ + if (Py_IsInitialized()) { + warnScript << "Setting paths when Python interpreter is already initialized"; + } #ifdef Q_OS_WIN - errScript << "Could not initialise Python interpreter"; + constexpr char pathSeparator = ';'; +#else + constexpr char pathSeparator = ':'; +#endif + QString joinedPaths = paths.join(pathSeparator); + // Append the default search path + // TODO: Properly handle embedded Python +#ifdef Q_OS_WIN + QString currentPaths; + // Find embeddable Python + // TODO: Don't hard-code the paths + QDir pythonDir(KoResourcePaths::getApplicationRoot()); + if (pythonDir.cd("python")) { + dbgScript << "Found embeddable Python at" << pythonDir.absolutePath(); + currentPaths = pythonDir.absolutePath() + pathSeparator + + pythonDir.absoluteFilePath("python36.zip"); + } else { +# if 1 + // Use local Python??? + currentPaths = QString::fromWCharArray(Py_GetPath()); + warnScript << "Embeddable Python not found."; + warnScript << "Default paths:" << currentPaths; +# else + // Or should we fail? + errScript << "Embeddable Python not found, not setting Python paths"; + return false; +# endif + } +#else + QString currentPaths = QString::fromLocal8Bit(qgetenv("PYTHONPATH")); +#endif + if (!currentPaths.isEmpty()) { + joinedPaths = joinedPaths + pathSeparator + currentPaths; + } + dbgScript << "Setting paths:" << joinedPaths; +#ifdef Q_OS_WIN + QVector joinedPathsWChars(joinedPaths.size() + 1, 0); + joinedPaths.toWCharArray(joinedPathsWChars.data()); + Py_SetPath(joinedPathsWChars.data()); #else - errScript << "Could not initialise" << PYKRITA_PYTHON_LIBRARY; + qputenv("PYTHONPATH", joinedPaths.toLocal8Bit()); #endif + return true; +} + +void Python::ensureInitialized() +{ + if (Py_IsInitialized()) { + warnScript << "Python interpreter is already initialized, not initializing again"; + } else { + dbgScript << "Initializing Python interpreter"; + Py_InitializeEx(0); + if (!Py_IsInitialized()) { + errScript << "Could not initialise Python interpreter"; } #if THREADED PyEval_InitThreads(); s_pythonThreadState = PyGILState_GetThisThreadState(); PyEval_ReleaseThread(s_pythonThreadState); #endif } } -void Python::libraryUnload() +void Python::maybeFinalize() { -#ifdef Q_OS_WIN - warnScript << "Explicitly unloading Python interpreter isn't supported for Windows"; - { -#else - if (s_pythonLibrary) { -#endif - // Shut the interpreter down if it has been started. - if (Py_IsInitialized()) { + if (!Py_IsInitialized()) { + warnScript << "Python interpreter not initialized, no need to finalize"; + } else { #if THREADED - PyEval_AcquireThread(s_pythonThreadState); + PyEval_AcquireThread(s_pythonThreadState); #endif - //Py_Finalize(); - } + Py_Finalize(); + } +} + +void Python::libraryUnload() +{ + // no-op on Windows #ifndef Q_OS_WIN + if (s_pythonLibrary) { + // Shut the interpreter down if it has been started. if (s_pythonLibrary->isLoaded()) { s_pythonLibrary->unload(); } delete s_pythonLibrary; s_pythonLibrary = 0; -#endif } +#endif } PyObject* Python::moduleActions(const char* moduleName) { return kritaHandler(moduleName, "moduleGetActions"); } PyObject* Python::moduleConfigPages(const char* const moduleName) { return kritaHandler(moduleName, "moduleGetConfigPages"); } QString Python::moduleHelp(const char* moduleName) { QString r; PyObject* const result = kritaHandler(moduleName, "moduleGetHelp"); if (result) { r = unicode(result); Py_DECREF(result); } return r; } PyObject* Python::moduleDict(const char* const moduleName) { PyObject* const module = moduleImport(moduleName); if (module) if (PyObject* const dictionary = PyModule_GetDict(module)) return dictionary; traceback(QString("Could not get dict %1").arg(moduleName)); return 0; } PyObject* Python::moduleImport(const char* const moduleName) { PyObject* const module = PyImport_ImportModule(moduleName); if (module) return module; traceback(QString("Could not import %1").arg(moduleName)); return 0; } void* Python::objectUnwrap(PyObject* o) { PyObject* const arguments = Py_BuildValue("(O)", o); PyObject* const result = functionCall("unwrapinstance", "sip", arguments); if (!result) return 0; void* const r = reinterpret_cast(ptrdiff_t(PyLong_AsLongLong(result))); Py_DECREF(result); return r; } PyObject* Python::objectWrap(void* const o, const QString& fullClassName) { const QString classModuleName = fullClassName.section('.', 0, -2); const QString className = fullClassName.section('.', -1); PyObject* const classObject = itemString(PQ(className), PQ(classModuleName)); if (!classObject) return 0; PyObject* const arguments = Py_BuildValue("NO", PyLong_FromVoidPtr(o), classObject); PyObject* const result = functionCall("wrapinstance", "sip", arguments); return result; } // Inspired by http://www.gossamer-threads.com/lists/python/python/150924. void Python::traceback(const QString& description) { m_traceback.clear(); if (!PyErr_Occurred()) // Return an empty string on no error. // NOTE "Return a string?" really?? return; PyObject* exc_typ; PyObject* exc_val; PyObject* exc_tb; PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb); // Include the traceback. if (exc_tb) { m_traceback = "Traceback (most recent call last):\n"; PyObject* const arguments = PyTuple_New(1); PyTuple_SetItem(arguments, 0, exc_tb); PyObject* const result = functionCall("format_tb", "traceback", arguments); if (result) { for (int i = 0, j = PyList_Size(result); i < j; i++) { PyObject* const tt = PyList_GetItem(result, i); PyObject* const t = Py_BuildValue("(O)", tt); char* buffer; if (!PyArg_ParseTuple(t, "s", &buffer)) break; m_traceback += buffer; } Py_DECREF(result); } Py_DECREF(exc_tb); } // Include the exception type and value. if (exc_typ) { PyObject* const temp = PyObject_GetAttrString(exc_typ, "__name__"); if (temp) { m_traceback += unicode(temp); m_traceback += ": "; } Py_DECREF(exc_typ); } if (exc_val) { PyObject* const temp = PyObject_Str(exc_val); if (temp) { m_traceback += unicode(temp); m_traceback += "\n"; } Py_DECREF(exc_val); } m_traceback += description; QStringList l = m_traceback.split("\n"); Q_FOREACH(const QString &s, l) { errScript << s; } /// \todo How about to show it somewhere else than "console output"? } PyObject* Python::unicode(const QString& string) { #if PY_MAJOR_VERSION < 3 /* Python 2.x. http://docs.python.org/2/c-api/unicode.html */ PyObject* s = PyString_FromString(PQ(string)); PyObject* u = PyUnicode_FromEncodedObject(s, "utf-8", "strict"); Py_DECREF(s); return u; #elif PY_MINOR_VERSION < 3 /* Python 3.2 or less. http://docs.python.org/3.2/c-api/unicode.html#unicode-objects */ # ifdef Py_UNICODE_WIDE return PyUnicode_DecodeUTF16((const char*)string.constData(), string.length() * 2, 0, 0); # else return PyUnicode_FromUnicode(string.constData(), string.length()); # endif #else /* Python 3.3 or greater. http://docs.python.org/3.3/c-api/unicode.html#unicode-objects */ return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, string.constData(), string.length()); #endif } QString Python::unicode(PyObject* const string) { #if PY_MAJOR_VERSION < 3 /* Python 2.x. http://docs.python.org/2/c-api/unicode.html */ if (PyString_Check(string)) return QString(PyString_AsString(string)); else if (PyUnicode_Check(string)) { const int unichars = PyUnicode_GetSize(string); # ifdef HAVE_USABLE_WCHAR_T return QString::fromWCharArray(PyUnicode_AsUnicode(string), unichars); # else # ifdef Py_UNICODE_WIDE return QString::fromUcs4((const unsigned int*)PyUnicode_AsUnicode(string), unichars); # else return QString::fromUtf16(PyUnicode_AsUnicode(string), unichars); # endif # endif } else return QString(); #elif PY_MINOR_VERSION < 3 /* Python 3.2 or less. http://docs.python.org/3.2/c-api/unicode.html#unicode-objects */ if (!PyUnicode_Check(string)) return QString(); const int unichars = PyUnicode_GetSize(string); # ifdef HAVE_USABLE_WCHAR_T return QString::fromWCharArray(PyUnicode_AsUnicode(string), unichars); # else # ifdef Py_UNICODE_WIDE return QString::fromUcs4(PyUnicode_AsUnicode(string), unichars); # else return QString::fromUtf16(PyUnicode_AsUnicode(string), unichars); # endif # endif #else /* Python 3.3 or greater. http://docs.python.org/3.3/c-api/unicode.html#unicode-objects */ if (!PyUnicode_Check(string)) return QString(); const int unichars = PyUnicode_GetLength(string); if (0 != PyUnicode_READY(string)) return QString(); switch (PyUnicode_KIND(string)) { case PyUnicode_1BYTE_KIND: return QString::fromLatin1((const char*)PyUnicode_1BYTE_DATA(string), unichars); case PyUnicode_2BYTE_KIND: return QString::fromUtf16(PyUnicode_2BYTE_DATA(string), unichars); case PyUnicode_4BYTE_KIND: return QString::fromUcs4(PyUnicode_4BYTE_DATA(string), unichars); default: break; } return QString(); #endif } bool Python::isUnicode(PyObject* const string) { #if PY_MAJOR_VERSION < 3 return PyString_Check(string) || PyUnicode_Check(string); #else return PyUnicode_Check(string); #endif } void Python::updateConfigurationFromDictionary(KConfigBase* const config, PyObject* const dictionary) { PyObject* groupKey; PyObject* groupDictionary; Py_ssize_t position = 0; while (PyDict_Next(dictionary, &position, &groupKey, &groupDictionary)) { if (!isUnicode(groupKey)) { traceback(QString("Configuration group name not a string")); continue; } QString groupName = unicode(groupKey); if (!PyDict_Check(groupDictionary)) { traceback(QString("Configuration group %1 top level key not a dictionary").arg(groupName)); continue; } // There is a group per module. KConfigGroup group = config->group(groupName); PyObject* key; PyObject* value; Py_ssize_t x = 0; while (PyDict_Next(groupDictionary, &x, &key, &value)) { if (!isUnicode(key)) { traceback(QString("Configuration group %1 itemKey not a string").arg(groupName)); continue; } PyObject* arguments = Py_BuildValue("(Oi)", value, 0); PyObject* pickled = functionCall("dumps", "pickle", arguments); if (pickled) { #if PY_MAJOR_VERSION < 3 QString ascii(unicode(pickled)); #else QString ascii(PyBytes_AsString(pickled)); #endif group.writeEntry(unicode(key), ascii); Py_DECREF(pickled); } else { errScript << "Cannot write" << groupName << unicode(key) << unicode(PyObject_Str(value)); } } } } void Python::updateDictionaryFromConfiguration(PyObject* const dictionary, const KConfigBase* const config) { qDebug() << config->groupList(); Q_FOREACH(QString groupName, config->groupList()) { KConfigGroup group = config->group(groupName); PyObject* groupDictionary = PyDict_New(); PyDict_SetItemString(dictionary, PQ(groupName), groupDictionary); Q_FOREACH(QString key, group.keyList()) { QString pickled = group.readEntry(key); #if PY_MAJOR_VERSION < 3 PyObject* arguments = Py_BuildValue("(s)", PQ(pickled)); #else PyObject* arguments = Py_BuildValue("(y)", PQ(pickled)); #endif PyObject* value = functionCall("loads", "pickle", arguments); if (value) { PyDict_SetItemString(groupDictionary, PQ(key), value); Py_DECREF(value); } else { errScript << "Cannot read" << groupName << key << pickled; } } Py_DECREF(groupDictionary); } } bool Python::prependPythonPaths(const QString& path) { PyObject* sys_path = itemString("path", "sys"); return bool(sys_path) && prependPythonPaths(path, sys_path); } bool Python::prependPythonPaths(const QStringList& paths) { PyObject* sys_path = itemString("path", "sys"); if (!sys_path) return false; /// \todo Heh, boosts' range adaptors would be good here! QStringList reversed_paths; std::reverse_copy( paths.begin() , paths.end() , std::back_inserter(reversed_paths) ); Q_FOREACH(const QString & path, reversed_paths) if (!prependPythonPaths(path, sys_path)) return false; return true; } bool Python::prependPythonPaths(const QString& path, PyObject* sys_path) { Q_ASSERT("Dir entry expected to be valid" && sys_path); return bool(prependStringToList(sys_path, path)); } } // namespace PyKrita // krita: indent-width 4; diff --git a/plugins/extensions/pykrita/plugin/utilities.h b/plugins/extensions/pykrita/plugin/utilities.h index 5e60410c80..0324268a4a 100644 --- a/plugins/extensions/pykrita/plugin/utilities.h +++ b/plugins/extensions/pykrita/plugin/utilities.h @@ -1,229 +1,246 @@ // This file is part of PyKrita, Krita' Python scripting plugin. // // A couple of useful macros and functions used inside of pykrita_engine.cpp and pykrita_plugin.cpp. // // Copyright (C) 2006 Paul Giannaros // Copyright (C) 2012, 2013 Shaheed Haque // // This library 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 2.1 of the License, or (at your option) version 3, or any // later version accepted by the membership of KDE e.V. (or its // successor approved by the membership of KDE e.V.), which shall // act as a proxy defined in Section 6 of version 3 of the license. // // This library 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 library. If not, see . // #ifndef __PYKRITA_UTILITIES_H__ # define __PYKRITA_UTILITIES_H__ #include #include #include class KConfigBase; /// Save us some ruddy time when printing out QStrings with UTF-8 # define PQ(x) x.toUtf8().constData() namespace PyKrita { /** * Instantiate this class on the stack to automatically get and release the * GIL. * * Also, making all the utility functions members of this class means that in * many cases the compiler tells us where the class in needed. In the remaining * cases (i.e. bare calls to the Python C API), inspection is used to needed * to add the requisite Python() object. To prevent this object being optimised * away in these cases due to lack of use, all instances have the form of an * assignment, e.g.: * * Python py = Python() * * This adds a little overhead, but this is a small price for consistency. */ class Python { public: Python(); ~Python(); /** - * Load the Python interpreter. + * Load the Python shared library. This does nothing on Windows. */ - static void libraryLoad(); + static bool libraryLoad(); /** - * Unload the Python interpreter. + * Set the Python paths by calling Py_SetPath. This should be called before + * initialization to ensure the proper libraries get loaded. + */ + static bool setPath(const QStringList& paths); + + /** + * Make sure the Python interpreter is initialized. Ideally should be only + * called once. + */ + static void ensureInitialized(); + + /** + * Finalize the Python interpreter. Not gauranteed to work. + */ + static void maybeFinalize(); + + /** + * Unload the Python shared library. This does nothing on Windows. */ static void libraryUnload(); /// Convert a QString to a Python unicode object. static PyObject* unicode(const QString& string); /// Convert a Python unicode object to a QString. static QString unicode(PyObject* string); /// Test if a Python object is compatible with a QString. static bool isUnicode(PyObject* string); /// Prepend a QString to a list as a Python unicode object bool prependStringToList(PyObject* list, const QString& value); /** * Print and save (see @ref lastTraceback()) the current traceback in a * form approximating what Python would print: * * Traceback (most recent call last): * File "/home/shahhaqu/.kde/share/apps/krita/pykrita/pluginmgr.py", line 13, in * import kdeui * ImportError: No module named kdeui * Could not import pluginmgr. */ void traceback(const QString& description); /** * Store the last traceback we handled using @ref traceback(). */ QString lastTraceback(void) const; /** * Create a Python dictionary from a KConfigBase instance, writing the * string representation of the values. */ void updateDictionaryFromConfiguration(PyObject* dictionary, const KConfigBase* config); /** * Write a Python dictionary to a configuration object, converting objects * to their string representation along the way. */ void updateConfigurationFromDictionary(KConfigBase* config, PyObject* dictionary); /** * Call the named module's named entry point. */ bool functionCall(const char* functionName, const char* moduleName = PYKRITA_ENGINE); PyObject* functionCall(const char* functionName, const char* moduleName, PyObject* arguments); /** * Delete the item from the named module's dictionary. */ bool itemStringDel(const char* item, const char* moduleName = PYKRITA_ENGINE); /** * Get the item from the named module's dictionary. * * @return 0 or a borrowed reference to the item. */ PyObject* itemString(const char* item, const char* moduleName = PYKRITA_ENGINE); /** * Get the item from the given dictionary. * * @return 0 or a borrowed reference to the item. */ PyObject* itemString(const char* item, PyObject* dict); /** * Set the item in the named module's dictionary. */ bool itemStringSet(const char* item, PyObject* value, const char* moduleName = PYKRITA_ENGINE); /** * Get the Actions defined by a module. The returned object is * [ { function, ( text, icon, shortcut, menu ) }... ] for each module * function decorated with @action. * * @return 0 or a new reference to the result. */ PyObject* moduleActions(const char* moduleName); /** * Get the ConfigPages defined by a module. The returned object is * [ { function, callable, ( name, fullName, icon ) }... ] for each module * function decorated with @configPage. * * @return 0 or a new reference to the result. */ PyObject* moduleConfigPages(const char* moduleName); /** * Get the named module's dictionary. * * @return 0 or a borrowed reference to the dictionary. */ PyObject* moduleDict(const char* moduleName = PYKRITA_ENGINE); /** * Get the help text defined by a module. */ QString moduleHelp(const char* moduleName); /** * Import the named module. * * @return 0 or a borrowed reference to the module. */ PyObject* moduleImport(const char* moduleName); /** * A void * for an arbitrary Qt/KDE object that has been wrapped by SIP. Nifty. * * @param o The object to be unwrapped. The reference is borrowed. */ void* objectUnwrap(PyObject* o); /** * A PyObject * for an arbitrary Qt/KDE object using SIP wrapping. Nifty. * * @param o The object to be wrapped. * @param className The full class name of o, e.g. "PyQt5.QtWidgets.QWidget". * @return @c 0 or a new reference to the object. */ PyObject* objectWrap(void* o, const QString& className); /** * Add a given path to to the front of \c PYTHONPATH * * @param path A string (path) to be added * @return @c true on success, @c false otherwise. */ bool prependPythonPaths(const QString& path); /** * Add listed paths to to the front of \c PYTHONPATH * * @param paths A string list (paths) to be added * @return @c true on success, @c false otherwise. */ bool prependPythonPaths(const QStringList& paths); static const char* PYKRITA_ENGINE; private: /// @internal Helper function for @c prependPythonPaths overloads bool prependPythonPaths(const QString&, PyObject*); PyGILState_STATE m_state; mutable QString m_traceback; /** * Run a handler function supplied by the krita module on another module. * * @return 0 or a new reference to the result. */ PyObject* kritaHandler(const char* moduleName, const char* handler); }; } // namespace PyKrita #endif // __PYKRITA_UTILITIES_H__ // krita: indent-width 4;