From 16fef181f8205a4d2f4676a5f025eecd70487063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 8 Jul 2024 19:35:44 +0200 Subject: [PATCH 01/21] Install libraries to lib instead of libs. Remove mo2_configure_uibase(). --- mo2_cpp.cmake | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/mo2_cpp.cmake b/mo2_cpp.cmake index 25b8125..75092d6 100644 --- a/mo2_cpp.cmake +++ b/mo2_cpp.cmake @@ -251,26 +251,6 @@ function(mo2_configure_tests TARGET) ) endfunction() -#! mo2_configure_uibase : configure the uibase target for MO2 -# -# this function does mostly nothing except calling mo2_configure_target, but is useful -# to be consistent with other mo2_configure_XXX -# -function(mo2_configure_uibase TARGET) - if (NOT (${TARGET} STREQUAL "uibase")) - message(WARNING "mo2_configure_uibase() should only be used on the uibase target") - endif() - - mo2_configure_target(${TARGET} ${ARGN}) - set_target_properties(${TARGET} PROPERTIES MO2_TARGET_TYPE "uibase") - - target_include_directories(${TARGET} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/game_features) - - mo2_set_project_to_run_from_install( - ${TARGET} EXECUTABLE ${CMAKE_INSTALL_PREFIX}/bin/ModOrganizer.exe) -endfunction() - #! mo2_configure_plugin : configure a target as a MO2 C++ plugin # # this function automatically set uibase as a dependency @@ -355,23 +335,19 @@ function(mo2_install_target TARGET) get_target_property(MO2_TARGET_TYPE ${TARGET} MO2_TARGET_TYPE) # core install: .lib, .dll or .exe, to the right folder - if (${MO2_TARGET_TYPE} STREQUAL "uibase") - mo2_set_if_not_defined(MO2_INSTALLDIR "bin") - install(TARGETS ${TARGET} RUNTIME DESTINATION ${MO2_INSTALLDIR}) - install(TARGETS ${TARGET} ARCHIVE DESTINATION libs) - elseif (${MO2_TARGET_TYPE} STREQUAL "plugin") + if (${MO2_TARGET_TYPE} STREQUAL "plugin") if (${MO2_FOLDER}) install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins/$) else() install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins) endif() - install(TARGETS ${TARGET} ARCHIVE DESTINATION libs) + install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) elseif (${MO2_TARGET_TYPE} STREQUAL "library-static") - install(TARGETS ${TARGET} ARCHIVE DESTINATION libs) + install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) elseif (${MO2_TARGET_TYPE} STREQUAL "library-shared") mo2_set_if_not_defined(MO2_INSTALLDIR "bin/dlls") install(TARGETS ${TARGET} RUNTIME DESTINATION ${MO2_INSTALLDIR}) - install(TARGETS ${TARGET} ARCHIVE DESTINATION libs) + install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) elseif (${MO2_TARGET_TYPE} STREQUAL "executable") mo2_set_if_not_defined(MO2_INSTALLDIR "bin") install(TARGETS ${TARGET} RUNTIME DESTINATION ${MO2_INSTALLDIR}) From 44457dc6dd2f2e2db1f53adbc60aa6d56b46769e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Wed, 10 Jul 2024 20:48:35 +0200 Subject: [PATCH 02/21] Import stuff from registry and update README. --- README.md | 168 ++++++++--------- mo2.cmake | 47 ++--- mo2_cpp.cmake | 347 ++++++++++++++--------------------- mo2_python.cmake | 78 +------- mo2_targets.cmake | 448 ---------------------------------------------- mo2_utils.cmake | 100 +++++++---- 6 files changed, 299 insertions(+), 889 deletions(-) delete mode 100644 mo2_targets.cmake diff --git a/README.md b/README.md index 062ede0..0f2f874 100644 --- a/README.md +++ b/README.md @@ -1,94 +1,96 @@ # MO2 CMake Common -This repository contains CMake macro and functions that are used in most MO2 -repository to build uibase, modorganizer itself and plugins (C++/Python). +This repository contains useful CMake macro and functions that are used to ease creating +CMake configuration for most of MO2 repositories (e.g., +[uibase](https://github.com/ModOrganizer2/modorganizer-uibase), +[plugin_python](https://github.com/ModOrganizer2/modorganizer-plugin_python) or +[ModOrganizer2](https://github.com/ModOrganizer2/modorganizer) itself). ## Getting Started -To get started, simply include the `mo2.cmake` file in your project: - -```cmake -# this can safely be included multiple time -include(path_to_cmake_common/mo2.cmake) +### 1. With VCPKG + +Add [MO2 VCPKG registry](https://github.com/ModOrganizer2/vcpkg-registry) to your +`vcpkg-configuration.json`: + +```json +{ + "default-registry": { + "kind": "git", + "repository": "https://github.com/Microsoft/vcpkg", + "baseline": "f61a294e765b257926ae9e9d85f96468a0af74e7" + }, + "registries": [ + { + "kind": "git", + "repository": "https://github.com/ModOrganizer2/vcpkg-registry", + "baseline": "27d8adbfe9e4ce88a875be3a45fadab69869eb60", + "packages": ["mo2-cmake"] + } + ] +} ``` -This will set some (not too intrusive) variables and define many useful functions, -all prefixed with `mo2_`. - -The basic MO2 plugin/executable CMake will then look like this: +Add `mo2-cmake` to your VCPKG dependencies (`vcpkg.json`) and then import the utilities +with `find_package` in your CMake configuration: ```cmake -# this is for a C++ plugin -add_library(my_plugin SHARED) - -# configure the plugin - this will also set the sources of the plugin -# based on the file in the current directory (and subdirectory) -mo2_configure_plugin(my_plugin) - -# install the target to MO2 installation path, typically in the -# plugins/ folder -mo2_install_target(my_plugin) +find_package(mo2-cmake CONFIG REQUIRED) ``` -## Configuring - -The main entry-points are the the configure functions: - -- C++ - - `mo2_configure_target` - this is the "base" configuration, - which should not be used most of the time (this one is called by - other functions), - - `mo2_configure_uibase` - specific for uibase, should not be used - elsewhere, - - `mo2_configure_plugin` - configuration for a C++ plugin, - - `mo2_configure_library` - configuration for static or shared libraries, - - `mo2_configure_executable` - configuration for executables, including the main - MO2 executable, - - `mo2_configure_tests` - configuration for tests. -- Python - - `mo2_configure_python`. - -The function are documented so you can look at the documentation to see what arguments -are available. - -### Dependencies - -You can add dependencies to the target by using the standard `target_link_libraries`, -but this might be difficult when looking for MO2 dependencies, such as other libraries -or Qt. - -The `mo2_configure_XXX` accept `PRIVATE_DEPENDS` and `PUBLIC_DEPENDS` parameters that -can be used to add dependencies to the target in an easier way. -These parameters accept 3 types of dependencies: - -- Qt dependencies, that should be specified as `Qt::COMPONENTS`, e.g., `Qt::Core` or - `Qt::Widgets`. Note that the `Qt::` is version-independent, MO2 will add the proper - version for you. -- Boost dependencies - For header only libraries, you can simply pass `boost`, for - non-header only libraries, you can pass `boost::COMPONENT`, e.g., `boost::threads`. -- MO2 dependencies - Those are either components available via the MO2 build system, - such as `zlib` or `libbsarch`, or other MO2 components such as `game_gamebryo`. - -## Installing - -For C++ plugin, you need to call `mo2_install_target` to install the plugins or -executable in MO2 installation directory. -Where the target should be installed is defined in the `mo2_configure_XXX` function -(which should be called before `mo2_install_target`). - -Installing Python plugins does not require extra call apart from `mo2_configure_python`. - -## Examples - -All MO2 repositories use these functions, so you can look at any repository to get -details on how to use them. -Here are entry points for the various type of plugins: - -- [`game_skyrimse`](https://github.com/ModOrganizer2/modorganizer-game_skyrimSE/) - for a plugin that depends on a static library built by MO2. -- [`game_gamebryo`](https://github.com/ModOrganizer2/modorganizer-game_gamebryo/) - for a static library example. -- [`installer_wizard`](https://github.com/ModOrganizer2/modorganizer-installer_wizard) - for a Python plugin (module). -- [`fnistool`](https://github.com/ModOrganizer2/modorganizer-fnistool/) for a Python - plugin (simple file). +### 2. Manually + +Clone this repository somewhere and then `include(mo2.cmake)` in your CMake +configuration files. + +## Usage + +Be aware that using these utilities will automatically some (not too intrusive) global +variable on your project. +You will need to have `Qt` in your CMake module path otherwise the import will fail +due to a broken `find_package(Qt6)`. + +For some functions to work properly, you should set `CMAKE_INSTALL_PREFIX` to a +directory where `${CMAKE_INSTALL_PREFIX}/bin/ModOrganizer.exe` can be found. + +All functions are prefixed by `mo2_` and should not conflict with other existing +functions. + +### Generic Utilities + +- `mo2_set_if_not_defined` - Set a variable to a given value if the variable is not + defined. +- `mo2_add_subdirectories` - +- `mo2_find_python_executable` - Find Python executable. +- `mo2_find_git_hash` - Find the hash of the current git HEAD. +- `mo2_find_qt_executable` - Find a given Qt executable. +- `mo2_set_project_to_run_from_install` - Configure the debug executable for a VS project. +- `mo2_add_filter` - Add a source group filter. +- `mo2_deploy_qt_for_tests` - Deploy Qt DLLs, etc., for tests. +- `mo2_deploy_qt` - Deploy Qt DLLs for ModOrganizer2. +- `mo2_add_lupdate` - Create a target to run Qt `lupdate`. +- `mo2_add_lrelease` - Create a target to Qt `lrelease`. +- `mo2_add_translations` - Add targets to generate translations for the given target. + +### C++ Utilities + +TODO: + +- `mo2_configure_warnings` - Utility function configure warnings for a target. +- `mo2_configure_sources` - Glob and configure sources, including Qt-related files + (.ui, etc.) for a target. +- `mo2_configure_msvc` - Set some MSVC-specific flag for a target. +- (Deprecated) `mo2_configure_target` - Combine the above function + Extra stuff. +- (Deprecated) `mo2_configure_plugin` +- `mo2_configure_tests` - TO BE CHANGED +- `mo2_install_plugin` - Install a plugin. + +### Python Utilities + +- `mo2_python_uifiles` - Add a target to generate `.py` files from `.ui` files. +- `mo2_python_pip_install` - Install Python packages. +- `mo2_python_requirements` - Install plugin requirements from a `plugin-requirements.txt` + file, ready to ship. +- `mo2_configure_python_module` - Configure a Python module plugin. +- (Deprecated) `mo2_configure_python_simple` - Configure a Python single file plugin. +- `mo2_configure_python` - Wrapper for the two above functions. diff --git a/mo2.cmake b/mo2.cmake index b4eed18..041632d 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -4,42 +4,21 @@ if (DEFINED MO2_INCLUDED) return() endif() +set(MO2_QT_MAJOR_VERSION 6) +set(MO2_QT_MINOR_VERSION 7) +set(MO2_QT_PATCH_VERSION 0) +set(MO2_QT_VERSION "${MO2_QT_MAJOR_VERSION}.${MO2_QT_MINOR_VERSION}.${MO2_QT_PATCH_VERSION}") + +set(MO2_PYTHON_VERSION "3.12") + +find_package(Qt6 ${MO2_QT_VERSION} REQUIRED COMPONENTS Core) + +# version-major independent variables +set(Qt_VERSION ${Qt6_VERSION}) +set(Qt_VERSION_MAJOR ${Qt6_VERSION_MAJOR}) + include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) -# setup path for find_package(), etc. -mo2_required_variable(NAME BOOST_ROOT TYPE PATH) -mo2_required_variable(NAME QT_ROOT TYPE PATH) -mo2_required_variable(NAME PYTHON_ROOT TYPE PATH) -mo2_required_variable(NAME CMAKE_INSTALL_PREFIX TYPE PATH) - -get_filename_component(MO2_BUILD_PATH "${CMAKE_CURRENT_LIST_DIR}/../.." REALPATH) -get_filename_component(MO2_SUPER_PATH "${MO2_BUILD_PATH}/modorganizer_super" REALPATH) -get_filename_component(MO2_UIBASE_PATH "${MO2_SUPER_PATH}/uibase" REALPATH) -get_filename_component(MO2_INSTALL_PATH "${MO2_SUPER_PATH}/../../install" REALPATH) -get_filename_component(MO2_INSTALL_LIBS_PATH "${MO2_INSTALL_PATH}/libs" REALPATH) - -list(APPEND CMAKE_PREFIX_PATH - ${QT_ROOT}/lib/cmake - ${BOOST_ROOT}/build - ${MO2_BUILD_PATH}/googletest/build/lib/cmake/GTest) - -# find Qt major version and set QT_DEFAULT_MAJOR_VERSION to avoid issues -# when including LinguistTools without a QtCore -mo2_find_qt_version(QT_VERSION) -string(REPLACE "." ";" QT_VERSION_LIST ${QT_VERSION}) -list(GET QT_VERSION_LIST 0 QT_MAJOR_VERSION) - -# we add the Qt DLL to the paths for some tools -set(ENV{PATH} "${QT_ROOT}/bin;$ENV{PATH}") - -# custom property, used to keep track of the type of target -define_property( - TARGET PROPERTY MO2_TARGET_TYPE INHERITED - BRIEF_DOCS "Target type for MO2 C++ target." - FULL_DOCS "Automatically set when using mo2_configure_XXX functions.") - -set(Boost_USE_STATIC_RUNTIME OFF) -set(Boost_USE_STATIC_LIBS ON) set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) set_property(GLOBAL PROPERTY USE_FOLDERS ON) diff --git a/mo2_cpp.cmake b/mo2_cpp.cmake index 75092d6..1990327 100644 --- a/mo2_cpp.cmake +++ b/mo2_cpp.cmake @@ -1,83 +1,27 @@ cmake_minimum_required(VERSION 3.21) -if (POLICY CMP0144) - cmake_policy(SET CMP0144 NEW) -endif() - include(CMakeParseArguments) include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/mo2_targets.cmake) -#! mo2_configure_target : do basic configuration for a MO2 C++ target -# -# this functions does many things: -# - glob relevant files and add them to the target -# - set many compile flags, definitions, etc. -# - add step to create translations (if not turned OFF) +#! mo2_configure_warnings : configuration warning for C++ target # -# \param:SOURCE_TREE if set, a source_group will be created using TREE -# \param:WARNINGS enable all warnings, possible values are ON/All, OFF, or 1, 2, 3, 4 +# \param:WARNINGS level of warnings, possible values are ON/All, OFF, or 1, 2, 3, 4 # for corresponding /W flags (ON is All) (default ON) -# \param:EXTERNAL_WARNINGS enable warnings for external libraries, possible values are +# \param:EXTERNAL enable warnings for external libraries, possible values are # the same as warnings, but ON is 3 (default 1) -# \param:PERMISSIVE permissive mode (default OFF) -# \param:BIGOBJ enable bigobj (default OFF) -# \param:CLI enable C++/CLR (default OFF) -# \param:TRANSLATIONS generate translations (default ON) -# \param:AUTOMOC automoc (and autouic, autoqrc), (default ON) -# \param:EXTRA_TRANSLATIONS extra translations to include (folder) -# \param:PUBLIC_DEPENDS adds PUBLIC dependencies to the target, see -# mo2_add_dependencies for information on what is available -# \param:PRIVATE_DEPENDS same a PUBLIC_DEPENDS, but link is PRIVATE instead of PUBLIC # -function(mo2_configure_target TARGET) - cmake_parse_arguments(MO2 "SOURCE_TREE" - "WARNINGS;EXTERNAL_WARNINGS;PERMISSIVE;BIGOBJ;CLI;TRANSLATIONS;AUTOMOC" - "EXTRA_TRANSLATIONS;PUBLIC_DEPENDS;PRIVATE_DEPENDS" - ${ARGN}) +function(mo2_configure_warnings TARGET) + cmake_parse_arguments(MO2 "" "WARNINGS;EXTERNAL" "" ${ARGN}) - # configure parameters and compiler flags mo2_set_if_not_defined(MO2_WARNINGS ON) - mo2_set_if_not_defined(MO2_EXTERNAL_WARNINGS 1) - mo2_set_if_not_defined(MO2_PERMISSIVE OFF) - mo2_set_if_not_defined(MO2_BIGOBJ OFF) - mo2_set_if_not_defined(MO2_CLI OFF) - mo2_set_if_not_defined(MO2_TRANSLATIONS ON) - mo2_set_if_not_defined(MO2_AUTOMOC ON) - mo2_set_if_not_defined(MO2_EXTRA_TRANSLATIONS "") - mo2_set_if_not_defined(MO2_PUBLIC_DEPENDS "") - mo2_set_if_not_defined(MO2_PRIVATE_DEPENDS "") - - if (${MO2_AUTOMOC}) - find_package(Qt6 COMPONENTS Widgets REQUIRED) - set_target_properties(${TARGET} - PROPERTIES AUTOMOC ON AUTOUIC ON AUTORCC ON) - endif() - - target_compile_options(${TARGET} - PRIVATE "/MP" - $<$:/O2> - ) - - set(CXX_STANDARD 20) - if (${MO2_CLI}) - set(CXX_STANDARD 17) - endif() - set_target_properties(${TARGET} PROPERTIES - CXX_STANDARD ${CXX_STANDARD} - CXX_EXTENSIONS OFF) - - # VS emits a warning for LTCG, at least for uibase, so maybe not required? - target_link_options(${TARGET} - PRIVATE - $<$:/LTCG /INCREMENTAL:NO /OPT:REF /OPT:ICF>) + mo2_set_if_not_defined(MO2_EXTERNAL 1) if (${MO2_WARNINGS} STREQUAL "ON") set(MO2_WARNINGS "All") endif() - if (${MO2_EXTERNAL_WARNINGS} STREQUAL "ON") - set(MO2_EXTERNAL_WARNINGS "3") + if (${MO2_EXTERNAL} STREQUAL "ON") + set(MO2_EXTERNAL "3") endif() if(NOT (${MO2_WARNINGS} STREQUAL "OFF")) @@ -85,23 +29,24 @@ function(mo2_configure_target TARGET) target_compile_options(${TARGET} PRIVATE "/W${MO2_WARNINGS}" "/wd4464") # external warnings - if (${MO2_EXTERNAL_WARNINGS} STREQUAL "OFF") + if (${MO2_EXTERNAL} STREQUAL "OFF") target_compile_options(${TARGET} PRIVATE "/external:anglebrackets" "/external:W0") else() - string(TOLOWER ${MO2_EXTERNAL_WARNINGS} MO2_EXTERNAL_WARNINGS) + string(TOLOWER ${MO2_EXTERNAL} MO2_EXTERNAL) target_compile_options(${TARGET} - PRIVATE "/external:anglebrackets" "/external:W${MO2_EXTERNAL_WARNINGS}") + PRIVATE "/external:anglebrackets" "/external:W${MO2_EXTERNAL}") endif() endif() - if(NOT ${MO2_PERMISSIVE}) - target_compile_options(${TARGET} PRIVATE "/permissive-") - endif() +endfunction() - if(${MO2_BIGOBJ}) - target_compile_options(${TARGET} PRIVATE "/bigobj") - endif() +#! mo2_configure_sources : configure sources for the given C++ target +# +# \param:SOURCE_TREE if set, a source_group will be created using TREE +# +function(mo2_configure_sources TARGET) + cmake_parse_arguments(MO2 "SOURCE_TREE" "" "" ${ARGN}) # find source files if(DEFINED AUTOGEN_BUILD_DIR) @@ -117,28 +62,20 @@ function(mo2_configure_target TARGET) file(GLOB_RECURSE ui_files CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.ui) file(GLOB_RECURSE ui_header_files CONFIGURE_DEPENDS ${UI_HEADERS_DIR}/*.h) file(GLOB_RECURSE rule_files CONFIGURE_DEPENDS ${CMAKE_BINARY_DIR}/*.rule) - file(GLOB_RECURSE misc_files CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../*.natvis) if (${MO2_SOURCE_TREE}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} - PREFIX src - FILES ${source_files} ${header_files}) + PREFIX src FILES ${source_files} ${header_files}) else() source_group(src REGULAR_EXPRESSION ".*\\.(h|cpp)") endif() + source_group(ui REGULAR_EXPRESSION ".*\\.ui") source_group(cmake FILES CMakeLists.txt) source_group(autogen FILES ${rule_files} ${qm_files} ${ui_header_files}) source_group(autogen REGULAR_EXPRESSION ".*\\cmake_pch.*") source_group(resources FILES ${rc_files} ${qrc_files}) - - if(${MO2_TRANSLATIONS}) - mo2_add_translations(${TARGET} - INSTALL_RELEASE - SOURCES ${CMAKE_CURRENT_SOURCE_DIR} ${MO2_EXTRA_TRANSLATIONS}) - endif() - target_sources(${TARGET} PRIVATE ${source_files} @@ -150,80 +87,136 @@ function(mo2_configure_target TARGET) ${misc_files} ${qm_files}) - execute_process( - COMMAND git log -1 --format=%h - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE GIT_COMMIT_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) +endfunction() - target_compile_definitions( - ${TARGET} - PRIVATE - _UNICODE - UNICODE - NOMINMAX - _CRT_SECURE_NO_WARNINGS - BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE - _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING - QT_MESSAGELOGCONTEXT - GITID="${GIT_COMMIT_HASH}") +#! mo2_configure_msvc : set flags for C++ target with MSVC +# +# \param:PERMISSIVE permissive mode (default OFF) +# \param:BIGOBJ enable bigobj (default OFF) +# \param:CLI enable C++/CLR (default OFF) +# +function(mo2_configure_msvc TARGET) - if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/pch.h) - target_precompile_headers(${PROJECT_NAME} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/pch.h) + if (NOT MSVC) + return() + endif() + + cmake_parse_arguments(MO2 "" "PERMISSIVE;BIGOBJ;CLI" "" ${ARGN}) + + set(CXX_STANDARD 20) + if (${MO2_CLI}) + set(CXX_STANDARD 17) + endif() + set_target_properties(${TARGET} PROPERTIES + CXX_STANDARD ${CXX_STANDARD} CXX_EXTENSIONS OFF) + + if(NOT ${MO2_PERMISSIVE}) + target_compile_options(${TARGET} PRIVATE "/permissive-") + endif() + + if(${MO2_BIGOBJ}) + target_compile_options(${TARGET} PRIVATE "/bigobj") endif() + # multi-threaded compilation + target_compile_options(${TARGET} PRIVATE "/MP") + + # VS emits a warning for LTCG, at least for uibase, so maybe not required? + target_link_options(${TARGET} + PRIVATE + $<$: + # enable link-time code generation + /LTCG + + # disable incremental linking + /INCREMENTAL:NO + + # eliminates functions and data that are never referenced + /OPT:REF + + # perform identical COMDAT folding + /OPT:ICF + >) + if(${MO2_CLI}) - if (CMAKE_GENERATOR MATCHES "Visual Studio") - set_target_properties(${TARGET} PROPERTIES COMMON_LANGUAGE_RUNTIME "") - else() - # can this really happen? - set(COMPILE_FLAGS "${COMPILE_FLAGS} /clr") - string(REPLACE "/EHs" "/EHa" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - endif() + set_target_properties(${TARGET} PROPERTIES COMMON_LANGUAGE_RUNTIME "") endif() set_target_properties(${TARGET} PROPERTIES VS_STARTUP_PROJECT ${TARGET}) - target_link_libraries(${TARGET} PRIVATE Version Dbghelp) +endfunction() - if (MO2_PUBLIC_DEPENDS) - mo2_add_dependencies(${TARGET} PUBLIC ${MO2_PUBLIC_DEPENDS}) +#! mo2_configure_target : do basic configuration for a MO2 C++ target +# +# this functions does many things: +# - glob relevant files and add them to the target +# - set many compile flags, definitions, etc. +# - add step to create translations (if not turned OFF) +# +# \param:SOURCE_TREE if set, a source_group will be created using TREE +# \param:WARNINGS enable all warnings, possible values are ON/All, OFF, or 1, 2, 3, 4 +# for corresponding /W flags (ON is All) (default ON) +# \param:EXTERNAL_WARNINGS enable warnings for external libraries, possible values are +# the same as warnings, but ON is 3 (default 1) +# \param:PERMISSIVE permissive mode (default OFF) +# \param:BIGOBJ enable bigobj (default OFF) +# \param:CLI enable C++/CLR (default OFF) +# \param:TRANSLATIONS generate translations (default ON) +# \param:AUTOMOC automoc (and autouic, autoqrc), (default ON) +# \param:EXTRA_TRANSLATIONS extra translations to include (folder) +# +function(mo2_configure_target TARGET) + cmake_parse_arguments(MO2 "SOURCE_TREE" + "WARNINGS;EXTERNAL_WARNINGS;PERMISSIVE;BIGOBJ;CLI;TRANSLATIONS;AUTOMOC" + "EXTRA_TRANSLATIONS" + ${ARGN}) + + # configure parameters and compiler flags + mo2_set_if_not_defined(MO2_PERMISSIVE OFF) + mo2_set_if_not_defined(MO2_BIGOBJ OFF) + mo2_set_if_not_defined(MO2_CLI OFF) + mo2_set_if_not_defined(MO2_TRANSLATIONS ON) + mo2_set_if_not_defined(MO2_AUTOMOC ON) + mo2_set_if_not_defined(MO2_EXTRA_TRANSLATIONS "") + + mo2_configure_warnings(${TARGET} ${ARGN}) + mo2_configure_sources(${TARGET} ${ARGN}) + mo2_configure_msvc(${TARGET} ${ARGN}) + + if (${MO2_AUTOMOC}) + find_package(Qt6 COMPONENTS Widgets REQUIRED) + set_target_properties(${TARGET} + PROPERTIES AUTOMOC ON AUTOUIC ON AUTORCC ON) endif() - if (MO2_PRIVATE_DEPENDS) - mo2_add_dependencies(${TARGET} PRIVATE ${MO2_PRIVATE_DEPENDS}) + if(${MO2_TRANSLATIONS}) + mo2_add_translations(${TARGET} + INSTALL_RELEASE + SOURCES ${CMAKE_CURRENT_SOURCE_DIR} ${MO2_EXTRA_TRANSLATIONS}) endif() - # set the VS startup project if not already set - get_property(startup_project DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT) + mo2_find_git_hash(GIT_COMMIT_HASH) + target_compile_definitions( + ${TARGET} PRIVATE NOMINMAX QT_MESSAGELOGCONTEXT GITID="${GIT_COMMIT_HASH}") - if (NOT startup_project) - set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${TARGET}) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/pch.h) + target_precompile_headers(${PROJECT_NAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/pch.h) endif() - endfunction() #! mo2_configure_tests : configure a target as a MO2 C++ tests # # this function creates a set of tests available in the ${TARET}_gtests variable # -# \param:DEPENDS dependencies to link to AND add folder for ctest to look for DLLs, -# typically the library being tests -# # extra arguments are given to mo2_configure_target, TRANSLATIONS and AUTOMOC are # OFF by default # function(mo2_configure_tests TARGET) mo2_configure_target(${TARGET} TRANSLATIONS OFF AUTOMOC OFF ${ARGN}) - cmake_parse_arguments(MO2 "" "" "DEPENDS" ${ARGN}) - - set_target_properties(${TARGET} PROPERTIES MO2_TARGET_TYPE "tests") find_package(GTest REQUIRED) target_link_libraries(${TARGET} PRIVATE GTest::gtest GTest::gmock GTest::gtest_main) - mo2_add_dependencies(${TARGET} PRIVATE ${MO2_DEPENDS}) # gtest_discover_tests would be nice but it requires Qt DLL, uibase, etc., in the # path, etc., and is not working right now @@ -231,9 +224,9 @@ function(mo2_configure_tests TARGET) # there is an open CMake issue: https://gitlab.kitware.com/cmake/cmake/-/issues/21453 # # gtest_discover_tests(${TARGET} - # WORKING_DIRECTORY ${MO2_INSTALL_PATH}/bin + # WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin # PROPERTIES - # VS_DEBUGGER_WORKING_DIRECTORY ${MO2_INSTALL_PATH}/bin + # VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin # ) # @@ -259,105 +252,27 @@ endfunction() # function(mo2_configure_plugin TARGET) mo2_configure_target(${TARGET} ${ARGN}) - mo2_add_dependencies(${TARGET} PUBLIC uibase) - - set_target_properties(${TARGET} PROPERTIES MO2_TARGET_TYPE "plugin") - mo2_set_project_to_run_from_install( ${TARGET} EXECUTABLE ${CMAKE_INSTALL_PREFIX}/bin/ModOrganizer.exe) endfunction() -#! mo2_configure_library : configure a C++ library (NOT a plugin), can be a STATIC -# or SHARED library -# -# extra arguments are given to mo2_configure_target, TRANSLATIONS and AUTOMOC are -# OFF by default -# -function(mo2_configure_library TARGET) - mo2_configure_target(${TARGET} AUTOMOC OFF TRANSLATIONS OFF ${ARGN}) - - get_target_property(TARGET_TYPE ${TARGET} TYPE) - - target_include_directories(${TARGET} - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) - - if (${TARGET_TYPE} STREQUAL "STATIC_LIBRARY") - set_target_properties(${TARGET} PROPERTIES MO2_TARGET_TYPE "library-static") - else() - mo2_set_project_to_run_from_install( - ${TARGET} EXECUTABLE ${CMAKE_INSTALL_PREFIX}/bin/ModOrganizer.exe) - set_target_properties(${TARGET} PROPERTIES MO2_TARGET_TYPE "library-shared") - endif() -endfunction() - -#! mo2_configure_executable : configure a target as MO2 C++ executable -# -# \param:ELEVATED set flag on the executable to run as elevated by default -# -# extra arguments are given to mo2_configure_target -# -function(mo2_configure_executable TARGET) - cmake_parse_arguments(MO2 "ELEVATED" "" "" ${ARGN}) - - mo2_configure_target(${TARGET} ${ARGN}) - set_target_properties(${TARGET} - PROPERTIES WIN32_EXECUTABLE TRUE MO2_TARGET_TYPE "executable") - - get_target_property(output_name ${TARGET} OUTPUT_NAME) - if("${output_name}" STREQUAL "output_name-NOTFOUND") - set(output_name ${TARGET}) - endif() - - mo2_set_project_to_run_from_install( - ${TARGET} EXECUTABLE ${CMAKE_INSTALL_PREFIX}/bin/${output_name}) - - if (${MO2_ELEVATED}) - # does not work with target_link_options, so keeping it that way for now... this - # is not a very used option anyway - set_target_properties(${TARGET} PROPERTIES LINK_FLAGS - "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\"") - endif() -endfunction() - -#! mo2_install_target : set install for a MO2 target +#! mo2_install_plugin : install the given MO2 plugin # # for this to work properly, the target must have been configured # -# \param:FOLDER install the plugin as a folder, instead of a single DLL, ignore for -# other target types -# \param:INSTALLDIR installation directory, default is automatically deduced based on -# the target type, this parameter is ignored for plugins and static libraries +# \param:FOLDER install the plugin as a folder, instead of a single DLL # -function(mo2_install_target TARGET) - cmake_parse_arguments(MO2 "FOLDER" "INSTALLDIR" "" ${ARGN}) +function(mo2_install_plugin TARGET) + cmake_parse_arguments(MO2 "FOLDER" "" "" ${ARGN}) - - get_target_property(MO2_TARGET_TYPE ${TARGET} MO2_TARGET_TYPE) - - # core install: .lib, .dll or .exe, to the right folder - if (${MO2_TARGET_TYPE} STREQUAL "plugin") - if (${MO2_FOLDER}) - install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins/$) - else() - install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins) - endif() - install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) - elseif (${MO2_TARGET_TYPE} STREQUAL "library-static") - install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) - elseif (${MO2_TARGET_TYPE} STREQUAL "library-shared") - mo2_set_if_not_defined(MO2_INSTALLDIR "bin/dlls") - install(TARGETS ${TARGET} RUNTIME DESTINATION ${MO2_INSTALLDIR}) - install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) - elseif (${MO2_TARGET_TYPE} STREQUAL "executable") - mo2_set_if_not_defined(MO2_INSTALLDIR "bin") - install(TARGETS ${TARGET} RUNTIME DESTINATION ${MO2_INSTALLDIR}) + if (${MO2_FOLDER}) + install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins/$) else() - message(FATAL_ERROR "unknown MO2 target type for target '${TARGET}', did you forget using mo2_configure_XXX?") + install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins) endif() + install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) # install PDB if possible - if (NOT (${MO2_TARGET_TYPE} STREQUAL "library-static")) - install(FILES $ DESTINATION pdb) - endif() + install(FILES $ DESTINATION pdb OPTIONAL) endfunction() diff --git a/mo2_python.cmake b/mo2_python.cmake index 8a5dd04..a1fc0bd 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -32,12 +32,10 @@ function(mo2_python_uifiles TARGET) set(output "${folder}/${name}.py") add_custom_command( OUTPUT "${output}" - COMMAND ${PYTHON_EXE} - -I - -m PyQt${QT_MAJOR_VERSION}.uic.pyuic + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_BINARY_DIR}/pylibs + ${CMAKE_BINARY_DIR}/pylibs/bin/pyuic${Qt_VERSION_MAJOR}.exe -o "${output}" "${UI_FILE}" - WORKING_DIRECTORY ${PYTHON_ROOT} DEPENDS "${UI_FILE}" ) @@ -53,61 +51,7 @@ function(mo2_python_uifiles TARGET) set_target_properties("${TARGET}_uic" PROPERTIES FOLDER autogen) add_dependencies(${TARGET} "${TARGET}_uic") - -endfunction() - -#! mo2_python_rcfiles : create .py files from .qrc files for a python target -# -# \param:TARGET target to generate .py files for -# \param:INPLACE if specified, .py files are generated next to the .qrc files, useful -# for Python modules, otherwise files are generated in the binary directory -# \param:FILES list of .qrc files to generate .py files from -# -function(mo2_python_rcfiles TARGET) - cmake_parse_arguments(MO2 "" "INPLACE" "FILES" ${ARGN}) - - if (NOT MO2_FILES) - return() - endif() - - mo2_find_python_executable(PYTHON_EXE) - - message(DEBUG "generating .py from qrc files: ${MO2_FILES}") - - set(pyrc_files "") - foreach (RC_FILE ${MO2_FILES}) - get_filename_component(name "${RC_FILE}" NAME_WLE) - get_filename_component(folder "${RC_FILE}" DIRECTORY) - if (${MO2_INPLACE}) - get_filename_component(folder "${RC_FILE}" DIRECTORY) - else() - set(folder "${CMAKE_CURRENT_BINARY_DIR}") - endif() - - - set(output "${folder}/${name}_rc.py") - add_custom_command( - OUTPUT "${output}" - COMMAND ${PYTHON_EXE} - -I - -m PyQt5.pyrcc_main - -o "${output}" - "${RC_FILE}" - WORKING_DIRECTORY ${PYTHON_ROOT} - DEPENDS "${RC_FILE}" - ) - - list(APPEND pyrc_files "${output}") - endforeach() - - if (${MO2_INPLACE}) - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} - PREFIX autogen FILES ${pyrc_files}) - endif() - - add_custom_target("${TARGET}_qrc" DEPENDS ${pyrc_files}) - set_target_properties("${TARGET}_qrc" PROPERTIES FOLDER autogen) - add_dependencies(${TARGET} "${TARGET}_qrc") + add_dependencies("${TARGET}_uic" PyQt6) endfunction() @@ -139,7 +83,6 @@ function(mo2_python_pip_install TARGET) --target="${MO2_DIRECTORY}" --log="${pip_log_file}" ${MO2_PACKAGES} - WORKING_DIRECTORY ${PYTHON_ROOT} ) set(pip_target_name "${TARGET}_pip_${PIP_FILE_LOG}") @@ -169,7 +112,6 @@ function(mo2_python_requirements TARGET) --target="${MO2_LIBDIR}" --log="${CMAKE_CURRENT_BINARY_DIR}/pip.log" -r "${PROJECT_SOURCE_DIR}/plugin-requirements.txt" - WORKING_DIRECTORY ${PYTHON_ROOT} DEPENDS "${PROJECT_SOURCE_DIR}/plugin-requirements.txt" ) add_custom_target("${TARGET}_libs" @@ -182,7 +124,7 @@ function(mo2_python_requirements TARGET) install( DIRECTORY "${MO2_LIBDIR}" - DESTINATION "${MO2_INSTALL_PATH}/bin/plugins/${TARGET}/" + DESTINATION "bin/plugins/${TARGET}/" PATTERN "__pycache__" EXCLUDE ) @@ -227,10 +169,6 @@ function(mo2_configure_python_module TARGET) file(GLOB_RECURSE ui_files CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.ui) mo2_python_uifiles(${TARGET} INPLACE FILES ${ui_files}) - # qrc file - file(GLOB_RECURSE qrc_files CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.qrc) - mo2_python_rcfiles(${TARGET} INPLACE FILES ${qrc_files}) - # install requirements if there are any if(EXISTS "${PROJECT_SOURCE_DIR}/plugin-requirements.txt") mo2_python_requirements(${TARGET} LIBDIR "${lib_dir}") @@ -241,7 +179,7 @@ function(mo2_configure_python_module TARGET) FILES "${PROJECT_SOURCE_DIR}/plugin-requirements.txt") endif() - set(install_dir "${MO2_INSTALL_PATH}/bin/plugins/${TARGET}") + set(install_dir "bin/plugins/${TARGET}") # directories that go in bin/plugins/${name} install( @@ -281,10 +219,6 @@ function(mo2_configure_python_simple TARGET) file(GLOB ui_files CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.ui) mo2_python_uifiles(${TARGET} FILES ${ui_files}) - # qrc file - file(GLOB qrc_files CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.qrc) - mo2_python_rcfiles(${TARGET} FILES ${qrc_files}) - # .py files directly in the directory file(GLOB py_files CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) @@ -305,7 +239,7 @@ function(mo2_configure_python_simple TARGET) PREFIX data FILES ${json_files}) - set(install_dir "${MO2_INSTALL_PATH}/bin/plugins") + set(install_dir "bin/plugins") # .py files directly in src/ go to plugins/ install(FILES ${py_files} DESTINATION ${install_dir}) diff --git a/mo2_targets.cmake b/mo2_targets.cmake deleted file mode 100644 index 7a35a5a..0000000 --- a/mo2_targets.cmake +++ /dev/null @@ -1,448 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -if (POLICY CMP0144) - cmake_policy(SET CMP0144 NEW) -endif() - -include(FetchContent) -include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) - -#! mo2_add_dependencies : add dependencies to the given target -# -# the name of the dependencies should be: -# - valid mo2 "libraries", as per mo2_find_libraries, -# - boost (for header-only Boost libraries) or "boost::XXX" for specific Boost -# components -# - or Qt::XXX for Qt components -# -function(mo2_add_dependencies TARGET PRIVATE_OR_PUBLIC) - - # remove everything boost related - set(standard_deps ${ARGN}) - list(FILTER standard_deps EXCLUDE REGEX "^boost.*") - list(FILTER standard_deps EXCLUDE REGEX "^Qt::.*") - - # keep everything boost related - set(boost_deps ${ARGN}) - list(FILTER boost_deps INCLUDE REGEX "^boost.*") - - # keep everything Qt related - set(qt_deps ${ARGN}) - list(FILTER qt_deps INCLUDE REGEX "^Qt::.*") - - message(DEBUG "deps: standards[ ${standard_deps} ], boost_deps[ ${boost_deps} ], qt_deps[ ${qt_deps} ]") - - # handle "standard" dependencies - mo2_find_libraries(${standard_deps}) - list(TRANSFORM standard_deps REPLACE "^(.+)$" "mo2::\\1") - message(DEBUG "standard: ${standard_deps}") - target_link_libraries(${TARGET} ${PRIVATE_OR_PUBLIC} ${standard_deps}) - - # handle Qt dependencies - if (qt_deps) - # remove Qt:: for find_package - list(TRANSFORM qt_deps REPLACE "Qt::" "") - find_package(Qt${QT_MAJOR_VERSION} COMPONENTS ${qt_deps} REQUIRED) - - set(qt_include_dirs "") - foreach(qt_dep ${qt_deps}) - list(APPEND qt_include_dirs ${Qt${qt_dep}_INCLUDE_DIRS}) - endforeach() - target_include_directories(${TARGET} SYSTEM ${PRIVATE_OR_PUBLIC} ${qt_include_dirs}) - - # add QtX:: for target ink - list(TRANSFORM qt_deps REPLACE "^(.+)$" "Qt${QT_MAJOR_VERSION}::\\1") - target_link_libraries(${TARGET} ${PRIVATE_OR_PUBLIC} ${qt_deps}) - endif() - - # handle boost dependencies - if (boost_deps) - find_package(Boost REQUIRED) - target_include_directories( - ${TARGET} SYSTEM ${PRIVATE_OR_PUBLIC} ${Boost_INCLUDE_DIRS}) - - list(TRANSFORM boost_deps REPLACE "boost(::)?" "") - list(FILTER boost_deps EXCLUDE REGEX "^$") - message(DEBUG "boost: ${boost_deps}") - - if (boost_deps) - find_package(Boost COMPONENTS ${boost_deps} REQUIRED) - message(DEBUG "boost: ${Boost_LIBRARIES}") - target_link_libraries(${TARGET} ${PRIVATE_OR_PUBLIC} ${Boost_LIBRARIES}) - endif() - endif() -endfunction() - -#! mo2_find_uibase : find and create a mo2::uibase target -# -# if a 'uibase' target already exists, makes an alias as 'mo2::uibase', otherwise -# creates an imported target -# -function(mo2_find_uibase) - - # target was already created - if (TARGET mo2-uibase) - return() - endif() - - # if the uibase target exists, we use it - if (TARGET uibase) - add_library(mo2-uibase ALIAS uibase) - add_library(mo2::uibase ALIAS uibase) - else() - - if (NOT DEFINED MO2_UIBASE_INCLUDE) - set(MO2_UIBASE_INCLUDE ${MO2_UIBASE_PATH}/src) - endif() - - - add_library(mo2-uibase IMPORTED SHARED) - set_target_properties(mo2-uibase PROPERTIES - IMPORTED_IMPLIB ${MO2_INSTALL_LIBS_PATH}/uibase.lib - IMPORTED_LOCATION ${MO2_INSTALL_PATH}/bin/uibase.dll) - mo2_add_dependencies(mo2-uibase - INTERFACE Qt::Widgets Qt::Network Qt::QuickWidgets) - target_include_directories(mo2-uibase - INTERFACE ${MO2_UIBASE_INCLUDE} ${MO2_UIBASE_INCLUDE}/game_features) - add_library(mo2::uibase ALIAS mo2-uibase) - endif() - -endfunction() - -#! mo2_find_corelib : find a static core library, e.g., bsatk or esptk, and create -# an appropriate target -# -function(mo2_find_corelib LIBRARY) - cmake_parse_arguments(MO2 "" "" "DEPENDS" ${ARGN}) - - mo2_set_if_not_defined(MO2_DEPENDS "") - - # target was already created - if (TARGET mo2-${LIBRARY}) - return() - endif() - - # if the target exists, we use it - if (TARGET ${LIBRARY}) - message(STATUS "Found existing ${LIBRARY} target, using it.") - - add_library(mo2-${LIBRARY} ALIAS ${LIBRARY}) - add_library(mo2::${LIBRARY} ALIAS ${LIBRARY}) - else() - message(STATUS "Existing ${LIBRARY} target not found, creating it.") - - add_library(mo2-${LIBRARY} IMPORTED STATIC) - set_target_properties(mo2-${LIBRARY} PROPERTIES - IMPORTED_LOCATION ${MO2_INSTALL_LIBS_PATH}/${LIBRARY}.lib) - target_include_directories(mo2-${LIBRARY} - INTERFACE ${MO2_SUPER_PATH}/${LIBRARY}/src) - - if (MO2_DEPENDS) - mo2_add_dependencies(mo2-${LIBRARY} INTERFACE ${MO2_DEPENDS}) - endif() - - add_library(mo2::${LIBRARY} ALIAS mo2-${LIBRARY}) - endif() -endfunction() - -#! mo2_find_bsatk : find and create a mo2::bsatk target -# -function(mo2_find_bsatk) - mo2_find_corelib(bsatk DEPENDS boost::thread zlib lz4) -endfunction() - -#! mo2_find_esptk : find and create a mo2::esptk target -# -function(mo2_find_esptk) - mo2_find_corelib(esptk) -endfunction() - -#! mo2_find_archive : find and create a mo2::archive target -# -function(mo2_find_archive) - mo2_find_corelib(archive) -endfunction() - -#! mo2_find_githubpp : find and create a mo2::githubpp target -# -function(mo2_find_githubpp) - mo2_find_corelib(githubpp DEPENDS Qt::Core Qt::Network) -endfunction() - -#! mo2_find_lootcli : find and create a mo2::lootcli target -# -function(mo2_find_lootcli) - - if (TARGET mo2-lootcli) - return() - endif() - - add_library(mo2-lootcli IMPORTED INTERFACE) - target_include_directories(mo2-lootcli INTERFACE - ${MO2_SUPER_PATH}/lootcli/include) - add_library(mo2::lootcli ALIAS mo2-lootcli) - -endfunction() - -#! mo2_find_gamebryo : find and create a mo2::gamebryo target -# -function(mo2_find_gamebryo) - - # this does not use mo2_find_corelib because the target name do not match (we - # want gamebryo, the target is game_gamebryo), and the src folder is not the same - # other lib (src/gamebryo instead of src/) - - # target was already created - if (TARGET mo2-gamebryo) - return() - endif() - - # if the target exists, we use it - if (TARGET game_gamebryo) - message(STATUS "Found existing game_gamebryo target, using it.") - - add_library(mo2-gamebryo ALIAS game_gamebryo) - add_library(mo2::gamebryo ALIAS game_gamebryo) - else() - message(STATUS "Existing game_gamebryo target not found, creating it.") - - add_library(mo2-gamebryo IMPORTED STATIC) - set_target_properties(mo2-gamebryo PROPERTIES - IMPORTED_LOCATION ${MO2_INSTALL_LIBS_PATH}/game_gamebryo.lib) - target_include_directories(mo2-gamebryo - INTERFACE ${MO2_SUPER_PATH}/game_gamebryo/src/gamebryo) - - mo2_add_dependencies(mo2-gamebryo INTERFACE zlib lz4) - - add_library(mo2::gamebryo ALIAS mo2-gamebryo) - endif() - -endfunction() - -#! mo2_find_creation : find and create a mo2::creation target -# -function(mo2_find_creation) - - # same as mo2_find_gamebryo, we do not use mo2_find_corelib (see mo2_find_gamebryo - # comment for why) - - # target was already created - if (TARGET mo2-creation) - return() - endif() - - # if the target exists, we use it - if (TARGET game_creation) - message(STATUS "Found existing game_creation target, using it.") - - add_library(mo2-creation ALIAS game_creation) - add_library(mo2::creation ALIAS game_creation) - else() - message(STATUS "Existing game_creation target not found, creating it.") - - add_library(mo2-creation IMPORTED STATIC) - set_target_properties(mo2-creation PROPERTIES - IMPORTED_LOCATION ${MO2_INSTALL_LIBS_PATH}/game_creation.lib) - target_include_directories(mo2-creation - INTERFACE ${MO2_SUPER_PATH}/game_gamebryo/src/creation) - - add_library(mo2::creation ALIAS mo2-creation) - - mo2_add_dependencies(mo2-creation INTERFACE gamebryo lz4) - endif() - -endfunction() - -#! mo2_find_loot : find and create a mo2::loot target -# -function(mo2_find_loot) - if (TARGET mo2-loot) - return() - endif() - - mo2_required_variable(NAME LOOT_PATH TYPE PATH) - - find_package(Boost COMPONENTS locale REQUIRED) - - add_library(mo2-loot IMPORTED SHARED) - set_target_properties(mo2-loot PROPERTIES IMPORTED_LOCATION ${LOOT_PATH}/bin/loot.dll) - set_target_properties(mo2-loot PROPERTIES IMPORTED_IMPLIB ${LOOT_PATH}/lib/loot.lib) - target_include_directories(mo2-loot INTERFACE ${LOOT_PATH}/include) - target_link_libraries(mo2-loot INTERFACE ${Boost_LIBRARIES}) - add_library(mo2::loot ALIAS mo2-loot) -endfunction() - -#! mo2_find_spdlog : find and carete a mo2::spdlog target -# -function(mo2_find_spdlog) - if (TARGET mo2-spdlog) - return() - endif() - - mo2_required_variable(NAME SPDLOG_ROOT TYPE PATH) - - add_library(mo2-spdlog INTERFACE) - target_compile_definitions(mo2-spdlog INTERFACE SPDLOG_USE_STD_FORMAT) - target_include_directories(mo2-spdlog SYSTEM INTERFACE ${SPDLOG_ROOT}/include) - add_library(mo2::spdlog ALIAS mo2-spdlog) - -endfunction() - -#! mo2_find_directxtex : find and create a mo2::directxtex target -# -function(mo2_find_directxtex) - if (TARGET mo2-directxtex) - return() - endif() - - mo2_required_variable(NAME DIRECTXTEX_ROOT TYPE PATH) - - add_library(mo2-directxtex IMPORTED STATIC) - set_target_properties(mo2-directxtex PROPERTIES - IMPORTED_LOCATION_DEBUG ${DIRECTXTEX_ROOT}/Lib/Debug/DirectXTex.lib - IMPORTED_LOCATION_MINSIZEREL ${DIRECTXTEX_ROOT}/Lib/Release/DirectXTex.lib - IMPORTED_LOCATION_RELEASE ${DIRECTXTEX_ROOT}/Lib/Release/DirectXTex.lib - IMPORTED_LOCATION_RELWITHDEBINFO ${DIRECTXTEX_ROOT}/Lib/Release/DirectXTex.lib - ) - target_include_directories(mo2-directxtex INTERFACE ${DIRECTXTEX_ROOT}/Include) - - add_library(mo2::DirectXTex ALIAS mo2-directxtex) - -endfunction() - -#! mo2_find_libbsarch : find and create a mo2::libbsarch target -# -function(mo2_find_libbsarch) - if (TARGET mo2-libbsarch) - return() - endif() - - mo2_find_directxtex() - - mo2_required_variable(NAME LIBBSARCH_ROOT TYPE PATH) - - add_library(mo2-libbsarch IMPORTED SHARED) - set_target_properties(mo2-libbsarch PROPERTIES - IMPORTED_IMPLIB ${LIBBSARCH_ROOT}/libbsarch.lib - IMPORTED_LOCATION ${LIBBSARCH_ROOT}/libbsarch.dll - ) - target_include_directories(mo2-libbsarch INTERFACE ${LIBBSARCH_ROOT}) - target_link_libraries(mo2-libbsarch INTERFACE ${LIBBSARCH_ROOT}/libbsarch_OOP.lib mo2::DirectXTex) - add_library(mo2::libbsarch ALIAS mo2-libbsarch) - -endfunction() - -#! mo2_find_zlib : find and carete a mo2::zlib target -# -function(mo2_find_zlib) - if (TARGET mo2-zlib) - return() - endif() - - mo2_required_variable(NAME ZLIB_ROOT TYPE PATH) - - # not using find_package(ZLIB) because we want the static version - add_library(mo2-zlib IMPORTED STATIC) - set_target_properties(mo2-zlib PROPERTIES - IMPORTED_LOCATION ${ZLIB_ROOT}/lib/zlibstatic.lib) - target_include_directories(mo2-zlib INTERFACE ${ZLIB_ROOT}/include) - add_library(mo2::zlib ALIAS mo2-zlib) - -endfunction() - -#! mo2_find_lz4 : find and carete a mo2::lz4 target -# -function(mo2_find_lz4) - if (TARGET mo2-lz4) - return() - endif() - - mo2_required_variable(NAME LZ4_ROOT TYPE PATH) - - add_library(mo2-lz4 IMPORTED SHARED) - set_target_properties(mo2-lz4 PROPERTIES - IMPORTED_LOCATION ${LZ4_ROOT}/bin/liblz4.dll - IMPORTED_IMPLIB ${LZ4_ROOT}/bin/liblz4.lib - ) - target_include_directories(mo2-lz4 INTERFACE ${LZ4_ROOT}/lib) - - add_library(mo2::lz4 ALIAS mo2-lz4) - -endfunction() - -#! mo2_find_7z : find and carete a mo2::7z target -# -function(mo2_find_7z) - if (TARGET mo2-7z) - return() - endif() - - mo2_required_variable(NAME SEVENZ_ROOT TYPE PATH) - - add_library(mo2-7z INTERFACE) - target_include_directories(mo2-7z INTERFACE ${SEVENZ_ROOT}/CPP) - - add_library(mo2::7z ALIAS mo2-7z) - -endfunction() - -#! mo2_find_tomlplusplus : find and create a mo2::tomlplusplus target -# -function(mo2_find_tomlplusplus) - if (TARGET mo2-tomlplusplus) - return() - endif() - - FetchContent_Declare( - tomlplusplus - URL "https://github.com/marzer/tomlplusplus/archive/v3.2.0.tar.gz" - URL_HASH "SHA256=aeba776441df4ac32e4d4db9d835532db3f90fd530a28b74e4751a2915a55565" - ) - FetchContent_MakeAvailable(tomlplusplus) - - add_library(mo2-tomlplusplus INTERFACE) - target_include_directories(mo2-tomlplusplus INTERFACE "${tomlplusplus_SOURCE_DIR}/include") - add_dependencies(mo2-tomlplusplus tomlplusplus) - - add_library(mo2::tomlplusplus ALIAS mo2-tomlplusplus) - -endfunction() - -#! mo2_find_usvfs : find and create a mo2::usvfs target -# -function(mo2_find_usvfs) - if (TARGET mo2-usvfs) - return() - endif() - - set(USVFS_PATH "${MO2_BUILD_PATH}/usvfs") - set(USVFS_INC_PATH "${USVFS_PATH}/include") - set(USVFS_LIB_PATH "${USVFS_PATH}/lib") - - add_library(mo2-usvfs IMPORTED SHARED) - set_target_properties(mo2-usvfs PROPERTIES - IMPORTED_LOCATION "${USVFS_LIB_PATH}/usvfs_x64.dll" - ) - set_target_properties(mo2-usvfs PROPERTIES - IMPORTED_IMPLIB "${USVFS_LIB_PATH}/usvfs_x64.lib" - ) - target_include_directories(mo2-usvfs INTERFACE ${USVFS_INC_PATH}) - - add_library(mo2::usvfs ALIAS mo2-usvfs) - -endfunction() - -#! mo2_find_libraries : find and create libraries to link to -# -# this function tries to find the given libraries and generates (if the libraries are -# found) targets mo2::LIBRARY_NAME for each library found -# -# example: mo2_find_libraries(uibase loot) will create mo2::uibase and mo2::loot -# -# available libraries are the ones with mo2_find_XXX functions -# -function(mo2_find_libraries) - foreach(target ${ARGN}) - cmake_language(CALL "mo2_find_${target}") - endforeach() -endfunction() diff --git a/mo2_utils.cmake b/mo2_utils.cmake index afc6cb7..2d48533 100644 --- a/mo2_utils.cmake +++ b/mo2_utils.cmake @@ -18,24 +18,71 @@ function (mo2_set_if_not_defined NAME VALUE) endif() endfunction() + +#! mo2_add_subdirectories : add all repositories matching the given list of patterns +# +# \param:FOLDER Folder (layout) to add the subdirectories to +# \param:GLOB List of glob patterns to find repositories +# +function (mo2_add_subdirectories) + cmake_parse_arguments(MO2 "" "FOLDER" "GLOB" ${ARGN}) + + if (NOT DEFINED MO2_FOLDER) + message(FATAL_ERROR "missing FOLDER in add_subdirectories") + endif() + if (NOT DEFINED MO2_GLOB) + message(FATAL_ERROR "missing GLOB in add_subdirectories") + endif() + + file(GLOB directories RELATIVE ${CMAKE_CURRENT_LIST_DIR} LIST_DIRECTORIES TRUE ${MO2_GLOB}) + + set(CMAKE_FOLDER ${MO2_FOLDER}) + foreach(directory ${directories}) + add_subdirectory(${directory}) + endforeach() + unset(CMAKE_FOLDER) + +endfunction() + #! mo2_find_python_executable : find the full path to the Python executable # # \param:VARNAME name of the variable that will contain the path to Python function(mo2_find_python_executable VARNAME) - if (EXISTS "${PYTHON_ROOT}/PCbuild/amd64/python_d.exe") - set(${VARNAME} "${PYTHON_ROOT}/PCbuild/amd64/python_d.exe" PARENT_SCOPE) - else() - set(${VARNAME} "${PYTHON_ROOT}/PCbuild/amd64/python.exe" PARENT_SCOPE) + if (NOT DEFINED Python_EXECUTABLE) + find_package(Python ${MO2_PYTHON_VERSION} COMPONENTS Interpreter REQUIRED) endif() + set(${VARNAME} ${Python_EXECUTABLE} PARENT_SCOPE) endfunction() -#! mo2_find_windeployqt_executable : find the full path to the windeployqt executable +#! mo2_find_git_hash : find the git hash of HEAD on the current source project # -# \param:VARNAME name of the variable that will contain the path to Python -function(mo2_find_windeployqt_executable VARNAME) - # find_program() does not work for whatever reason, just going for the whole - # name - set(${VARNAME} ${QT_ROOT}/bin/windeployqt.exe PARENT_SCOPE) +# \param:VARNAME variable to store the git hash +function(mo2_find_git_hash VARNAME) + execute_process( + COMMAND git log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(${VARNAME} ${GIT_COMMIT_HASH} PARENT_SCOPE) +endfunction() + +#! mo2_find_qt_executable : find the path to the executable from Qt +# +function(mo2_find_qt_executable VARNAME EXECUTABLE) + + # retrieve the absolute path to qmake and then use that path to find + # the windeployqt and macdeployqt binaries + get_target_property(_qmake_executable Qt6::qmake IMPORTED_LOCATION) + get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY) + + # need to use a custom varname per executable to use the cache + find_program(QT_${EXECUTABLE} ${EXECUTABLE} HINTS "${_qt_bin_dir}") + if(WIN32 AND NOT QT_${EXECUTABLE}) + message(FATAL_ERROR "${EXECUTABLE} not found") + endif() + + set(${VARNAME} ${QT_${EXECUTABLE}} PARENT_SCOPE) endfunction() #! mo2_set_project_to_run_from_install : set a target to run from a given executable @@ -101,27 +148,6 @@ function(mo2_add_filter) source_group(${filter_name} FILES ${files}) endfunction() -#! mo2_find_qt_version : try to deduce Qt version from QT_ROOT variable -# -# this function caches the given variable -# -# \param:VAR name of the variable to store the version into -# -function(mo2_find_qt_version VAR) - - if (DEFINED CACHE{${VAR}}) - return() - endif() - - # TODO: deduce version from the QT_ROOT folder - get_filename_component(VFOLDER ${QT_ROOT} DIRECTORY) - get_filename_component(${VAR} ${VFOLDER} NAME) - - message(STATUS "deduced Qt version to ${${VAR}}") - - set(${VAR} ${${VAR}} CACHE STRING "Qt Version}") -endfunction() - #! mo2_deploy_qt_for_tests : add comments to deploy Qt for tests # # unlike mo2_deploy_qt(), this function does not perform any cleaning @@ -132,7 +158,7 @@ endfunction() function(mo2_deploy_qt_for_tests) cmake_parse_arguments(DEPLOY "" "TARGET" "BINARIES" ${ARGN}) - mo2_find_windeployqt_executable(windeployqt) + mo2_find_qt_executable(windeployqt windeployqt) add_custom_command(TARGET "${DEPLOY_TARGET}" POST_BUILD @@ -160,7 +186,7 @@ endfunction() function(mo2_deploy_qt) cmake_parse_arguments(DEPLOY "NOPLUGINS" "" "BINARIES" ${ARGN}) - mo2_find_windeployqt_executable(windeployqt) + mo2_find_qt_executable(windeployqt windeployqt) set(args "--no-translations \ @@ -287,12 +313,12 @@ function(mo2_add_lupdate TARGET) message(TRACE "TS_FILE: ${MO2_TS_FILE}, SOURCES: ${MO2_SOURCES}, FILES: ${translation_files}") if (${is_cpp}) - set(lupdate_command ${QT_ROOT}/bin/lupdate) + mo2_find_qt_executable(lupdate_command lupdate) set(lupdate_args ${MO2_SOURCES} -ts ${MO2_TS_FILE}) else() mo2_find_python_executable(PYTHON_EXE) set(lupdate_command ${PYTHON_EXE}) - set(lupdate_args -I -m PyQt${QT_MAJOR_VERSION}.lupdate.pylupdate --ts "${MO2_TS_FILE}" ${translation_files}) + set(lupdate_args -I -m PyQt${Qt_VERSION_MAJOR}.lupdate.pylupdate --ts "${MO2_TS_FILE}" ${translation_files}) endif() add_custom_command(OUTPUT ${MO2_TS_FILE} @@ -325,8 +351,10 @@ endfunction() function(mo2_add_lrelease TARGET) cmake_parse_arguments(MO2 "INSTALL" "QM_FILE" "TS_FILES" ${ARGN}) + mo2_find_qt_executable(lrelease_command lrelease) + add_custom_command(OUTPUT ${MO2_QM_FILE} - COMMAND ${QT_ROOT}/bin/lrelease + COMMAND ${lrelease_command} ARGS ${MO2_TS_FILES} -qm ${MO2_QM_FILE} DEPENDS "${MO2_TS_FILES}" VERBATIM) From 8b2c068e7392d7927b7914d2dbb3f05eb4dfc796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Wed, 10 Jul 2024 21:12:14 +0200 Subject: [PATCH 03/21] Remove find_package(Qt6) in root to avoid needing Qt6 in Python plugin. --- README.md | 10 ++++++++-- mo2.cmake | 14 ++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0f2f874..eea10fc 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,18 @@ configuration files. Be aware that using these utilities will automatically some (not too intrusive) global variable on your project. -You will need to have `Qt` in your CMake module path otherwise the import will fail -due to a broken `find_package(Qt6)`. For some functions to work properly, you should set `CMAKE_INSTALL_PREFIX` to a directory where `${CMAKE_INSTALL_PREFIX}/bin/ModOrganizer.exe` can be found. +Importing the utilities will make the following variables available: + +- `MO2_QT_VERSION` - The Qt version used by MO2, as `major.minor.patch`. +- `MO2_QT_MAJOR_VERSION`, `MO2_QT_MINOR_VERSION` and `MO2_QT_PATCH_VERSION` - + Respectively the major, minor and patch version of Qt used by MO2. +- `MO2_PYTHON_VERSION` - The Python version used by MO2 as `major.minor` (patch version + should not be relevant). + All functions are prefixed by `mo2_` and should not conflict with other existing functions. diff --git a/mo2.cmake b/mo2.cmake index 041632d..eda080f 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -4,19 +4,13 @@ if (DEFINED MO2_INCLUDED) return() endif() -set(MO2_QT_MAJOR_VERSION 6) -set(MO2_QT_MINOR_VERSION 7) -set(MO2_QT_PATCH_VERSION 0) -set(MO2_QT_VERSION "${MO2_QT_MAJOR_VERSION}.${MO2_QT_MINOR_VERSION}.${MO2_QT_PATCH_VERSION}") +set(MO2_QT_VERSION_MAJOR 6) +set(MO2_QT_VERSION_MINOR 7) +set(MO2_QT_VERSION_PATCH 0) +set(MO2_QT_VERSION "${MO2_QT_VERSION_MAJOR}.${MO2_QT_VERSION_MINOR}.${MO2_QT_VERSION_PATCH}") set(MO2_PYTHON_VERSION "3.12") -find_package(Qt6 ${MO2_QT_VERSION} REQUIRED COMPONENTS Core) - -# version-major independent variables -set(Qt_VERSION ${Qt6_VERSION}) -set(Qt_VERSION_MAJOR ${Qt6_VERSION_MAJOR}) - include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) From 6eaa1e0432d1d2871253b435f17dfa621a0e8c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 11 Jul 2024 19:49:32 +0200 Subject: [PATCH 04/21] Add extra options to mo2_python_pip_install. --- mo2_python.cmake | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/mo2_python.cmake b/mo2_python.cmake index a1fc0bd..5795cff 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -63,12 +63,30 @@ endfunction() # "PyQt6==6.3.0" # function(mo2_python_pip_install TARGET) - cmake_parse_arguments(MO2 "" "DIRECTORY" "PACKAGES" ${ARGN}) + cmake_parse_arguments(MO2 "NO_DEPENDENCIES;PRE_RELEASE" "DIRECTORY" "PACKAGES;EXTRA_INDEX_URLS" ${ARGN}) if (NOT MO2_DIRECTORY) message(FATAL_ERROR "must specified a DIRECTORY for pip install") endif() + mo2_set_if_not_defined(MO2_NO_DEPENDENCIES OFF) + mo2_set_if_not_defined(MO2_PRE_RELEASE OFF) + mo2_set_if_not_defined(MO2_EXTRA_INDEX_URLS "") + + set(pip_extra_arguments "") + + if (MO2_NO_DEPENDENCIES) + list(APPEND pip_extra_arguments --no-deps) + endif() + + if (MO2_PRE_RELEASE) + list(APPEND pip_extra_arguments --pre) + endif() + + foreach(_extra_index_url ${MO2_EXTRA_INDEX_URLS}) + list(APPEND pip_extra_arguments --extra-index-url ${_extra_index_url}) + endforeach() + mo2_find_python_executable(PYTHON_EXE) string(MAKE_C_IDENTIFIER "${MO2_PACKAGES}" PIP_FILE_LOG) @@ -79,7 +97,13 @@ function(mo2_python_pip_install TARGET) COMMAND ${PYTHON_EXE} -I -m pip - install --force --upgrade --disable-pip-version-check + install + ${pip_extra_arguments} + --force + --upgrade + --disable-pip-version-check + --isolated + --no-cache-dir --target="${MO2_DIRECTORY}" --log="${pip_log_file}" ${MO2_PACKAGES} From 46170ad77ef422b634a960a552022b18c9fe8cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 11 Jul 2024 19:50:43 +0200 Subject: [PATCH 05/21] Bring config file from the VCPKG registry. --- mo2-cmake-config.cmake | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 mo2-cmake-config.cmake diff --git a/mo2-cmake-config.cmake b/mo2-cmake-config.cmake new file mode 100644 index 0000000..3b4ed19 --- /dev/null +++ b/mo2-cmake-config.cmake @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.22) + +include(${CMAKE_CURRENT_LIST_DIR}/mo2.cmake) From 15edc019c209bf3ecc82d16dc1e4f9cb04b52c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 11 Jul 2024 20:04:41 +0200 Subject: [PATCH 06/21] Install PyQt6 automatically when needed. --- mo2_python.cmake | 125 +++++++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 53 deletions(-) diff --git a/mo2_python.cmake b/mo2_python.cmake index 5795cff..18d3ec6 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -2,59 +2,6 @@ cmake_minimum_required(VERSION 3.16) include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) -#! mo2_python_uifiles : create .py files from .ui files for a python target -# -# \param:TARGET target to generate .py files for -# \param:INPLACE if specified, .py files are generated next to the .ui files, useful -# for Python modules, otherwise files are generated in the binary directory -# \param:FILES list of .ui files to generate .py files from -# -function(mo2_python_uifiles TARGET) - cmake_parse_arguments(MO2 "INPLACE" "" "FILES" ${ARGN}) - - if (NOT MO2_FILES) - return() - endif() - - mo2_find_python_executable(PYTHON_EXE) - - message(DEBUG "generating .py from ui files: ${MO2_FILES}") - - set(pyui_files "") - foreach (UI_FILE ${MO2_FILES}) - get_filename_component(name "${UI_FILE}" NAME_WLE) - if (${MO2_INPLACE}) - get_filename_component(folder "${UI_FILE}" DIRECTORY) - else() - set(folder "${CMAKE_CURRENT_BINARY_DIR}") - endif() - - set(output "${folder}/${name}.py") - add_custom_command( - OUTPUT "${output}" - COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_BINARY_DIR}/pylibs - ${CMAKE_BINARY_DIR}/pylibs/bin/pyuic${Qt_VERSION_MAJOR}.exe - -o "${output}" - "${UI_FILE}" - DEPENDS "${UI_FILE}" - ) - - list(APPEND pyui_files "${output}") - endforeach() - - if (${MO2_INPLACE}) - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} - PREFIX autogen FILES ${pyui_files}) - endif() - - add_custom_target("${TARGET}_uic" DEPENDS ${pyui_files}) - set_target_properties("${TARGET}_uic" PROPERTIES FOLDER autogen) - - add_dependencies(${TARGET} "${TARGET}_uic") - add_dependencies("${TARGET}_uic" PyQt6) - -endfunction() - #! mo2_python_pip_install : run "pip install ..." # # \param:TARGET target to install Python package for @@ -118,6 +65,78 @@ function(mo2_python_pip_install TARGET) endfunction() +#! mo2_python_install_pyqt : install PyQt6 and create a PyQt6 target for it +# +# it is safe to call this function multiple times, PyQt6 will only be installed once +# +function(mo2_python_install_pyqt) + if (TARGET PyQt6) + return() + endif() + + add_custom_target(PyQt6) + mo2_python_pip_install(PyQt6 + DIRECTORY ${CMAKE_BINARY_DIR}/pylibs/ + PACKAGES + PyQt${MO2_QT_VERSION_MAJOR}==${MO2_QT_VERSION} + sip==6.8.5) +endfunction() + +#! mo2_python_uifiles : create .py files from .ui files for a python target +# +# \param:TARGET target to generate .py files for +# \param:INPLACE if specified, .py files are generated next to the .ui files, useful +# for Python modules, otherwise files are generated in the binary directory +# \param:FILES list of .ui files to generate .py files from +# +function(mo2_python_uifiles TARGET) + cmake_parse_arguments(MO2 "INPLACE" "" "FILES" ${ARGN}) + + if (NOT MO2_FILES) + return() + endif() + + mo2_find_python_executable(PYTHON_EXE) + mo2_python_install_pyqt() + + message(DEBUG "generating .py from ui files: ${MO2_FILES}") + + set(pyui_files "") + foreach (UI_FILE ${MO2_FILES}) + get_filename_component(name "${UI_FILE}" NAME_WLE) + if (${MO2_INPLACE}) + get_filename_component(folder "${UI_FILE}" DIRECTORY) + else() + set(folder "${CMAKE_CURRENT_BINARY_DIR}") + endif() + + set(output "${folder}/${name}.py") + add_custom_command( + OUTPUT "${output}" + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_BINARY_DIR}/pylibs + ${CMAKE_BINARY_DIR}/pylibs/bin/pyuic${Qt_VERSION_MAJOR}.exe + -o "${output}" + "${UI_FILE}" + DEPENDS "${UI_FILE}" + ) + + list(APPEND pyui_files "${output}") + endforeach() + + if (${MO2_INPLACE}) + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} + PREFIX autogen FILES ${pyui_files}) + endif() + + add_custom_target("${TARGET}_uic" DEPENDS ${pyui_files}) + set_target_properties("${TARGET}_uic" PROPERTIES FOLDER autogen) + + add_dependencies(${TARGET} "${TARGET}_uic") + + add_dependencies("${TARGET}_uic" PyQt6) + +endfunction() + #! mo2_python_requirements : install requirements for a python target # # \param:TARGET target to install requirements for From beb323435de3c8765af5ac8742836606a0b39ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 11 Jul 2024 20:22:58 +0200 Subject: [PATCH 07/21] Fix UI and translation file generation for Python. --- mo2_python.cmake | 2 +- mo2_utils.cmake | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/mo2_python.cmake b/mo2_python.cmake index 18d3ec6..57f9a59 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -114,7 +114,7 @@ function(mo2_python_uifiles TARGET) add_custom_command( OUTPUT "${output}" COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_BINARY_DIR}/pylibs - ${CMAKE_BINARY_DIR}/pylibs/bin/pyuic${Qt_VERSION_MAJOR}.exe + ${CMAKE_BINARY_DIR}/pylibs/bin/pyuic${MO2_QT_VERSION_MAJOR}.exe -o "${output}" "${UI_FILE}" DEPENDS "${UI_FILE}" diff --git a/mo2_utils.cmake b/mo2_utils.cmake index 2d48533..a582972 100644 --- a/mo2_utils.cmake +++ b/mo2_utils.cmake @@ -312,22 +312,27 @@ function(mo2_add_lupdate TARGET) message(TRACE "TS_FILE: ${MO2_TS_FILE}, SOURCES: ${MO2_SOURCES}, FILES: ${translation_files}") + add_custom_target("${TARGET}_lupdate" DEPENDS ${MO2_TS_FILE}) + if (${is_cpp}) - mo2_find_qt_executable(lupdate_command lupdate) - set(lupdate_args ${MO2_SOURCES} -ts ${MO2_TS_FILE}) + mo2_find_qt_executable(lupdate lupdate) + set(lupdate_command ${lupdate} ${MO2_SOURCES} -ts ${MO2_TS_FILE}) else() - mo2_find_python_executable(PYTHON_EXE) - set(lupdate_command ${PYTHON_EXE}) - set(lupdate_args -I -m PyQt${Qt_VERSION_MAJOR}.lupdate.pylupdate --ts "${MO2_TS_FILE}" ${translation_files}) + mo2_python_install_pyqt() + set(lupdate_command + ${CMAKE_COMMAND} + -E env PYTHONPATH=${CMAKE_BINARY_DIR}/pylibs + ${CMAKE_BINARY_DIR}/pylibs/bin/pylupdate${MO2_QT_VERSION_MAJOR}.exe + --ts "${MO2_TS_FILE}" ${translation_files}) + + add_dependencies("${TARGET}_lupdate" PyQt6) endif() add_custom_command(OUTPUT ${MO2_TS_FILE} - COMMAND ${lupdate_command} ARGS ${lupdate_args} + COMMAND ${lupdate_command} DEPENDS ${translation_files} VERBATIM) - add_custom_target("${TARGET}_lupdate" DEPENDS ${MO2_TS_FILE}) - # we need to set this property otherwise there is an issue with C# projects # requiring nuget packages (e.g., installer_omod) that tries to resolve Nuget # packages on these target but fails because there are obviously none From b9ce33eb112e12a89e8735eb3cf1a08b9dd80392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 11 Jul 2024 21:13:51 +0200 Subject: [PATCH 08/21] Set imported config map. --- mo2.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mo2.cmake b/mo2.cmake index eda080f..4bf23dd 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -15,6 +15,10 @@ include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL MinSizeRel RelWithDebInfo Release None) +set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release MinSizeRel None) +set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE Release RelWithDebInfo MinSizeRel None) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP autogen) set_property(GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP autogen) From d5ee29c10043be7acba74dec8ddd4d9f7126017a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sat, 13 Jul 2024 11:15:15 +0200 Subject: [PATCH 09/21] Add specific files to store version-related stuff. --- mo2.cmake | 10 ++-------- mo2_versions.cmake | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 mo2_versions.cmake diff --git a/mo2.cmake b/mo2.cmake index 4bf23dd..628f612 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -1,16 +1,10 @@ cmake_minimum_required(VERSION 3.22) -if (DEFINED MO2_INCLUDED) +if (DEFINED MO2_DEFINED) return() endif() -set(MO2_QT_VERSION_MAJOR 6) -set(MO2_QT_VERSION_MINOR 7) -set(MO2_QT_VERSION_PATCH 0) -set(MO2_QT_VERSION "${MO2_QT_VERSION_MAJOR}.${MO2_QT_VERSION_MINOR}.${MO2_QT_VERSION_PATCH}") - -set(MO2_PYTHON_VERSION "3.12") - +include(${CMAKE_CURRENT_LIST_DIR}/mo2_versions.cmake) include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) diff --git a/mo2_versions.cmake b/mo2_versions.cmake new file mode 100644 index 0000000..0e8d307 --- /dev/null +++ b/mo2_versions.cmake @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.22) + +if (DEFINED MO2_VERSIONS_INCLUDED) + return() +endif() + + +set(MO2_QT_VERSION_MAJOR 6) +set(MO2_QT_VERSION_MINOR 7) +set(MO2_QT_VERSION_PATCH 0) +set(MO2_QT_VERSION "${MO2_QT_VERSION_MAJOR}.${MO2_QT_VERSION_MINOR}.${MO2_QT_VERSION_PATCH}") + +set(MO2_PYTHON_VERSION "3.12") + +set(MO2_PYQT_VERSION ${MO2_QT_VERSION}) +set(MO2_SIP_VERSION "6.8.5") + + +# mark as included +set(MO2_VERSIONS_INCLUDED TRUE) From 18c4679eb436fa7ea5a134d6652eaf699f3af1c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sat, 13 Jul 2024 11:15:58 +0200 Subject: [PATCH 10/21] More flexibility to define sources of a target. --- mo2.cmake | 2 ++ mo2_cpp.cmake | 70 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/mo2.cmake b/mo2.cmake index 628f612..66be9dd 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -21,5 +21,7 @@ set_property(GLOBAL PROPERTY AUTORCC_SOURCE_GROUP autogen) include(${CMAKE_CURRENT_LIST_DIR}/mo2_cpp.cmake) include(${CMAKE_CURRENT_LIST_DIR}/mo2_python.cmake) +set(QT_TARGETS_FOLDER autogen) + # mark as included set(MO2_DEFINED true) diff --git a/mo2_cpp.cmake b/mo2_cpp.cmake index 1990327..f9c0000 100644 --- a/mo2_cpp.cmake +++ b/mo2_cpp.cmake @@ -41,6 +41,56 @@ function(mo2_configure_warnings TARGET) endfunction() +#! mo2_target_sources : add sources to a given target, eventually putting them in +# a folder +# +# \param: FILES list of .ui files to add +# \param: RC_FILES list of .qrc or .rc files to add +# +function(mo2_target_sources TARGET) + cmake_parse_arguments(MO2 "" "FOLDER" "PRIVATE;PUBLIC" ${ARGN}) + + mo2_set_if_not_defined(MO2_PRIVATE "") + mo2_set_if_not_defined(MO2_PUBLIC "") + + set(_sources "") + + if (MO2_PRIVATE) + target_sources(${TARGET} PRIVATE ${MO2_PRIVATE}) + list(APPEND _sources ${MO2_PRIVATE}) + endif() + + if (MO2_PUBLIC) + target_sources(${TARGET} PUBLIC ${MO2_PUBLIC}) + list(APPEND _sources ${MO2_PUBLIC}) + endif() + + if ((DEFINED MO2_FOLDER) AND _sources) + source_group(${MO2_FOLDER} FILES ${_sources}) + endif() + +endfunction() + +#! mo2_default_source_group : configure default source groups for MO2 +# +# \param:NO_SRC if set, the src source_group will not be created, default if false +# +function(mo2_default_source_group) + cmake_parse_arguments(MO2 "SOURCE_TREE" "" "" ${ARGN}) + + # remove the CMake Rules autogenerated folder + source_group("CMake Rules" REGULAR_EXPRESSION "^$") + source_group(ui REGULAR_EXPRESSION ".*\\.ui") + source_group(cmake FILES CMakeLists.txt) + source_group(autogen REGULAR_EXPRESSION ".*\\cmake_pch.*|.*\\.rule") + source_group(resources REGULAR_EXPRESSION ".*\\.qrc|.*\\.rc") + + if (NOT NO_SRC) + message(STATUS "XXX: SETTING SRC") + source_group(src REGULAR_EXPRESSION ".*\\.(h|cpp)$") + endif() +endfunction() + #! mo2_configure_sources : configure sources for the given C++ target # # \param:SOURCE_TREE if set, a source_group will be created using TREE @@ -63,19 +113,16 @@ function(mo2_configure_sources TARGET) file(GLOB_RECURSE ui_header_files CONFIGURE_DEPENDS ${UI_HEADERS_DIR}/*.h) file(GLOB_RECURSE rule_files CONFIGURE_DEPENDS ${CMAKE_BINARY_DIR}/*.rule) + + if (${MO2_SOURCE_TREE}) + mo2_default_source_group(NO_SRC) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX src FILES ${source_files} ${header_files}) else() - source_group(src REGULAR_EXPRESSION ".*\\.(h|cpp)") + mo2_default_source_group() endif() - source_group(ui REGULAR_EXPRESSION ".*\\.ui") - source_group(cmake FILES CMakeLists.txt) - source_group(autogen FILES ${rule_files} ${qm_files} ${ui_header_files}) - source_group(autogen REGULAR_EXPRESSION ".*\\cmake_pch.*") - source_group(resources FILES ${rc_files} ${qrc_files}) - target_sources(${TARGET} PRIVATE ${source_files} @@ -154,6 +201,7 @@ endfunction() # - add step to create translations (if not turned OFF) # # \param:SOURCE_TREE if set, a source_group will be created using TREE +# \param:NO_SOURCES if set, mo2_configure_sources will not be called # \param:WARNINGS enable all warnings, possible values are ON/All, OFF, or 1, 2, 3, 4 # for corresponding /W flags (ON is All) (default ON) # \param:EXTERNAL_WARNINGS enable warnings for external libraries, possible values are @@ -166,12 +214,13 @@ endfunction() # \param:EXTRA_TRANSLATIONS extra translations to include (folder) # function(mo2_configure_target TARGET) - cmake_parse_arguments(MO2 "SOURCE_TREE" + cmake_parse_arguments(MO2 "SOURCE_TREE;NO_SOURCES" "WARNINGS;EXTERNAL_WARNINGS;PERMISSIVE;BIGOBJ;CLI;TRANSLATIONS;AUTOMOC" "EXTRA_TRANSLATIONS" ${ARGN}) # configure parameters and compiler flags + mo2_set_if_not_defined(MO2_NO_SOURCES OFF) mo2_set_if_not_defined(MO2_PERMISSIVE OFF) mo2_set_if_not_defined(MO2_BIGOBJ OFF) mo2_set_if_not_defined(MO2_CLI OFF) @@ -180,9 +229,12 @@ function(mo2_configure_target TARGET) mo2_set_if_not_defined(MO2_EXTRA_TRANSLATIONS "") mo2_configure_warnings(${TARGET} ${ARGN}) - mo2_configure_sources(${TARGET} ${ARGN}) mo2_configure_msvc(${TARGET} ${ARGN}) + if (NOT MO2_NO_SOURCES) + mo2_configure_sources(${TARGET} ${ARGN}) + endif() + if (${MO2_AUTOMOC}) find_package(Qt6 COMPONENTS Widgets REQUIRED) set_target_properties(${TARGET} From 45b3d8ae38afadd4c169ca09decd125070752f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sat, 13 Jul 2024 11:16:23 +0200 Subject: [PATCH 11/21] More flexibility Python pip install and ability to customize default pylibs folder. --- mo2_python.cmake | 41 ++++++++++++++++++++++++----------------- mo2_utils.cmake | 4 ++-- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/mo2_python.cmake b/mo2_python.cmake index 57f9a59..7511f79 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -2,6 +2,9 @@ cmake_minimum_required(VERSION 3.16) include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) +set(MO2_PYLIBS_DIR "${CMAKE_BINARY_DIR}/pylibs" CACHE PATH + "default for path for Python libraries") + #! mo2_python_pip_install : run "pip install ..." # # \param:TARGET target to install Python package for @@ -10,28 +13,36 @@ include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) # "PyQt6==6.3.0" # function(mo2_python_pip_install TARGET) - cmake_parse_arguments(MO2 "NO_DEPENDENCIES;PRE_RELEASE" "DIRECTORY" "PACKAGES;EXTRA_INDEX_URLS" ${ARGN}) - - if (NOT MO2_DIRECTORY) - message(FATAL_ERROR "must specified a DIRECTORY for pip install") - endif() + cmake_parse_arguments(MO2 + "NO_DEPENDENCIES;PRE_RELEASE;NO_FORCE;USE_CACHE" "DIRECTORY" "PACKAGES;EXTRA_INDEX_URLS" ${ARGN}) + mo2_set_if_not_defined(DIRECTORY ${MO2_PYLIBS_DIR}) mo2_set_if_not_defined(MO2_NO_DEPENDENCIES OFF) mo2_set_if_not_defined(MO2_PRE_RELEASE OFF) + mo2_set_if_not_defined(MO2_NO_FORCE OFF) + mo2_set_if_not_defined(MO2_USE_CACHE OFF) mo2_set_if_not_defined(MO2_EXTRA_INDEX_URLS "") - set(pip_extra_arguments "") + set(pip_install_arguments "") if (MO2_NO_DEPENDENCIES) - list(APPEND pip_extra_arguments --no-deps) + list(APPEND pip_install_arguments --no-deps) endif() if (MO2_PRE_RELEASE) - list(APPEND pip_extra_arguments --pre) + list(APPEND pip_install_arguments --pre) + endif() + + if (NOT MO2_NO_FORCE) + list(APPEND pip_install_arguments --force) + endif() + + if (NOT USE_CACHE) + list(APPEND pip_install_arguments --no-cache-dir) endif() foreach(_extra_index_url ${MO2_EXTRA_INDEX_URLS}) - list(APPEND pip_extra_arguments --extra-index-url ${_extra_index_url}) + list(APPEND pip_install_arguments --extra-index-url ${_extra_index_url}) endforeach() mo2_find_python_executable(PYTHON_EXE) @@ -45,9 +56,7 @@ function(mo2_python_pip_install TARGET) -I -m pip install - ${pip_extra_arguments} - --force - --upgrade + ${pip_install_arguments} --disable-pip-version-check --isolated --no-cache-dir @@ -62,7 +71,6 @@ function(mo2_python_pip_install TARGET) set_target_properties(${pip_target_name} PROPERTIES FOLDER autogen) add_dependencies(${TARGET} ${pip_target_name}) - endfunction() #! mo2_python_install_pyqt : install PyQt6 and create a PyQt6 target for it @@ -75,11 +83,10 @@ function(mo2_python_install_pyqt) endif() add_custom_target(PyQt6) - mo2_python_pip_install(PyQt6 - DIRECTORY ${CMAKE_BINARY_DIR}/pylibs/ + mo2_python_pip_install(PyQt6 NO_FORCE PACKAGES PyQt${MO2_QT_VERSION_MAJOR}==${MO2_QT_VERSION} - sip==6.8.5) + sip==${MO2_SIP_VERSION}) endfunction() #! mo2_python_uifiles : create .py files from .ui files for a python target @@ -114,7 +121,7 @@ function(mo2_python_uifiles TARGET) add_custom_command( OUTPUT "${output}" COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_BINARY_DIR}/pylibs - ${CMAKE_BINARY_DIR}/pylibs/bin/pyuic${MO2_QT_VERSION_MAJOR}.exe + ${MO2_PYLIBS_DIR}/bin/pyuic${MO2_QT_VERSION_MAJOR}.exe -o "${output}" "${UI_FILE}" DEPENDS "${UI_FILE}" diff --git a/mo2_utils.cmake b/mo2_utils.cmake index a582972..62a671d 100644 --- a/mo2_utils.cmake +++ b/mo2_utils.cmake @@ -321,8 +321,8 @@ function(mo2_add_lupdate TARGET) mo2_python_install_pyqt() set(lupdate_command ${CMAKE_COMMAND} - -E env PYTHONPATH=${CMAKE_BINARY_DIR}/pylibs - ${CMAKE_BINARY_DIR}/pylibs/bin/pylupdate${MO2_QT_VERSION_MAJOR}.exe + -E env PYTHONPATH=${MO2_PYLIBS_DIR} + ${MO2_PYLIBS_DIR}/bin/pylupdate${MO2_QT_VERSION_MAJOR}.exe --ts "${MO2_TS_FILE}" ${translation_files}) add_dependencies("${TARGET}_lupdate" PyQt6) From 87caedbb1041d95d3a669e742d977798173c2b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sat, 13 Jul 2024 11:49:23 +0200 Subject: [PATCH 12/21] Remove message. --- mo2_cpp.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/mo2_cpp.cmake b/mo2_cpp.cmake index f9c0000..a7dae9d 100644 --- a/mo2_cpp.cmake +++ b/mo2_cpp.cmake @@ -86,7 +86,6 @@ function(mo2_default_source_group) source_group(resources REGULAR_EXPRESSION ".*\\.qrc|.*\\.rc") if (NOT NO_SRC) - message(STATUS "XXX: SETTING SRC") source_group(src REGULAR_EXPRESSION ".*\\.(h|cpp)$") endif() endfunction() From b2c7511e2d6f6a87688fb416d8f92c70b6220bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sat, 13 Jul 2024 14:42:00 +0200 Subject: [PATCH 13/21] Move PyQt6 target in autogen folder. --- mo2_python.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/mo2_python.cmake b/mo2_python.cmake index 7511f79..0f21206 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -83,6 +83,7 @@ function(mo2_python_install_pyqt) endif() add_custom_target(PyQt6) + set_target_properties(PyQt6 PROPERTIES FOLDER autogen) mo2_python_pip_install(PyQt6 NO_FORCE PACKAGES PyQt${MO2_QT_VERSION_MAJOR}==${MO2_QT_VERSION} From 717968fd45b2c53e96937504a49e5f33415129b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sat, 13 Jul 2024 14:59:20 +0200 Subject: [PATCH 14/21] Prevent Python from using virtualenv and fix typo in mo2_python_pip_install. --- mo2.cmake | 2 ++ mo2_python.cmake | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mo2.cmake b/mo2.cmake index 66be9dd..12975d7 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -9,6 +9,8 @@ include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +set(Python_FIND_VIRTUALENV STANDARD) + set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL MinSizeRel RelWithDebInfo Release None) set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release MinSizeRel None) set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE Release RelWithDebInfo MinSizeRel None) diff --git a/mo2_python.cmake b/mo2_python.cmake index 0f21206..719e0c9 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -16,7 +16,7 @@ function(mo2_python_pip_install TARGET) cmake_parse_arguments(MO2 "NO_DEPENDENCIES;PRE_RELEASE;NO_FORCE;USE_CACHE" "DIRECTORY" "PACKAGES;EXTRA_INDEX_URLS" ${ARGN}) - mo2_set_if_not_defined(DIRECTORY ${MO2_PYLIBS_DIR}) + mo2_set_if_not_defined(MO2_DIRECTORY ${MO2_PYLIBS_DIR}) mo2_set_if_not_defined(MO2_NO_DEPENDENCIES OFF) mo2_set_if_not_defined(MO2_PRE_RELEASE OFF) mo2_set_if_not_defined(MO2_NO_FORCE OFF) From 57055551c99094fa267cd266d98247831f583263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sun, 14 Jul 2024 11:36:56 +0200 Subject: [PATCH 15/21] Allow specifying deploy directory in mo2_deploy_qt. --- mo2_utils.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mo2_utils.cmake b/mo2_utils.cmake index 62a671d..fe7d174 100644 --- a/mo2_utils.cmake +++ b/mo2_utils.cmake @@ -184,7 +184,9 @@ endfunction() # \param:BINARIES names of the binaries (in the install path) to deploy from # function(mo2_deploy_qt) - cmake_parse_arguments(DEPLOY "NOPLUGINS" "" "BINARIES" ${ARGN}) + cmake_parse_arguments(DEPLOY "NOPLUGINS" "DIRECTORY" "BINARIES" ${ARGN}) + + mo2_set_if_not_defined(DEPLOY_DIRECTORY "bin") mo2_find_qt_executable(windeployqt windeployqt) @@ -203,7 +205,7 @@ function(mo2_deploy_qt) set(args "${args} --plugindir qtplugins") endif() - set(bin "${CMAKE_INSTALL_PREFIX}/bin") + set(bin "${CMAKE_INSTALL_PREFIX}/${DEPLOY_DIRECTORY}") set(deploys "") foreach(binary ${DEPLOY_BINARIES}) From c8ecc87b8441c74f41ac8eb0428dddd015ef8efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sun, 14 Jul 2024 11:53:29 +0200 Subject: [PATCH 16/21] Allow specifying installation directory when adding translations. --- mo2_utils.cmake | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/mo2_utils.cmake b/mo2_utils.cmake index fe7d174..cf60049 100644 --- a/mo2_utils.cmake +++ b/mo2_utils.cmake @@ -351,12 +351,16 @@ endfunction() # this function adds a ${TARGET}_lrelease target # # \param:TARGET target to generate releases for -# \param:INSTALL if set, QM files will be installed to bin/translations +# \param:INSTALL if set, QM files will be installed +# \param:DIRECTORY if INSTALL is set, path where translations should be installed, +# default to bin/translations # \param:QM_FILE .qm file to generate # \param:TS_FILES source ts # function(mo2_add_lrelease TARGET) - cmake_parse_arguments(MO2 "INSTALL" "QM_FILE" "TS_FILES" ${ARGN}) + cmake_parse_arguments(MO2 "INSTALL" "DIRECTORY;QM_FILE" "TS_FILES" ${ARGN}) + + mo2_set_if_not_defined(MO2_DIRECTORY "bin/translations") mo2_find_qt_executable(lrelease_command lrelease) @@ -378,7 +382,7 @@ function(mo2_add_lrelease TARGET) FOLDER autogen) if (${MO2_INSTALL}) - install(FILES ${MO2_QM_FILE} DESTINATION bin/translations) + install(FILES ${MO2_QM_FILE} DESTINATION ${MO2_DIRECTORY}) endif() endfunction() @@ -391,20 +395,17 @@ endfunction() # \param:TARGET target to generate translations for # \param:RELEASE if set, will use mo2_add_lrelease to generate .qm files # \param:INSTALL_RELEASE if true, will install generated .qm files (force RELEASE) +# \param:INSTALL_DIRECTORY installation directory for .qm files, default to bin/translations # \param:TS_FILE intermediate .ts file to generate # \param:QM_FILE file .qm file to generate if RELEASE or INSTALL_RELEASE is set # \param:SOURCES source directories to look for translations, send to mo2_add_lupdate # function(mo2_add_translations TARGET) - cmake_parse_arguments(MO2 "RELEASE;INSTALL_RELEASE" "TS_FILE;QM_FILE" "SOURCES" ${ARGN}) - - if (NOT MO2_TS_FILE) - set(MO2_TS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET}_en.ts) - endif() + cmake_parse_arguments(MO2 "RELEASE;INSTALL_RELEASE" "TS_FILE;QM_FILE;INSTALL_DIRECTORY" "SOURCES" ${ARGN}) - if (NOT MO2_QM_FILE) - set(MO2_QM_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_en.qm) - endif() + mo2_set_if_not_defined(MO2_TS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET}_en.ts) + mo2_set_if_not_defined(MO2_QM_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_en.qm) + mo2_set_if_not_defined(MO2_INSTALL_DIRECTORY "bin/translations") # force release with install if (${MO2_INSTALL_RELEASE}) @@ -416,6 +417,7 @@ function(mo2_add_translations TARGET) if (${MO2_RELEASE}) mo2_add_lrelease(${TARGET} INSTALL ${MO2_INSTALL_RELEASE} + DIRECTORY ${MO2_INSTALL_DIRECTORY} TS_FILES ${MO2_TS_FILE} QM_FILE ${MO2_QM_FILE}) From d5bf19e0266cd303953050e3b32b9dd9ccb252a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Sun, 14 Jul 2024 13:31:59 +0200 Subject: [PATCH 17/21] Add MO2_INSTALL_IS_BIN option. --- mo2.cmake | 9 +++++++++ mo2_cpp.cmake | 14 ++++++++------ mo2_python.cmake | 6 +++--- mo2_utils.cmake | 12 +++++++----- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/mo2.cmake b/mo2.cmake index 12975d7..4bc4e53 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -4,6 +4,15 @@ if (DEFINED MO2_DEFINED) return() endif() +option(MO2_INSTALL_IS_BIN + "if set, CMAKE_INSTALL_PREFIX is assumed to point to bin rather than one level below") + +if (MO2_INSTALL_IS_BIN) + set(MO2_INSTALL_BIN ".") +else() + set(MO2_INSTALL_BIN "bin") +endif() + include(${CMAKE_CURRENT_LIST_DIR}/mo2_versions.cmake) include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) diff --git a/mo2_cpp.cmake b/mo2_cpp.cmake index a7dae9d..26a2409 100644 --- a/mo2_cpp.cmake +++ b/mo2_cpp.cmake @@ -304,7 +304,7 @@ endfunction() function(mo2_configure_plugin TARGET) mo2_configure_target(${TARGET} ${ARGN}) mo2_set_project_to_run_from_install( - ${TARGET} EXECUTABLE ${CMAKE_INSTALL_PREFIX}/bin/ModOrganizer.exe) + ${TARGET} EXECUTABLE ${CMAKE_INSTALL_PREFIX}/${MO2_INSTALL_BIN}/ModOrganizer.exe) endfunction() #! mo2_install_plugin : install the given MO2 plugin @@ -317,13 +317,15 @@ function(mo2_install_plugin TARGET) cmake_parse_arguments(MO2 "FOLDER" "" "" ${ARGN}) if (${MO2_FOLDER}) - install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins/$) + install(TARGETS ${TARGET} RUNTIME DESTINATION ${MO2_INSTALL_BIN}/plugins/$) else() - install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins) + install(TARGETS ${TARGET} RUNTIME DESTINATION ${MO2_INSTALL_BIN}/plugins) endif() - install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) - # install PDB if possible - install(FILES $ DESTINATION pdb OPTIONAL) + if (NOT MO2_INSTALL_IS_BIN) + install(TARGETS ${TARGET} ARCHIVE DESTINATION lib) + # install PDB if possible + install(FILES $ DESTINATION pdb OPTIONAL) + endif() endfunction() diff --git a/mo2_python.cmake b/mo2_python.cmake index 719e0c9..1ef8419 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -175,7 +175,7 @@ function(mo2_python_requirements TARGET) install( DIRECTORY "${MO2_LIBDIR}" - DESTINATION "bin/plugins/${TARGET}/" + DESTINATION "${MO2_INSTALL_BIN}/plugins/${TARGET}/" PATTERN "__pycache__" EXCLUDE ) @@ -230,7 +230,7 @@ function(mo2_configure_python_module TARGET) FILES "${PROJECT_SOURCE_DIR}/plugin-requirements.txt") endif() - set(install_dir "bin/plugins/${TARGET}") + set(install_dir "${MO2_INSTALL_BIN}/plugins/${TARGET}") # directories that go in bin/plugins/${name} install( @@ -290,7 +290,7 @@ function(mo2_configure_python_simple TARGET) PREFIX data FILES ${json_files}) - set(install_dir "bin/plugins") + set(install_dir "${MO2_INSTALL_BIN}/plugins") # .py files directly in src/ go to plugins/ install(FILES ${py_files} DESTINATION ${install_dir}) diff --git a/mo2_utils.cmake b/mo2_utils.cmake index cf60049..25bdbde 100644 --- a/mo2_utils.cmake +++ b/mo2_utils.cmake @@ -181,12 +181,14 @@ endfunction() # this function attach install() entries that deploy Qt for the given binaries # # \param:NOPLUGINS do not deploy Qt plugins +# \param:DIRECTORY directory, relative to CMAKE_INSTALL_PREFIX, to deploy to, default +# to ${MO2_INSTALL_BIN} # \param:BINARIES names of the binaries (in the install path) to deploy from # function(mo2_deploy_qt) cmake_parse_arguments(DEPLOY "NOPLUGINS" "DIRECTORY" "BINARIES" ${ARGN}) - mo2_set_if_not_defined(DEPLOY_DIRECTORY "bin") + mo2_set_if_not_defined(DEPLOY_DIRECTORY "${MO2_INSTALL_BIN}") mo2_find_qt_executable(windeployqt windeployqt) @@ -353,14 +355,14 @@ endfunction() # \param:TARGET target to generate releases for # \param:INSTALL if set, QM files will be installed # \param:DIRECTORY if INSTALL is set, path where translations should be installed, -# default to bin/translations +# default to ${MO2_INSTALL_BIN}/translations # \param:QM_FILE .qm file to generate # \param:TS_FILES source ts # function(mo2_add_lrelease TARGET) cmake_parse_arguments(MO2 "INSTALL" "DIRECTORY;QM_FILE" "TS_FILES" ${ARGN}) - mo2_set_if_not_defined(MO2_DIRECTORY "bin/translations") + mo2_set_if_not_defined(MO2_DIRECTORY "${MO2_INSTALL_BIN}/translations") mo2_find_qt_executable(lrelease_command lrelease) @@ -395,7 +397,7 @@ endfunction() # \param:TARGET target to generate translations for # \param:RELEASE if set, will use mo2_add_lrelease to generate .qm files # \param:INSTALL_RELEASE if true, will install generated .qm files (force RELEASE) -# \param:INSTALL_DIRECTORY installation directory for .qm files, default to bin/translations +# \param:INSTALL_DIRECTORY installation directory for .qm files, default to ${MO2_INSTALL_BIN}/translations # \param:TS_FILE intermediate .ts file to generate # \param:QM_FILE file .qm file to generate if RELEASE or INSTALL_RELEASE is set # \param:SOURCES source directories to look for translations, send to mo2_add_lupdate @@ -405,7 +407,7 @@ function(mo2_add_translations TARGET) mo2_set_if_not_defined(MO2_TS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET}_en.ts) mo2_set_if_not_defined(MO2_QM_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_en.qm) - mo2_set_if_not_defined(MO2_INSTALL_DIRECTORY "bin/translations") + mo2_set_if_not_defined(MO2_INSTALL_DIRECTORY "${MO2_INSTALL_BIN}/translations") # force release with install if (${MO2_INSTALL_RELEASE}) From 19d7ca60b83be1e6ca9fed810031a204b2f882d8 Mon Sep 17 00:00:00 2001 From: Mikael CAPELLE Date: Mon, 5 Aug 2024 13:34:26 +0200 Subject: [PATCH 18/21] Bump Qt version to 6.7.1 and sip to 6.8.6. --- mo2_versions.cmake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mo2_versions.cmake b/mo2_versions.cmake index 0e8d307..46db244 100644 --- a/mo2_versions.cmake +++ b/mo2_versions.cmake @@ -7,14 +7,13 @@ endif() set(MO2_QT_VERSION_MAJOR 6) set(MO2_QT_VERSION_MINOR 7) -set(MO2_QT_VERSION_PATCH 0) +set(MO2_QT_VERSION_PATCH 1) set(MO2_QT_VERSION "${MO2_QT_VERSION_MAJOR}.${MO2_QT_VERSION_MINOR}.${MO2_QT_VERSION_PATCH}") set(MO2_PYTHON_VERSION "3.12") set(MO2_PYQT_VERSION ${MO2_QT_VERSION}) -set(MO2_SIP_VERSION "6.8.5") - +set(MO2_SIP_VERSION "6.8.6") # mark as included set(MO2_VERSIONS_INCLUDED TRUE) From 0185ac7493a12cff22cf222a1e8cc6b83da5808e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 5 Aug 2024 22:17:28 +0200 Subject: [PATCH 19/21] Add comments and update README. --- README.md | 17 ++++++++++++----- mo2.cmake | 12 ++++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index eea10fc..c66a5a3 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,18 @@ configuration files. ## Usage -Be aware that using these utilities will automatically some (not too intrusive) global -variable on your project. - -For some functions to work properly, you should set `CMAKE_INSTALL_PREFIX` to a -directory where `${CMAKE_INSTALL_PREFIX}/bin/ModOrganizer.exe` can be found. +Be aware that using these utilities will automatically set some (not too intrusive) +global variable on your project. + +In order to properly use this package, you should set `CMAKE_INSTALL_PREFIX` to a valid +location. +There are two possible way of using this package controlled by the `MO2_INSTALL_IS_BIN` +option: + +- if `MO2_INSTALL_IS_BIN` is `OFF` (default), this assumes a layout with a `bin`, + `lib`, `include` and `pdb` folder under `CMAKE_INSTALL_PREFIX`, +- if `MO2_INSTALL_IS_BIN` is `ON` (default when building standalone), this assumes + that `CMAKE_INSTALL_PREFIX` point directly to the equivalent `bin` folder. Importing the utilities will make the following variables available: diff --git a/mo2.cmake b/mo2.cmake index 4bc4e53..ff8f0b8 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -16,23 +16,31 @@ endif() include(${CMAKE_CURRENT_LIST_DIR}/mo2_versions.cmake) include(${CMAKE_CURRENT_LIST_DIR}/mo2_utils.cmake) +# this makes VS install everything when building solution set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +# this find Python globally rather than virtual environments, even when one is active set(Python_FIND_VIRTUALENV STANDARD) +# this set the imported location of targets for missing configurations - this silents +# many CMP0111 warnings from CMake set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL MinSizeRel RelWithDebInfo Release None) set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release MinSizeRel None) set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE Release RelWithDebInfo MinSizeRel None) +# allow setting folder property on targets for better organization in VS set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# put code generated by Qt in a autogen group in VS set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP autogen) set_property(GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP autogen) set_property(GLOBAL PROPERTY AUTORCC_SOURCE_GROUP autogen) +# put targets generated by Qt into a autogen folder (this is not the same as the above) +set(QT_TARGETS_FOLDER autogen) + include(${CMAKE_CURRENT_LIST_DIR}/mo2_cpp.cmake) include(${CMAKE_CURRENT_LIST_DIR}/mo2_python.cmake) -set(QT_TARGETS_FOLDER autogen) - # mark as included set(MO2_DEFINED true) From 434f71f71e688698a158a45af1fec9563549afa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 9 Aug 2024 12:37:00 +0200 Subject: [PATCH 20/21] Set working directory for tests and allow not linking to mock and main. (#35) --- mo2_cpp.cmake | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mo2_cpp.cmake b/mo2_cpp.cmake index 26a2409..b51b26c 100644 --- a/mo2_cpp.cmake +++ b/mo2_cpp.cmake @@ -264,10 +264,18 @@ endfunction() # OFF by default # function(mo2_configure_tests TARGET) + cmake_parse_arguments(MO2 "NO_MOCK;NO_MAIN" "" "" ${ARGN}) mo2_configure_target(${TARGET} TRANSLATIONS OFF AUTOMOC OFF ${ARGN}) find_package(GTest REQUIRED) - target_link_libraries(${TARGET} PRIVATE GTest::gtest GTest::gmock GTest::gtest_main) + target_link_libraries(${TARGET} PRIVATE GTest::gtest) + + if (NOT MO2_NO_MOCK) + target_link_libraries(${TARGET} PRIVATE GTest::gmock) + endif() + if (NOT MO2_NO_MAIN) + target_link_libraries(${TARGET} PRIVATE GTest::gtest_main) + endif() # gtest_discover_tests would be nice but it requires Qt DLL, uibase, etc., in the # path, etc., and is not working right now @@ -281,7 +289,10 @@ function(mo2_configure_tests TARGET) # ) # - gtest_add_tests(TARGET ${TARGET} TEST_LIST ${TARGET}_gtests) + gtest_add_tests( + TARGET ${TARGET} + TEST_LIST ${TARGET}_gtests + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) set(${TARGET}_gtests ${${TARGET}_gtests} PARENT_SCOPE) mo2_deploy_qt_for_tests( From 80a2eed4d7ad3964d34e89cdfb87e13e96fcb1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 14 Oct 2024 18:48:46 +0200 Subject: [PATCH 21/21] Fix setup of startup project. --- mo2_cpp.cmake | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mo2_cpp.cmake b/mo2_cpp.cmake index b51b26c..9c42a06 100644 --- a/mo2_cpp.cmake +++ b/mo2_cpp.cmake @@ -184,11 +184,17 @@ function(mo2_configure_msvc TARGET) /OPT:ICF >) - if(${MO2_CLI}) + if(${MO2_CLI}) set_target_properties(${TARGET} PROPERTIES COMMON_LANGUAGE_RUNTIME "") - endif() + endif() + + get_property(CURRENT_STARTUP_PROJECT + DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT) - set_target_properties(${TARGET} PROPERTIES VS_STARTUP_PROJECT ${TARGET}) + if (NOT CURRENT_STARTUP_PROJECT) + message(STATUS "MO2: Setting startup project to " ${TARGET} ".") + set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${TARGET}) + endif() endfunction()