Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions CMCONF.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down Expand Up @@ -168,24 +173,27 @@ 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
# - In script mode: validates filename matches CMCONF_<SYSTEM_NAME>Config.cmake pattern
# - Registers the package using EXPORT(PACKAGE) command
# - Installation is performed only once per session
#
# System name cannot be changed once is set.
#
# <function>(
# <system_name> # Name of the system. Will be converted to uppercase,
# # must contain only [a-zA-Z_] characters.
Expand All @@ -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}")
Expand Down Expand Up @@ -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 <CONFIG_FILE>.'")
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}")
Expand Down Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 .
```

Comment thread
koudis marked this conversation as resolved.
## Function list

Each entry in list represents one feature for CMake.
Expand Down
4 changes: 4 additions & 0 deletions resource/CMakeLists.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -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")
Expand Down
29 changes: 29 additions & 0 deletions test/test_cases/fail/install_with_override_allowed/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()

Original file line number Diff line number Diff line change
@@ -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()

18 changes: 18 additions & 0 deletions test/test_cases/pass/system_name_override_allowed/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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'"
)

Original file line number Diff line number Diff line change
@@ -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()

24 changes: 24 additions & 0 deletions test/test_cases/pass/system_name_reinit_same/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()