diff --git a/CMCONF.cmake b/CMCONF.cmake index d7a20c9..484ea83 100644 --- a/CMCONF.cmake +++ b/CMCONF.cmake @@ -111,6 +111,11 @@ INCLUDE_GUARD(GLOBAL) FIND_PACKAGE(CMLIB REQUIRED) +SET(CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED OFF + CACHE BOOL + "If set to ON the system name can be overriden from cmdline. If OFF the system name cannot be changed once set." +) + SET(CMCONF_SYSTEM_NAME "" CACHE STRING "Name of the system for which the configuration is intended." @@ -168,15 +173,20 @@ SET(CMCONF_INSTALL_CMAKELISTS_TEMPLATE_FILE "${CMCONF_SCRIPT_DIR}/resource/CMake # # Sets the system name that will be used to group configuration variables # and identify the configuration package in the CMake user package registry. -# The system name can only be set once per configuration session. # # If CMCONF_INSTALL_AS_SYMLINK is ON, this function will automatically install # the configuration to the CMake user package registry as a symlink, making it # available for other projects to find via FIND_PACKAGE. # # The system name is normalized to uppercase and validated to contain only -# [a-zA-Z_] characters. Once set, attempting to change it will result in -# a fatal error. +# [a-zA-Z_] characters. +# +# System Name Override Behavior: +# - By default (CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED=OFF), attempting to change +# the system name after it has been set will result in a fatal error. +# - If CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED is set to ON, the system name can be +# changed. A warning message is emitted when the system name is overridden. +# - Re-initializing with the same system name is allowed and proceeds silently. # # Installation Process (when CMCONF_INSTALL_AS_SYMLINK=ON): # - Validates that no conflicting package exists in a different location @@ -184,8 +194,6 @@ SET(CMCONF_INSTALL_CMAKELISTS_TEMPLATE_FILE "${CMCONF_SCRIPT_DIR}/resource/CMake # - Registers the package using EXPORT(PACKAGE) command # - Installation is performed only once per session # -# System name cannot be changed once is set. -# # ( # # Name of the system. Will be converted to uppercase, # # must contain only [a-zA-Z_] characters. @@ -195,7 +203,11 @@ FUNCTION(CMCONF_INIT_SYSTEM system_name) _CMCONF_CHECK_AND_NORMALIZE_SYSTEM_NAME("${system_name}" system_name_upper) IF(CMCONF_SYSTEM_NAME) IF(NOT CMCONF_SYSTEM_NAME STREQUAL "${system_name_upper}") - _CMCONF_MESSAGE(FATAL_ERROR "System name already set. Cannot change system name from '${CMCONF_SYSTEM_NAME}' to '${system_name_upper}'") + IF(NOT CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED) + _CMCONF_MESSAGE(FATAL_ERROR "System name already set. Cannot change system name from '${CMCONF_SYSTEM_NAME}' to '${system_name_upper}'") + ELSE() + _CMCONF_MESSAGE(WARNING "Overriding system name from '${CMCONF_SYSTEM_NAME}' to '${system_name_upper}'") + ENDIF() ENDIF() ENDIF() SET_PROPERTY(CACHE CMCONF_SYSTEM_NAME PROPERTY VALUE "${system_name_upper}") @@ -416,6 +428,10 @@ FUNCTION(_CMCONF_UNINSTALL system_name) _CMCONF_MESSAGE(FATAL_ERROR "Cannot uninstall configuration. Uninstalation can be invoked only as 'cmake -DCMCONF_UNINSTALL=ON -P .'") ENDIF() + IF(CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED) + _CMCONF_MESSAGE(FATAL_ERROR "Cannot uninstall configuration. CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED is not allowed during CMCONF config uninstallation.") + ENDIF() + SET(package_registry_path) IF(WIN32) SET(winreg_path "HKCU\\Software\\Kitware\\CMake\\Packages\\${package_name}") @@ -490,7 +506,7 @@ FUNCTION(_CMCONF_RUN_INSTALL_PROJECT system_name) CONFIGURE_FILE("${cmakelist_template}" "${cmakelist_path}" @ONLY) EXECUTE_PROCESS( - COMMAND "${CMAKE_COMMAND}" -DCMCONF_INSTALL_AS_SYMLINK=ON . + COMMAND "${CMAKE_COMMAND}" -DCMCONF_INSTALL_AS_SYMLINK=ON -DCMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED=${CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED} . WORKING_DIRECTORY "${script_dir}" RESULT_VARIABLE result_var ERROR_VARIABLE errout diff --git a/README.md b/README.md index f6a18d0..29286b4 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,26 @@ CMCONF_GET("PAHOMQTT_URI") Examples can be found at [example] directory. +## Configuration Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `CMCONF_INSTALL_AS_SYMLINK` | OFF | Triggers configuration installation to CMake user package registry | +| `CMCONF_UNINSTALL` | OFF | Triggers configuration uninstallation from CMake user package registry | +| `CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED` | OFF | When set during a normal CMake project configure, allows changing `CMCONF_SYSTEM_NAME` after it has been set (emits warning). When used during installation via config script (e.g., running with `-P` or with `CMCONF_INSTALL_AS_SYMLINK`/`CMCONF_UNINSTALL`), a configuration error occurs. | +| `CMCONF_SYSTEM_NAME` | "" | Name of the system (usually set via `CMCONF_INIT_SYSTEM`, but can be preset if `CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED` is ON) | + +```bash +# Install configuration +cmake -DCMCONF_INSTALL_AS_SYMLINK=ON -P CMCONF_EXAMPLEConfig.cmake + +# Uninstall configuration +cmake -DCMCONF_UNINSTALL=ON -P CMCONF_EXAMPLEConfig.cmake + +# Allow system name override (e.g., in CMake project root directory) +cmake -DCMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED=ON -DCMCONF_SYSTEM_NAME=MY_EXAMPLE . +``` + ## Function list Each entry in list represents one feature for CMake. diff --git a/resource/CMakeLists.txt.in b/resource/CMakeLists.txt.in index 3d79159..c36522d 100644 --- a/resource/CMakeLists.txt.in +++ b/resource/CMakeLists.txt.in @@ -9,6 +9,10 @@ SET(CMCONF_CMAKE_PROJECT_ALLOW_INSTALL ON "Set by resource/CMakeLists.txt.in. Do not set manually. Controls whetnever CMake project is allowed to install the configuration." ) +IF(CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED) + MESSAGE(FATAL_ERROR "CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED is not allowed during CMCONF config installation.") +ENDIF() + _CMCONF_GET_PACKAGE_CONFIG_FILENAME("@TO_REPLACE_CMCONF_SYSTEM_NAME@" package_filename) FILE(TO_CMAKE_PATH "${CMAKE_CURRENT_LIST_DIR}/${package_filename}" package_path) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 26aa52f..03af195 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,6 +21,8 @@ TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/pass/different_system_names") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/pass/env_variable_fallback") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/pass/env_variable_override") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/pass/multiple_variables") +TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/pass/system_name_override_allowed") +TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/pass/system_name_reinit_same") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/pass/uninstall_verification") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/config_not_installed") @@ -29,6 +31,7 @@ TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/env_variable_config_required TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/get_after_set") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/install_from_cmake_project") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/install_project_execution_failure") +TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/install_with_override_allowed") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/invalid_system_name") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/invalid_variable_name") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/package_exists_different_location") @@ -38,6 +41,7 @@ TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/system_not_set") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/uninstall_from_cmake_project") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/uninstall_install_conflict") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/uninstall_not_needed_warning") +TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/uninstall_with_override_allowed") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/variable_already_exists") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/variable_not_defined") TEST_RUN("${CMAKE_CURRENT_LIST_DIR}/test_cases/fail/wrong_filename") diff --git a/test/test_cases/fail/install_with_override_allowed/CMakeLists.txt b/test/test_cases/fail/install_with_override_allowed/CMakeLists.txt new file mode 100644 index 0000000..e25f28b --- /dev/null +++ b/test/test_cases/fail/install_with_override_allowed/CMakeLists.txt @@ -0,0 +1,29 @@ +## Main +# +# Test CMCONF installation error when CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED is ON +# + +IF(NOT DEFINED CMAKE_SCRIPT_MODE_FILE) + CMAKE_MINIMUM_REQUIRED(VERSION 3.22) + PROJECT(CMCONF_INSTALL_WITH_OVERRIDE_ALLOWED_TEST) +ENDIF() + +FIND_PACKAGE(CMLIB REQUIRED) + +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/../../../TEST.cmake") + +EXECUTE_PROCESS( + COMMAND "${CMAKE_COMMAND}" -DCMCONF_INSTALL_AS_SYMLINK=ON -DCMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED=ON -P "${CMAKE_CURRENT_LIST_DIR}/../../../config_templates/CMCONF_TEST_SYSTEMConfig.cmake" + RESULT_VARIABLE install_result + ERROR_VARIABLE install_error + OUTPUT_VARIABLE install_output +) + +IF(install_result EQUAL 0) + MESSAGE(FATAL_ERROR "Expected installation to fail when CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED=ON but it succeeded") +ENDIF() + +IF(NOT install_error MATCHES "CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED is not allowed during CMCONF config.*installation") + MESSAGE(FATAL_ERROR "Expected error message not found in output: ${install_error}") +ENDIF() + diff --git a/test/test_cases/fail/uninstall_with_override_allowed/CMakeLists.txt b/test/test_cases/fail/uninstall_with_override_allowed/CMakeLists.txt new file mode 100644 index 0000000..731e0b8 --- /dev/null +++ b/test/test_cases/fail/uninstall_with_override_allowed/CMakeLists.txt @@ -0,0 +1,29 @@ +## Main +# +# Test CMCONF uninstallation error when CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED is ON +# + +IF(NOT DEFINED CMAKE_SCRIPT_MODE_FILE) + CMAKE_MINIMUM_REQUIRED(VERSION 3.22) + PROJECT(CMCONF_UNINSTALL_WITH_OVERRIDE_ALLOWED_TEST) +ENDIF() + +FIND_PACKAGE(CMLIB REQUIRED) + +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/../../../TEST.cmake") + +EXECUTE_PROCESS( + COMMAND "${CMAKE_COMMAND}" -DCMCONF_UNINSTALL=ON -DCMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED=ON -P "${CMAKE_CURRENT_LIST_DIR}/../../../config_templates/CMCONF_TEST_SYSTEMConfig.cmake" + RESULT_VARIABLE uninstall_result + ERROR_VARIABLE uninstall_error + OUTPUT_VARIABLE uninstall_output +) + +IF(uninstall_result EQUAL 0) + MESSAGE(FATAL_ERROR "Expected uninstallation to fail when CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED=ON but it succeeded") +ENDIF() + +IF(NOT uninstall_error MATCHES "CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED is not allowed during CMCONF config.*uninstallation") + MESSAGE(FATAL_ERROR "Expected error message not found in output: ${uninstall_error}") +ENDIF() + diff --git a/test/test_cases/pass/system_name_override_allowed/CMakeLists.txt b/test/test_cases/pass/system_name_override_allowed/CMakeLists.txt new file mode 100644 index 0000000..a2c06ab --- /dev/null +++ b/test/test_cases/pass/system_name_override_allowed/CMakeLists.txt @@ -0,0 +1,18 @@ +## Main +# +# Test CMCONF_INIT_SYSTEM allows changing system name when CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED=ON +# + +IF(NOT DEFINED CMAKE_SCRIPT_MODE_FILE) + CMAKE_MINIMUM_REQUIRED(VERSION 3.22) + PROJECT(CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED_TEST) +ENDIF() + +FIND_PACKAGE(CMLIB REQUIRED) + +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/../../../TEST.cmake") + +TEST_RUN_AND_CHECK_OUTPUT("test_config" + WARNING_MESSAGE "CMCONF\\\\[FIRST_SYSTEM\\\\] - Overriding system name from 'FIRST_SYSTEM' to.*'SECOND_SYSTEM'" +) + diff --git a/test/test_cases/pass/system_name_override_allowed/test_config/CMakeLists.txt b/test/test_cases/pass/system_name_override_allowed/test_config/CMakeLists.txt new file mode 100644 index 0000000..653b0f7 --- /dev/null +++ b/test/test_cases/pass/system_name_override_allowed/test_config/CMakeLists.txt @@ -0,0 +1,19 @@ +IF(NOT DEFINED CMAKE_SCRIPT_MODE_FILE) + CMAKE_MINIMUM_REQUIRED(VERSION 3.22) + PROJECT(CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED_TEST_CONFIG) +ENDIF() + +FIND_PACKAGE(CMLIB REQUIRED) + +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/../../../../../CMCONF.cmake") + +SET(CMCONF_SYSTEM_NAME_OVERRIDE_ALLOWED ON CACHE INTERNAL "" FORCE) + +CMCONF_INIT_SYSTEM("FIRST_SYSTEM") + +CMCONF_INIT_SYSTEM("SECOND_SYSTEM") + +IF(NOT CMCONF_SYSTEM_NAME STREQUAL "SECOND_SYSTEM") + MESSAGE(FATAL_ERROR "System name should be SECOND_SYSTEM but is '${CMCONF_SYSTEM_NAME}'") +ENDIF() + diff --git a/test/test_cases/pass/system_name_reinit_same/CMakeLists.txt b/test/test_cases/pass/system_name_reinit_same/CMakeLists.txt new file mode 100644 index 0000000..1db2ebb --- /dev/null +++ b/test/test_cases/pass/system_name_reinit_same/CMakeLists.txt @@ -0,0 +1,24 @@ +## Main +# +# Test CMCONF_INIT_SYSTEM allows re-initialization with the same system name silently +# + +IF(NOT DEFINED CMAKE_SCRIPT_MODE_FILE) + CMAKE_MINIMUM_REQUIRED(VERSION 3.22) + PROJECT(CMCONF_SYSTEM_NAME_REINIT_SAME_TEST) +ENDIF() + +FIND_PACKAGE(CMLIB REQUIRED) + +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/../../../TEST.cmake") +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/../../../cache_var.cmake") +INCLUDE("${CMAKE_CURRENT_LIST_DIR}/../../../../CMCONF.cmake") + +CMCONF_INIT_SYSTEM("TEST_REINIT") +CMCONF_INIT_SYSTEM("TEST_REINIT") +CMCONF_INIT_SYSTEM("test_reinit") + +IF(NOT CMCONF_SYSTEM_NAME STREQUAL "TEST_REINIT") + MESSAGE(FATAL_ERROR "System name should be TEST_REINIT but is '${CMCONF_SYSTEM_NAME}'") +ENDIF() +