Import Geant4 11.1.0 source tree
This commit is contained in:
@@ -72,19 +72,12 @@ include(G4CTest)
|
||||
#-----------------------------------------------------------------------
|
||||
# Add the source and environments subdirectories
|
||||
# source : Process all the Geant4 core targets
|
||||
# environments : Process optional wrappings of Geant4 (NOTYETIMPLEMENTED)
|
||||
add_subdirectory(source)
|
||||
|
||||
option(GEANT4_USE_PYTHON "Build Python bindings for Geant4" OFF)
|
||||
if(GEANT4_USE_PYTHON)
|
||||
# We can only build g4py with MT geant4 if TLS is global-dynamic (or auto?)
|
||||
if(GEANT4_BUILD_MULTITHREADED AND (NOT GEANT4_BUILD_TLS_MODEL MATCHES "global-dynamic"))
|
||||
message(FATAL_ERROR "Geant4Py only supports 'global-dynamic' thread local storage, but
|
||||
'${GEANT4_BUILD_TLS_MODEL}' selected by GEANT4_BUILD_TLS_MODEL option
|
||||
")
|
||||
endif()
|
||||
|
||||
add_subdirectory(environments/g4py)
|
||||
message(WARNING "Geant4Py is no longer distributed with the Geant4 toolkit."
|
||||
" It can be downloaded from:\n https://github.com/koichi-murakami/g4python \n"
|
||||
"and requests for support on installation and use should be directed there.\n")
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
@@ -113,18 +113,26 @@ add_custom_target(validate_sources
|
||||
)
|
||||
|
||||
#.rst:
|
||||
# A custom ``validate_no_module_cycles`` is declared if Python3.9 is available.
|
||||
# This runs the geant4_check_module_cycles Python script to check that the
|
||||
# source module dependency graph has no cycles. It will fail if any cycles
|
||||
# are found, listing the first found to stderr.
|
||||
# Custom ``validate_no_module_cycles`` and ``validate_module_consistency`` targets
|
||||
# are declared if Python3.9 is available.
|
||||
# These runs the geant4_module_check.py Python script to check for cycles or
|
||||
# inconsistencies in the module dependency graph and declarations.
|
||||
find_package(Python3 3.9 QUIET COMPONENTS Interpreter)
|
||||
if(Python3_FOUND)
|
||||
set(G4MODULE_VALIDATION_CMD Python3::Interpreter
|
||||
${PROJECT_SOURCE_DIR}/cmake/Modules/geant4_module_check.py
|
||||
-db ${PROJECT_BINARY_DIR}/G4ModuleInterfaceMap.csv)
|
||||
|
||||
add_custom_target(validate_no_module_cycles
|
||||
COMMAND Python3::Interpreter
|
||||
${PROJECT_SOURCE_DIR}/cmake/Modules/geant4_check_module_cycles.py
|
||||
-f ${PROJECT_BINARY_DIR}/G4ModuleAdjacencyList.txt
|
||||
COMMAND ${G4MODULE_VALIDATION_CMD} --find-cycles
|
||||
COMMENT "Checking for cycles in declared source module dependencies"
|
||||
)
|
||||
|
||||
add_custom_target(validate_module_consistency
|
||||
COMMAND ${G4MODULE_VALIDATION_CMD} --find-inconsistencies
|
||||
COMMENT "Checking for inconsistencies in declared source module dependencies"
|
||||
USES_TERMINAL
|
||||
)
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
@@ -214,16 +214,13 @@ if(NOT GEANT4_BUILD_GRANULAR_LIBS)
|
||||
# - USolids
|
||||
if(GEANT4_USE_USOLIDS OR GEANT4_USE_PARTIAL_USOLIDS)
|
||||
set(G4_BUILTWITH_USOLIDS "yes")
|
||||
set(G4_USOLIDS_INCLUDE_DIRS "${USOLIDS_INCLUDE_DIRS} ${VECGEOM_EXTERNAL_INCLUDES}")
|
||||
get_target_property(G4_USOLIDS_INCLUDE_DIRS VecGeom::vecgeom INTERFACE_INCLUDE_DIRECTORIES)
|
||||
list(REMOVE_DUPLICATES G4_USOLIDS_INCLUDE_DIRS)
|
||||
if(_cxx_compiler_dirs)
|
||||
list(REMOVE_ITEM G4_USOLIDS_INCLUDE_DIRS ${_cxx_compiler_dirs})
|
||||
endif()
|
||||
|
||||
string(REPLACE ";" " " G4_USOLIDS_CFLAGS "${VECGEOM_DEFINITIONS}")
|
||||
foreach(_dir ${G4_USOLIDS_INCLUDE_DIRS})
|
||||
set(G4_USOLIDS_CFLAGS "${G4_USOLIDS_CFLAGS} -I${_dir}")
|
||||
endforeach()
|
||||
# NB: should ALSO account for VecGeom having compile_options, but
|
||||
# this is better handled through proper pkg-config support
|
||||
else()
|
||||
set(G4_BUILTWITH_USOLIDS "no")
|
||||
endif()
|
||||
@@ -245,7 +242,15 @@ if(NOT GEANT4_BUILD_GRANULAR_LIBS)
|
||||
# - Qt
|
||||
if(GEANT4_USE_QT)
|
||||
set(G4_BUILTWITH_QT "yes")
|
||||
set(G4_QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS} ${Qt5PrintSupport_INCLUDE_DIRS} ${Qt53DCore_INCLUDE_DIRS} ${Qt53DExtras_INCLUDE_DIRS} ${Qt53DRender_INCLUDE_DIRS})
|
||||
set(_qtcomps Core Gui Widgets OpenGL PrintSupport)
|
||||
if(QT_VERSION_MAJOR VERSION_GREATER_EQUAL 5.15)
|
||||
list(APPEND _qtcomp 3DCore 3DExtras 3DRender)
|
||||
endif()
|
||||
|
||||
set(G4_QT_INCLUDE_DIRS )
|
||||
foreach(_qtc ${_qtcomps})
|
||||
list(APPEND G4_QT_INCLUDE_DIRS ${Qt${QT_VERSION_MAJOR}${_qtc}_INCLUDE_DIRS})
|
||||
endforeach()
|
||||
|
||||
list(REMOVE_DUPLICATES G4_QT_INCLUDE_DIRS)
|
||||
if(_cxx_compiler_dirs)
|
||||
|
||||
@@ -18,11 +18,11 @@ geant4_add_dataset(
|
||||
# - Low energy electromagnetics
|
||||
geant4_add_dataset(
|
||||
NAME G4EMLOW
|
||||
VERSION 8.1
|
||||
VERSION 8.2
|
||||
FILENAME G4EMLOW
|
||||
EXTENSION tar.gz
|
||||
ENVVAR G4LEDATA
|
||||
MD5SUM e4abc7d2cd73a2f6d19837e9bcce984a
|
||||
MD5SUM 07773e57be3f6f2ebb744da5ed574f6d
|
||||
)
|
||||
|
||||
# - Photon evaporation
|
||||
|
||||
@@ -873,6 +873,14 @@ function(geant4_compose_targets)
|
||||
# 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)
|
||||
|
||||
@@ -888,9 +896,10 @@ function(geant4_compose_targets)
|
||||
# 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(__parent_target ${__module} PARENT_TARGET)
|
||||
get_filename_component(__listdir "${__listfile}" DIRECTORY)
|
||||
list(TRANSFORM __publichdrs REPLACE "^/.*/" "")
|
||||
file(APPEND "${PROJECT_BINARY_DIR}/G4ModuleInterfaceMap.csv" "${__module},${__listdir},${__publichdrs},${__publicdeps},${__privatedeps},${__interfacedeps}\n")
|
||||
file(APPEND "${PROJECT_BINARY_DIR}/G4ModuleInterfaceMap.csv" "${__module},${__listdir},${__publichdrs},${__publicdeps},${__privatedeps},${__interfacedeps},${__parent_target},${__gmc_build_settings}\n")
|
||||
endforeach()
|
||||
|
||||
# Process all defined libraries
|
||||
@@ -1183,6 +1192,7 @@ function(__geant4_add_library _name _type)
|
||||
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")
|
||||
@@ -1249,11 +1259,21 @@ function(__geant4_add_library _name _type)
|
||||
# sets of rules should apply (i.e. can't specify inc dirs
|
||||
# at source level).
|
||||
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} ${_prop} ${_incdirs})
|
||||
target_include_directories(${_target_name} ${_target_prop} ${_incdirs})
|
||||
|
||||
geant4_get_module_property(_defs ${__g4mod} ${_prop}_COMPILE_DEFINITIONS)
|
||||
target_compile_definitions(${_target_name} ${_prop} ${_defs})
|
||||
target_compile_definitions(${_target_name} ${_target_prop} ${_defs})
|
||||
|
||||
# Target linking requires additional processing to resolve
|
||||
geant4_get_module_property(_linklibs ${__g4mod} ${_prop}_LINK_LIBRARIES)
|
||||
@@ -1277,7 +1297,7 @@ function(__geant4_add_library _name _type)
|
||||
set(_linklibs ${_g4linklibs})
|
||||
endif()
|
||||
|
||||
target_link_libraries(${_target_name} ${_prop} ${_linklibs})
|
||||
target_link_libraries(${_target_name} ${_target_prop} ${_linklibs})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
@@ -402,19 +402,28 @@ function(geant4_install_dataset _name _destination _timeout)
|
||||
|
||||
message(STATUS "Configuring download of missing dataset ${_name} (${_ds_version})")
|
||||
|
||||
# - Dispatch to ExternalProject or our own implementation.
|
||||
# Use of URL_MD5 *and* TIMEOUT require CMake 2.8.2 or higher.
|
||||
# - Dispatch to ExternalProject
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(${_name}
|
||||
|
||||
# - 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 ""
|
||||
)
|
||||
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}")
|
||||
|
||||
@@ -106,15 +106,6 @@ if(WIN32)
|
||||
geant4_add_feature(GEANT4_USE_OPENGL_WIN32 "Build OpenGL driver with Win32 support")
|
||||
endif()
|
||||
|
||||
# Overall ToolsSG enable flag
|
||||
set(GEANT4_USE_TOOLSSG OFF)
|
||||
if(GEANT4_USE_TOOLSSG_QT_GLES
|
||||
OR GEANT4_USE_TOOLSSG_XT_GLES
|
||||
OR GEANT4_USE_TOOLSSG_X11_GLES
|
||||
OR GEANT4_USE_TOOLSSG_WINDOWS_GLES)
|
||||
set(GEANT4_USE_TOOLSSG ON)
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Find dependencies
|
||||
#
|
||||
@@ -147,14 +138,9 @@ if(GEANT4_USE_QT)
|
||||
# Use versioned targets to support Qt5 < 5.15
|
||||
# Once 5.15 is the minimum version, the "Qt${QT_VERSION_MAJOR}_..." variables can be dropped
|
||||
# - https://doc.qt.io/qt-6/cmake-manual.html
|
||||
find_package(QT NAMES Qt5 Qt6 COMPONENTS Core REQUIRED)
|
||||
find_package(QT NAMES Qt5 COMPONENTS Core REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Widgets OpenGL PrintSupport REQUIRED)
|
||||
|
||||
if(QT_VERSION_MAJOR VERSION_LESS 6)
|
||||
get_target_property(QT_QMAKE_EXECUTABLE ${Qt${QT_VERSION_MAJOR}Core_QMAKE_EXECUTABLE} IMPORTED_LOCATION)
|
||||
else()
|
||||
get_target_property(QT_QMAKE_EXECUTABLE Qt6::qmake IMPORTED_LOCATION)
|
||||
endif()
|
||||
geant4_save_package_variables(Qt${QT_VERSION_MAJOR}
|
||||
Qt${QT_VERSION_MAJOR}Core_DIR
|
||||
Qt${QT_VERSION_MAJOR}Gui_DIR
|
||||
@@ -162,14 +148,14 @@ if(GEANT4_USE_QT)
|
||||
Qt${QT_VERSION_MAJOR}OpenGL_DIR
|
||||
Qt${QT_VERSION_MAJOR}PrintSupport_DIR)
|
||||
|
||||
|
||||
get_target_property(QT_QMAKE_EXECUTABLE ${Qt${QT_VERSION_MAJOR}Core_QMAKE_EXECUTABLE} IMPORTED_LOCATION)
|
||||
geant4_add_feature(GEANT4_USE_QT "Build Geant4 with Qt${QT_VERSION_MAJOR} support")
|
||||
|
||||
# Qt3D is only supported on 5.15 and above, but always on if available
|
||||
set(QT3D_MINIMUM_VERSION 5.15.0)
|
||||
set(GEANT4_USE_QT3D OFF)
|
||||
|
||||
if(QT_VERSION GREATER_EQUAL QT3D_MINIMUM_VERSION)
|
||||
if(QT_VERSION VERSION_GREATER_EQUAL QT3D_MINIMUM_VERSION)
|
||||
find_package(Qt${QT_VERSION_MAJOR}3DCore ${QT_VERSION} EXACT QUIET)
|
||||
find_package(Qt${QT_VERSION_MAJOR}3DExtras ${QT_VERSION} EXACT QUIET)
|
||||
find_package(Qt${QT_VERSION_MAJOR}3DRender ${QT_VERSION} EXACT QUIET)
|
||||
|
||||
@@ -81,7 +81,7 @@ endif()
|
||||
# Sufficient id on all platforms?
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
|
||||
# Warnings
|
||||
set(CMAKE_CXX_FLAGS_INIT "-W -Wall -pedantic -Wno-non-virtual-dtor -Wno-long-long -Wwrite-strings -Wpointer-arith -Woverloaded-virtual -Wno-variadic-macros -Wshadow -fp-model precise")
|
||||
set(CMAKE_CXX_FLAGS_INIT "-W -Wall -pedantic -Wno-non-virtual-dtor -Wno-long-long -Wwrite-strings -Wpointer-arith -Woverloaded-virtual -Wno-variadic-macros -Wshadow -fp-model precise -diag-disable=10441")
|
||||
# Use pipes rather than temp files
|
||||
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} -pipe")
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ endif()
|
||||
option(GEANT4_USE_SYSTEM_CLHEP "Use system CLHEP library" ${_default_use_system_clhep})
|
||||
|
||||
if(GEANT4_USE_SYSTEM_CLHEP)
|
||||
find_package(CLHEP 2.4.5.1 REQUIRED CONFIG)
|
||||
find_package(CLHEP 2.4.6.0 REQUIRED CONFIG)
|
||||
geant4_save_package_variables(CLHEP CLHEP_DIR)
|
||||
else()
|
||||
set(CLHEP_FOUND TRUE)
|
||||
@@ -295,7 +295,7 @@ endif()
|
||||
|
||||
# - Geant4 USolids/VecGom setup
|
||||
if(GEANT4_USE_ALL_USOLIDS OR GEANT4_USE_PARTIAL_USOLIDS)
|
||||
find_package(VecGeom 1.1.18 REQUIRED)
|
||||
find_package(VecGeom 1.2.0 REQUIRED)
|
||||
# Shim until VecGeom supports config mode properly
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/G4VecGeomShim.cmake")
|
||||
# Backward Compatibility
|
||||
|
||||
Regular → Executable
@@ -24,38 +24,95 @@ import sys
|
||||
import re
|
||||
import json
|
||||
import graphlib
|
||||
from collections import defaultdict, OrderedDict
|
||||
|
||||
|
||||
def initdb(filename):
|
||||
db = {}
|
||||
db = {'modules': {}, 'build_settings': set()}
|
||||
with open(filename, 'r') as csvfile:
|
||||
reader = csv.reader(csvfile)
|
||||
for row in reader:
|
||||
db[row[0]] = {'location': row[1],
|
||||
'headers': set(row[2].split(';')) - set(['']),
|
||||
'public_deps': set(row[3].split(';')) - set(['']),
|
||||
'private_deps': set(row[4].split(';')) - set(['']),
|
||||
'interface_deps': set(row[5].split(';')) - set([''])}
|
||||
db['modules'][row[0]] = {'location': row[1],
|
||||
'headers': set(row[2].split(';')) - set(['']),
|
||||
'public_deps': set(row[3].split(';')) - set(['']),
|
||||
'private_deps': set(row[4].split(';')) - set(['']),
|
||||
'interface_deps': set(row[5].split(';')) - set(['']),
|
||||
'parent_target': row[6]}
|
||||
db['build_settings'].add(row[7])
|
||||
|
||||
return db
|
||||
|
||||
|
||||
def scan_includes(input_file):
|
||||
def get_modules(db):
|
||||
return db['modules'].keys()
|
||||
|
||||
|
||||
def get_module(name, db):
|
||||
return db['modules'][name]
|
||||
|
||||
|
||||
def get_location(name, db):
|
||||
return get_module(name, db)['location']
|
||||
|
||||
|
||||
def get_headers(name, db):
|
||||
return get_module(name, db)['headers']
|
||||
|
||||
|
||||
def get_public_deps(name, db):
|
||||
return get_module(name, db)['public_deps']
|
||||
|
||||
|
||||
def get_private_deps(name, db):
|
||||
return get_module(name, db)['private_deps']
|
||||
|
||||
|
||||
def get_interface_deps(name, db):
|
||||
return get_module(name, db)['interface_deps']
|
||||
|
||||
|
||||
def get_parent_target(name, db):
|
||||
return get_module(name, db)['parent_target']
|
||||
|
||||
|
||||
def what_provides(h, db):
|
||||
return set([m for m in get_modules(db) if h in get_headers(m, db)])
|
||||
|
||||
|
||||
def has_setting(setting, build_settings):
|
||||
if setting.startswith('!'):
|
||||
return setting[1:] not in build_settings
|
||||
else:
|
||||
return setting in build_settings
|
||||
|
||||
|
||||
def scan_includes(input_file, settings):
|
||||
result = set()
|
||||
lines = open(input_file).readlines()
|
||||
for line in lines:
|
||||
m = re.search("^ *# *include *\"(.*)\"", line)
|
||||
m = re.search("^ *# *include *\"(.*)\"(.*)$", line)
|
||||
if m:
|
||||
c = re.search(
|
||||
"\s*//\s*no_geant4_module_check\s*(\((.*)\))?.*$", m.group(2))
|
||||
if c:
|
||||
conds = set()
|
||||
if c.group(2):
|
||||
conds = set(
|
||||
filter(None, [''.join(c.split(' ')) for c in c.group(2).strip().split(',')]))
|
||||
|
||||
if not conds or any(has_setting(s, settings) for s in conds):
|
||||
continue
|
||||
|
||||
result.add(m.group(1))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def scan_files(input_path):
|
||||
def scan_files(input_path, settings):
|
||||
used_headers = set()
|
||||
with os.scandir(input_path) as hdr_it:
|
||||
for entry in hdr_it:
|
||||
used_headers |= scan_includes(entry.path)
|
||||
used_headers |= scan_includes(entry.path, settings)
|
||||
|
||||
return used_headers
|
||||
|
||||
@@ -64,8 +121,8 @@ def find_modules(list_of_headers, module_db):
|
||||
found_modules = set()
|
||||
orphan_hdrs = set()
|
||||
for h in list_of_headers:
|
||||
ms = [k for k, v in module_db.items() if h in v['headers']]
|
||||
found_modules |= set(ms)
|
||||
ms = what_provides(h, module_db)
|
||||
found_modules |= ms
|
||||
if not ms:
|
||||
orphan_hdrs.add(h)
|
||||
|
||||
@@ -75,21 +132,28 @@ def find_modules(list_of_headers, module_db):
|
||||
def usage_requirements(module_name, module_db):
|
||||
""" Find modules needed publically and privately by input module
|
||||
"""
|
||||
module_path = module_db[module_name]['location']
|
||||
module_headers = module_db[module_name]['headers']
|
||||
try:
|
||||
module_path = get_location(module_name, module_db)
|
||||
module_headers = get_headers(module_name, module_db)
|
||||
except KeyError as err:
|
||||
print(f'No module named \'{module_name}\'', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Determine public usage reqs
|
||||
includes_from_headers = scan_files(os.path.join(module_path, "include"))
|
||||
includes_from_headers = scan_files(os.path.join(module_path, "include"), module_db['build_settings'])
|
||||
includes_from_headers -= module_headers
|
||||
public_deps = find_modules(includes_from_headers, module_db)
|
||||
|
||||
# The same for private
|
||||
includes_from_srcs = scan_files(os.path.join(module_path, "src"))
|
||||
includes_from_srcs -= module_headers
|
||||
private_deps = find_modules(includes_from_srcs, module_db)
|
||||
# Public deps are higher priority
|
||||
private_deps['modules'] -= public_deps['modules']
|
||||
private_deps['headers'] -= public_deps['headers']
|
||||
# The same for private, if module has srcs
|
||||
try:
|
||||
includes_from_srcs = scan_files(os.path.join(module_path, "src"), module_db['build_settings'])
|
||||
includes_from_srcs -= module_headers
|
||||
private_deps = find_modules(includes_from_srcs, module_db)
|
||||
# Public deps are higher priority
|
||||
private_deps['modules'] -= public_deps['modules']
|
||||
private_deps['headers'] -= public_deps['headers']
|
||||
except FileNotFoundError:
|
||||
private_deps = {'modules': set(), 'headers': set()}
|
||||
|
||||
# Transform results to output dict
|
||||
d = {'module': module_name,
|
||||
@@ -108,18 +172,22 @@ def usage_requirements(module_name, module_db):
|
||||
def check_consistency(module_name, module_db):
|
||||
""" Check module declared/apparent dependencies for consistency
|
||||
"""
|
||||
# NB: Can have false positives from externals G4expat, G4clhep, G4zlib, G4tools (plus imported :: targets)
|
||||
def filter_dependencies(dep_list):
|
||||
return set([x for x in dep_list if not re.match("^.+::.+", x)]) - set(['G4expat', 'G4clhep', 'G4zlib', 'G4tools', 'G4ptl'])
|
||||
# NB: Can have false positives from externals G4expat, G4clhep, G4zlib, G4tools (plus imported :: targets and stdlib)
|
||||
def filter_dependencies(dep_list, module_name):
|
||||
return set([x for x in dep_list if not re.match("^.+::.+", x)]) - set(['G4expat', 'G4clhep', 'G4zlib', 'G4tools', 'G4ptl', 'stdc++fs'])
|
||||
|
||||
ur = usage_requirements(module_name, module_db)
|
||||
apparent_public_deps = filter_dependencies(ur['dependencies']['public'])
|
||||
apparent_private_deps = filter_dependencies(ur['dependencies']['private'])
|
||||
apparent_public_deps = filter_dependencies(
|
||||
ur['dependencies']['public'], module_name)
|
||||
apparent_private_deps = filter_dependencies(
|
||||
ur['dependencies']['private'], module_name)
|
||||
|
||||
declared_public_deps = filter_dependencies(
|
||||
module_db[module_name]['public_deps'])
|
||||
get_public_deps(module_name, module_db), module_name)
|
||||
declared_interface_deps = filter_dependencies(
|
||||
get_interface_deps(module_name, module_db), module_name)
|
||||
declared_private_deps = filter_dependencies(
|
||||
module_db[module_name]['private_deps'])
|
||||
get_private_deps(module_name, module_db), module_name)
|
||||
|
||||
# Collate any consistency errors
|
||||
report = []
|
||||
@@ -128,41 +196,72 @@ def check_consistency(module_name, module_db):
|
||||
duplicated_deps = declared_public_deps & declared_private_deps
|
||||
if duplicated_deps:
|
||||
report.append(
|
||||
f'- has duplicated PUBLIC/PRIVATE dependencies: {duplicated_deps}')
|
||||
f' - has duplicated PUBLIC/PRIVATE dependencies: {duplicated_deps}')
|
||||
|
||||
# - Apparent dep not declared
|
||||
missing_public_deps = apparent_public_deps - declared_public_deps
|
||||
missing_public_deps = apparent_public_deps - \
|
||||
declared_public_deps - declared_interface_deps
|
||||
if missing_public_deps:
|
||||
report.append(
|
||||
f'- may require PUBLIC dependencies: {missing_public_deps}')
|
||||
f' + may require PUBLIC or INTERFACE dependencies: {missing_public_deps}')
|
||||
|
||||
missing_private_deps = apparent_private_deps - declared_private_deps
|
||||
if missing_private_deps:
|
||||
report.append(
|
||||
f'- may require PRIVATE dependencies: {missing_private_deps}')
|
||||
f' + may require PRIVATE dependencies: {missing_private_deps}')
|
||||
|
||||
# - Declared dep not in apparent (overdeclared)
|
||||
overdeclared_public_deps = declared_public_deps - apparent_public_deps
|
||||
if overdeclared_public_deps:
|
||||
report.append(
|
||||
f'- may not require PUBLIC dependencies: {overdeclared_public_deps}')
|
||||
f' - may not require PUBLIC dependencies: {overdeclared_public_deps}')
|
||||
|
||||
overdeclared_private_deps = declared_private_deps - apparent_private_deps
|
||||
if overdeclared_private_deps:
|
||||
report.append(
|
||||
f'- may not require PRIVATE dependencies: {overdeclared_private_deps}')
|
||||
f' - may not require PRIVATE dependencies: {overdeclared_private_deps}')
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def find_cycles(module_db, verbose):
|
||||
def do_provides(header_name, module_db, verbose):
|
||||
# find the header, there should not be duplicates!
|
||||
mods = what_provides(header_name, module_db)
|
||||
if len(mods) == 0:
|
||||
print(f"No module provides header '{header_name}'", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if len(mods) > 1:
|
||||
print(
|
||||
f"Header '{args.provides}' is provided by multiple modules: {mods}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(mods.pop())
|
||||
|
||||
|
||||
def do_check_consistency(module_name, module_db):
|
||||
try:
|
||||
cc = check_consistency(module_name, module_db)
|
||||
if cc:
|
||||
print(f'{module_name} has inconsistent dependencies:', file=sys.stderr)
|
||||
print("\n".join(cc), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f'Module {module_name} appears consistent')
|
||||
except KeyError as err:
|
||||
print(f'No module named \'{module_name}\'', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def do_find_cycles(module_db, verbose):
|
||||
""" Check for any cycles in the complete module dependency graph
|
||||
"""
|
||||
# Build adjacency list
|
||||
adjlist = {}
|
||||
for k, v in module_db.items():
|
||||
adjlist[k] = list(v['public_deps'] | v['private_deps']
|
||||
| v['interface_deps'])
|
||||
|
||||
for m in get_modules(module_db):
|
||||
adjlist[m] = list(get_public_deps(m, module_db)
|
||||
| get_private_deps(m, module_db)
|
||||
| get_interface_deps(m, module_db))
|
||||
|
||||
try:
|
||||
# Topo sort throws cycle error if one occurs during prepare
|
||||
@@ -182,19 +281,45 @@ def find_cycles(module_db, verbose):
|
||||
ts.done(*nodes)
|
||||
generation += 1
|
||||
|
||||
print(f"pass: No cycles detected in module dependency graph")
|
||||
print(f"No cycles detected in module dependency graph")
|
||||
except graphlib.CycleError as err:
|
||||
print(
|
||||
f"error: cycles detected in module dependency graph",
|
||||
f"Cycles detected in module dependency graph:",
|
||||
file=sys.stderr,
|
||||
)
|
||||
cycle = " <- ".join(err.args[1])
|
||||
print(f"cycle: {cycle}", file=sys.stderr)
|
||||
cycle = " -> ".join(reversed(err.args[1]))
|
||||
print(f"{cycle}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def do_find_inconsistencies(db, verbose):
|
||||
inconsistent = {}
|
||||
for m in get_modules(db):
|
||||
cc = check_consistency(m, db)
|
||||
if cc:
|
||||
inconsistent[m] = "\n".join(cc)
|
||||
|
||||
if len(inconsistent) > 0:
|
||||
for k, v in inconsistent.items():
|
||||
print(f'{k}:\n{v}', file=sys.stderr)
|
||||
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(
|
||||
"No inconsistencies detected in declared/apparent module dependencies")
|
||||
|
||||
|
||||
def do_libraries(db, verbose):
|
||||
libmap = defaultdict(set)
|
||||
|
||||
for m in get_modules(db):
|
||||
libmap[get_parent_target(m, db)].add(m)
|
||||
|
||||
for k, v in sorted(libmap.items()):
|
||||
print(f'{k}: {v}')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Parse command line to get command to run
|
||||
parser = argparse.ArgumentParser(
|
||||
description=str(__doc__), formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
@@ -210,6 +335,11 @@ if __name__ == "__main__":
|
||||
action='store_true',
|
||||
help="list declared source code modules"
|
||||
)
|
||||
query_group.add_argument(
|
||||
"-s", "--source",
|
||||
metavar="<module>",
|
||||
help="print directory holding CMake file where module is defined"
|
||||
)
|
||||
query_group.add_argument(
|
||||
"-i", "--interface",
|
||||
metavar="<module>",
|
||||
@@ -240,6 +370,16 @@ if __name__ == "__main__":
|
||||
action="store_true",
|
||||
help="find inconsistencies in apparent/declared dependencies of all modules"
|
||||
)
|
||||
query_group.add_argument(
|
||||
"--library",
|
||||
metavar="<module>",
|
||||
help="print final library (.so/.dll) that module is compiled into"
|
||||
)
|
||||
query_group.add_argument(
|
||||
"--libraries",
|
||||
action="store_true",
|
||||
help="print final libraries and their module compositions"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Initialize the module/header "database"
|
||||
@@ -251,52 +391,36 @@ if __name__ == "__main__":
|
||||
|
||||
# Implementations
|
||||
if args.list:
|
||||
print("\n".join(db.keys()))
|
||||
print("\n".join(get_modules(db)))
|
||||
elif args.source:
|
||||
try:
|
||||
print(get_location(args.source, db))
|
||||
except KeyError as err:
|
||||
print(f"No module named {err}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
elif args.interface:
|
||||
try:
|
||||
print("\n".join(db[args.interface]['headers']))
|
||||
print(
|
||||
"\n".join(sorted(get_headers(args.interface, db))))
|
||||
except KeyError as err:
|
||||
print(f"No module named {err}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
elif args.provides:
|
||||
# find the header, there should not be duplicates!
|
||||
mods = [k for k, v in db.items() if args.provides in v['headers']]
|
||||
if len(mods) == 0:
|
||||
print(
|
||||
f"No module provides header '{args.provides}'",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if len(mods) > 1:
|
||||
print(
|
||||
f"Header '{args.provides}' is provided by multiple modules: {mods}",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(mods[0])
|
||||
do_provides(args.provides, db, args.verbose)
|
||||
elif args.usage_requirements:
|
||||
ur = usage_requirements(args.usage_requirements, db)
|
||||
print(json.dumps(ur, indent=2))
|
||||
elif args.check_consistency:
|
||||
cc = check_consistency(args.check_consistency, db)
|
||||
if cc:
|
||||
print(f'{args.check_consistency}:', file=sys.stderr)
|
||||
print("\n".join(cc), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(
|
||||
f'{args.check_consistency} appears consistent')
|
||||
do_check_consistency(args.check_consistency, db)
|
||||
elif args.find_cycles:
|
||||
find_cycles(db, args.verbose)
|
||||
do_find_cycles(db, args.verbose)
|
||||
elif args.find_inconsistencies:
|
||||
inconsistent = {}
|
||||
for m in db.keys():
|
||||
cc = check_consistency(m, db)
|
||||
if cc:
|
||||
inconsistent[m] = "\n".join(cc)
|
||||
|
||||
if len(inconsistent) > 0:
|
||||
print(f'Inconsistent dependencies in modules:', file=sys.stderr)
|
||||
for k, v in inconsistent.items():
|
||||
print(f'{k}:\n{v}', file=sys.stderr)
|
||||
|
||||
do_find_inconsistencies(db, args.verbose)
|
||||
elif args.library:
|
||||
try:
|
||||
print(get_parent_target(args.library, db))
|
||||
except KeyError as err:
|
||||
print(f"No module named {err}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
elif args.libraries:
|
||||
do_libraries(db, args.verbose)
|
||||
|
||||
Reference in New Issue
Block a user