# - Configure and install Geant4 Data Libraries # The following functions and macros are defined to help define, install, # reuse and export datasets and associated variables. # # FUNCTIONS AND MACROS # ==================== # Public API # ---------- # function geant4_get_datasetnames() # Store list of currently known dataset names in output variable # # function geant4_tupleize_datasets() # Set output variable to list of old-style dataset tuples. # A tuple has the format: # NAME/VERSION/FILENAME/EXTENSION/ENVVAR/MD5SUM # Provided for backward compatibility. # # function geant4_export_datasets([BUILD|INSTALL] ) # Set output variable to list of dataset tuples for export to # configuration scripts # A tuple has the format: # NAME/ENVVAR/PATH/FILENAME/MD5SUM # BUILD will set the PATH entry to the path to the dataset used # for the build of Geant4. # # INSTALL will set the PATH entry to the path to the dataset used # by an install of Geant4. # # function geant4_add_dataset(NAME # VERSION # FILENAME # EXTENSION # ENVVAR # MD5SUM ) # Define a new dataset with the following properties # NAME common name of the dataset # VERSION dataset version # FILENAME name of packaged dataset file # EXTENSION extension of packaged dataset file # ENVVAR environment variable used by Geant4 code # to locate this dataset # MD5SUM MD5 hash of packaged dataset file # # function geant4_has_dataset( ) # Set output variable to TRUE if the named dataset exists # in the list of known datasets. # # function geant4_get_dataset_property( ) # Set output variable to value of dataset "name"'s property. # If property does not exist, then value will be blank. # # function geant4_set_dataset_property( ) # Set value of dataset property to supplied value # # function geant4_configure_datasets(DESTINATION # DOWNLOAD # TIMEOUT # ) # Perform the actual heavy lifting to configure each declared # dataset for reuse or download as needed. # # function geant4_dataset_isinstalled( # # ) # Check if named dataset is installed under the root directory. # Set output variable to TRUE if it is, FALSE otherwise. # # function geant4_install_dataset( ) # Download dataset with name to build directory, timing out the # download after timeout seconds, and install it # into its directory under the destination directory. # # function geant4_reuse_dataset( ) # Reuse the dataset with name located at destination directory. # If it is not installed, warn user that it will need installing # manually in destination. # #----------------------------------------------------------------------- # GEANT4 PHYSICS DATA - GLOBAL CMAKE VARIABLES #----------------------------------------------------------------------- # URLs, directories and dataset entries # We may want these as properties so we can have a small API for # retrieving them globally #----------------------------------------------------------------------- # Geant4 Data Repository set(GEANT4_DATASETS_URL "https://cern.ch/geant4-data/datasets") # Where to install data in the build tree set(GEANT4_BUILD_FULL_DATADIR ${PROJECT_BINARY_DIR}/data) # Where to install data in the install tree (a Default) set(GEANT4_INSTALL_DATADIR_DEFAULT "${CMAKE_INSTALL_DATADIR}/data") # File containing dataset list set(GEANT4_DATASETS_DEFINITIONS "G4DatasetDefinitions") #----------------------------------------------------------------------- # GEANT4 PHYSICS DATA - PUBLIC CMAKE API FOR DATASET HANDLING #----------------------------------------------------------------------- # Properties? Shouldn't clash with the tuplized variable... define_property(GLOBAL PROPERTY "GEANT4_DATASETS" BRIEF_DOCS "List of all defined Geant4 dataset names" FULL_DOCS "Each element of the list gives the name defined for the dataset. This name can be used in other Geant4 Data API functions to extract other properties of the dataset" ) #----------------------------------------------------------------------- # function geant4_get_datasetnames() # Store list of currently known dataset names in output variable # function(geant4_get_datasetnames _output) get_property(_tmp GLOBAL PROPERTY GEANT4_DATASETS) set(${_output} ${_tmp} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- # function geant4_tupleize_datasets() # Set output variable to list of old-style dataset tuples. # A tuple has the format: # NAME/VERSION/FILENAME/EXTENSION/ENVVAR/MD5SUM # Provided for backward compatibility. # function(geant4_tupleize_datasets _output) geant4_get_datasetnames(_names) set(_tmplist) foreach(_ds ${_names}) set(_tuple ${_ds}) foreach(_p VERSION FILENAME EXTENSION ENVVAR MD5SUM) get_property(_tmpprop GLOBAL PROPERTY ${_ds}_${_p}) list(APPEND _tuple ${_tmpprop}) endforeach() string(REPLACE ";" "/" _tuple "${_tuple}") list(APPEND _tmplist "${_tuple}") endforeach() set(${_output} ${_tmplist} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- # function geant4_export_datasets([BUILD|INSTALL] ) # Set output variable to list of dataset tuples for export to # Geant4Config.cmake # A tuple has the format: # NAME/ENVVAR/PATH/FILENAME/MD5SUM # BUILD will set the PATH entry to the path to the dataset used # for the build of Geant4. # # INSTALL will set the PATH entry to the path to the dataset used # by an install of Geant4. # # function(geant4_export_datasets _type _output) geant4_get_datasetnames(_names) set(_tmplist) foreach(_ds ${_names}) set(_tuple ${_ds}) get_property(_tmpprop GLOBAL PROPERTY ${_ds}_ENVVAR) list(APPEND _tuple ${_tmpprop}) if(${_type} STREQUAL "BUILD") get_property(_tmpprop GLOBAL PROPERTY ${_ds}_BUILD_DIR) elseif(${_type} STREQUAL "INSTALL") get_property(_tmpprop GLOBAL PROPERTY ${_ds}_INSTALL_DIR) else() message(FATAL_ERROR "incorrect argument to geant4_export_datasets") endif() # Ensure CMake paths file(TO_CMAKE_PATH "${_tmpprop}" _tmpprop) list(APPEND _tuple ${_tmpprop}) get_property(_fname GLOBAL PROPERTY ${_ds}_FILENAME) get_property(_fvers GLOBAL PROPERTY ${_ds}_VERSION) get_property(_fextn GLOBAL PROPERTY ${_ds}_EXTENSION) list(APPEND _tuple "${_fname}.${_fvers}.${_fextn}") get_property(_tmpprop GLOBAL PROPERTY ${_ds}_MD5SUM) list(APPEND _tuple ${_tmpprop}) # Because we have paths, use tuple separator that should not # appear in a path. string(REPLACE ";" "|" _tuple "${_tuple}") list(APPEND _tmplist "${_tuple}") endforeach() set(${_output} ${_tmplist} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- # function geant4_add_dataset(NAME # VERSION # FILENAME # EXTENSION # ENVVAR # MD5SUM ) # Define a new dataset with the following properties # NAME common name of the dataset # VERSION dataset version # FILENAME name of packaged dataset file # EXTENSION extension of packaged dataset file # ENVVAR environment variable used by Geant4 code # to locate this dataset # MD5SUM MD5 hash of packaged dataset file # function(geant4_add_dataset) # - Parse arguments and create variables set(oneValueArgs NAME VERSION FILENAME EXTENSION ENVVAR MD5SUM) cmake_parse_arguments(_G4ADDDATA "" "${oneValueArgs}" "" ${ARGN}) # - Fail if already defined geant4_has_dataset(${_G4ADDDATA_NAME} _dsexists) if(_dsexists) message(FATAL_ERROR "Dataset ${_G4ADDDATA_NAME} is already defined") endif() # - Set properties as global props _ # Simple properties... foreach(_prop VERSION FILENAME EXTENSION ENVVAR MD5SUM) set_property(GLOBAL PROPERTY ${_G4ADDDATA_NAME}_${_prop} ${_G4ADDDATA_${_prop}}) endforeach() # Derived properties... # FILE : the full filename of the packed dataset set_property(GLOBAL PROPERTY ${_G4ADDDATA_NAME}_FILE "${_G4ADDDATA_FILENAME}.${_G4ADDDATA_VERSION}.${_G4ADDDATA_EXTENSION}" ) # DIRECTORY : the name of the directory that results from unpacking # the packed dataset file. set_property(GLOBAL PROPERTY ${_G4ADDDATA_NAME}_DIRECTORY "${_G4ADDDATA_NAME}${_G4ADDDATA_VERSION}" ) # URL : remote and full URL to the packed dataset file set_property(GLOBAL PROPERTY ${_G4ADDDATA_NAME}_URL "${GEANT4_DATASETS_URL}/${_G4ADDDATA_FILENAME}.${_G4ADDDATA_VERSION}.${_G4ADDDATA_EXTENSION}" ) # - add it to the list of defined datasets set_property(GLOBAL APPEND PROPERTY GEANT4_DATASETS ${_G4ADDDATA_NAME}) endfunction() #----------------------------------------------------------------------- # function geant4_has_dataset( ) # Set output variable to TRUE if the named dataset exists # in the list of known datasets. # function(geant4_has_dataset _name _output) get_property(_dslist GLOBAL PROPERTY GEANT4_DATASETS) list(FIND _dslist ${_name} _index) if(_index GREATER -1) set(${_output} TRUE PARENT_SCOPE) else() set(${_output} FALSE PARENT_SCOPE) endif() endfunction() #----------------------------------------------------------------------- # function geant4_get_dataset_property( ) # Set output variable to value of dataset "name"'s property. # If property does not exist, then value will be blank. # function(geant4_get_dataset_property _name _prop _output) geant4_has_dataset(${_name} _dsexists) if(NOT _dsexists) message(FATAL_ERROR "non-existent dataset ${_name}") endif() get_property(_tmp GLOBAL PROPERTY ${_name}_${_prop}) set(${_output} ${_tmp} PARENT_SCOPE) endfunction() #----------------------------------------------------------------------- # function geant4_set_dataset_property( ) # Set value of dataset property to supplied value # function(geant4_set_dataset_property _name _prop _value) geant4_has_dataset(${_name} _dsexists) if(NOT _dsexists) message(FATAL_ERROR "non-existent dataset ${_name}") endif() set_property(GLOBAL PROPERTY ${_name}_${_prop} "${_value}") endfunction() #----------------------------------------------------------------------- # function geant4_configure_datasets(DESTINATION # DOWNLOAD # TIMEOUT # ) # Perform the actual heavy lifting to configure each declared # dataset for reuse or download as needed. # function(geant4_configure_datasets) # - Parse arguments and create variables set(oneValueArgs DESTINATION DOWNLOAD TIMEOUT) cmake_parse_arguments(_G4CFGDSS "" "${oneValueArgs}" "" ${ARGN}) # - Load configuration include(${GEANT4_DATASETS_DEFINITIONS}) geant4_get_datasetnames(_dsnames) set(_notinstalled ) foreach(_ds ${_dsnames}) geant4_dataset_isinstalled(${_ds} "${_G4CFGDSS_DESTINATION}" _installed) if(NOT _installed AND _G4CFGDSS_DOWNLOAD) geant4_install_dataset(${_ds} "${_G4CFGDSS_DESTINATION}" ${_G4CFGDSS_TIMEOUT}) else() geant4_reuse_dataset(${_ds} "${_G4CFGDSS_DESTINATION}" ${_installed}) if(NOT _installed) list(APPEND _notinstalled ${_ds}) endif() endif() endforeach() # - Produce report on datasets needing manual install, advising # user on how to handle these. # Yes, it's long, but at least it's clear :-) if(_notinstalled) message(" *WARNING*") message(" Geant4 has been pre-configured to look for datasets") message(" in the directory:") message(" ") message(" ${_G4CFGDSS_DESTINATION}") message(" ") message(" but the following datasets are NOT present on disk at") message(" that location:") message(" ") foreach(_missing ${_notinstalled}) geant4_get_dataset_property(${_missing} VERSION _vers) message(" ${_missing} (${_vers})") endforeach() message(" ") message(" - If you have access to cvmfs, you can use standard datasets") message(" by reconfiguring with:") message(" cmake -DGEANT4_INSTALL_DATADIR=/cvmfs/geant4.cern.ch/share/data <...>") message(" The variable can also be set in ccmake or cmake-gui.") message(" - If you want to have these datasets installed by Geant4,") message(" simply re-run cmake with GEANT4_INSTALL_DATA=ON. This will") message(" configure the build to download and install these datasets for you.") message(" For example, use:") message(" cmake -DGEANT4_INSTALL_DATA=ON ") message(" ") message(" If you're running on a Windows system, this is the best") message(" solution as CMake will unpack the datasets for you") message(" without any further software being required") message(" ") message(" - Alternatively, you can install these datasets manually") message(" now or after you have installed Geant4. To do this,") message(" download the following files:") message(" ") foreach(_missing ${_notinstalled}) geant4_get_dataset_property(${_missing} URL _url) message(" ${_url}") endforeach() message(" ") message(" and unpack them under the directory:") message(" ") message(" ${_G4CFGDSS_DESTINATION}") message(" ") message(" As we supply the datasets packed in gzipped tar files,") message(" you will need the 'tar' utility to unpack them.") message(" ") message(" Nota bene: Missing datasets will not affect or break") message(" compilation and installation of the Geant4") message(" libraries.") message(" ") endif() endfunction() #----------------------------------------------------------------------- # function geant4_dataset_isinstalled( # # ) # Check if named dataset is installed under the root directory. # Set output variable to TRUE if it is, FALSE otherwise. # function(geant4_dataset_isinstalled _name _rdirectory _output) geant4_get_dataset_property(${_name} DIRECTORY _dsdir) set(_expectedpath ${_rdirectory}/${_dsdir}) if(IS_DIRECTORY ${_expectedpath}) set(${_output} TRUE PARENT_SCOPE) else() set(${_output} FALSE PARENT_SCOPE) endif() endfunction() #----------------------------------------------------------------------- # function geant4_install_dataset( ) # Download dataset with name to build directory, timing out the # download after timeout seconds, and install it # into its directory under the destination directory. # function(geant4_install_dataset _name _destination _timeout) # - Extract needed dataset properties geant4_get_dataset_property(${_name} DIRECTORY _ds_dir) geant4_get_dataset_property(${_name} VERSION _ds_version) geant4_get_dataset_property(${_name} URL _ds_url) geant4_get_dataset_property(${_name} MD5SUM _ds_md5sum) message(STATUS "Configuring download of missing dataset ${_name} (${_ds_version})") # - Dispatch to ExternalProject include(ExternalProject) # - We want to retain timestamps in extracted archives as the data files # are "constants". CMake >= 3.24 changed this behaviour, so we setup # ExternalProject based on running version set(__ep_extract_timestamp_arg ) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) set(__ep_extract_timestamp_arg DOWNLOAD_EXTRACT_TIMESTAMP TRUE) endif() ExternalProject_add(${_name} PREFIX Externals/${_name}-${_ds_version} SOURCE_DIR ${GEANT4_BUILD_FULL_DATADIR}/${_ds_dir} URL ${_ds_url} URL_MD5 ${_ds_md5sum} TIMEOUT ${_timeout} ${__ep_extract_timestamp_arg} CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "") # - Configure the dataset's build and install locations geant4_set_dataset_property(${_name} BUILD_DIR "${PROJECT_BINARY_DIR}/data/${_ds_dir}") geant4_set_dataset_property(${_name} INSTALL_DIR "${_destination}/${_ds_dir}") # - Add install target, and report back paths... install(DIRECTORY ${PROJECT_BINARY_DIR}/data/${_ds_dir} DESTINATION ${_destination} COMPONENT Data ) endfunction() #----------------------------------------------------------------------- # function geant4_reuse_dataset( ) # Reuse the dataset with name located at destination directory. # If it is not installed, warn user that it will need installing # manually in destination. # function(geant4_reuse_dataset _name _destination _ispresent) geant4_get_dataset_property(${_name} VERSION _ds_ver) geant4_get_dataset_property(${_name} DIRECTORY _ds_dir) if(_ispresent) message(STATUS "Reusing dataset ${_name} (${_ds_ver})") else() message(STATUS "Pre-configuring dataset ${_name} (${_ds_ver})") endif() # - In both cases, the build and install dirs are identical geant4_set_dataset_property(${_name} BUILD_DIR "${_destination}/${_ds_dir}") geant4_set_dataset_property(${_name} INSTALL_DIR "${_destination}/${_ds_dir}") endfunction() #----------------------------------------------------------------------- # GEANT4 PHYSICS DATA - USER INTERFACE AND PROCESSING #----------------------------------------------------------------------- # User options for installing data # - Choose a directory under which to install the data. # - Choose whether to download and install missing datasets. # - Change download timeout for problematic connections #----------------------------------------------------------------------- # Choose Physics Data Install Dir # This follows the pattern for interface and setting as in GNUInstallDirs if(NOT GEANT4_INSTALL_DATADIR) set(GEANT4_INSTALL_DATADIR "" CACHE PATH "read-only architecture independent Geant4 physics data (DATADIR/data") set(GEANT4_INSTALL_DATADIR "${GEANT4_INSTALL_DATADIR_DEFAULT}") endif() if(NOT IS_ABSOLUTE ${GEANT4_INSTALL_DATADIR}) set(GEANT4_INSTALL_FULL_DATADIR "${CMAKE_INSTALL_PREFIX}/${GEANT4_INSTALL_DATADIR}") else() set(GEANT4_INSTALL_FULL_DATADIR "${GEANT4_INSTALL_DATADIR}") endif() #----------------------------------------------------------------------- # Select whether to download and install missing datasets option(GEANT4_INSTALL_DATA "Download/Install datasets missing from GEANT4_INSTALL_DATADIR" OFF) #----------------------------------------------------------------------- # Provide an option for increasing the download timeout # Helps with large datasets over slow connections. set(GEANT4_INSTALL_DATA_TIMEOUT 1500 CACHE STRING "Timeout for Data Library downloads") mark_as_advanced(GEANT4_INSTALL_DATA_TIMEOUT) #----------------------------------------------------------------------- # Set up check, download and install of needed data # geant4_configure_datasets( DESTINATION ${GEANT4_INSTALL_FULL_DATADIR} DOWNLOAD ${GEANT4_INSTALL_DATA} TIMEOUT ${GEANT4_INSTALL_DATA_TIMEOUT} )