cmake/cmake_celix/ContainerPackaging.cmake (598 lines of code) (raw):

# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. set(CELIX_DEFAULT_CONTAINER_CXX_OPTION ON CACHE BOOL "Whether the default container options is CXX. If OFF this will be C") # Setup bundles/container target if (NOT TARGET celix-containers) add_custom_target(celix-containers ALL DEPENDS $<TARGET_PROPERTY:celix-containers,CONTAINER_DEPLOYMENTS> ) set_target_properties(celix-containers PROPERTIES "CONTAINER_DEPLOYMENTS" "") #initial empty deps list get_directory_property(CLEANFILES ADDITIONAL_CLEAN_FILES) list(APPEND CLEANFILES "${CMAKE_BINARY_DIR}/deploy") list(APPEND CLEANFILES "${CMAKE_BINARY_DIR}/celix") set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES "${CLEANFILES}") endif () #[[ Add a Celix container, consisting out of a selection of bundles and a Celix launcher. Celix containers can be used to start a Celix framework together with a selection of bundles. A Celix container will be build in `<cmake_build_dir>/deploy[/<group_name>]/<celix_container_name>`. Use the `<celix_container_name>` executable to run the containers. There are three variants of 'add_celix_container': - If no launcher is specified a custom Celix launcher will be generated. This launcher also contains the configured properties. - If a LAUNCHER_SRC is provided a Celix launcher will be build using the provided sources. Additional sources can be added with the CMake 'target_sources' command. - If a LAUNCHER (absolute path to a executable of CMake `add_executable` target) is provided that will be used as Celix launcher. Creating a Celix containers using 'add_celix_container' will lead to a CMake executable target (unless a LAUNCHER is used). These targets can be used to run/debug Celix containers from a IDE (if the IDE supports CMake). Optional Arguments: - COPY: With this option the bundles used in the container will be copied in and configured for a bundles directory next to the container executable. Only one of the COPY or NO_COPY options can be provided. Default is COPY. - NO_COPY: With this option the bundles used in the container will be configured using absolute paths to the bundles zip files. Only one of the COPY or NO_COPY options can be provided. Default is COPY. - CXX: With this option the generated Celix launcher (if used) will be a C++ source. This ensures that the Celix launcher is linked against stdlibc++. Only one of the C or CXX options can be provided. Default is CXX - C: With this option the generated Celix launcher (if used) will be a C source. Only one of the C or CXX options can be provided. Default is CXX - USE_CONFIG: With this option the config properties are generated in a 'config.properties' instead of embedded in the Celix launcher. - GROUP: If configured the build location will be prefixed the GROUP. Default is empty. - NAME: The name of the executable. Default is <celix_container_name>. Only useful for generated/LAUNCHER_SRC Celix launchers. - DIR: The base build directory of the Celix container. Default is `<cmake_build_dir>/deploy`. - BUNDLES: A list of bundles for the Celix container to install and start. These bundle will be configured for run level 3. See 'celix_container_bundles' for more info. - INSTALL_BUNDLES: A list of bundles for the Celix container to install (but not start). - PROPERTIES: A list of configuration properties, these can be used to configure the Celix framework and/or bundles. Normally this will be EMBEDDED_PROPERTIES, but if the USE_CONFIG option is used this will be RUNTIME_PROPERTIES. See the framework library or bundles documentation about the available configuration options. - EMBEDDED_PROPERTIES: A list of configuration properties which will be used in the generated Celix launcher. - RUNTIME_PROPERTIES: A list of configuration properties which will be used in the generated config.properties file. ```CMake add_celix_container(<celix_container_name> [COPY] [NO_COPY] [CXX] [C] [USE_CONFIG] [GROUP group_name] [NAME celix_container_name] [DIR dir] [BUNDLES <bundle1> <bundle2> ...] [INSTALL_BUNDLES <bundle1> <bundle2> ...] [PROPERTIES "prop1=val1" "prop2=val2" ...] [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] ) ``` ```CMake add_celix_container(<celix_container_name> LAUNCHER launcher [COPY] [NO_COPY] [CXX] [C] [USE_CONFIG] [GROUP group_name] [NAME celix_container_name] [DIR dir] [BUNDLES <bundle1> <bundle2> ...] [INSTALL_BUNDLES <bundle1> <bundle2> ...] [PROPERTIES "prop1=val1" "prop2=val2" ...] [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] ) ``` ```CMake add_celix_container(<celix_container_name> LAUNCHER_SRC launcher_src [COPY] [NO_COPY] [CXX] [C] [USE_CONFIG] [GROUP group_name] [NAME celix_container_name] [DIR dir] [BUNDLES <bundle1> <bundle2> ...] [INSTALL_BUNDLES <bundle1> <bundle2> ...] [PROPERTIES "prop1=val1" "prop2=val2" ...] [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] ) ``` Examples: ```CMake #Creates a Celix container in ${CMAKE_BINARY_DIR}/deploy/simple_container which starts 3 bundles located at #${CMAKE_BINARY_DIR}/deploy/simple_container/bundles. add_celix_container(simple_container BUNDLES Celix::shell Celix::shell_tui Celix::log_admin PROPERTIES CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL=debug ) ``` ]] function(add_celix_container) list(GET ARGN 0 CONTAINER_TARGET) list(REMOVE_AT ARGN 0) set(OPTIONS COPY NO_COPY C CXX FAT USE_CONFIG) set(ONE_VAL_ARGS GROUP NAME LAUNCHER LAUNCHER_SRC DIR) set(MULTI_VAL_ARGS BUNDLES INSTALL_BUNDLES PROPERTIES EMBEDDED_PROPERTIES RUNTIME_PROPERTIES) cmake_parse_arguments(CONTAINER "${OPTIONS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN}) ##### Check arguments ##### if (NOT DEFINED CONTAINER_NAME) set(CONTAINER_NAME "${CONTAINER_TARGET}") endif () if (NOT DEFINED CONTAINER_DIR) set(CONTAINER_DIR "${CMAKE_BINARY_DIR}/deploy") endif () if (CONTAINER_COPY AND CONTAINER_NO_COPY) message(FATAL_ERROR "Only COPY of NO_COPY can be configured for a Celix container, not both") endif () if (CONTAINER_C AND CONTAINER_CXX) message(FATAL_ERROR "Only CXX of C can be configured for a Celix container. Not both") endif () ###### ##### Setting defaults ##### if (CONTAINER_GROUP) set(CONTAINER_LOC "${CONTAINER_DIR}/${CONTAINER_GROUP}/${CONTAINER_NAME}") set(CONTAINER_PRINT_NAME "${CONTAINER_GROUP}/${CONTAINER_NAME}") else () set(CONTAINER_LOC "${CONTAINER_DIR}/${CONTAINER_NAME}") set(CONTAINER_PRINT_NAME "${CONTAINER_NAME}") endif () ###### #add this target as depependency to the celix-containers target get_target_property(CONTAINERDEPS celix-containers "CONTAINER_DEPLOYMENTS") list(APPEND CONTAINERDEPS ${CONTAINER_TARGET}) set_target_properties(celix-containers PROPERTIES "CONTAINER_DEPLOYMENTS" "${CONTAINERDEPS}") set(LAUNCHER_DEP ) if (CONTAINER_LAUNCHER) if (IS_ABSOLUTE "${CONTAINER_LAUNCHER}") set(LAUNCHER "${CONTAINER_LAUNCHER}") else() #assuming target set(LAUNCHER "$<TARGET_FILE:${CONTAINER_LAUNCHER}>") set(LAUNCHER_DEP ${CONTAINER_LAUNCHER}) endif() elseif (CONTAINER_LAUNCHER_SRC) get_filename_component(SRC_FILENAME ${CONTAINER_LAUNCHER_SRC} NAME) set(LAUNCHER_SRC "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/${SRC_FILENAME}") set(LAUNCHER_ORG "${CONTAINER_LAUNCHER_SRC}") file(GENERATE OUTPUT "${LAUNCHER_SRC}" INPUT "${LAUNCHER_ORG}") else () #generate custom launcher if (CONTAINER_C) set(LAUNCHER_SRC "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/main.c") elseif (CONTAINER_CXX) set(LAUNCHER_SRC "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/main.cc") else() if (CELIX_DEFAULT_CONTAINER_CXX_OPTION) set(LAUNCHER_SRC "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/main.cc") else () set(LAUNCHER_SRC "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/main.c") endif () endif() set(STAGE1_LAUNCHER_SRC "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/main.stage1.c") file(GENERATE OUTPUT "${STAGE1_LAUNCHER_SRC}" CONTENT "#include <celix_launcher.h> #define CELIX_MULTI_LINE_STRING(...) #__VA_ARGS__ int main(int argc, char *argv[]) { const char * config = CELIX_MULTI_LINE_STRING( { $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_0>>:\"CELIX_AUTO_START_0\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_0>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_1>>:\"CELIX_AUTO_START_1\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_1>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_2>>:\"CELIX_AUTO_START_2\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_2>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_3>>:\"CELIX_AUTO_START_3\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_3>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>>:\"CELIX_AUTO_START_4\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>>:\"CELIX_AUTO_START_5\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>>:\"CELIX_AUTO_START_6\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_INSTALL>>:\"CELIX_AUTO_INSTALL\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_INSTALL>,$<COMMA>>\"$<COMMA>> $<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_EMBEDDED_PROPERTIES>,$<COMMA> >$<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_EMBEDDED_PROPERTIES>>:$<COMMA>> \"CELIX_BUNDLES_PATH\":\"bundles\"$<COMMA> \"CELIX_CONTAINER_NAME\":\"$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_NAME>\" }); return celix_launcher_launchAndWait(argc, argv, config); } " ) #Rerun generate to do a second parsing of generator expressions file(GENERATE OUTPUT "${LAUNCHER_SRC}" INPUT "${STAGE1_LAUNCHER_SRC}") endif () if (LAUNCHER_SRC) #compilation needed add_executable(${CONTAINER_TARGET} ${LAUNCHER_SRC}) set_target_properties(${CONTAINER_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CONTAINER_LOC}) set_target_properties(${CONTAINER_TARGET} PROPERTIES OUTPUT_NAME ${CONTAINER_NAME}) target_link_libraries(${CONTAINER_TARGET} PRIVATE Celix::framework) if(NOT APPLE) #Add --no-as-needed options, to ensure that libraries are always linked. #This is needed because most libraries are not used by the executable, but by the bundle libraries instead. set_target_properties(${CONTAINER_TARGET} PROPERTIES LINK_FLAGS -Wl,--no-as-needed) endif() set(LAUNCHER "$<TARGET_FILE:${CONTAINER_TARGET}>") else () #LAUNCHER already set add_custom_target(${CONTAINER_TARGET} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${LAUNCHER} ${CONTAINER_LOC}/${CONTAINER_NAME} ) endif () #generate config.properties. C set(CONTAINER_PROPS "${CONTAINER_LOC}/config.properties") set(STAGE1_PROPERTIES "${CMAKE_BINARY_DIR}/celix/gen/containers/${CONTAINER_TARGET}/container-config-stage1.properties") if (CONTAINER_USE_CONFIG) file(GENERATE OUTPUT "${STAGE1_PROPERTIES}" CONTENT "{ $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_0>>:\"CELIX_AUTO_START_0\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_0>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_1>>:\"CELIX_AUTO_START_1\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_1>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_2>>:\"CELIX_AUTO_START_2\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_2>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_3>>:\"CELIX_AUTO_START_3\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_3>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>>:\"CELIX_AUTO_START_4\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>>:\"CELIX_AUTO_START_5\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>>:\"CELIX_AUTO_START_6\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>,$<COMMA>>\"$<COMMA>> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_INSTALL>>:\"CELIX_AUTO_INSTALL\":\"$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_INSTALL>,$<COMMA>>\"$<COMMA>> $<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_EMBEDDED_PROPERTIES>,$<COMMA> >$<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_EMBEDDED_PROPERTIES>>:$<COMMA>> \"CELIX_BUNDLES_PATH\":\"bundles\"$<COMMA> \"CELIX_CONTAINER_NAME\":\"$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_NAME>\" }" ) file(GENERATE OUTPUT "${CONTAINER_PROPS}" INPUT "${STAGE1_PROPERTIES}" ) else () file(GENERATE OUTPUT "${STAGE1_PROPERTIES}" CONTENT "{ $<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_RUNTIME_PROPERTIES>,$<COMMA> > }" ) #Condition is there so that config.properties file will only be generated if there are runtime properties file(GENERATE OUTPUT "${CONTAINER_PROPS}" INPUT "${STAGE1_PROPERTIES}" CONDITION $<NOT:$<STREQUAL:,$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_RUNTIME_PROPERTIES>>> ) endif () #needed in the release.sh & run.sh files #Setting CELIX_LIB_DIRS if (TARGET Celix::framework) #Celix Main Project set(CELIX_LIB_DIRS "$<TARGET_FILE_DIR:Celix::framework>:$<TARGET_FILE_DIR:Celix::utils>:$<TARGET_FILE_DIR:Celix::dfi>") else () message(FATAL_ERROR "No Celix::framework target is defined. Did you use find_package(Celix REQUIRED)?") endif() #generate release.sh and optional run.sh if(APPLE) set(LIB_PATH_NAME "DYLD_LIBRARY_PATH") else() set(LIB_PATH_NAME "LD_LIBRARY_PATH") endif() if (CONTAINER_NO_COPY) set(CONTAINER_COPY FALSE) elseif (CONTAINER_COPY) set(CONTAINER_COPY TRUE) else () set(CONTAINER_COPY TRUE) endif () ##### Container Target Properties ##### #internal use set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_0" "") #bundles to deploy for the container for startup level 0 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_1" "") #bundles to deploy for the container for startup level 1 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_2" "") #bundles to deploy for the container for startup level 2 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_3" "") #bundles to deploy for the container for startup level 3, the default used startup level set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_4" "") #bundles to deploy for the container for startup level 4 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_5" "") #bundles to deploy for the container for startup level 5 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_6" "") #bundles to deploy for the container for startup level 6 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_INSTALL" "") #bundles to install for the container set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_COPY_BUNDLES" ${CONTAINER_COPY}) #copy bundles in bundle dir or link using abs paths. NOTE this cannot be changed after a add_deploy command #deploy specific set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_NAME" "${CONTAINER_NAME}") set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_GROUP" "${CONTAINER_GROUP}") set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_LOC" "${CONTAINER_LOC}") set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_USE_CONFIG" "${CONTAINER_USE_CONFIG}") set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_RUNTIME_PROPERTIES" "") set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_EMBEDDED_PROPERTIES" "") ##### celix_container_bundles(${CONTAINER_TARGET} LEVEL 3 ${CONTAINER_BUNDLES}) celix_container_bundles(${CONTAINER_TARGET} INSTALL ${CONTAINER_INSTALL_BUNDLES}) if (CONTAINER_USE_CONFIG) celix_container_runtime_properties(${CONTAINER_TARGET} ${CONTAINER_PROPERTIES}) else () celix_container_embedded_properties(${CONTAINER_TARGET} ${CONTAINER_PROPERTIES}) endif () celix_container_runtime_properties(${CONTAINER_TARGET} ${CONTAINER_RUNTIME_PROPERTIES}) celix_container_embedded_properties(${CONTAINER_TARGET} ${CONTAINER_EMBEDDED_PROPERTIES}) if (LAUNCHER_DEP) add_dependencies(${CONTAINER_TARGET} ${LAUNCHER_DEP}) endif () #ensure the container dir will be deleted during clean get_directory_property(CLEANFILES ADDITIONAL_CLEAN_FILES) list(APPEND CLEANFILES "$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_LOC>") set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES "${CLEANFILES}") endfunction() #[[ Copy the specified bundles to a specified dir in the container build dir. ```CMake celix_container_bundles_dir(<celix_container_target_name> DIR_NAME dir_name BUNDLES bundle1 bundle2 ... ) ``` ]] function(celix_container_bundles_dir) list(GET ARGN 0 CONTAINER_TARGET) list(REMOVE_AT ARGN 0) set(OPTIONS) set(ONE_VAL_ARGS DIR_NAME) set(MULTI_VAL_ARGS BUNDLES) cmake_parse_arguments(BD "${OPTIONS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN}) if(NOT BD_DIR_NAME) message(FATAL_ERROR "Missing mandatory DIR_NAME argument") endif() get_target_property(CONTAINER_LOC ${CONTAINER_TARGET} "CONTAINER_LOC") set(DEST_DIR "${CONTAINER_LOC}/${BD_DIR_NAME}") get_target_property(CLEAN_FILES ${CONTAINER_TARGET} "ADDITIONAL_CLEAN_FILES") list(APPEND CLEAN_FILES ${DEST_DIR}) list(REMOVE_DUPLICATES CLEAN_FILES) set_target_properties(${CONTAINER_TARGET} PROPERTIES "ADDITIONAL_CLEAN_FILES" "${CLEAN_FILES}") foreach(BUNDLE IN ITEMS ${BD_BUNDLES}) if (IS_ABSOLUTE ${BUNDLE} AND EXISTS ${BUNDLE}) get_filename_component(BUNDLE_FILENAME ${BUNDLE} NAME) string(MAKE_C_IDENTIFIER ${BUNDLE_FILENAME} BUNDLE_ID) #Create id with no special chars set(DEST "${DEST_DIR}/${BUNDLE_FILENAME}") elseif (TARGET ${BUNDLE}) get_target_property(TARGET_TYPE ${BUNDLE} TYPE) if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") #ignore else () get_target_property(IMPORTED_TARGET ${BUNDLE} BUNDLE_IMPORTED) get_target_property(ALIASED_TARGET ${BUNDLE} ALIASED_TARGET) if (IMPORTED_TARGET) #An imported bundle target -> handle target without DEPENDS _celix_extract_imported_bundle_info(${BUNDLE}) #extracts BUNDLE_FILE and BUNDLE_FILENAME string(MAKE_C_IDENTIFIER ${BUNDLE} BUNDLE_ID) #Create id with no special chars set(DEST "${DEST_DIR}/${BUNDLE_FILENAME}") elseif (ALIASED_TARGET) #An alias target use the aliased target string(MAKE_C_IDENTIFIER ${ALIASED_TARGET} BUNDLE_ID) #Create id with no special chars set(BUNDLE_FILE "$<TARGET_PROPERTY:${ALIASED_TARGET},BUNDLE_FILE>") celix_get_bundle_filename(${ALIASED_TARGET} BUNDLE_FILENAME) set(DEST "${DEST_DIR}/${BUNDLE_FILENAME}") set(DEPS ${ALIASED_TARGET}_bundle) #Note white-box knowledge of celix_bundle custom target else() #Assuming a bundle target (library or add_custom_target) string(MAKE_C_IDENTIFIER ${BUNDLE} BUNDLE_ID) #Create id with no special chars set(BUNDLE_FILE "$<TARGET_PROPERTY:${BUNDLE},BUNDLE_FILE>") celix_get_bundle_filename(${BUNDLE} BUNDLE_FILENAME) set(DEST "${DEST_DIR}/${BUNDLE_FILENAME}") set(DEPS ${BUNDLE}_bundle) #Note white-box knowledge of celix_bundle custom target endif () endif () else () message(FATAL_ERROR "Cannot add bundle in container ${CONTAINER_TARGET}. Provided bundle is not a abs path to an existing file nor a cmake target (${BUNDLE}).") endif () if (BUNDLE_ID AND DEST AND BUNDLE_FILE AND NOT TARGET ${CONTAINER_TARGET}_copy_bundle_${BUNDLE_ID}) add_custom_target(${CONTAINER_TARGET}_copy_bundle_${BUNDLE_ID} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BUNDLE_FILE} ${DEST} BYPRODUCTS ${DEST} DEPENDS ${DEPS} ) add_dependencies(${CONTAINER_TARGET} ${CONTAINER_TARGET}_copy_bundle_${BUNDLE_ID}) get_target_property(CLEAN_FILES ${CONTAINER_TARGET} "ADDITIONAL_CLEAN_FILES") list(APPEND CLEAN_FILES ${DEST}) set_target_properties(${CONTAINER_TARGET} PROPERTIES "ADDITIONAL_CLEAN_FILES" "${CLEAN_FILES}") endif () endforeach() endfunction() #[[ Add a selection of bundles to the Celix container. ```CMake celix_container_bundles(<celix_container_target_name> [COPY] [NO_COPY] [LEVEL (0..6)] [INSTALL] bundle1 bundle2 ... ) ``` Example: ```CMake celix_container_bundles(my_container Celix::shell Celix::shell_tui) ``` The selection of bundles are (if configured) copied to the container build dir and are added to the configuration properties so that they are installed and started when the Celix container is executed. The Celix framework supports 7 (0 - 6) run levels. Run levels can be used to control the start and stop order of bundles. Bundles in run level 0 are started first and bundles in run level 6 are started last. When stopping bundles in run level 6 are stopped first and bundles in run level 0 are stopped last. Within a run level the order of configured decides the start order; bundles added earlier are started first. Optional Arguments: - LEVEL: The run level for the added bundles. Default is 3. - INSTALL: If this option is present, the bundles will only be installed instead of the default install and start. The bundles will be installed after all bundle in LEVEL 0..6 are installed and started. - COPY: If this option is present, the bundles will be copied to the container build dir. This option overrides the NO_COPY option used in the add_celix_container call. - NO_COPY: If this option is present, the install/start bundles will be configured using a absolute path to the bundle. This option overrides optional NO_COPY option used in the add_celix_container call. ]] function(celix_container_bundles) #0 is container TARGET list(GET ARGN 0 CONTAINER_TARGET) list(REMOVE_AT ARGN 0) set(OPTIONS INSTALL COPY NO_COPY) set(ONE_VAL_ARGS LEVEL) set(MULTI_VAL_ARGS ) cmake_parse_arguments(BUNDLES "${OPTIONS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN}) set(BUNDLES_LIST ${BUNDLES_UNPARSED_ARGUMENTS}) #Check arguments if (BUNDLES_COPY AND BUNDLES_NO_COPY) message(FATAL_ERROR "Only COPY of NO_COPY can be configured for celix_container_bundles, not both.") endif () if (NOT DEFINED BUNDLES_LEVEL) set(BUNDLES_LEVEL 3) endif () if (BUNDLES_INSTALL) get_target_property(BUNDLES ${CONTAINER_TARGET} "CONTAINER_BUNDLES_INSTALL") else () #bundle level 0,1,2,3,4,5 or 6 get_target_property(BUNDLES ${CONTAINER_TARGET} "CONTAINER_BUNDLES_LEVEL_${BUNDLES_LEVEL}") endif () get_target_property(COPY ${CONTAINER_TARGET} "CONTAINER_COPY_BUNDLES") if (BUNDLES_COPY) set(COPY TRUE) elseif (BUNDLES_NO_COPY) set(COPY FALSE) endif () foreach(BUNDLE IN ITEMS ${BUNDLES_LIST}) if (TARGET ${BUNDLE}) get_target_property(TARGET_TYPE ${BUNDLE} TYPE) if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") #ignore. this can be used to keep targets name, but ignore it use in containers (e.g. Celix::dm_shell) message(DEBUG "Ignoring bundle target ${BUNDLE} add to container ${CONTAINER_TARGET}. Target type is not SHARED_LIBRARY") elseif (TARGET_TYPE STREQUAL "SHARED_LIBRARY" OR TARGET_TYPE STREQUAL "UTILITY") celix_get_bundle_file(${BUNDLE} ABS_LOC) celix_get_bundle_filename(${BUNDLE} COPY_LOC) add_celix_bundle_dependencies(${CONTAINER_TARGET} ${BUNDLE}) else () message(FATAL_ERROR "Cannot add bundle target ${BUNDLE} with target type ${TARGET_TYPE} to container ${CONTAINER_TARGET}. Only INTERFACE_LIBRARY, SHARED_LIBRARY or UTILITY is supported.") endif () elseif (IS_ABSOLUTE ${BUNDLE} AND EXISTS ${BUNDLE}) get_filename_component(BUNDLE_FILENAME ${BUNDLE} NAME) set(COPY_LOC "${BUNDLE_FILENAME}") set(ABS_LOC "${BUNDLE}") else () message(FATAL_ERROR "Cannot add bundle `${BUNDLE}` to container target ${CONTAINER_TARGET}. Argument is not a path or cmake target") endif () if (COPY) set(BUNDLE_TO_ADD ${COPY_LOC}) else () set(BUNDLE_TO_ADD ${ABS_LOC}) endif () list(FIND BUNDLES ${BUNDLE_TO_ADD} INDEX) if (INDEX EQUAL -1) #Note this ignores the same bundle for the same level _celix_container_check_duplicate_bundles(${CONTAINER_TARGET} ${BUNDLE_TO_ADD} ${BUNDLES_LEVEL}) list(APPEND BUNDLES ${BUNDLE_TO_ADD}) endif () endforeach() list(REMOVE_DUPLICATES BUNDLES) if (BUNDLES_INSTALL) set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_INSTALL" "${BUNDLES}") else () #bundle level 0,1,2,3,4,5 or 6 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_${BUNDLES_LEVEL}" "${BUNDLES}") endif () if (COPY) celix_container_bundles_dir(${CONTAINER_TARGET} DIR_NAME bundles BUNDLES ${BUNDLES_LIST}) endif() endfunction() #[[ Private function to check if there are duplicate bundles in the container and print a warning if so. Arg CONTAINER_TARGET ADDED_BUNDLES_LIST TARGET_LEVEL ]] function(_celix_container_check_duplicate_bundles) list(GET ARGN 0 CONTAINER_TARGET) list(GET ARGN 1 TO_ADD_BUNDLE) list(GET ARGN 2 TARGET_LEVEL) if (NOT TARGET_LEVEL) #install return() #Bundles can be installed and added to a level endif() set(PARTIAL_MSG "Bundle `${TO_ADD_BUNDLE}` is added to the container multiple times. This can lead to errors \ during bundle installation. Bundle `${TO_ADD_BUNDLE}` for level ${TARGET_LEVEL} is already added to the \ container '${CONTAINER_TARGET}` at level ") foreach(BUNDLE_LEVEL RANGE 0 6) get_target_property(BUNDLES ${CONTAINER_TARGET} "CONTAINER_BUNDLES_LEVEL_${BUNDLE_LEVEL}") list(FIND BUNDLES ${TO_ADD_BUNDLE} INDEX) if (INDEX GREATER -1) message(WARNING "${PARTIAL_MSG} ${BUNDLE_LEVEL}.") endif() endforeach() endfunction() #[[ Add the provided properties to the target Celix container config properties. These properties will be added to the config.properties in the container build dir. ```CMake celix_container_runtime_properties(<celix_container_target_name> "prop1=val1" "prop2=val2" ... ) ``` ]] function(celix_container_runtime_properties) #0 is container TARGET #1..n is property "key=val" pairs list(GET ARGN 0 CONTAINER_TARGET) list(REMOVE_AT ARGN 0) get_target_property(PROPS ${CONTAINER_TARGET} "CONTAINER_RUNTIME_PROPERTIES") foreach (PROP IN ITEMS ${ARGN}) _celix_convert_keyval_to_json(${PROP} "=" PROP) list(APPEND PROPS ${PROP}) endforeach() set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_RUNTIME_PROPERTIES" "${PROPS}") endfunction() #[[ Add the provided properties to the target Celix container config properties. These properties will be embedded into the generated Celix launcher. ```CMake celix_container_embedded_properties(<celix_container_target_name> "prop1=val1" "prop2=val2" ... ) ``` ]] function(celix_container_embedded_properties) #0 is container TARGET #1..n is property "key=val" pairs list(GET ARGN 0 CONTAINER_TARGET) list(REMOVE_AT ARGN 0) get_target_property(PROPS ${CONTAINER_TARGET} "CONTAINER_EMBEDDED_PROPERTIES") foreach (PROP IN ITEMS ${ARGN}) _celix_convert_keyval_to_json(${PROP} "=" PROP) list(APPEND PROPS ${PROP}) endforeach() set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_EMBEDDED_PROPERTIES" "${PROPS}") endfunction() #[[ Add the provided properties to the target Celix container config properties. If the USE_CONFIG option is used these configuration properties will be added to the 'config.properties' file else these will be embedded into the generated Celix launcher. ```CMake celix_container_properties(<celix_container_target_name> "prop1=val1" "prop2=val2" ... ) ``` ]] function(celix_container_properties) get_target_property(USE_CNF ${CONTAINER_TARGET} "CONTAINER_USE_CONFIG") if (USE_CNF) celix_container_runtime_properties(${ARGN}) else () celix_container_embedded_properties(${ARGN}) endif () endfunction()