#.rst: # G4DeveloperAPI # -------------- # # .. code-block::cmake # # include(G4DeveloperAPI) # # CMake functions and macros for declaring and working with build # products of Geant4. # #----------------------------------------------------------------- # License and Disclaimer # # The Geant4 software is copyright of the Copyright Holders of # the Geant4 Collaboration. It is provided under the terms and # conditions of the Geant4 Software License, included in the file # LICENSE and available at http://cern.ch/geant4/license . These # include a list of copyright holders. # # Neither the authors of this software system, nor their employing # institutes,nor the agencies providing financial support for this # work make any representation or warranty, express or implied, # regarding this software system or assume any liability for its # use. Please see the license in the file LICENSE and URL above # for the full disclaimer and the limitation of liability. # # This code implementation is the result of the scientific and # technical work of the GEANT4 collaboration. # By using, copying, modifying or distributing the software (or # any work based on the software) you agree to acknowledge its # use in resulting scientific publications, and indicate your # acceptance of all terms of the Geant4 Software license. # #----------------------------------------------------------------- include_guard(DIRECTORY) #----------------------------------------------------------------------- #----------------------------------------------------------------------- #.rst: # Module Commands # ^^^^^^^^^^^^^^^ # # .. cmake:command:: geant4_add_module # # .. code-block:: cmake # # geant4_add_module( # PUBLIC_HEADERS header1 [header2 ...] # PRIVATE_HEADERS header1 [header2 ..] # [SOURCES source1 [source2 ...]]) # # Add a Geant4 module called ```` to the project, composed # of the source files listed in the ``PUBLIC_HEADERS``, ``PRIVATE_HEADERS``, # and ``SOURCES`` arguments. The ```` must be unique within the project. # The directory in which the module is added (i.e. ``CMAKE_CURRENT_LIST_DIR`` # for the CMake script in which ``geant4_add_module`` is called) must contain: # # * An ``include`` subdirectory for the public headers # * A ``include/private`` subdirectory for the private headers # * A ``src`` subdirectory for source files if the module provides these # # Any module with only ``PUBLIC_HEADERS`` has its ``IS_INTERFACE`` property # set to ``TRUE``. This is for downstream use in composing and building end # targets appropriately. # # The ``PUBLIC_HEADERS`` argument must list the headers comprising the # public interface of the module. If a header is supplied as a relative path, # this is interpreted as being relative to the ``include`` subdirectory of the module. # Absolute paths may also be supplied, e.g. if headers are generated by the project. # # The ``PRIVATE_HEADERS`` argument must list the headers comprising any # interfaces that are implementation details of the module. If a header is supplied # as a relative path, this is interpreted as being relative to the ``include/private`` # subdirectory of the module. Absolute paths may also be supplied, e.g. if headers # are generated by the project. # # The ``SOURCES`` argument should list any source files for the module. # If a source is is supplied as a relative path, this is interpreted as being # relative to the ``src`` subdirectory of the module. Absolute paths may # also be supplied, e.g. if sources are generated by the project. # function(geant4_add_module _name) __geant4_module_assert_not_exists(${_name}) set_property(GLOBAL APPEND PROPERTY GEANT4_DEFINED_MODULES ${_name}) cmake_parse_arguments(G4ADDMOD "" "" "PUBLIC_HEADERS;PRIVATE_HEADERS;SOURCES" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4ADDMOD geant4_add_module) # - Check required directory structure at definition point # Headers always if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/include") message(FATAL_ERROR "Missing required 'include' subdirectory for module '${_name}' at '${CMAKE_CURRENT_LIST_DIR}'") endif() # Sources if defined if(G4ADDMOD_SOURCES AND (NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/src")) message(FATAL_ERROR "Missing required 'src' subdirectory for module '${_name}' at '${CMAKE_CURRENT_LIST_DIR}'") endif() # Record where we're defined so we can pop the file into IDEs geant4_set_module_property(${_name} PROPERTY CMAKE_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}") # Compose header/source lists geant4_module_sources(${_name} PUBLIC_HEADERS ${G4ADDMOD_PUBLIC_HEADERS} PRIVATE_HEADERS ${G4ADDMOD_PRIVATE_HEADERS} SOURCES ${G4ADDMOD_SOURCES}) # Set default usage requirements for this module, dependent on whether it is an INTERFACE or not # TODO: It's a hard error if a module is INTERFACE and has PRIVATE_HEADERS if(NOT G4ADDMOD_SOURCES) geant4_module_include_directories(${_name} INTERFACE $) geant4_set_module_property(${_name} PROPERTY IS_INTERFACE TRUE) else() # Set the default include directory for this module # Private header inclusions are, for now, dealt with at the target level (until we require CMake >=3.18) # - See set_source_files_properties for details geant4_module_include_directories(${_name} PUBLIC $) # Some compile definitions are still transported via directory level # "add_definitions" calls (primarily DLL exports) # These, if they exist, are equivalent to PRIVATE compile defs for the module (and hence library) # and hence are only added for compiled modules. get_directory_property(__local_compile_defs COMPILE_DEFINITIONS) geant4_module_compile_definitions(${_name} PRIVATE ${__local_compile_defs}) endif() # Backward compatibility shim for direct usage of build directory # Not all clients (esp. those using ROOT) may not fully support usage requirements # and expect GEANT4_INCLUDE_DIRS to be complete set_property(GLOBAL APPEND PROPERTY GEANT4_BUILDTREE_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include") endfunction() #----------------------------------------------------------------------- # .. cmake:command:: geant4_module_sources # # .. code-block:: cmake # # geant4_module_sources( # PUBLIC_HEADERS header1 [header2 ...] # PRIVATE_HEADERS header1 [header2 ...] # [SOURCES source1 [source2 ...]]) # # Append sources to a Geant4 module called ````, composed of the source # files listed in the ``PUBLIC_HEADERS`` and ``SOURCES`` arguments. The module # ``name`` must have already been created by the ``geant4_add_module`` command. # # The requirements on the ``PUBLIC_HEADERS`` and ``SOURCES`` arguments as the same # as for the ``geant4_add_module`` command. Intended for the use case of modules with # optional sources dependent on a configuration flag. # function(geant4_module_sources _name) __geant4_module_assert_exists(${_name}) cmake_parse_arguments(G4MODSRCS "" "" "PUBLIC_HEADERS;PRIVATE_HEADERS;SOURCES" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4MODSRCS geant4_module_sources) # Restrict calls to the same CMake file as the original add # Ensures we don't mix up sources/get confused on paths given assumptions made geant4_get_module_property(__location ${_name} CMAKE_LIST_FILE) if(NOT ("${__location}" STREQUAL "${CMAKE_CURRENT_LIST_FILE}")) message(FATAL_ERROR "adding sources to module '${_name}' in '${CMAKE_CURRENT_LIST_FILE}', but module was defined in '${__location}'") endif() # Append header/source lists to module set(__tmp_HEADERS) foreach(__elem ${G4MODSRCS_PUBLIC_HEADERS}) if(IS_ABSOLUTE "${__elem}") list(APPEND __tmp_HEADERS "${__elem}") else() list(APPEND __tmp_HEADERS "${CMAKE_CURRENT_LIST_DIR}/include/${__elem}") endif() endforeach() geant4_set_module_property(${_name} APPEND PROPERTY PUBLIC_HEADERS ${__tmp_HEADERS}) set(__tmp_HEADERS) foreach(__elem ${G4MODSRCS_PRIVATE_HEADERS}) if(IS_ABSOLUTE "${__elem}") list(APPEND __tmp_HEADERS "${__elem}") else() list(APPEND __tmp_HEADERS "${CMAKE_CURRENT_LIST_DIR}/include/private/${__elem}") endif() endforeach() geant4_set_module_property(${_name} APPEND PROPERTY PRIVATE_HEADERS ${__tmp_HEADERS}) set(__tmp_SOURCES) foreach(__elem ${G4MODSRCS_SOURCES}) if(IS_ABSOLUTE "${__elem}") list(APPEND __tmp_SOURCES "${__elem}") else() list(APPEND __tmp_SOURCES "${CMAKE_CURRENT_LIST_DIR}/src/${__elem}") endif() endforeach() geant4_set_module_property(${_name} APPEND PROPERTY SOURCES ${__tmp_SOURCES}) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_module_include_directories # # .. code-block:: cmake # # geant4_module_include_directories( # [PUBLIC pub1 [pub2 ...] # [PRIVATE pri1 [pri2 ...] # [INTERFACE int1 [int2 ...]) # # Add include directories to given module. If the module is an ``INTERFACE``, # i.e. header-only, then only ``INTERFACE`` properties may be set. # function(geant4_module_include_directories _module) __geant4_module_assert_exists(${_module}) cmake_parse_arguments(G4MODINCDIR "" "" "PUBLIC;PRIVATE;INTERFACE" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4MODINCDIR geant4_module_include_directories) # An interface module can only have interface requirements set geant4_get_module_property(__is_interface ${_module} IS_INTERFACE) if(__is_interface AND (G4MODINCDIR_PUBLIC OR G4MODINCDIR_PRIVATE)) message(FATAL_ERROR "geant4_module_include_directories: may only set INTERFACE properties on header-only module ${_module}") endif() foreach(_dir ${G4MODINCDIR_PUBLIC}) geant4_set_module_property(${_module} APPEND PROPERTY PUBLIC_INCLUDE_DIRECTORIES ${_dir}) endforeach() foreach(_dir ${G4MODINCDIR_PRIVATE}) geant4_set_module_property(${_module} APPEND PROPERTY PRIVATE_INCLUDE_DIRECTORIES ${_dir}) endforeach() foreach(_dir ${G4MODINCDIR_INTERFACE}) geant4_set_module_property(${_module} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${_dir}) endforeach() endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_module_link_libraries # # .. code-block:: cmake # # geant4_module_link_libraries( # [PUBLIC pub1 [pub2 ...] # [PRIVATE pri1 [pri2 ...] # [INTERFACE int1 [int2 ...]) # # Link ```` to given targets. If the module is an ``INTERFACE``, # i.e. header-only, then only ``INTERFACE`` properties may be set. # function(geant4_module_link_libraries _module) __geant4_module_assert_exists(${_module}) cmake_parse_arguments(G4MODLINKLIB "" "" "PUBLIC;PRIVATE;INTERFACE" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4MODLINKLIB geant4_module_link_libraries) # An interface module can only have interface requirements set geant4_get_module_property(__is_interface ${_module} IS_INTERFACE) if(__is_interface AND (G4MODLINKLIB_PUBLIC OR G4MODLINKLIB_PRIVATE)) message(FATAL_ERROR "geant4_module_link_libraries: may only set INTERFACE properties on header-only module ${_module}") endif() foreach(_lib ${G4MODLINKLIB_PUBLIC}) geant4_set_module_property(${_module} APPEND PROPERTY PUBLIC_LINK_LIBRARIES ${_lib}) endforeach() foreach(_lib ${G4MODLINKLIB_PRIVATE}) geant4_set_module_property(${_module} APPEND PROPERTY PRIVATE_LINK_LIBRARIES ${_lib}) endforeach() foreach(_lib ${G4MODLINKLIB_INTERFACE}) geant4_set_module_property(${_module} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_lib}) endforeach() endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_module_compile_definitions # # .. code-block:: cmake # # geant4_module_compile_definitions( # [PUBLIC pub1 [pub2 ...] # [PRIVATE pri1 [pri2 ...] # [INTERFACE int1 [int2 ...]) # # Add compile definitions for this module. If the module is an ``INTERFACE``, # i.e. header-only, then only ``INTERFACE`` properties may be set. # # Use cases: # 1. workarounds when a config header isn't suitable # # Needs care with DLLs if used for import/export declspecs # Application of specific defs to single files not considered # Expected that uses cases here minimal, and developers should # in that case use set_source_files_properties or similar # directly. # function(geant4_module_compile_definitions _module) __geant4_module_assert_exists(${_module}) cmake_parse_arguments(G4MODCOMPILEDEF "" "" "PUBLIC;PRIVATE;INTERFACE" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4MODCOMPILEDEF geant4_module_compile_definitions) # An interface module can only have interface requirements set geant4_get_module_property(__is_interface ${_module} IS_INTERFACE) if(__is_interface AND (G4MODCOMPILEDEF_PUBLIC OR G4MODCOMPILEDEF_PRIVATE)) message(FATAL_ERROR "geant4_module_compile_definitions: may only set INTERFACE properties on header-only module ${_module}") endif() foreach(_def ${G4MODCOMPILEDEF_PUBLIC}) geant4_set_module_property(${_module} APPEND PROPERTY PUBLIC_COMPILE_DEFINITIONS ${_def}) endforeach() foreach(_def ${G4MODCOMPILEDEF_PRIVATE}) geant4_set_module_property(${_module} APPEND PROPERTY PRIVATE_COMPILE_DEFINITIONS ${_def}) endforeach() foreach(_def ${G4MODCOMPILEDEF_INTERFACE}) geant4_set_module_property(${_module} APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS ${_def}) endforeach() endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_get_modules # # .. code-block:: cmake # # geant4_get_modules() # # Store the list of currently defined modules in the variable ````. # function(geant4_get_modules _result) get_property(__tmp GLOBAL PROPERTY GEANT4_DEFINED_MODULES) set(${_result} ${__tmp} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_has_module # # .. code-block:: cmake # # geant4_has_module( ) # # Set variable ```` to a boolean which will be true if the module # ```` is defined. # function(geant4_has_module _result _name) set(__exists FALSE) geant4_get_modules(__tmp) if(__tmp) list(FIND __tmp ${_name} __index) if(__index GREATER -1) set(__exists TRUE) endif() endif() set(${_result} ${__exists} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- #.rst: # Module Properties # ================= # # A Geant4 module stores its build and usage requirements in a series # of properties: # # * ``PUBLIC_HEADERS`` # * ``PRIVATE_HEADERS`` # * ``SOURCES`` # * ``PRIVATE_COMPILE_DEFINITIONS`` # * ``PUBLIC_COMPILE_DEFINITIONS`` # * ``INTERFACE_COMPILE_DEFINITIONS`` # * ``PRIVATE_INCLUDE_DIRECTORIES`` # * ``PUBLIC_INCLUDE_DIRECTORIES`` # * ``INTERFACE_INCLUDE_DIRECTORIES`` # * ``PRIVATE_LINK_LIBRARIES`` # * ``PUBLIC_LINK_LIBRARIES`` # * ``INTERFACE_LINK_LIBRARIES`` # * ``PARENT_TARGET`` # * ``CMAKE_LIST_FILE`` # * ``GLOBAL_DEPENDENCIES`` # * ``IS_INTERFACE`` # # The properties of a module may be queried and set using the following # commands. #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_get_module_property # # .. code-block:: cmake # # geant4_get_module_property( ) # # Store value of property ```` for ```` in variable # ````. # # If ```` is not a valid module property, a FATAL_ERROR is # emitted. # function(geant4_get_module_property _output _module _propertyname) __geant4_module_assert_exists(${_module}) __geant4_module_validate_property(${_propertyname}) get_property(__result GLOBAL PROPERTY ${_module}_${_propertyname}) set(${_output} ${__result} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_set_module_property # # .. code-block:: cmake # # geant4_set_module_property( # [APPEND | APPEND_STRING] # PROPERTY ) # # Set property ```` of module ```` to ````. # # If ``APPEND`` is supplied, ```` will be appended to any existing # value for the property as a list. # # If ``APPEND_STRING`` is supplied, ```` will be appended to any existing # value for the property as a string. This option is mutually exclusive with # ``APPEND``. # # If ```` is not a valid module property, a FATAL_ERROR is # emitted. # function(geant4_set_module_property _module) __geant4_module_assert_exists(${_module}) cmake_parse_arguments(G4SMP "APPEND;APPEND_STRING" "" "PROPERTY" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4SMP geant4_set_module_property) # Append/Append_string are mutually exclusive if(G4SMP_APPEND AND G4SMP_APPEND_STRING) message(FATAL_ERROR "geant4_set_module_property: cannot set both APPEND and APPEND_STRING") elseif(G4SMP_APPEND) set(G4SMP_APPEND_MODE "APPEND") elseif(G4SMP_APPEND_MODE) set(G4SMP_APPEND_MODE "APPEND_STRING") endif() # First element of PROPERTY list is prop name list(GET G4SMP_PROPERTY 0 G4SMP_PROPERTY_NAME) if(NOT G4SMP_PROPERTY_NAME) message(FATAL_ERROR "geant4_set_module_property: Required PROPERTY argument is missing") endif() __geant4_module_validate_property(${G4SMP_PROPERTY_NAME}) # Remainder is arguments, so strip first element list(REMOVE_AT G4SMP_PROPERTY 0) set_property(GLOBAL ${G4SMP_APPEND_MODE} PROPERTY ${_module}_${G4SMP_PROPERTY_NAME} ${G4SMP_PROPERTY}) endfunction() #----------------------------------------------------------------------- #----------------------------------------------------------------------- #.rst: # Category Commands # ^^^^^^^^^^^^^^^^^ # # .. cmake:command:: geant4_add_category # # .. code-block:: cmake # # geant4_add_category( MODULES [ ...]) # # Add a Geant4 category ```` to the project, composed of the modules # supplied in the ``MODULES`` list. # # Calling this function does not create an actual CMake library target. # Because modules declare dependencies on modules rather than libraries, we # defer creation of library targets to after creation of categories, which # allows resolution of module <-> category use. Additionally, category specific # actions such as install may be added. # function(geant4_add_category _name) __geant4_category_assert_not_exists(${_name}) set_property(GLOBAL APPEND PROPERTY GEANT4_DEFINED_CATEGORIES ${_name}) cmake_parse_arguments(G4ADDCAT "" "" "MODULES" ${ARGN} ) # - Modules must not be empty (Could also just be ARGN) if(NOT G4ADDCAT_MODULES) message(FATAL_ERROR "geant4_add_category: Missing/empty 'MODULES' argument") endif() # Default to an interface module, geant4_category_modules will update this if needed geant4_set_category_property(${_name} PROPERTY IS_INTERFACE TRUE) # Compose Category from Modules geant4_category_modules(${_name} ${G4ADDCAT_MODULES}) # As we do not create a physical target, store the script in which we're called geant4_set_category_property(${_name} PROPERTY CMAKE_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}") endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_category_modules # # .. code-block:: cmake # # geant4_category_modules( [ ...]) # # Add modules to category ```` # function(geant4_category_modules _name) __geant4_category_assert_exists(${_name}) # ARGN must not be empty if(NOT ARGN) message(FATAL_ERROR "no modules given to add to to category '${_name}'") endif() # Need to track if any added module has source files. If so, must set IS_INTERFACE to false # Compose Category from Modules, accounting for potential interface-only composition geant4_get_category_property(__cat_is_interface ${_name} IS_INTERFACE) foreach(__g4module ${ARGN}) # Module must not have been composed already (Might want to mark special case where module added twice into same category) geant4_get_module_property(_parent ${__g4module} PARENT_TARGET) if(_parent) message(FATAL_ERROR "trying to compose category '${_name}' using module '${__g4module}' which is already composed into category '${_parent}'") endif() # Compose it geant4_set_module_property(${__g4module} PROPERTY PARENT_TARGET ${_name}) geant4_set_category_property(${_name} APPEND PROPERTY MODULES ${__g4module}) geant4_get_module_property(_headers ${__g4module} PUBLIC_HEADERS) geant4_set_category_property(${_name} APPEND PROPERTY PUBLIC_HEADERS ${_headers}) # Is this module interface only? geant4_get_module_property(_is_interface ${__g4module} IS_INTERFACE) if(NOT _is_interface) set(__cat_is_interface FALSE) endif() endforeach() geant4_set_category_property(${_name} PROPERTY IS_INTERFACE ${__cat_is_interface}) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_get_categories # # .. code-block:: cmake # # geant4_get_categories() # # Store the list of currently defined categories in the variable ````. # function(geant4_get_categories _result) get_property(__tmp GLOBAL PROPERTY GEANT4_DEFINED_CATEGORIES) set(${_result} ${__tmp} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_has_category # # .. code-block:: cmake # # geant4_has_category( ) # # Set variable ```` to a boolean which will be true if the category # ```` is defined. # function(geant4_has_category _result _name) set(__exists FALSE) geant4_get_categories(__tmp) if(__tmp) list(FIND __tmp ${_name} __index) if(__index GREATER -1) set(__exists TRUE) endif() endif() set(${_result} ${__exists} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- #.rst: # Category Properties # =================== # # A Geant4 category stores its build and usage requirements in a series # of properties: # # * ``CMAKE_LIST_FILE`` # * ``IS_INTERFACE`` # * ``MODULES`` # * ``PUBLIC_HEADERS`` # # The properties of a category may be queried and set using the following # commands. # #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_get_category_property # # .. code-block:: cmake # # geant4_get_category_property( ) # # Store value of property ```` for ```` in variable # ````. # # If ```` is not a valid module property, a FATAL_ERROR is # emitted. # function(geant4_get_category_property _output _category _propertyname) __geant4_category_assert_exists(${_category}) __geant4_category_validate_property(${_propertyname}) get_property(__result GLOBAL PROPERTY GEANT4_CATEGORY_${_category}_${_propertyname}) set(${_output} ${__result} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_set_category_property # # .. code-block:: cmake # # geant4_set_category_property( # [APPEND | APPEND_STRING] # PROPERTY ) # # Set property ```` of category ```` to ````. # # If ``APPEND`` is supplied, ```` will be appended to any existing # value for the property as a list. # # If ``APPEND_STRING`` is supplied, ```` will be appended to any existing # value for the property as a string. This option is mutually exclusive with # ``APPEND``. # # If ```` is not a valid category property, a FATAL_ERROR is # emitted. # function(geant4_set_category_property _category) __geant4_category_assert_exists(${_category}) cmake_parse_arguments(G4CMP "APPEND;APPEND_STRING" "" "PROPERTY" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4CMP geant4_set_category_property) # Append/Append_string are mutually exclusive if(G4CMP_APPEND AND G4CMP_APPEND_STRING) message(FATAL_ERROR "geant4_set_category_property: cannot set both APPEND and APPEND_STRING") elseif(G4CMP_APPEND) set(G4CMP_APPEND_MODE "APPEND") elseif(G4CMP_APPEND_MODE) set(G4CMP_APPEND_MODE "APPEND_STRING") endif() # First element of PROPERTY list is prop name list(GET G4CMP_PROPERTY 0 G4CMP_PROPERTY_NAME) if(NOT G4CMP_PROPERTY_NAME) message(FATAL_ERROR "geant4_set_category_property: Required PROPERTY argument is missing") endif() __geant4_category_validate_property(${G4CMP_PROPERTY_NAME}) # Remainder is arguments, so strip first element list(REMOVE_AT G4CMP_PROPERTY 0) set_property(GLOBAL ${G4CMP_APPEND_MODE} PROPERTY GEANT4_CATEGORY_${_category}_${G4CMP_PROPERTY_NAME} ${G4CMP_PROPERTY}) endfunction() #----------------------------------------------------------------------- #----------------------------------------------------------------------- #.rst: # External Library Commands # ^^^^^^^^^^^^^^^^^^^^^^^^^ # # Geant4 provides internal copies of libraries that are required to build # the toolkit: # # - CLHEP # - PTL # - Expat # - ZLIB # - Tools # # Because these may have different build systems and source/header structures than # Geant4, the following commands help adapt their build and use to: # # - Provide shared and/or static library targets using the same settings as the # toolkit libraries (for example, C++ Standard) # - Use of these targets as other Geant4 libraries to allow consistent full static/full # shared linking # #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_add_external_library # # .. code-block:: # # geant4_add_external_library(NAME # SOURCES source1 [source2 ...]) # # Maintained for building external targets because we try and reuse their # upstream code/build layout as far as possible, but want to ensure the # targets are compiled in same way as toolkit libraries. # function(geant4_add_external_library) cmake_parse_arguments(G4GLOBLIB "" "NAME" "SOURCES" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4GLOBLIB geant4_add_external_library) # Only supported for externals G4clhep/G4expat/G4zlib, so an error if used elsewhere if(NOT (${G4GLOBLIB_NAME} MATCHES "G4clhep|G4expat|G4zlib")) message(FATAL_ERROR "geant4_add_external_library called for '${G4GLOBLIB_NAME}' in '${CMAKE_CURRENT_LIST_DIR}'") endif() if(BUILD_SHARED_LIBS) add_library(${G4GLOBLIB_NAME} SHARED ${G4GLOBLIB_SOURCES}) add_library(Geant4::${G4GLOBLIB_NAME} ALIAS ${G4GLOBLIB_NAME}) target_compile_features(${G4GLOBLIB_NAME} PUBLIC ${GEANT4_TARGET_COMPILE_FEATURES}) target_compile_definitions(${G4GLOBLIB_NAME} PUBLIC G4LIB_BUILD_DLL) target_include_directories(${G4GLOBLIB_NAME} PUBLIC $ ) set_target_properties(${G4GLOBLIB_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) install(TARGETS ${G4GLOBLIB_NAME} EXPORT Geant4LibraryDepends RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) endif() if(BUILD_STATIC_LIBS) add_library(${G4GLOBLIB_NAME}-static STATIC ${G4GLOBLIB_SOURCES}) add_library(Geant4::${G4GLOBLIB_NAME}-static ALIAS ${G4GLOBLIB_NAME}-static) target_compile_features(${G4GLOBLIB_NAME}-static PUBLIC ${GEANT4_TARGET_COMPILE_FEATURES}) target_include_directories(${G4GLOBLIB_NAME}-static PUBLIC $ ) if(NOT WIN32) set_target_properties(${G4GLOBLIB_NAME}-static PROPERTIES OUTPUT_NAME ${G4GLOBLIB_NAME}) endif() install(TARGETS ${G4GLOBLIB_NAME}-static EXPORT Geant4LibraryDepends RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) endif() # As with a standard library, always add the local include/ directory to exported list # for scripts set_property(GLOBAL APPEND PROPERTY GEANT4_BUILDTREE_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include") # Needs to be in defined category list for static/shared to be linked correctly. geant4_add_external_category(${G4GLOBLIB_NAME}) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_add_external_category # # .. code-block:: cmake # # geant4_add_external_category() # # Mark ```` as a target that supports the Geant4 target naming # convention of ```` as the shared library and ``-static`` # as the static. It allows Geant4 modules to link to targets built # outside the module/category system and retain consistent shared-shared # or static-static linking chains. # # If ``BUILD_SHARED_LIBS``(``BUILD_STATIC_LIBS``) is ``ON``, then ````(``-static``) # must correspond to a shared(static) library target that exists at # the time this function is called. # function(geant4_add_external_category _name) # Targets must have been defined! if(BUILD_SHARED_LIBS AND (NOT TARGET ${_name})) message(FATAL_ERROR "geant4_add_external_category: no target '${_name}' has been declared") endif() if(BUILD_STATIC_LIBS AND (NOT TARGET ${_name}-static)) message(FATAL_ERROR "geant4_add_external_category: no target '${_name}-static' has been declared") endif() set_property(GLOBAL APPEND PROPERTY GEANT4_EXTERNAL_CATEGORIES ${_name}) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: geant4_get_external_categories # # .. code-block:: cmake # # geant4_get_external_categories() # # Store the list of currently defined external_categories in the variable ````. # function(geant4_get_external_categories _result) get_property(__tmp GLOBAL PROPERTY GEANT4_EXTERNAL_CATEGORIES) set(${_result} ${__tmp} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- #----------------------------------------------------------------------- #.rst: # Test Program Commands # ^^^^^^^^^^^^^^^^^^^^^ # .. cmake:command:: geant4_test_link_libraries # # .. code-block:: cmake # # geant4_test_link_libraries( # [PUBLIC pub1 [pub2 ...] # [PRIVATE pri1 [pri2 ...] # [INTERFACE int1 [int2 ...]) function(geant4_test_link_libraries _target) cmake_parse_arguments(G4TESTLINKLIB "" "" "PUBLIC;PRIVATE;INTERFACE" ${ARGN} ) __geant4_assert_no_unparsed_arguments(G4TESTLINKLIB geant4_test_link_libraries) # Need defined libraries and externals to be able to resolve between static/shared get_property(__g4definedlibraries GLOBAL PROPERTY GEANT4_DEFINED_CATEGORIES) geant4_get_external_categories(__g4externalcategories) list(APPEND __g4definedlibraries ${__g4externalcategories}) foreach(__prop PUBLIC PRIVATE INTERFACE) __geant4_resolve_link_libraries(G4TESTLINKLIB_${__prop}) if(G4TESTLINKLIB_${__prop}) # Filter list for internal static targets # NB: This only works assuming that the input target is an executable # If we introduce test libraries, would need same treatment as for main libraries if(BUILD_STATIC_LIBS AND NOT BUILD_SHARED_LIBS) set(_g4linklibs ) foreach(_linklib ${G4TESTLINKLIB_${__prop}}) # If the linklib is a G4Library, change name to "name-static" list(FIND __g4definedlibraries ${_linklib} _isg4lib) if(_isg4lib GREATER -1) list(APPEND _g4linklibs "${_linklib}-static") else() list(APPEND _g4linklibs "${_linklib}") endif() endforeach() set(_linklibs ${_g4linklibs}) else() set(_linklibs ${G4TESTLINKLIB_${__prop}}) endif() target_link_libraries(${_target} ${__prop} ${_linklibs}) endif() endforeach() endfunction() #----------------------------------------------------------------------- #----------------------------------------------------------------------- # Composition Functions #.rst: # .. cmake:command:: geant4_compose_targets # # .. code-block:: cmake # # geant4_compose_targets() # # Create physical SHARED/STATIC library targets from the defined Geant4 # categories and modules. # # Can only be called once, and must be done so after all Geant4 libraries # and modules are defined. # function(geant4_compose_targets) get_property(__alreadyCalled GLOBAL PROPERTY GEANT4_COMPOSE_TARGETS_CALLED) if(__alreadyCalled) get_property(__callsite GLOBAL PROPERTY GEANT4_COMPOSE_TARGETS_LIST_FILE) message(FATAL_ERROR "geant4_compose_targets already called from ${__callsite}") endif() # Check that every defined module is composed geant4_get_modules(__g4definedmodules) foreach(__module ${__g4definedmodules}) geant4_get_module_property(__iscomposed ${__module} PARENT_TARGET) if(NOT __iscomposed) message(FATAL_ERROR "Geant4 module '${__module}' is not composed into any category") endif() endforeach() # - For each module # 1. Write out files for # 1.1. module -> used modules adjacency list for detecting module-module cycles file(WRITE "${PROJECT_BINARY_DIR}/G4ModuleAdjacencyList.txt" "# Geant4 Module - Module Adjacencies\n") # 1.2. module,location,public headers for developer query operations # Basically need a dict of module : [headers, ...] # - Two main queries to run # - Given a module, what public headers does it provide? # - Given a header, what module provides this? # Also want something to check a module # - Only really possible by parsing module headers (what it exposes publically) # and sources (what it uses internally) # Probaly just needs module : location # - Checking of files is runtime operation, so will refind/check each time # - Store set of build settings relevant to filtering headers in dependency checks # - "Dumb" as simply reproduced as CMake list in last column of every row (de-dupped in script) # - Settings are designed to overcome limitation of ifdef in parsing, so map # to symbols in code set(__gmc_build_settings ) if(GEANT4_BUILD_MULTITHREADED) list(APPEND __gmc_build_settings "G4MULTITHREADED") endif() file(WRITE "${PROJECT_BINARY_DIR}/G4ModuleInterfaceMap.csv" "") configure_file("${PROJECT_SOURCE_DIR}/cmake/Modules/geant4_module_check.py" "${PROJECT_BINARY_DIR}/geant4_module_check.py" COPYONLY) # 2. Check it does not link to a composed library composed of N>1 modules get_property(__g4definedlibraries GLOBAL PROPERTY GEANT4_DEFINED_CATEGORIES) set(__g4disallowedlinks ${__g4definedlibraries}) list(REMOVE_ITEM __g4disallowedlinks ${__g4definedmodules}) foreach(__module ${__g4definedmodules}) geant4_get_module_property(__publicdeps ${__module} PUBLIC_LINK_LIBRARIES) geant4_get_module_property(__privatedeps ${__module} PRIVATE_LINK_LIBRARIES) geant4_get_module_property(__interfacedeps ${__module} INTERFACE_LINK_LIBRARIES) # 1.1 Adjacency list - take all dependencies set(__alldeps_l ${__publicdeps} ${__privatedeps} ${__interfacedeps}) list(JOIN __alldeps_l " " __alldeps) file(APPEND "${PROJECT_BINARY_DIR}/G4ModuleAdjacencyList.txt" "${__module} ${__alldeps}\n") # 1.2 Module interfaces, needs CMAKE, PUBLIC_HEADER geant4_get_module_property(__listfile ${__module} CMAKE_LIST_FILE) geant4_get_module_property(__publichdrs ${__module} PUBLIC_HEADERS) geant4_get_module_property(__privatehdrs ${__module} PRIVATE_HEADERS) geant4_get_module_property(__srcs ${__module} SOURCES) geant4_get_module_property(__parent_target ${__module} PARENT_TARGET) get_filename_component(__listdir "${__listfile}" DIRECTORY) # Remove generated headers list(TRANSFORM __publichdrs REPLACE "^${PROJECT_BINARY_DIR}/.*$" "") list(TRANSFORM __publichdrs REPLACE "^/.*/" "") list(TRANSFORM __privatehdrs REPLACE "^/.*/" "") list(TRANSFORM __srcs REPLACE "^/.*/" "") file(APPEND "${PROJECT_BINARY_DIR}/G4ModuleInterfaceMap.csv" "${__module},${__listdir},${__publichdrs},${__privatehdrs},${__srcs},${__publicdeps},${__privatedeps},${__interfacedeps},${__parent_target},${__gmc_build_settings}\n") # 2. Check for disallowed links in each link type foreach(__linktype IN ITEMS public private interface) foreach(__link ${__${__linktype}deps}) if(__link IN_LIST __g4disallowedlinks) geant4_get_category_property(__linktothese ${__link} MODULES) string(REPLACE "${PROJECT_SOURCE_DIR}/" "" __badlistfile ${__listfile}) message(FATAL_ERROR "Geant4 module '${__module}' has a ${__linktype} link to composed category '${__link}'." "It must link to one or more of its component modules instead:\n" "${__linktothese}\n" "in ${__badlistfile}\n") endif() endforeach() endforeach() endforeach() # Process all defined libraries set(__g4builtlibraries) set(__g4public_headers) foreach(__g4lib ${__g4definedlibraries}) if(BUILD_SHARED_LIBS) __geant4_add_library(${__g4lib} SHARED) list(APPEND __g4builtlibraries ${__g4lib}) endif() if(BUILD_STATIC_LIBS) __geant4_add_library(${__g4lib} STATIC) list(APPEND __g4builtlibraries ${__g4lib}-static) endif() geant4_get_category_property(__headers ${__g4lib} PUBLIC_HEADERS) list(APPEND __g4public_headers ${__headers}) endforeach() #----------------------------------------------------------------------- # TEMP INSTALL - do here purely to review exported links. Should be # factored out into separate function later. install(TARGETS ${__g4builtlibraries} EXPORT Geant4LibraryDepends ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT Development LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT Runtime RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT Runtime INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}") install(FILES ${__g4public_headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}" COMPONENT Development) set_property(GLOBAL PROPERTY GEANT4_COMPOSE_TARGETS_CALLED ON) set_property(GLOBAL PROPERTY GEANT4_COMPOSE_TARGETS_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}") endfunction() #----------------------------------------------------------------------- #----------------------------------------------------------------------- #.rst: # Internal Helper Commands # ^^^^^^^^^^^^^^^^^^^^^^^^ # # These macros and functions are for use in the implementation of the # module and library functions. They should never be used directly # in developer-level scripts. #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_assert_no_unparsed_arguments # # .. code-block:: cmake # # __geant4_assert_no_unparsed_arguments( ) # # Emit a ``FATAL_ERROR`` if ``_UNPARSED_ARGUMENTS`` is non-empty # in ```` # # This is a macro intended for use in G4DeveloperAPI functions that cannot # have unparsed arguments to validate their input. # # From CMake 3.17, the ```` argument is no longer required and # ``CMAKE_CURRENT_FUNCTION`` can be used # macro(__geant4_assert_no_unparsed_arguments _prefix _function) if(${_prefix}_UNPARSED_ARGUMENTS) message(FATAL_ERROR "${_function} called with unparsed arguments: '${${_prefix}_UNPARSED_ARGUMENTS}'") endif() endmacro() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_module_assert_exists # # .. code-block:: cmake # # __geant4_module_assert_exists() # # Emit a ``FATAL_ERROR`` if the module ```` is not defined. # # This is a macro intended for use in G4DeveloperAPI functions when # the existence of a module is required for further processing # macro(__geant4_module_assert_exists _module) geant4_has_module(__geant4_module_assert_exists_tmp ${_module}) if(NOT __geant4_module_assert_exists_tmp) message(FATAL_ERROR "Geant4 module '${_module}' has not been created") endif() endmacro() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_module_assert_not_exists # # .. code-block:: cmake # # __geant4_module_assert_not_exists() # # Emit a ``FATAL_ERROR`` if the module ```` is defined # # This is a macro intended for use in G4DeveloperAPI functions when # the non-existence of a module is required for further processing # macro(__geant4_module_assert_not_exists _module) geant4_has_module(__geant4_module_assert_not_exists_tmp ${_module}) if(__geant4_module_assert_not_exists_tmp) geant4_get_module_property(__previous_cmake_list ${_module} CMAKE_LIST_FILE) message(FATAL_ERROR "Geant4 module '${_module}' has already been created by call in '${__previous_cmake_list}'") endif() endmacro() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_module_validate_property # # .. code-block:: cmake # # __geant4_module_validate_property() # # Emit a ``FATAL_ERROR`` if the ```` is not one of the valid # properties for a module: # # This is used internally by the property get/set functions. # function(__geant4_module_validate_property _property) if(NOT (${_property} MATCHES "PUBLIC_HEADERS|PRIVATE_HEADERS|SOURCES|PRIVATE_COMPILE_DEFINITIONS|PUBLIC_COMPILE_DEFINITIONS|INTERFACE_COMPILE_DEFINITIONS|PRIVATE_INCLUDE_DIRECTORIES|PUBLIC_INCLUDE_DIRECTORIES|INTERFACE_INCLUDE_DIRECTORIES|PRIVATE_LINK_LIBRARIES|PUBLIC_LINK_LIBRARIES|INTERFACE_LINK_LIBRARIES|PARENT_TARGET|CMAKE_LIST_FILE|GLOBAL_DEPENDENCIES|IS_INTERFACE|AUTOMOC")) message(FATAL_ERROR "Undefined property '${_property}'") endif() endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_category_assert_exists # # .. code-block:: cmake # # __geant4_category_assert_exists() # # Emit a ``FATAL_ERROR`` if the category ```` is not defined. # # This is a macro intended for use in G4DeveloperAPI functions when # the existence of a category is required for further processing # macro(__geant4_category_assert_exists _category) geant4_has_category(__geant4_category_assert_exists_tmp ${_category}) if(NOT __geant4_category_assert_exists_tmp) message(FATAL_ERROR "Geant4 category '${_category}' has not been created") endif() endmacro() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_category_assert_not_exists # # .. code-block:: cmake # # __geant4_module_category_not_exists() # # Emit a ``FATAL_ERROR`` if the category ```` is defined # # This is a macro intended for use in G4DeveloperAPI functions when # the non-existence of a category is required for further processing # macro(__geant4_category_assert_not_exists _category) geant4_has_category(__geant4_category_assert_not_exists_tmp ${_category}) if(__geant4_category_assert_not_exists_tmp) geant4_get_category_property(__previous_cmake_list ${_category} CMAKE_LIST_FILE) message(FATAL_ERROR "Geant4 category '${_category}' has already been created by call in '${__previous_cmake_list}'") endif() endmacro() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_category_validate_property # # .. code-block:: cmake # # __geant4_category_validate_property() # # Emit a ``FATAL_ERROR`` if the ```` is not one of the valid # properties for a category. # # This is used internally by the property get/set functions. # function(__geant4_category_validate_property _property) if(NOT (${_property} MATCHES "CMAKE_LIST_FILE|IS_INTERFACE|MODULES|PUBLIC_HEADERS")) message(FATAL_ERROR "Undefined property '${_property}'") endif() endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_category_reset # # .. code-block:: cmake # # __geant4_category_reset() # # Reset all existing categories to allow redefinition. # function(__geant4_category_reset) # Reset parent targets of modules to allow recomposition geant4_get_modules(__all_modules) foreach(__mod ${__all_modules}) geant4_set_module_property(${__mod} PROPERTY PARENT_TARGET) endforeach() # Reset category properties, then categories geant4_get_categories(__all_categories) foreach(__cat ${__all_categories}) foreach(_prop CMAKE_LIST_FILE IS_INTERFACE MODULES PUBLIC_HEADERS) geant4_set_category_property(${__cat} PROPERTY ${_prop}) endforeach() endforeach() set_property(GLOBAL PROPERTY GEANT4_DEFINED_CATEGORIES) endfunction() #----------------------------------------------------------------------- #----------------------------------------------------------------------- # Library Build functions # # Resolve a list of links # These may include Geant4 modules as well as standard targets or other expressions # Resolve Modules to PARENT_TARGET, removing duplicates # Leave other links unchanged function(__geant4_resolve_link_libraries _list) set(_resolved_list ) foreach(__lib ${${_list}}) # If "library" is a module, resolve it to PARENT_TARGET geant4_has_module(__is_module ${__lib}) if(__is_module) geant4_get_module_property(__parent_lib ${__lib} PARENT_TARGET) if(NOT __parent_lib) message(FATAL_ERROR "Module '${__lib}' has no PARENT_TARGET set") endif() list(APPEND _resolved_list ${__parent_lib}) else() list(APPEND _resolved_list ${__lib}) endif() endforeach() if(_resolved_list) list(REMOVE_DUPLICATES _resolved_list) endif() set(${_list} ${_resolved_list} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- #.rst: # .. cmake:command:: __geant4_add_library # # .. code-block:: cmake # # __geant4_add_library( ) # # Declare an actual CMake library target of type ```` from a category # ````. The category must have been declared previously, and # ```` must be ``SHARED`` or ``STATIC``. # In the special case that the category is header-only, ```` is only # used to determine the name of the created target: ```` for # ``SHARED``, and ``-static`` otherwise. This ensures consistent # link chains of only shared or only static Geant4 libraries. # # This function is used internally by the ``geant4_compose_targets`` command # and should not be called directly in developer build scripts. # function(__geant4_add_library _name _type) if(NOT (${_type} MATCHES "SHARED|STATIC")) message(FATAL_ERROR "Invalid library type '${_type}'") endif() # Check if the overall library is binary or header-only set(_lib_cmake_type ${_type}) geant4_get_category_property(_lib_is_interface ${_name} IS_INTERFACE) if(_lib_is_interface) set(_lib_cmake_type "INTERFACE") endif() set(_target_name ${_name}) if(_type STREQUAL "STATIC") set(_target_name ${_name}-static) endif() # - General target creation/properties add_library(${_target_name} ${_lib_cmake_type} "") # Alias for transparent use with imported targets add_library(Geant4::${_target_name} ALIAS ${_target_name}) if(_lib_cmake_type STREQUAL "INTERFACE") target_compile_features(${_target_name} INTERFACE ${GEANT4_TARGET_COMPILE_FEATURES}) set(_props_to_process "INTERFACE") else() target_compile_features(${_target_name} PUBLIC ${GEANT4_TARGET_COMPILE_FEATURES}) set(_props_to_process "PUBLIC" "PRIVATE" "INTERFACE") set(_promote_interface_to_public TRUE) endif() if(_lib_cmake_type STREQUAL "SHARED") # G4LIB_BUILD_DLL is public as despite the name it indicates the shared/archive mode # and clients must apply it when linking to the shared libs. The global # category handles the exact import/export statements target_compile_definitions(${_target_name} PUBLIC G4LIB_BUILD_DLL) set_target_properties(${_target_name} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) # MacOS # Use '@rpath' in install names of libraries on macOS to provide relocatibility # Add '@loader_path' to INSTALL_RPATH on macOS so that Geant4 # libraries self-locate each other whilst remaining relocatable set_target_properties(${_target_name} PROPERTIES MACOSX_RPATH 1) if(APPLE) set_property(TARGET ${_target_name} APPEND PROPERTY INSTALL_RPATH "@loader_path") endif() endif() if((_lib_cmake_type STREQUAL "STATIC") AND NOT WIN32) set_target_properties(${_target_name} PROPERTIES OUTPUT_NAME ${_name}) endif() # Get Modules to build into library, and list of defined interna/external libraries to resolve/link against geant4_get_categories(__g4definedlibraries) geant4_get_external_categories(__g4externallibraries) list(APPEND __g4definedlibraries ${__g4externallibraries}) geant4_get_category_property(__g4modules ${_name} MODULES) foreach(__g4mod ${__g4modules}) # - Process sources... geant4_get_module_property(_headers ${__g4mod} PUBLIC_HEADERS) geant4_get_module_property(_headers_private ${__g4mod} PRIVATE_HEADERS) geant4_get_module_property(_srcs ${__g4mod} SOURCES) geant4_get_module_property(_cmakescript ${__g4mod} CMAKE_LIST_FILE) get_filename_component(_moddir "${_cmakescript}" DIRECTORY) # - Group sources and scripts for IDEs # NB: Seemingly has to be done at same level we define target. # TODO: If lib name is same as mod name, don't group avoid extra # folder layer. source_group(${__g4mod}\\Headers FILES ${_headers} ${_headers_private}) source_group(${__g4mod}\\Sources FILES ${_srcs}) source_group(${__g4mod} FILES ${_cmakescript}) # - Add sources to target - PRIVATE, because consuming targets don't need them target_sources(${_target_name} PRIVATE ${_headers} ${_headers_private} ${_srcs} ${_cmakescript}) # - Process usage properties # Include dirs, compile definitions and libraries can be handled together # Important to note that these promote PUBLIC/PRIVATE/INTERFACE # from module level to library level. I.e. other modules in the # library can see each other's PRIVATE include paths/compile defs. # The only known way to fully wall things off is OBJECT libs, # but then run into issues mentioned at start - linking and # use in IDEs (though newer CMake versions should resolve these) # This "promotion" is probably correct though - interfaces are # at physical library level rather than module, and in Geant4 # all files must have globally unique names (no nested headers # nor namespaces). Also, DLL export symbols may need this # behaviour (esp. ALLOC_EXPORT). # Only really an issue if header names/definitions aren't # globally (in Geant4) unique. Or if a module is moved and hasn't # declared its deps correctly (but then an error will occur # anyway, and point is that libs are linked, not modules!) # "Module" level really means "Source file" level, so same # sets of rules should apply. # Can use set_source_files_properties for module-level PRIVATE_HEADERS, # but that's it set_property(SOURCE ${_srcs} APPEND PROPERTY INCLUDE_DIRECTORIES "${_moddir}/include/private") foreach(_prop ${_props_to_process}) # Further gotcha with INTERFACE modules here # - If an interface module is composed into a non-interface module # its headers are exposed correctly to *clients* of the library, but # *not* the internals of the library! This being the case, we promote # these properties to PUBLIC set(_target_prop ${_prop}) if(_promote_interface_to_public AND (_prop STREQUAL "INTERFACE")) set(_target_prop "PUBLIC") endif() geant4_get_module_property(_incdirs ${__g4mod} ${_prop}_INCLUDE_DIRECTORIES) target_include_directories(${_target_name} ${_target_prop} ${_incdirs}) geant4_get_module_property(_defs ${__g4mod} ${_prop}_COMPILE_DEFINITIONS) target_compile_definitions(${_target_name} ${_target_prop} ${_defs}) # Target linking requires additional processing to resolve geant4_get_module_property(_linklibs ${__g4mod} ${_prop}_LINK_LIBRARIES) __geant4_resolve_link_libraries(_linklibs) if(_linklibs) # Remove self-linking list(REMOVE_ITEM _linklibs ${_name}) # Filter list for internal static targets if(_lib_cmake_type STREQUAL "STATIC") set(_g4linklibs ) foreach(_linklib ${_linklibs}) # If the linklib is a G4Library, change name to "name-static" list(FIND __g4definedlibraries ${_linklib} _isg4lib) if(_isg4lib GREATER -1) list(APPEND _g4linklibs "${_linklib}-static") else() list(APPEND _g4linklibs "${_linklib}") endif() endforeach() set(_linklibs ${_g4linklibs}) endif() target_link_libraries(${_target_name} ${_target_prop} ${_linklibs}) endif() endforeach() # Apply any additional properties supported in modules to target if(GEANT4_USE_QT) geant4_get_module_property(_needs_moc ${__g4mod} AUTOMOC) if(_needs_moc) set_target_properties(${_target_name} PROPERTIES AUTOMOC ON) endif() endif() endforeach() # - Postprocess target properties to remove duplicates # NB: This makes the assumption that there is no order dependence here (and any is considered a bug!) # CMake will handle static link ordering internally foreach(_link_prop IN ITEMS LINK_LIBRARIES INTERFACE_LINK_LIBRARIES INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES COMPILE_DEFINITIONS INTERFACE_COMPILE_DEFINITIONS) get_target_property(__g4lib_link_libs ${_target_name} ${_link_prop}) if(__g4lib_link_libs) list(SORT __g4lib_link_libs) list(REMOVE_DUPLICATES __g4lib_link_libs) set_property(TARGET ${_target_name} PROPERTY ${_link_prop} ${__g4lib_link_libs}) endif() endforeach() endfunction()