diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index e6400a2d19e62..1c7b1639cf3d0 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -609,17 +609,22 @@ jobs: ccache -s || true - name: Install - if: ${{ success() && !matrix.is_special }} - run: "cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}" + id: install + run: cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }} + + - name: Check headers + if: steps.install.outcome == 'success' + run: bash test/PostInstall/check-headers.sh ${{ env.INSTALL_DIR }}/include/ - name: Build post-install test project - if: ${{ success() && !matrix.is_special }} + id: postInstall + if: steps.install.outcome == 'success' run: | cmake -S test/PostInstall/ -B ${{ env.POST_INSTALL_DIR }} -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }}; cmake --build ${{ env.POST_INSTALL_DIR }}; - name: CTest in post-install test project - if: ${{ success() && !matrix.is_special }} + if: steps.postInstall.outcome == 'success' && matrix.property != 'asan' working-directory: ${{ env.POST_INSTALL_DIR }} run: ctest --output-on-failure -j $(nproc) diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index 7d938f6883548..8e824685acb33 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -1223,60 +1223,69 @@ function(ROOT_FIND_DIRS_WITH_HEADERS result_dirs) endfunction() #--------------------------------------------------------------------------------------------------- -#---ROOT_INSTALL_HEADERS([dir1 dir2 ...] [FILTER ]) -# Glob for headers in the folder where this target is defined, and install them in -# /include +#---ROOT_INSTALL_HEADERS([dir1 dir2 ...] [FILTER ] [HEADERS ...]) +# Declare the install command for headers and copy them into /include. +# This function supports two modes to build the list of headers: +# - [New] If headers are passed explicitly using HEADERS ..., install only these +# - [Old] Otherwise, glob in the specified folders or where this target is defined #--------------------------------------------------------------------------------------------------- function(ROOT_INSTALL_HEADERS) - CMAKE_PARSE_ARGUMENTS(ARG "OPTIONS" "" "FILTER" ${ARGN}) + CMAKE_PARSE_ARGUMENTS(ARG "OPTIONS" "" "FILTER;HEADERS" ${ARGN}) if (${ARG_OPTIONS}) message(FATAL_ERROR "ROOT_INSTALL_HEADERS no longer supports the OPTIONS argument. Rewrite using the FILTER argument.") endif() - ROOT_FIND_DIRS_WITH_HEADERS(dirs ${ARG_UNPARSED_ARGUMENTS}) - set (filter "LinkDef") - set (options REGEX "LinkDef" EXCLUDE) - foreach (f ${ARG_FILTER}) - set (filter "${filter}|${f}") - set (options ${options} REGEX "${f}" EXCLUDE) - endforeach() - set (filter "(${filter})") - set(include_files "") - foreach(d ${dirs}) - install(DIRECTORY ${d} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - COMPONENT headers - ${options}) - string(REGEX REPLACE "(.*)/$" "\\1" d ${d}) - ROOT_GLOB_FILES(globbed_files - RECURSE - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - FILTER ${filter} - ${d}/*.h ${d}/*.hxx ${d}/*.icc ) - list(APPEND include_files ${globbed_files}) - endforeach() - string(REPLACE ${CMAKE_SOURCE_DIR} "" target_name ${CMAKE_CURRENT_SOURCE_DIR}) - string(REPLACE / _ target_name "copy_header_${target_name}") - string(REGEX REPLACE "_$" "" target_name ${target_name}) + unset(include_files) + + if(ARG_HEADERS) + # Headers have been listed explicitly, find them one by one + foreach(regex ${ARG_FILTER} "LinkDef") + list(FILTER ARG_HEADERS EXCLUDE REGEX "${regex}") + endforeach() + foreach(header ${ARG_HEADERS}) + file(GLOB globbed_header ${header} */${header}) + if(globbed_header STREQUAL "") + message(SEND_ERROR "No header corresponding to ${header} found in ${CMAKE_CURRENT_SOURCE_DIR}") + endif() + list(APPEND include_files ${globbed_header}) + endforeach() + else() + # Glob across all include directories + ROOT_FIND_DIRS_WITH_HEADERS(dirs ${ARG_UNPARSED_ARGUMENTS}) + set (filter "LinkDef") + foreach (f ${ARG_FILTER}) + set (filter "${filter}|${f}") + endforeach() + set (filter "(${filter})") + foreach(d ${dirs}) + string(REGEX REPLACE "(.*)/$" "\\1" d ${d}) + ROOT_GLOB_FILES(globbed_files + RECURSE + FILTER ${filter} + ${d}/*.h ${d}/*.hxx ${d}/*.icc ) + list(APPEND include_files ${globbed_files}) + endforeach() + endif() # Register the files to be copied for each target directory (e.g. include/ include/ROOT include/v7/inc/ ...) list(REMOVE_DUPLICATES include_files) list(TRANSFORM include_files REPLACE "(.*)/[^/]*" "\\1/" OUTPUT_VARIABLE subdirs) list(REMOVE_DUPLICATES subdirs) foreach(subdir ${subdirs}) + string(REGEX REPLACE ".*/inc/" "" destination_subdir ${subdir}) + set(input_files ${include_files}) list(FILTER input_files INCLUDE REGEX "^${subdir}[^/]*$") - set(output_files ${input_files}) - string(REGEX REPLACE ".*/*inc/" "" destination ${subdir}) + install(FILES ${input_files} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${destination_subdir} COMPONENT headers) - list(TRANSFORM input_files PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") - list(TRANSFORM output_files REPLACE ".*/" "${CMAKE_BINARY_DIR}/include/${destination}") - - set(destination destination_${destination}) + set(output_files ${input_files}) + list(TRANSFORM output_files REPLACE ".*/" "${CMAKE_BINARY_DIR}/include/${destination_subdir}") - set_property(GLOBAL APPEND PROPERTY ROOT_HEADER_COPY_LISTS ${destination}) - set_property(GLOBAL APPEND PROPERTY ROOT_HEADER_INPUT_${destination} ${input_files}) - set_property(GLOBAL APPEND PROPERTY ROOT_HEADER_OUTPUT_${destination} ${output_files}) + set(destination_target_name destination_${destination_subdir}) + set_property(GLOBAL APPEND PROPERTY ROOT_HEADER_COPY_LISTS ${destination_target_name}) + set_property(GLOBAL APPEND PROPERTY ROOT_HEADER_INPUT_${destination_target_name} ${input_files}) + set_property(GLOBAL APPEND PROPERTY ROOT_HEADER_OUTPUT_${destination_target_name} ${output_files}) endforeach() endfunction() @@ -1317,6 +1326,7 @@ endmacro() #--------------------------------------------------------------------------------------------------- #---ROOT_STANDARD_LIBRARY_PACKAGE(libname # [NO_INSTALL_HEADERS] : don't install headers for this package +# [NO_GLOB_HEADERS] : don't glob for headers, only install listed ones # [STAGE1] : use rootcling_stage1 for generating # HEADERS header1 header2 : relative header path as #included; pass -I to find them. If not specified, globbing for *.h is used # NODEPHEADERS header1 header2 : like HEADERS, but no dependency is generated @@ -1335,7 +1345,7 @@ endmacro() # ) #--------------------------------------------------------------------------------------------------- function(ROOT_STANDARD_LIBRARY_PACKAGE libname) - set(options NO_INSTALL_HEADERS STAGE1 NO_HEADERS NO_SOURCES OBJECT_LIBRARY NO_CXXMODULE) + set(options NO_INSTALL_HEADERS NO_GLOB_HEADERS STAGE1 NO_HEADERS NO_SOURCES OBJECT_LIBRARY NO_CXXMODULE) set(oneValueArgs LINKDEF) set(multiValueArgs DEPENDENCIES HEADERS NODEPHEADERS SOURCES BUILTINS LIBRARIES DICTIONARY_OPTIONS INSTALL_OPTIONS) CMAKE_PARSE_ARGUMENTS(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -1441,7 +1451,11 @@ function(ROOT_STANDARD_LIBRARY_PACKAGE libname) # Install headers if we have any headers and if the user didn't explicitly # disabled this. if (NOT ARG_NO_INSTALL_HEADERS OR ARG_NO_HEADERS) - ROOT_INSTALL_HEADERS(${ARG_INSTALL_OPTIONS}) + if(ARG_NO_GLOB_HEADERS) + ROOT_INSTALL_HEADERS(${ARG_INSTALL_OPTIONS} HEADERS ${ARG_HEADERS}) + else() + ROOT_INSTALL_HEADERS(${ARG_INSTALL_OPTIONS}) + endif() endif() endfunction() diff --git a/core/imt/inc/ROOT/TThreadExecutor.hxx b/core/imt/inc/ROOT/TThreadExecutor.hxx index 7ab57cdce6628..dd1494f0c1cf1 100644 --- a/core/imt/inc/ROOT/TThreadExecutor.hxx +++ b/core/imt/inc/ROOT/TThreadExecutor.hxx @@ -18,7 +18,7 @@ #ifndef R__USE_IMT // No need to error out for dictionaries. # if !defined(__ROOTCLING__) && !defined(G__DICTIONARY) -# error "Cannot use ROOT::TThreadExecutor without defining R__USE_IMT." +#error "Cannot use ROOT::TThreadExecutor when build option imt=Off." # endif #else diff --git a/geom/geom/inc/TGeoTypedefs.h b/geom/geom/inc/TGeoTypedefs.h index c29f33febca72..a1f468e26789d 100644 --- a/geom/geom/inc/TGeoTypedefs.h +++ b/geom/geom/inc/TGeoTypedefs.h @@ -15,6 +15,14 @@ /// Typedefs used by the geometry group #include +namespace ROOT::Geom { +struct Vertex_t; +} + +namespace ROOT::Geom { +struct Vertex_t; +} + namespace Tessellated { using Vertex_t = ROOT::Geom::Vertex_t; diff --git a/geom/geom/inc/TGeoVoxelGrid.h b/geom/geom/inc/TGeoVoxelGrid.h index 98021630a8b62..5d7401eaef6e2 100644 --- a/geom/geom/inc/TGeoVoxelGrid.h +++ b/geom/geom/inc/TGeoVoxelGrid.h @@ -15,6 +15,7 @@ #include #include #include +#include // a simple structure to encode voxel indices, to address // individual voxels in the 3D grid. diff --git a/test/PostInstall/check-headers.sh b/test/PostInstall/check-headers.sh new file mode 100644 index 0000000000000..4104f3d7e0963 --- /dev/null +++ b/test/PostInstall/check-headers.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Adapted from the XRootD project with friendly permission from G. Amadio. +# +# This script checks that each installed ROOT header can be included individually +# without errors. The intention is to identify which headers may have missing +# includes, missing forward declarations, or missing header dependencies, that is, +# headers from ROOT which it includes, but were not installed by the install target. + +# We need to split CXXFLAGS +# shellcheck disable=SC2086 + +: "${INCLUDE_DIR:=${1}}" +: "${CXX:=$(${INCLUDE_DIR}/../bin/root-config --cxx || echo c++)}" +: "${CXXFLAGS:=-Wall -Wextra -Wno-unused-parameter -Wno-unused-const-variable}" +: "${NCPU:=$(getconf _NPROCESSORS_ONLN)}" +: "${CXXSTANDARD:=$(${INCLUDE_DIR}/../bin/root-config --cxxstandard || echo 17)}" + +if ! command -v "${CXX}" >/dev/null; then + echo "Please set CXX to a valid compiler" + exit 2 +fi +if [ ! -d "${INCLUDE_DIR}" ]; then + echo "Usage: ${0} " + echo "Alternatively, set INCLUDE_DIR in the environment" + exit 2 +fi + + +# Check all installed headers for include errors. Some headers cannot be used standalone: +suppressions="TMVA\|vdt" # External +suppressions+="\|RField[A-Z]\|RtypesImp.h\|TAtomicCount[A-Z]\|CladDerivator.h\|TBranchProxyTemplate" # Not to be used standalone +suppressions+="\|TWin32" # Why are these installed in Linux? +suppressions+="\|xRooHypoSpace.h\|xRooFit" # Uses macros to declare namespaces +suppressions+="\|RDaos.h" # Might not be installed +suppressions+="\|RIoUring.hxx" # Might not be installed +suppressions+="\|CPyCppyy/DispatchPtr.h\|CPyCppyy/API.h" # Would need to include Python.h +suppressions+="\|/bvh" # Includes a non-functioning std::span in c++17 +suppressions+="\|cfortran.h" # Seems unable to run with modern compilers +suppressions+="\|hipSYCL.h\|GenVectorX" # Unconditionally installed on Fedora/Ubuntu even if broken +suppressions+="\|TR[A-Z].*__ctors.h" # R interface without any includes, so cannot be parsed as C++ +suppressions+="\|RTaskArena.hxx\|TThreadExecutor.hxx\|TTreeProcessorMT.hxx" # Will raise errors if imt=Off + +HEADERS=$(find "${INCLUDE_DIR}" -type f -name '*.h*' | grep -v "${suppressions}") + +xargs -P ${NCPU:-1} -n 1 "${CXX}" -fsyntax-only -x c++ -std=c++${CXXSTANDARD} ${CXXFLAGS} -I"${INCLUDE_DIR}" <<< "${HEADERS}" + diff --git a/tree/dataframe/CMakeLists.txt b/tree/dataframe/CMakeLists.txt index a9d4b000922df..b16e21c7506b0 100644 --- a/tree/dataframe/CMakeLists.txt +++ b/tree/dataframe/CMakeLists.txt @@ -36,6 +36,7 @@ if (runtime_cxxmodules AND WIN32) endif() ROOT_STANDARD_LIBRARY_PACKAGE(ROOTDataFrame + NO_GLOB_HEADERS # Don't install RArrowDS and similar if they are off HEADERS ROOT/RCsvDS.hxx ROOT/RVecDS.hxx diff --git a/tree/ntuple/CMakeLists.txt b/tree/ntuple/CMakeLists.txt index 699d390a1a8fa..29c7aff41ea4e 100644 --- a/tree/ntuple/CMakeLists.txt +++ b/tree/ntuple/CMakeLists.txt @@ -27,6 +27,7 @@ if(daos OR daos_mock) endif() ROOT_STANDARD_LIBRARY_PACKAGE(ROOTNTuple +NO_GLOB_HEADERS # Ensure that the optional headers above only get installed when enabled HEADERS ROOT/RCluster.hxx ROOT/RClusterPool.hxx @@ -123,6 +124,15 @@ DEPENDENCIES ${ROOTNTuple_OPTIONAL_DEPENDENCIES} ) +# Non-standalone headers must be left out of the dictionary, so a dedicated install command is required. +ROOT_INSTALL_HEADERS(HEADERS + ROOT/RField/RFieldFundamental.hxx + ROOT/RField/RFieldProxiedCollection.hxx + ROOT/RField/RFieldRecord.hxx + ROOT/RField/RFieldSequenceContainer.hxx + ROOT/RField/RFieldSoA.hxx + ROOT/RField/RFieldSTLMisc.hxx) + target_link_libraries(ROOTNTuple PRIVATE xxHash::xxHash) # Enable RNTuple support for Intel DAOS