diff --git a/README.md b/README.md index 062ede0..c66a5a3 100644 --- a/README.md +++ b/README.md @@ -1,94 +1,109 @@ # 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 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: + +- `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. + +### 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-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) diff --git a/mo2.cmake b/mo2.cmake index b4eed18..ff8f0b8 100644 --- a/mo2.cmake +++ b/mo2.cmake @@ -1,52 +1,44 @@ cmake_minimum_required(VERSION 3.22) -if (DEFINED MO2_INCLUDED) +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) -# 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) +# 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) diff --git a/mo2_cpp.cmake b/mo2_cpp.cmake index 25b8125..9c42a06 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,73 @@ 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-") +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_BIGOBJ}) - target_compile_options(${TARGET} PRIVATE "/bigobj") + 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) + 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 +# +function(mo2_configure_sources TARGET) + cmake_parse_arguments(MO2 "SOURCE_TREE" "" "" ${ARGN}) # find source files if(DEFINED AUTOGEN_BUILD_DIR) @@ -117,26 +111,15 @@ 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}) + mo2_default_source_group(NO_SRC) 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}) + mo2_default_source_group() endif() target_sources(${TARGET} @@ -150,80 +133,155 @@ 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() - 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() - endif() + cmake_parse_arguments(MO2 "" "PERMISSIVE;BIGOBJ;CLI" "" ${ARGN}) - set_target_properties(${TARGET} PROPERTIES VS_STARTUP_PROJECT ${TARGET}) + set(CXX_STANDARD 20) + if (${MO2_CLI}) + set(CXX_STANDARD 17) + endif() + set_target_properties(${TARGET} PROPERTIES + CXX_STANDARD ${CXX_STANDARD} CXX_EXTENSIONS OFF) - target_link_libraries(${TARGET} PRIVATE Version Dbghelp) + if(NOT ${MO2_PERMISSIVE}) + target_compile_options(${TARGET} PRIVATE "/permissive-") + endif() - if (MO2_PUBLIC_DEPENDS) - mo2_add_dependencies(${TARGET} PUBLIC ${MO2_PUBLIC_DEPENDS}) + if(${MO2_BIGOBJ}) + target_compile_options(${TARGET} PRIVATE "/bigobj") endif() - if (MO2_PRIVATE_DEPENDS) - mo2_add_dependencies(${TARGET} PRIVATE ${MO2_PRIVATE_DEPENDS}) + # 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}) + set_target_properties(${TARGET} PROPERTIES COMMON_LANGUAGE_RUNTIME "") endif() - # set the VS startup project if not already set - get_property(startup_project DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT) + get_property(CURRENT_STARTUP_PROJECT + DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT) - if (NOT startup_project) + 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() +#! 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: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 +# 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;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) + 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_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} + PROPERTIES AUTOMOC ON AUTOUIC ON AUTORCC ON) + endif() + + if(${MO2_TRANSLATIONS}) + mo2_add_translations(${TARGET} + INSTALL_RELEASE + SOURCES ${CMAKE_CURRENT_SOURCE_DIR} ${MO2_EXTRA_TRANSLATIONS}) + endif() + + mo2_find_git_hash(GIT_COMMIT_HASH) + target_compile_definitions( + ${TARGET} PRIVATE NOMINMAX QT_MESSAGELOGCONTEXT GITID="${GIT_COMMIT_HASH}") + + 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) + cmake_parse_arguments(MO2 "NO_MOCK;NO_MAIN" "" "" ${ARGN}) 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}) + 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 @@ -231,13 +289,16 @@ 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 # ) # - 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( @@ -251,26 +312,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 @@ -279,109 +320,29 @@ 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) + ${TARGET} EXECUTABLE ${CMAKE_INSTALL_PREFIX}/${MO2_INSTALL_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 "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_FOLDER}) - install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins/$) - else() - install(TARGETS ${TARGET} RUNTIME DESTINATION bin/plugins) - endif() - install(TARGETS ${TARGET} ARCHIVE DESTINATION libs) - elseif (${MO2_TARGET_TYPE} STREQUAL "library-static") - install(TARGETS ${TARGET} ARCHIVE DESTINATION libs) - 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) - 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 ${MO2_INSTALL_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 ${MO2_INSTALL_BIN}/plugins) endif() - # install PDB if possible - if (NOT (${MO2_TARGET_TYPE} STREQUAL "library-static")) - install(FILES $ DESTINATION pdb) + 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 8a5dd04..1ef8419 100644 --- a/mo2_python.cmake +++ b/mo2_python.cmake @@ -2,6 +2,94 @@ 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 +# \param:DIRECTORY directory to install libraries to, REQUIRED +# \param:PACKAGES packages to install, REQUIRED, can contain version constraints, e.g., +# "PyQt6==6.3.0" +# +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(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) + mo2_set_if_not_defined(MO2_USE_CACHE OFF) + mo2_set_if_not_defined(MO2_EXTRA_INDEX_URLS "") + + set(pip_install_arguments "") + + if (MO2_NO_DEPENDENCIES) + list(APPEND pip_install_arguments --no-deps) + endif() + + if (MO2_PRE_RELEASE) + 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_install_arguments --extra-index-url ${_extra_index_url}) + endforeach() + + mo2_find_python_executable(PYTHON_EXE) + + string(MAKE_C_IDENTIFIER "${MO2_PACKAGES}" PIP_FILE_LOG) + set(pip_log_file "${CMAKE_CURRENT_BINARY_DIR}/${PIP_FILE_LOG}.log") + + add_custom_command( + OUTPUT "${pip_log_file}" + COMMAND ${PYTHON_EXE} + -I + -m pip + install + ${pip_install_arguments} + --disable-pip-version-check + --isolated + --no-cache-dir + --target="${MO2_DIRECTORY}" + --log="${pip_log_file}" + ${MO2_PACKAGES} + ) + + set(pip_target_name "${TARGET}_pip_${PIP_FILE_LOG}") + + add_custom_target(${pip_target_name} ALL DEPENDS "${pip_log_file}") + 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 +# +# 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) + set_target_properties(PyQt6 PROPERTIES FOLDER autogen) + mo2_python_pip_install(PyQt6 NO_FORCE + PACKAGES + PyQt${MO2_QT_VERSION_MAJOR}==${MO2_QT_VERSION} + sip==${MO2_SIP_VERSION}) +endfunction() + #! mo2_python_uifiles : create .py files from .ui files for a python target # # \param:TARGET target to generate .py files for @@ -17,6 +105,7 @@ function(mo2_python_uifiles TARGET) endif() mo2_find_python_executable(PYTHON_EXE) + mo2_python_install_pyqt() message(DEBUG "generating .py from ui files: ${MO2_FILES}") @@ -32,12 +121,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 + ${MO2_PYLIBS_DIR}/bin/pyuic${MO2_QT_VERSION_MAJOR}.exe -o "${output}" "${UI_FILE}" - WORKING_DIRECTORY ${PYTHON_ROOT} DEPENDS "${UI_FILE}" ) @@ -54,100 +141,7 @@ function(mo2_python_uifiles TARGET) 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") - -endfunction() - -#! mo2_python_pip_install : run "pip install ..." -# -# \param:TARGET target to install Python package for -# \param:DIRECTORY directory to install libraries to, REQUIRED -# \param:PACKAGES packages to install, REQUIRED, can contain version constraints, e.g., -# "PyQt6==6.3.0" -# -function(mo2_python_pip_install TARGET) - cmake_parse_arguments(MO2 "" "DIRECTORY" "PACKAGES" ${ARGN}) - - if (NOT MO2_DIRECTORY) - message(FATAL_ERROR "must specified a DIRECTORY for pip install") - endif() - - mo2_find_python_executable(PYTHON_EXE) - - string(MAKE_C_IDENTIFIER "${MO2_PACKAGES}" PIP_FILE_LOG) - set(pip_log_file "${CMAKE_CURRENT_BINARY_DIR}/${PIP_FILE_LOG}.log") - - add_custom_command( - OUTPUT "${pip_log_file}" - COMMAND ${PYTHON_EXE} - -I - -m pip - install --force --upgrade --disable-pip-version-check - --target="${MO2_DIRECTORY}" - --log="${pip_log_file}" - ${MO2_PACKAGES} - WORKING_DIRECTORY ${PYTHON_ROOT} - ) - - set(pip_target_name "${TARGET}_pip_${PIP_FILE_LOG}") - - add_custom_target(${pip_target_name} ALL DEPENDS "${pip_log_file}") - set_target_properties(${pip_target_name} PROPERTIES FOLDER autogen) - - add_dependencies(${TARGET} ${pip_target_name}) + add_dependencies("${TARGET}_uic" PyQt6) endfunction() @@ -169,7 +163,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 +175,7 @@ function(mo2_python_requirements TARGET) install( DIRECTORY "${MO2_LIBDIR}" - DESTINATION "${MO2_INSTALL_PATH}/bin/plugins/${TARGET}/" + DESTINATION "${MO2_INSTALL_BIN}/plugins/${TARGET}/" PATTERN "__pycache__" EXCLUDE ) @@ -227,10 +220,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 +230,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 "${MO2_INSTALL_BIN}/plugins/${TARGET}") # directories that go in bin/plugins/${name} install( @@ -281,10 +270,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 +290,7 @@ function(mo2_configure_python_simple TARGET) PREFIX data FILES ${json_files}) - set(install_dir "${MO2_INSTALL_PATH}/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_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..25bdbde 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 @@ -155,12 +181,16 @@ 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" "" "BINARIES" ${ARGN}) + cmake_parse_arguments(DEPLOY "NOPLUGINS" "DIRECTORY" "BINARIES" ${ARGN}) - mo2_find_windeployqt_executable(windeployqt) + mo2_set_if_not_defined(DEPLOY_DIRECTORY "${MO2_INSTALL_BIN}") + + mo2_find_qt_executable(windeployqt windeployqt) set(args "--no-translations \ @@ -177,7 +207,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}) @@ -286,22 +316,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}) - set(lupdate_command ${QT_ROOT}/bin/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_MAJOR_VERSION}.lupdate.pylupdate --ts "${MO2_TS_FILE}" ${translation_files}) + mo2_python_install_pyqt() + set(lupdate_command + ${CMAKE_COMMAND} + -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) 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 @@ -318,15 +353,21 @@ 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 ${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" "QM_FILE" "TS_FILES" ${ARGN}) + cmake_parse_arguments(MO2 "INSTALL" "DIRECTORY;QM_FILE" "TS_FILES" ${ARGN}) + + mo2_set_if_not_defined(MO2_DIRECTORY "${MO2_INSTALL_BIN}/translations") + + 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) @@ -343,7 +384,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() @@ -356,20 +397,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 ${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 # function(mo2_add_translations TARGET) - cmake_parse_arguments(MO2 "RELEASE;INSTALL_RELEASE" "TS_FILE;QM_FILE" "SOURCES" ${ARGN}) + cmake_parse_arguments(MO2 "RELEASE;INSTALL_RELEASE" "TS_FILE;QM_FILE;INSTALL_DIRECTORY" "SOURCES" ${ARGN}) - if (NOT MO2_TS_FILE) - set(MO2_TS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET}_en.ts) - endif() - - 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 "${MO2_INSTALL_BIN}/translations") # force release with install if (${MO2_INSTALL_RELEASE}) @@ -381,6 +419,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}) diff --git a/mo2_versions.cmake b/mo2_versions.cmake new file mode 100644 index 0000000..46db244 --- /dev/null +++ b/mo2_versions.cmake @@ -0,0 +1,19 @@ +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 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.6") + +# mark as included +set(MO2_VERSIONS_INCLUDED TRUE)