diff --git a/cmake/modules/lcgsoft-macros.cmake b/cmake/modules/lcgsoft-macros.cmake index 33da2e953cd5d6bb9db63405b383de5e63dc2fbd..d6e14597ee732b6110e0ac0c2c74f8f49712b566 100644 --- a/cmake/modules/lcgsoft-macros.cmake +++ b/cmake/modules/lcgsoft-macros.cmake @@ -850,13 +850,13 @@ function(LCG_get_environment name var) if(_path) set(_path ${_path}:${${dep}_home}/bin) set(_cmake ${_cmake}:${${dep}_home}) - set(_pkg ${_pkg}:${${dep}_home}/lib/pkgconfig) + set(_pkg ${_pkg}:${${dep}_home}/lib64/pkgconfig:${${dep}_home}/lib/pkgconfig) set(_python ${_python}:${${dep}_home}/lib/python${Python_config_version_twodigit}/site-packages) set(_libpath ${_libpath}:${${dep}_home}/lib64:${${dep}_home}/lib) else() set(_path PATH=${${dep}_home}/bin) set(_cmake CMAKE_PREFIX_PATH=${${dep}_home}) - set(_pkg PKG_CONFIG_PATH=${${dep}_home}/lib/pkgconfig) + set(_pkg PKG_CONFIG_PATH=${${dep}_home}/lib64/pkgconfig:${${dep}_home}/lib/pkgconfig) set(_python PYTHONPATH=${${dep}_home}/lib/python${Python_config_version_twodigit}/site-packages) set(_libpath LD_LIBRARY_PATH=${${dep}_home}/lib64:${${dep}_home}/lib) endif() diff --git a/cmake/scripts/argparse.py b/cmake/scripts/argparse2.py similarity index 100% rename from cmake/scripts/argparse.py rename to cmake/scripts/argparse2.py diff --git a/cmake/scripts/create_bundle.py b/cmake/scripts/create_bundle.py index e04e86d6d06e65e4dcd509bd1a222ef54b4178f7..ed86588857dc4d5f9b3677efe2fcedcd1a14aebb 100755 --- a/cmake/scripts/create_bundle.py +++ b/cmake/scripts/create_bundle.py @@ -5,7 +5,10 @@ import shutil import sys import time import logging -import argparse +try: + import argparse +except ImportError: + import argparse2 as argparse class InstallBundle(object): def __init__(self, prefix, blacklist=[], greylist=[]): diff --git a/cmake/scripts/create_lcg_view.py b/cmake/scripts/create_lcg_view.py index fc6fa8f45bc52338a1d922bb1809a668f617975b..f775b54ca806f5d63ffc548cd4b197aee17df173 100755 --- a/cmake/scripts/create_lcg_view.py +++ b/cmake/scripts/create_lcg_view.py @@ -17,7 +17,11 @@ # --loglevel LEVEL Change logging level # --version show program's version number and exit -import argparse +try: + import argparse +except ImportError: + import argparse2 as argparse + import glob import logging import os diff --git a/cmake/scripts/create_lcg_view_setup_sh.in b/cmake/scripts/create_lcg_view_setup_sh.in index de344113509f0d508f8a4a7fb182f4d80fbc0055..ab3d23fc51a75f6138f19afede354eeadf841922 100644 --- a/cmake/scripts/create_lcg_view_setup_sh.in +++ b/cmake/scripts/create_lcg_view_setup_sh.in @@ -109,6 +109,8 @@ if [ -x $thisdir/bin/root ]; then else JUPYTER_PATH=${thisdir}/etc/notebook:$JUPYTER_PATH; export JUPYTER_PATH fi + export CPPYY_BACKEND_LIBRARY=$ROOTSYS/lib/libcppyy_backend`python -c "import sys; print('{0}_{1}'.format(*sys.version_info))"` + export CLING_STANDARD_PCH=none fi #---then Gaudi diff --git a/cmake/scripts/install_binary_repository.py b/cmake/scripts/install_binary_repository.py index 69e3a29144e5f21944bf3e8e7b5be459b195cf7f..1debc7673b4e814b9206c9e06d4c76285ebf937a 100755 --- a/cmake/scripts/install_binary_repository.py +++ b/cmake/scripts/install_binary_repository.py @@ -6,7 +6,12 @@ Version 1.0 """ #------------------------------------------------------------------------------- from __future__ import print_function -import argparse, os, sys, tarfile, subprocess, shutil, glob +import os, sys, tarfile, subprocess, shutil, glob +try: + import argparse +except ImportError: + import argparse2 as argparse + if sys.version_info[0] >= 3 : import urllib.request as urllib2 else: diff --git a/cmake/scripts/install_binary_tarfile.py b/cmake/scripts/install_binary_tarfile.py index 9fec8f9e6a97286b71ed9eba8b2c380a1100637d..901581d5e45e9a17c4c7c596a0db4ff2f6981ae0 100755 --- a/cmake/scripts/install_binary_tarfile.py +++ b/cmake/scripts/install_binary_tarfile.py @@ -7,7 +7,12 @@ Version 1.0 #------------------------------------------------------------------------------- from __future__ import print_function -import argparse, os, sys, tarfile, subprocess, shutil +import os, sys, tarfile, subprocess, shutil +try: + import argparse +except ImportError: + import argparse2 as argparse + if sys.version_info[0] >= 3 : import urllib.request as urllib2 else: diff --git a/cmake/scripts/lockfile.py b/cmake/scripts/lockfile.py index ec0386955c04191801ee9b7678abab803a653acf..081afaa80d6d378cd96d66c7815b3cd9a8f53aed 100755 --- a/cmake/scripts/lockfile.py +++ b/cmake/scripts/lockfile.py @@ -7,7 +7,10 @@ Version 1.0 from __future__ import print_function import os, sys, time, socket, errno, shutil -import argparse +try: + import argparse +except ImportError: + import argparse2 as argparse parser = argparse.ArgumentParser() parser.add_argument('-v', '--version', action='version', version='1.0', help='display the version number and exit') diff --git a/cmake/scripts/purge_binary_repository.py b/cmake/scripts/purge_binary_repository.py index 1c5a2304e26fbffdcba02b381bc8290a8a741d56..4da8ed9a8b5d302fb3bb1b28fcd41dba979c14fc 100755 --- a/cmake/scripts/purge_binary_repository.py +++ b/cmake/scripts/purge_binary_repository.py @@ -6,7 +6,11 @@ Version 1.0 """ #------------------------------------------------------------------------------- from __future__ import print_function -import argparse, os, sys, glob, time +import os, sys, glob, time +try: + import argparse +except ImportError: + import argparse2 as argparse def creation_date(path_to_file): stat = os.stat(path_to_file) diff --git a/cmake/toolchain/heptools-dev-base.cmake b/cmake/toolchain/heptools-dev-base.cmake index 0a35708565be6f8452cf7dd5b451749eb2933947..9cba6e2aa3de1ae292ab1926224f5f1d3bb576a5 100644 --- a/cmake/toolchain/heptools-dev-base.cmake +++ b/cmake/toolchain/heptools-dev-base.cmake @@ -113,7 +113,7 @@ if(${LCG_OS}${LCG_OSVERS} MATCHES centos7|ubuntu) endif() endif() -LCG_AA_project(DD4hep 01-12-01 ) +LCG_AA_project(DD4hep 01-13 ) # Externals LCG_external_package(lcgenv 1.3.14 ) @@ -144,7 +144,7 @@ LCG_external_package(backports_abc 0.5 LCG_external_package(benchmark 1.5.0 ) LCG_external_package(bison 3.3.2 ) #LCG_external_package(blas 3.8.0.netlib ) -LCG_external_package(blas 0.3.9.openblas ) +LCG_external_package(blas 0.3.10.openblas ) LCG_external_package(bleach 3.1.0 ) LCG_external_package(brotli 1.0.7 ) LCG_external_package(bzip2 1.0.6 ) @@ -174,7 +174,7 @@ LCG_external_package(colorcet 2.0.2 LCG_external_package(configparser 3.7.4 ) LCG_external_package(CouchDB 1.2 ) LCG_external_package(coverage 4.5.3 ) -LCG_external_package(cppgsl 3.0.1 ) +LCG_external_package(cppgsl 3.1.0 ) LCG_external_package(CppUnit 1.14.0 author=1.14.0 ) LCG_external_package(cpymad 1.4.1 ) LCG_external_package(cryptography 2.7 ) @@ -187,10 +187,10 @@ if(NOT ${LCG_OS} STREQUAL mac) LCG_external_package(cx_oracle 7.1.3 ) endif() LCG_external_package(cycler 0.10.0 ) -LCG_external_package(cython 0.29.16 ) +LCG_external_package(cython 0.29.20 ) LCG_external_package(dask 1.2.2 ) if(NOT ${LCG_OS} STREQUAL mac) - LCG_external_package(Davix 0.7.3 ) + LCG_external_package(Davix 0.7.6 ) endif() LCG_external_package(decorator 4.3.2 ) LCG_external_package(defusedxml 0.6.0 ) @@ -204,7 +204,7 @@ LCG_external_package(eigen 3.3.7 LCG_external_package(elasticsearch 6.3.0 ) LCG_external_package(entrypoints 0.3 ) LCG_external_package(expat 2.2.6 ) -LCG_external_package(fastjet 3.3.2 ) +LCG_external_package(fastjet 3.3.4 ) LCG_external_package(fftw 3.3.8 fftw3 ) LCG_external_package(fjcontrib 1.044 ) LCG_external_package(flake8 3.7.8 ) @@ -306,7 +306,7 @@ LCG_external_package(joblib 0.14.0 LCG_external_package(jpeg v6b ) LCG_external_package(jpype 0.6.3 ) LCG_external_package(jsonc 0.12 ) -LCG_external_package(jsoncpp 1.8.4 ) +LCG_external_package(jsoncpp 1.9.3 ) LCG_external_package(jsonmcpp 3.6.1 ) LCG_external_package(jsonschema 3.0.1 ) LCG_external_package(pyjson5 0.9.1 ) @@ -328,7 +328,7 @@ LCG_external_package(libedit 3.1 LCG_external_package(libevent 2.1.11 ) LCG_external_package(libffi 3.2.1 ) LCG_external_package(libgeotiff 1.5.1 ) -LCG_external_package(libgit2 0.28.2 ) +LCG_external_package(libgit2 1.0.1 ) LCG_external_package(libpqxx 7.1.1 ) LCG_external_package(libsodium 1.0.18 ) LCG_external_package(libsvm 3.23 ) @@ -338,7 +338,7 @@ if( NOT ${LCG_OS} MATCHES ubuntu|mac ) endif() LCG_external_package(libxml2 2.9.9 ) LCG_external_package(libxslt 1.1.33 ) -LCG_external_package(LCIO 02.13.03 ) +LCG_external_package(LCIO 02.14.02 ) LCG_external_package(logilabcommon 1.4.2 ) LCG_external_package(lwtnn 2.8.1 ) LCG_external_package(lxml 4.3.3 ) @@ -524,13 +524,13 @@ if(${LCG_OS} STREQUAL slc OR ${LCG_OS} STREQUAL centos OR ${LCG_OS}${LCG_OSVERS} endif() LCG_external_package(spdlog 1.5.0 ) LCG_external_package(sqlalchemy 1.2.10 ) -LCG_external_package(sqlite 3280000 ) +LCG_external_package(sqlite 3320300 ) LCG_external_package(statsmodels 0.10.2 ) LCG_external_package(stomppy 3.1.3 ) LCG_external_package(storm 0.23 ) LCG_external_package(swig 3.0.12 author=3.0.12 ) LCG_external_package(sympy 1.4 ) -LCG_external_package(tbb 2020_U1 ) +LCG_external_package(tbb 2020_U2 ) LCG_external_package(tblib 1.4.0 ) #LCG_external_package(tcmalloc 2.4 ) if(NOT ${LCG_OS}${LCG_OSVERS} STREQUAL slc6) @@ -569,7 +569,7 @@ LCG_external_package(uproot_methods 0.7.1 LCG_external_package(urllib3 1.25.3 ) LCG_external_package(uuid 1.42 ) if(NOT ${LCG_OS} STREQUAL mac) - LCG_external_package(valgrind 3.15.0 ) + LCG_external_package(valgrind 3.16.1 ) endif() LCG_external_package(Vc 1.4.1 ) LCG_external_package(vcversioner 2.16.0.0 ) @@ -591,7 +591,7 @@ if(NOT ${LCG_OS} STREQUAL mac) LCG_external_package(xgboost 0.90 ) endif() LCG_external_package(xqilla 2.3.3 ) -LCG_external_package(xrootd 4.12.2 ) +LCG_external_package(xrootd 4.12.3 ) LCG_external_package(xz 5.0.4 ) LCG_external_package(yamlcpp 0.6.3 ) LCG_external_package(zeromq 4.3.2 ) diff --git a/cmake/toolchain/heptools-dev-generators.cmake b/cmake/toolchain/heptools-dev-generators.cmake index 9e46f614f6d5a78d29665b6869e48a64f2e5eb4b..f89c13aa6ac4ce54635a25291b5ca109bb5f6c22 100644 --- a/cmake/toolchain/heptools-dev-generators.cmake +++ b/cmake/toolchain/heptools-dev-generators.cmake @@ -18,7 +18,7 @@ LCG_external_package(syscalc 1.1.7 ${MCGENPATH}/syscalc) LCG_external_package(madgraph5amc 2.7.2 ${MCGENPATH}/madgraph5amc) -LCG_external_package(lhapdf 6.2.3 ${MCGENPATH}/lhapdf ) +LCG_external_package(lhapdf 6.3.0 ${MCGENPATH}/lhapdf ) LCG_external_package(powheg-box-v2 r3043.lhcb ${MCGENPATH}/powheg-box-v2 author=r3043 ) @@ -80,7 +80,7 @@ LCG_external_package(professor 2.3.2 ${MCGENPATH}/professor LCG_external_package(jhu 5.6.3 ${MCGENPATH}/jhu ) -LCG_external_package(log4cpp 2.8.3p1 ${MCGENPATH}/log4cpp ) +LCG_external_package(log4cpp 2.9.1 ${MCGENPATH}/log4cpp ) LCG_external_package(rapidsim 1.4.4 ${MCGENPATH}/rapidsim ) diff --git a/cmake/toolchain/heptools-hsf.cmake b/cmake/toolchain/heptools-hsf.cmake index a5760f5d2c34e1ce3bb0eaf53f3726edcae66928..97526c04c26d68c5faba023e94d77d18398c0a6b 100644 --- a/cmake/toolchain/heptools-hsf.cmake +++ b/cmake/toolchain/heptools-hsf.cmake @@ -5,8 +5,7 @@ include(heptools-dev-base) include(heptools-dev-generators) #---Overwrites----------------------------------------------------- -LCG_external_package(ROOT v6.20.02 ) -LCG_external_package(hepmc3 3.0.0 ) +LCG_external_package(ROOT v6.22.00 ) if(${LCG_OS} MATCHES mac) # AppleClang is more strict on C++17 deprecated functionality LCG_remove_package(sherpa) diff --git a/documentation/packages.json b/documentation/packages.json index e78c209a2e3f3d32cd45a7ce632c0f58e46db337..2e1288d836c01c3a488c9168f5b178b8d70bc542 100644 --- a/documentation/packages.json +++ b/documentation/packages.json @@ -39,16 +39,6 @@ "license": null, "name": "colorcet" }, - { - "category": null, - "contacts": [], - "description": null, - "fullname": "", - "homepage": null, - "language": null, - "license": null, - "name": "gflags" - }, { "category": null, "contacts": [], @@ -4151,6 +4141,16 @@ "license": "GPL", "name": "gettext" }, + { + "category": "Tool", + "contacts": [], + "description": "Gflags, the commandline flags library used within Google", + "fullname": "Gflags", + "homepage": "https://github.com/gflags/gflags", + "language": "C++", + "license": "BSD3c", + "name": "gflags" + }, { "category": "Other", "contacts": [], diff --git a/documentation/releases.json b/documentation/releases.json index 58f608a41cb5873e81d8127c85774c238ff1b7dd..73a2e6c26c2bb07f33f4ea96389718aa905b2467 100644 --- a/documentation/releases.json +++ b/documentation/releases.json @@ -59,6 +59,10 @@ "extra_notes": null, "version": "97apython3" }, + { + "extra_notes": null, + "version": "97a_LHCB_2" + }, { "extra_notes": null, "version": "97a_LHCB_1" @@ -207,6 +211,10 @@ "extra_notes": null, "version": "89" }, + { + "extra_notes": null, + "version": "88b" + }, { "extra_notes": null, "version": "88a" diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index fd7bdf63b93339a710b1d9234354f301a50c784d..5d0230ce9009a559f80c182c47f2e388a274ac57 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -745,7 +745,11 @@ LCGPackage_Add( IF tbb_native_version MATCHES "^4._.*" THEN URL ${GenURL}/tbb${tbb_native_version}oss_src.tgz ELSE - URL ${GenURL}/tbb${tbb_native_version}.tar.gz + IF tbb_native_version STRGREATER "2020_U1" THEN + URL ${GenURL}/oneTBB-${tbb_native_version}.tar.gz + ELSE + URL ${GenURL}/tbb${tbb_native_version}.tar.gz + ENDIF ENDIF CONFIGURE_COMMAND <VOID> BUILD_COMMAND ${MAKE} ${tbb_build_options} @@ -2278,10 +2282,12 @@ LCGPackage_Add( IF NOT APPLE THEN COMMAND ${CMAKE_COMMAND} -E copy_directory <INSTALL_DIR>/${_LIBDIR}/R/library/IRkernel/kernelspec <INSTALL_DIR>/share/jupyter/kernels/ir ENDIF + COMMAND ${CMAKE_COMMAND} -E make_directory <INSTALL_DIR>/etc + COMMAND ${CMAKE_COMMAND} -E copy <SOURCE_DIR>/etc/Renviron <INSTALL_DIR>/etc/Renviron LIST_SEPARATOR SEMICOLON BUILD_IN_SOURCE 1 DEPENDS ${R_DEPS} IF LCG_OS STREQUAL mac THEN openssl ELSE cairo ENDIF - REVISION 5 + REVISION 6 ) #---ocaml----------------------------------------------------------------------- @@ -2660,6 +2666,8 @@ LCGPackage_Add( -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} + -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=ON -DBUILD_gflags_LIB=ON + REVISION 1 ) #---gflags--------------------------------------------------------------------- diff --git a/generators/CMakeLists.txt b/generators/CMakeLists.txt index 6596ed22ab855774c4b5bca16efd76e347bc3f26..a06e7ff93139ff87e245893f053ec3cc721f64ac 100644 --- a/generators/CMakeLists.txt +++ b/generators/CMakeLists.txt @@ -214,10 +214,11 @@ LCGPackage_Add( ENDIF ) -#---Rivet----------------------------------------------------------------------------------------- +#---rivet----------------------------------------------------------------------------------------- LCGPackage_Add( rivet URL ${gen_url}/Rivet-<VERSION>.tar.bz2 + ENVIRONMENT LIBRARY_PATH=${FORTRAN_LIBRARY_DIR} IF NOT <VERSION> VERSION_LESS 2.6.2 THEN CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rivet/gsl.m4 <SOURCE_DIR>/m4 COMMAND autoreconf --install --force @@ -242,7 +243,6 @@ LCGPackage_Add( --with-gsl=${GSL_home} ENDIF IF <VERSION> VERSION_GREATER 2.9.9 THEN - --with-fastjet=${fastjet_home} --with-fjcontrib=${fjcontrib_home} ENDIF --with-lcgtag=${LCG_platform} @@ -2013,7 +2013,7 @@ LCGPackage_Add( BUILD_COMMAND ${MAKE} COMMAND $ENV{SHELL} -c "${CMAKE_SOURCE_DIR}/cmake/scripts/static2shared.sh build/libFH.a" INSTALL_COMMAND ${MAKE} -j 1 install - COMMAND ${CMAKE_COMMAND} -E copy build/libFH.so <INSTALL_DIR>/${LIBDIR_DEFAULT}/ + COMMAND ${CMAKE_COMMAND} -E copy <SOURCE_DIR>/build/libFH.so <INSTALL_DIR>/${LIBDIR_DEFAULT}/ BUILD_IN_SOURCE 1 ) diff --git a/generators/patches/feynhiggs-2.16.0.patch b/generators/patches/feynhiggs-2.16.0.patch new file mode 100644 index 0000000000000000000000000000000000000000..0cb1cf370eb2737ae97481910696a7ba50c9c833 --- /dev/null +++ b/generators/patches/feynhiggs-2.16.0.patch @@ -0,0 +1,33 @@ +--- configure.orig 2018-08-29 14:57:46.500082106 +0200 ++++ configure 2020-07-13 13:07:34.853530407 +0200 +@@ -293,8 +293,8 @@ + CONF_CFLAGS+=" $CONF_BITS" + CONF_CXXFLAGS+=" $CONF_BITS" + +-test "$CONF_OS$CONF_BITS" = "Linux-m64" && CONF_LIBDIRSUFFIX=64 +- ++## patch to avoid lib64 directory for Ubuntu/Debian ++test "$CONF_OS$CONF_BITS" = "Linux-m64" && test ! -f /etc/debian_version && CONF_LIBDIRSUFFIX=64 + + ## does f77 support REAL*16? + +--- makefile.in.orig 2020-01-15 10:01:18.171956613 +0100 ++++ makefile.in 2020-07-13 13:12:55.597988694 +0200 +@@ -44,11 +44,12 @@ + $(BLD)/PDG.h "$(INCLUDEDIR)" + strip $(FHBIN) + cp -p $(FHBIN) $(BLD)/fcc "$(BINDIR)" +- test ! -f $(MFHBIN) || { \ +- test -z "$(EXE)" || ldd $(MFHBIN) | awk '/\/usr\/bin\/cyg/ { system("cp -p " $$3 " $(BINDIR)/"); }' ; \ +- strip $(MFHBIN) ; \ +- cp -p $(MFHBIN) $(BINDIR); \ +- } ++# Commented out in previous versions. GG 13/7/2020 ++# test ! -f $(MFHBIN) || { \ ++# test -z "$(EXE)" || ldd $(MFHBIN) | awk '/\/usr\/bin\/cyg/ { system("cp -p " $$3 " $(BINDIR)/"); }' ; \ ++# strip $(MFHBIN) ; \ ++# cp -p $(MFHBIN) $(BINDIR); \ ++# } + + force: $(BLD)/version.h + diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index c618f3cb9f724c72f8f0d806d8c48dd05ac6a29c..72ac114aa47c0797d2b7881ba6165ca6977bb987 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -66,10 +66,13 @@ if(ROOT_native_version) ELSE -Dpyroot=ON ENDIF - IF Python_native_version VERSION_GREATER 3 THEN + IF ROOT_version VERSION_LESS 6.22 AND Python_native_version VERSION_GREATER 3 THEN -Dpython3=ON -Dpython_version=3 ENDIF + IF ROOT_version VERSION_GREATER_EQUAL 6.22 THEN + -DPYTHON_EXECUTABLE=${Python_home}/bin/python + ENDIF -Dbuiltin_lz4=ON -Dbuiltin_pcre=ON -Dbuiltin_xxhash=ON @@ -379,30 +382,20 @@ add_custom_target(Geant4-externals #---LCIO----------------------------------------------------------------------------------------------- LCGPackage_add( LCIO - #SVN_REPOSITORY svn://svn.freehep.org/lcio/tags/<LCIO_native_version> --quiet - #UPDATE_COMMAND <VOID> URL ${GenURL}/LCIO-${LCIO_native_version}.tar.gz - CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> + CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR> + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DLCIO_GENERATE_HEADERS=off -DBUILD_ROOTDICT=ON -DROOT_DIR=${ROOT_home} - IF LCG_CPP11 THEN - -DCMAKE_CXX_FLAGS=-std=c++11 - ENDIF - IF LCG_CPP1Y THEN - -DCMAKE_CXX_FLAGS=-std=c++1y - ENDIF - IF LCG_CPP14 THEN - -DCMAKE_CXX_FLAGS=-std=c++14 - ENDIF - IF LCG_CPP17 THEN - -DCMAKE_CXX_FLAGS=-std=c++17 - ENDIF BUILD_COMMAND ${MAKE} ROOTSYS=${ROOT_home} INSTALL_COMMAND ${MAKE} install COMMAND ${EXEC} bash -c "mkdir -p <INSTALL_DIR>/lib/cmake/LCIO" COMMAND ${EXEC} bash -c "cp -n -r <INSTALL_DIR>/*.cmake <INSTALL_DIR>/lib/cmake/LCIO" - DEPENDS ROOT + DEPENDS ROOT clhep ) #---VecGeom------------------------------------------------------------------------------------------ diff --git a/projects/patches/ROOT-v6.22.00.patch b/projects/patches/ROOT-v6.22.00.patch index 30fbc2ecc4a53defa427c85e456bdc47f8ca94ff..384d0b488048be56f93a4810b2e6bec9043aa4e4 100644 --- a/projects/patches/ROOT-v6.22.00.patch +++ b/projects/patches/ROOT-v6.22.00.patch @@ -23,3 +23,822 @@ if (fd->isOverloadedOperator()) property |= kIsOperator; if (llvm::isa<clang::CXXConversionDecl>(fd)) + +From efa71aa238aadb040ca634634be14dde850595c5 Mon Sep 17 00:00:00 2001 +From: Enric Tejedor Saavedra <enric.tejedor.saavedra@cern.ch> +Date: Wed, 8 Jul 2020 17:40:30 +0200 +Subject: [PATCH] [ROOT-10872][PyROOT] Update Dispatcher to prevent use of + deleted copy constructor + +--- + .../cppyy/CPyCppyy/src/CPPConstructor.cxx | 114 +++++++ + .../cppyy/CPyCppyy/src/CPPConstructor.h | 15 + + .../pyroot/cppyy/CPyCppyy/src/CPPScope.cxx | 9 +- + bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h | 3 +- + bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h | 4 + + .../pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx | 320 ++++++++++++++---- + .../pyroot/cppyy/CPyCppyy/src/Dispatcher.h | 5 +- + .../cppyy/CPyCppyy/src/ProxyWrappers.cxx | 15 +- + .../pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h | 4 +- + .../clingwrapper/src/clingwrapper.cxx | 28 ++ + .../clingwrapper/src/cpp_cppyy.h | 4 + + 11 files changed, 435 insertions(+), 86 deletions(-) + +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.cxx +index 4e46363bd43..32a47c182d9 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.cxx ++++ bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.cxx +@@ -11,6 +11,10 @@ + // Standard + #include <string> + ++//- data _____________________________________________________________________ ++namespace CPyCppyy { ++ extern PyObject* gNullPtrObject; ++} + + //- protected members -------------------------------------------------------- + bool CPyCppyy::CPPConstructor::InitExecutor_(Executor*& executor, CallContext*) +@@ -143,6 +147,116 @@ PyObject* CPyCppyy::CPPConstructor::Call( + } + + ++//---------------------------------------------------------------------------- ++CPyCppyy::CPPMultiConstructor::CPPMultiConstructor(Cppyy::TCppScope_t scope, Cppyy::TCppMethod_t method) : ++ CPPConstructor(scope, method) ++{ ++ fNumBases = Cppyy::GetNumBases(scope); ++} ++ ++//---------------------------------------------------------------------------- ++CPyCppyy::CPPMultiConstructor::CPPMultiConstructor(const CPPMultiConstructor& s) : ++ CPPConstructor(s), fNumBases(s.fNumBases) ++{ ++} ++ ++//---------------------------------------------------------------------------- ++CPyCppyy::CPPMultiConstructor& CPyCppyy::CPPMultiConstructor::operator=(const CPPMultiConstructor& s) ++{ ++ if (this != &s) { ++ CPPConstructor::operator=(s); ++ fNumBases = s.fNumBases; ++ } ++ return *this; ++} ++ ++//---------------------------------------------------------------------------- ++PyObject* CPyCppyy::CPPMultiConstructor::Call( ++ CPPInstance*& self, PyObject* _args, PyObject* kwds, CallContext* ctxt) ++{ ++// By convention, initialization parameters of multiple base classes are grouped ++// by target base class. Here, we disambiguate and put in "sentinel" parameters ++// that allow the dispatcher to propagate them. ++ ++// Three options supported: ++// 0. empty args: default constructor call ++// 1. fNumBases tuples, each handed to individual constructors ++// 2. less than fNumBases, assuming (void) for the missing base constructors ++// 3. normal arguments, going to the first base only ++ ++ Py_INCREF(_args); ++ PyObject* args = _args; ++ ++ if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args)) { // case 0. falls through ++ Py_ssize_t nArgs = PyTuple_GET_SIZE(args); ++ ++ bool isAllTuples = true; ++ Py_ssize_t nArgsTot = 0; ++ for (Py_ssize_t i = 0; i < nArgs; ++i) { ++ PyObject* argi = PyTuple_GET_ITEM(args, i); ++ if (!PyTuple_CheckExact(argi)) { ++ isAllTuples = false; ++ break; ++ } ++ nArgsTot += PyTuple_GET_SIZE(argi); ++ } ++ ++ if (isAllTuples) { ++ // copy over the arguments, while filling in the sentinels (case 1. & 2.), with ++ // just sentinels for the remaining (void) calls (case 2.) ++ PyObject* newArgs = PyTuple_New(nArgsTot + fNumBases - 1); ++ Py_ssize_t idx = 0; ++ for (Py_ssize_t i = 0; i < nArgs; ++i) { ++ if (i != 0) { ++ // add sentinel ++ Py_INCREF(gNullPtrObject); ++ PyTuple_SET_ITEM(newArgs, idx, gNullPtrObject); ++ idx += 1; ++ } ++ ++ PyObject* argi = PyTuple_GET_ITEM(args, i); ++ for (Py_ssize_t j = 0; j < PyTuple_GET_SIZE(argi); ++j) { ++ PyObject* item = PyTuple_GET_ITEM(argi, j); ++ Py_INCREF(item); ++ PyTuple_SET_ITEM(newArgs, idx, item); ++ idx += 1; ++ } ++ } ++ ++ // add final sentinels as needed ++ while (idx < (nArgsTot+fNumBases-1)) { ++ Py_INCREF(gNullPtrObject); ++ PyTuple_SET_ITEM(newArgs, idx, gNullPtrObject); ++ idx += 1; ++ } ++ ++ Py_DECREF(args); ++ args = newArgs; ++ } else { // case 3. add sentinels ++ // copy arguments as-is, then add sentinels at the end ++ PyObject* newArgs = PyTuple_New(PyTuple_GET_SIZE(args) + fNumBases - 1); ++ for (Py_ssize_t i = 0; i < nArgs; ++i) { ++ PyObject* item = PyTuple_GET_ITEM(args, i); ++ Py_INCREF(item); ++ PyTuple_SET_ITEM(newArgs, i, item); ++ } ++ for (Py_ssize_t i = 0; i < fNumBases - 1; ++i) { ++ Py_INCREF(gNullPtrObject); ++ PyTuple_SET_ITEM(newArgs, i+nArgs, gNullPtrObject); ++ } ++ Py_DECREF(args); ++ args = newArgs; ++ } ++ } ++ ++ PyObject* result = CPPConstructor::Call(self, args, kwds, ctxt); ++ Py_DECREF(args); ++ return result; ++} ++ ++ ++ ++ + //---------------------------------------------------------------------------- + PyObject* CPyCppyy::CPPAbstractClassConstructor::Call( + CPPInstance*& self, PyObject* args, PyObject* kwds, CallContext* ctxt) +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.h +index f07bb9b966c..349ecf91092 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.h ++++ bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.h +@@ -24,6 +24,21 @@ class CPPConstructor : public CPPMethod { + }; + + ++// specialization for multiple inheritance disambiguation ++class CPPMultiConstructor : public CPPConstructor { ++public: ++ CPPMultiConstructor(Cppyy::TCppScope_t scope, Cppyy::TCppMethod_t method); ++ CPPMultiConstructor(const CPPMultiConstructor&); ++ CPPMultiConstructor& operator=(const CPPMultiConstructor&); ++ ++public: ++ virtual PyObject* Call(CPPInstance*&, PyObject*, PyObject*, CallContext* = nullptr); ++ ++private: ++ Py_ssize_t fNumBases; ++}; ++ ++ + // specializations of prohibiting constructors + class CPPAbstractClassConstructor : public CPPConstructor { + public: +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.cxx +index 4a0be8e6c6e..18b48d670c7 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.cxx ++++ bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.cxx +@@ -254,9 +254,12 @@ static PyObject* pt_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) + Py_ssize_t sz = PyDict_Size(dct); + if (0 < sz && !Cppyy::IsNamespace(result->fCppType)) { + result->fFlags |= CPPScope::kIsPython; +- if (!InsertDispatcher(result, dct)) { +- if (!PyErr_Occurred()) +- PyErr_Warn(PyExc_RuntimeWarning, (char*)"no python-side overrides supported"); ++ if (1 < PyTuple_GET_SIZE(PyTuple_GET_ITEM(args, 1))) ++ result->fFlags |= CPPScope::kIsMultiCross; ++ std::ostringstream errmsg; ++ if (!InsertDispatcher(result, PyTuple_GET_ITEM(args, 1), dct, errmsg)) { ++ PyErr_Format(PyExc_TypeError, "no python-side overrides supported (%s)", errmsg.str().c_str()); ++ return nullptr; + } else { + // the direct base can be useful for some templates, such as shared_ptrs, + // so make it accessible (the __cpp_cross__ data member also signals that +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h +index a9e2f8c0e02..289abcc66ae 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h ++++ bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h +@@ -43,7 +43,8 @@ class CPPScope { + kIsException = 0x0004, + kIsSmart = 0x0008, + kIsPython = 0x0010, +- kIsInComplete = 0x0020 }; ++ kIsMultiCross = 0x0020, ++ kIsInComplete = 0x0040 }; + + public: + PyHeapTypeObject fType; +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h b/bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h +index e50e13ebc0e..ea4d222050d 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h ++++ bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h +@@ -208,6 +208,8 @@ namespace Cppyy { + CPPYY_IMPORT + bool IsPublicMethod(TCppMethod_t method); + CPPYY_IMPORT ++ bool IsProtectedMethod(TCppMethod_t method); ++ CPPYY_IMPORT + bool IsConstructor(TCppMethod_t method); + CPPYY_IMPORT + bool IsDestructor(TCppMethod_t method); +@@ -230,6 +232,8 @@ namespace Cppyy { + CPPYY_IMPORT + bool IsPublicData(TCppScope_t scope, TCppIndex_t idata); + CPPYY_IMPORT ++ bool IsProtectedData(TCppScope_t scope, TCppIndex_t idata); ++ CPPYY_IMPORT + bool IsStaticData(TCppScope_t scope, TCppIndex_t idata); + CPPYY_IMPORT + bool IsConstData(TCppScope_t scope, TCppIndex_t idata); +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx +index b56bb54d1f9..50b0de4b850 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx ++++ bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx +@@ -42,7 +42,7 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m + #else + code << " PyObject* mtPyName = PyUnicode_FromString(\"" << mtCppName << "\");\n" + #endif +- " PyObject* pyresult = PyObject_CallMethodObjArgs((PyObject*)m_self, mtPyName"; ++ " PyObject* pyresult = PyObject_CallMethodObjArgs((PyObject*)_internal_self, mtPyName"; + for (Cppyy::TCppIndex_t i = 0; i < nArgs; ++i) + code << ", pyargs[" << i << "]"; + code << ", NULL);\n Py_DECREF(mtPyName);\n"; +@@ -52,27 +52,120 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m + } + + //---------------------------------------------------------------------------- +-bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* dct) ++namespace { ++ struct BaseInfo { ++ BaseInfo(Cppyy::TCppType_t t, std::string&& bn, std::string&& bns) : ++ btype(t), bname(bn), bname_scoped(bns) {} ++ Cppyy::TCppType_t btype; ++ std::string bname; ++ std::string bname_scoped; ++ }; ++ ++ typedef std::vector<BaseInfo> BaseInfos_t; ++ typedef std::vector<Cppyy::TCppMethod_t> Ctors_t; ++ typedef std::vector<Ctors_t> AllCtors_t; ++ typedef std::vector<std::pair<Cppyy::TCppMethod_t, size_t>> CtorInfos_t; ++} // unnamed namespace ++ ++static void build_constructors( ++ const std::string& derivedName, const BaseInfos_t& base_infos, const AllCtors_t& ctors, ++ std::ostringstream& code, const CtorInfos_t& methods = CtorInfos_t{}, size_t idx = 0) ++{ ++ if (idx < ctors.size()) { ++ for (const auto& method : ctors[idx]) { ++ size_t argsmin = (size_t)Cppyy::GetMethodReqArgs(method); ++ size_t argsmax = (size_t)Cppyy::GetMethodNumArgs(method); ++ for (size_t i = argsmin; i <= argsmax; ++i) { ++ CtorInfos_t methods1{methods}; ++ methods1.emplace_back(method, i); ++ build_constructors(derivedName, base_infos, ctors, code, methods1, idx+1); ++ } ++ } ++ } else { ++ // this is as deep as we go; start writing ++ code << " " << derivedName << "("; ++ ++ // declare arguments ++ std::vector<size_t> arg_tots; arg_tots.reserve(methods.size()); ++ for (Ctors_t::size_type i = 0; i < methods.size(); ++i) { ++ const auto& cinfo = methods[i]; ++ if (i != 0 && (arg_tots.back() || 1 < arg_tots.size())) code << ", "; ++ size_t nArgs = cinfo.second; ++ arg_tots.push_back(i == 0 ? nArgs : nArgs+arg_tots.back()); ++ ++ if (i != 0) code << "__cppyy_internal::Sep*"; ++ size_t offset = (i != 0 ? arg_tots[i-1] : 0); ++ for (size_t j = 0; j < nArgs; ++j) { ++ if (i != 0 || j != 0) code << ", "; ++ code << Cppyy::GetMethodArgType(cinfo.first, j) << " a" << (j+offset); ++ } ++ } ++ code << ") : "; ++ ++ // pass arguments to base constructors ++ for (BaseInfos_t::size_type i = 0; i < base_infos.size(); ++i) { ++ if (i != 0) code << ", "; ++ code << base_infos[i].bname << "("; ++ size_t first = (i != 0 ? arg_tots[i-1] : 0); ++ for (size_t j = first; j < arg_tots[i]; ++j) { ++ if (j != first) code << ", "; ++ bool isRValue = CPyCppyy::Utility::Compound(\ ++ Cppyy::GetMethodArgType(methods[i].first, j-first)) == "&&"; ++ if (isRValue) code << "std::move("; ++ code << "a" << j; ++ if (isRValue) code << ")"; ++ } ++ code << ")"; ++ } ++ code << " {}\n"; ++ } ++} ++ ++bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, std::ostringstream& err) + { + // Scan all methods in dct and where it overloads base methods in klass, create + // dispatchers on the C++ side. Then interject the dispatcher class. +- if (Cppyy::IsNamespace(klass->fCppType) || !PyDict_Check(dct)) { +- PyErr_Format(PyExc_TypeError, +- "%s not an acceptable base: is namespace or has no dict", Cppyy::GetScopedFinalName(klass->fCppType).c_str()); ++ ++ if (!PyTuple_Check(bases) || !PyTuple_GET_SIZE(bases) || !dct || !PyDict_Check(dct)) { ++ err << "internal error: expected tuple of bases and proper dictionary"; + return false; + } + +- if (!Cppyy::HasVirtualDestructor(klass->fCppType)) { +- PyErr_Format(PyExc_TypeError, +- "%s not an acceptable base: no virtual destructor", Cppyy::GetScopedFinalName(klass->fCppType).c_str()); ++ if (!Utility::IncludePython()) { ++ err << "failed to include Python.h"; + return false; + } + +- if (!Utility::IncludePython()) ++// collect all bases, error checking the hierarchy along the way ++ const Py_ssize_t nBases = PyTuple_GET_SIZE(bases); ++ BaseInfos_t base_infos; base_infos.reserve(nBases); ++ for (Py_ssize_t ibase = 0; ibase < nBases; ++ibase) { ++ Cppyy::TCppType_t basetype = ((CPPScope*)PyTuple_GET_ITEM(bases, ibase))->fCppType; ++ ++ if (!basetype) { ++ err << "base class is incomplete"; ++ break; ++ } ++ ++ if (Cppyy::IsNamespace(basetype)) { ++ err << Cppyy::GetScopedFinalName(basetype) << " is a namespace"; ++ break; ++ } ++ ++ if (!Cppyy::HasVirtualDestructor(basetype)) { ++ const std::string& bname = Cppyy::GetScopedFinalName(basetype); ++ PyErr_Warn(PyExc_RuntimeWarning, (char*)("class \""+bname+"\" has no virtual destructor").c_str()); ++ } ++ ++ base_infos.emplace_back( ++ basetype, TypeManip::template_base(Cppyy::GetFinalName(basetype)), Cppyy::GetScopedFinalName(basetype)); ++ } ++ ++ if ((Py_ssize_t)base_infos.size() != nBases) + return false; + +- const std::string& baseName = TypeManip::template_base(Cppyy::GetFinalName(klass->fCppType)); +- const std::string& baseNameScoped = Cppyy::GetScopedFinalName(klass->fCppType); ++// TODO: check deep hierarchy for multiple inheritance ++ bool isDeepHierarchy = klass->fCppType && base_infos.front().btype != klass->fCppType; + + // once classes can be extended, should consider re-use; for now, since derived + // python classes can differ in what they override, simply use different shims +@@ -86,14 +179,20 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* dct) + + // start class declaration + code << "namespace __cppyy_internal {\n" +- << "class " << derivedName << " : public ::" << baseNameScoped << " {\n" +- " CPyCppyy::DispatchPtr m_self;\n" +- "public:\n"; ++ << "class " << derivedName << " : "; ++ for (BaseInfos_t::size_type ibase = 0; ibase < base_infos.size(); ++ibase) { ++ if (ibase != 0) code << ", "; ++ code << "public ::" << base_infos[ibase].bname_scoped; ++ } ++ code << " {\n"; ++ if (!isDeepHierarchy) ++ code << "protected:\n CPyCppyy::DispatchPtr _internal_self;\n"; ++ code << "public:\n"; + + // add a virtual destructor for good measure + code << " virtual ~" << derivedName << "() {}\n"; + +-// methods: first collect all callables, then get overrides from base class, for ++// methods: first collect all callables, then get overrides from base classes, for + // those that are still missing, search the hierarchy + PyObject* clbs = PyDict_New(); + PyObject* items = PyDict_Items(dct); +@@ -106,101 +205,176 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* dct) + if (PyDict_DelItem(clbs, PyStrings::gInit) != 0) + PyErr_Clear(); + +-// simple case: methods from current class +- bool has_default = false; +- bool has_cctor = false; ++// protected methods and data need their access changed in the C++ trampoline and then ++// exposed on the Python side; so, collect their names as we go along ++ std::vector<std::string> protected_names; ++ ++// simple case: methods from current class (collect constructors along the way) ++ int has_default = 0; ++ int has_cctor = 0; + bool has_constructors = false; +- const Cppyy::TCppIndex_t nMethods = Cppyy::GetNumMethods(klass->fCppType); +- for (Cppyy::TCppIndex_t imeth = 0; imeth < nMethods; ++imeth) { +- Cppyy::TCppMethod_t method = Cppyy::GetMethod(klass->fCppType, imeth); +- +- if (Cppyy::IsConstructor(method)) { +- has_constructors = true; +- Cppyy::TCppIndex_t nreq = Cppyy::GetMethodReqArgs(method); +- if (nreq == 0) +- has_default = true; +- else if (nreq == 1) { +- if (TypeManip::clean_type(Cppyy::GetMethodArgType(method, 0), false) == baseNameScoped) +- has_cctor = true; ++ AllCtors_t ctors{base_infos.size()}; ++ for (BaseInfos_t::size_type ibase = 0; ibase < base_infos.size(); ++ibase) { ++ const auto& binfo = base_infos[ibase]; ++ ++ const Cppyy::TCppIndex_t nMethods = Cppyy::GetNumMethods(binfo.btype); ++ bool cctor_found = false, default_found = false, any_ctor_found = false; ++ for (Cppyy::TCppIndex_t imeth = 0; imeth < nMethods; ++imeth) { ++ Cppyy::TCppMethod_t method = Cppyy::GetMethod(binfo.btype, imeth); ++ ++ if (Cppyy::IsConstructor(method)) { ++ any_ctor_found = true; ++ if (Cppyy::IsPublicMethod(method) || Cppyy::IsProtectedMethod(method)) { ++ Cppyy::TCppIndex_t nreq = Cppyy::GetMethodReqArgs(method); ++ if (nreq == 0) default_found = true; ++ else if (!cctor_found && nreq == 1) { ++ const std::string& argtype = Cppyy::GetMethodArgType(method, 0); ++ if (Utility::Compound(argtype) == "&" && TypeManip::clean_type(argtype, false) == binfo.bname_scoped) ++ cctor_found = true; ++ } ++ ctors[ibase].push_back(method); ++ } ++ continue; ++ } ++ ++ std::string mtCppName = Cppyy::GetMethodName(method); ++ PyObject* key = CPyCppyy_PyText_FromString(mtCppName.c_str()); ++ int contains = PyDict_Contains(dct, key); ++ if (contains == -1) PyErr_Clear(); ++ if (contains != 1) { ++ Py_DECREF(key); ++ ++ // if the method is protected, we expose it with a 'using' ++ if (Cppyy::IsProtectedMethod(method)) { ++ protected_names.push_back(mtCppName); ++ code << " using " << binfo.bname << "::" << mtCppName << ";\n"; ++ } ++ ++ continue; + } +- continue; +- } + +- std::string mtCppName = Cppyy::GetMethodName(method); +- PyObject* key = CPyCppyy_PyText_FromString(mtCppName.c_str()); +- int contains = PyDict_Contains(dct, key); +- if (contains == -1) PyErr_Clear(); +- if (contains != 1) { ++ InjectMethod(method, mtCppName, code); ++ ++ if (PyDict_DelItem(clbs, key) != 0) ++ PyErr_Clear(); // happens for overloads + Py_DECREF(key); +- continue; + } + +- InjectMethod(method, mtCppName, code); +- +- if (PyDict_DelItem(clbs, key) != 0) +- PyErr_Clear(); // happens for overloads +- Py_DECREF(key); ++ // count the cctors and default ctors to determine whether each base has one ++ if (cctor_found || (!cctor_found && !any_ctor_found)) has_cctor += 1; ++ if (default_found || (!default_found && !any_ctor_found)) has_default += 1; ++ if (any_ctor_found) has_constructors = true; + } + + // try to locate left-overs in base classes +- if (PyDict_Size(clbs)) { +- size_t nbases = Cppyy::GetNumBases(klass->fCppType); +- for (size_t ibase = 0; ibase < nbases; ++ibase) { +- Cppyy::TCppScope_t tbase = (Cppyy::TCppScope_t)Cppyy::GetScope( \ +- Cppyy::GetBaseName(klass->fCppType, ibase)); +- +- PyObject* keys = PyDict_Keys(clbs); +- for (Py_ssize_t i = 0; i < PyList_GET_SIZE(keys); ++i) { +- // TODO: should probably invert this looping; but that makes handling overloads clunky +- PyObject* key = PyList_GET_ITEM(keys, i); +- std::string mtCppName = CPyCppyy_PyText_AsString(key); +- const auto& v = Cppyy::GetMethodIndicesFromName(tbase, mtCppName); +- for (auto idx : v) +- InjectMethod(Cppyy::GetMethod(tbase, idx), mtCppName, code); +- if (!v.empty()) { +- if (PyDict_DelItem(clbs, key) != 0) PyErr_Clear(); ++ for (const auto& binfo : base_infos) { ++ if (PyDict_Size(clbs)) { ++ size_t nbases = Cppyy::GetNumBases(binfo.btype); ++ for (size_t ibase = 0; ibase < nbases; ++ibase) { ++ Cppyy::TCppScope_t tbase = (Cppyy::TCppScope_t)Cppyy::GetScope( \ ++ Cppyy::GetBaseName(binfo.btype, ibase)); ++ ++ PyObject* keys = PyDict_Keys(clbs); ++ for (Py_ssize_t i = 0; i < PyList_GET_SIZE(keys); ++i) { ++ // TODO: should probably invert this looping; but that makes handling overloads clunky ++ PyObject* key = PyList_GET_ITEM(keys, i); ++ std::string mtCppName = CPyCppyy_PyText_AsString(key); ++ const auto& v = Cppyy::GetMethodIndicesFromName(tbase, mtCppName); ++ for (auto idx : v) ++ InjectMethod(Cppyy::GetMethod(tbase, idx), mtCppName, code); ++ if (!v.empty()) { ++ if (PyDict_DelItem(clbs, key) != 0) PyErr_Clear(); ++ } + } +- } +- Py_DECREF(keys); ++ Py_DECREF(keys); ++ } + } + } + Py_DECREF(clbs); + +-// constructors: most are simply inherited, for use by the Python derived class +- code << " using " << baseName << "::" << baseName << ";\n"; ++// constructors: build up from the argument types of the base class, for use by the Python ++// derived class (inheriting with/ "using" does not work b/c base class constructors may ++// have been deleted), ++ build_constructors(derivedName, base_infos, ctors, code); + + // for working with C++ templates, additional constructors are needed to make + // sure the python object is properly carried, but they can only be generated + // if the base class supports them +- if (has_default || !has_constructors) +- code << " " << derivedName << "() {}\n" +- << " " << derivedName << "(PyObject* pyobj) : m_self(pyobj) {}\n"; +- if (has_default || has_cctor || !has_constructors) { ++ if (1 < nBases && (!has_constructors || has_default == nBases)) ++ code << " " << derivedName << "() {}\n"; ++ if (!has_constructors || has_cctor == nBases) { + code << " " << derivedName << "(const " << derivedName << "& other) : "; +- if (has_cctor) +- code << baseName << "(other), "; +- code << "m_self(other.m_self, this) {}\n"; ++ for (BaseInfos_t::size_type ibase = 0; ibase < base_infos.size(); ++ibase) { ++ if (ibase != 0) code << ", "; ++ code << base_infos[ibase].bname << "(other)"; ++ } ++ if (!isDeepHierarchy) { ++ if (has_cctor) code << ", "; ++ else code << " : "; ++ code << "_internal_self(other._internal_self, this)"; ++ } ++ code << " {}\n"; + } + + // destructor: default is fine + ++// pull in data members that are protected ++ bool setPublic = false; ++ for (const auto& binfo : base_infos) { ++ Cppyy::TCppIndex_t nData = Cppyy::GetNumDatamembers(binfo.btype); ++ for (Cppyy::TCppIndex_t idata = 0; idata < nData; ++idata) { ++ if (Cppyy::IsProtectedData(binfo.btype, idata)) { ++ const std::string dm_name = Cppyy::GetDatamemberName(binfo.btype, idata); ++ if (dm_name != "_internal_self") { ++ protected_names.push_back(Cppyy::GetDatamemberName(binfo.btype, idata)); ++ if (!setPublic) { ++ code << "public:\n"; ++ setPublic = true; ++ } ++ code << " using " << binfo.bname << "::" << protected_names.back() << ";\n"; ++ } ++ } ++ } ++ } ++ + // finish class declaration + code << "};\n}"; + + // finally, compile the code +- if (!Cppyy::Compile(code.str())) ++ if (!Cppyy::Compile(code.str())) { ++ err << "failed to compile the dispatcher code"; + return false; ++ } + + // keep track internally of the actual C++ type (this is used in + // CPPConstructor to call the dispatcher's one instead of the base) + Cppyy::TCppScope_t disp = Cppyy::GetScope("__cppyy_internal::"+derivedName); +- if (!disp) return false; ++ if (!disp) { ++ err << "failed to retrieve the internal dispatcher"; ++ return false; ++ } + klass->fCppType = disp; + + // at this point, the dispatcher only lives in C++, as opposed to regular classes + // that are part of the hierarchy in Python, so create it, which will cache it for + // later use by e.g. the MemoryRegulator +- PyObject* disp_proxy = CPyCppyy::CreateScopeProxy(disp); ++ unsigned int flags = (unsigned int)(klass->fFlags & CPPScope::kIsMultiCross); ++ PyObject* disp_proxy = CPyCppyy::CreateScopeProxy(disp, flags); ++ if (flags) ((CPPScope*)disp_proxy)->fFlags |= CPPScope::kIsMultiCross; ++ ++// finally, to expose protected members, copy them over from the C++ dispatcher base ++// to the Python dictionary (the C++ dispatcher's Python proxy is not a base of the ++// Python class to keep the inheritance tree intact) ++ for (const auto& name : protected_names) { ++ PyObject* disp_dct = PyObject_GetAttr(disp_proxy, PyStrings::gDict); ++ PyObject* pyf = PyMapping_GetItemString(disp_dct, (char*)name.c_str()); ++ if (pyf) { ++ PyObject_SetAttrString((PyObject*)klass, (char*)name.c_str(), pyf); ++ Py_DECREF(pyf); ++ } ++ Py_DECREF(disp_dct); ++ } ++ + Py_XDECREF(disp_proxy); + + return true; +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.h b/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.h +index a9281117e1a..f8d4dc57131 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.h ++++ bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.h +@@ -1,12 +1,15 @@ + #ifndef CPYCPPYY_DISPATCHER_H + #define CPYCPPYY_DISPATCHER_H + ++// Standard ++#include <sstream> ++ + namespace CPyCppyy { + + class CPPScope; + + // helper that inserts dispatchers for virtual methods +-bool InsertDispatcher(CPPScope* klass, PyObject* dct); ++bool InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, std::ostringstream& err); + + } // namespace CPyCppyy + +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.cxx +index 8ea89a21ff1..cb819487b9e 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.cxx ++++ bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.cxx +@@ -141,7 +141,7 @@ static inline void sync_templates( + Py_DECREF(pyname); + } + +-static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass) ++static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, const unsigned int flags) + { + // Collect methods and data for the given scope, and add them to the given python + // proxy object. +@@ -226,7 +226,10 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass) + mtName = "__init__"; + hasConstructor = true; + if (!isAbstract) +- pycall = new CPPConstructor(scope, method); ++ if (flags & CPPScope::kIsMultiCross) { ++ pycall = new CPPMultiConstructor(scope, method); ++ } else ++ pycall = new CPPConstructor(scope, method); + else + pycall = new CPPAbstractClassConstructor(scope, method); + } else // member function +@@ -484,14 +487,14 @@ PyObject* CPyCppyy::GetScopeProxy(Cppyy::TCppScope_t scope) + } + + //---------------------------------------------------------------------------- +-PyObject* CPyCppyy::CreateScopeProxy(Cppyy::TCppScope_t scope) ++PyObject* CPyCppyy::CreateScopeProxy(Cppyy::TCppScope_t scope, const unsigned flags) + { + // Convenience function with a lookup first through the known existing proxies. + PyObject* pyclass = GetScopeProxy(scope); + if (pyclass) + return pyclass; + +- return CreateScopeProxy(Cppyy::GetScopedFinalName(scope)); ++ return CreateScopeProxy(Cppyy::GetScopedFinalName(scope), nullptr, flags); + } + + //---------------------------------------------------------------------------- +@@ -506,7 +509,7 @@ PyObject* CPyCppyy::CreateScopeProxy(PyObject*, PyObject* args) + } + + //---------------------------------------------------------------------------- +-PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent) ++PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent, const unsigned flags) + { + // Build a python shadow class for the named C++ class or namespace. + +@@ -670,7 +673,7 @@ PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent) + + // fill the dictionary, if successful + if (pyscope) { +- if (BuildScopeProxyDict(klass, pyscope)) { ++ if (BuildScopeProxyDict(klass, pyscope, flags)) { + // something failed in building the dictionary + Py_DECREF(pyscope); + pyscope = nullptr; +diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h b/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h +index a462064fa35..bf905f35260 100644 +--- bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h ++++ bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h +@@ -9,10 +9,10 @@ namespace CPyCppyy { + + // construct a Python shadow class for the named C++ class + PyObject* GetScopeProxy(Cppyy::TCppScope_t); +-PyObject* CreateScopeProxy(Cppyy::TCppScope_t); ++PyObject* CreateScopeProxy(Cppyy::TCppScope_t, const unsigned flags = 0); + PyObject* CreateScopeProxy(PyObject*, PyObject* args); + PyObject* CreateScopeProxy( +- const std::string& scope_name, PyObject* parent = nullptr); ++ const std::string& scope_name, PyObject* parent = nullptr, const unsigned flags = 0); + + // C++ exceptions form a special case b/c they have to derive from BaseException + PyObject* CreateExcScopeProxy(PyObject* pyscope, PyObject* pyname, PyObject* parent); +diff --git a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx +index ba4e7ad6bac..ae52fbe635c 100644 +--- bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx ++++ bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx +@@ -1763,6 +1763,15 @@ bool Cppyy::IsPublicMethod(TCppMethod_t method) + return false; + } + ++bool Cppyy::IsProtectedMethod(TCppMethod_t method) ++{ ++ if (method) { ++ TFunction* f = m2f(method); ++ return f->Property() & kIsProtected; ++ } ++ return false; ++} ++ + bool Cppyy::IsConstructor(TCppMethod_t method) + { + if (method) { +@@ -1954,6 +1963,17 @@ bool Cppyy::IsPublicData(TCppScope_t scope, TCppIndex_t idata) + return m->Property() & kIsPublic; + } + ++bool Cppyy::IsProtectedData(TCppScope_t scope, TCppIndex_t idata) ++{ ++ if (scope == GLOBAL_HANDLE) ++ return true; ++ TClassRef& cr = type_from_handle(scope); ++ if (cr->Property() & kIsNamespace) ++ return true; ++ TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); ++ return m->Property() & kIsProtected; ++} ++ + bool Cppyy::IsStaticData(TCppScope_t scope, TCppIndex_t idata) + { + if (scope == GLOBAL_HANDLE) +@@ -2469,6 +2489,10 @@ int cppyy_is_publicmethod(cppyy_method_t method) { + return (int)Cppyy::IsPublicMethod((Cppyy::TCppMethod_t)method); + } + ++int cppyy_is_protectedmethod(cppyy_method_t method) { ++ return (int)Cppyy::IsProtectedMethod((Cppyy::TCppMethod_t)method); ++} ++ + int cppyy_is_constructor(cppyy_method_t method) { + return (int)Cppyy::IsConstructor((Cppyy::TCppMethod_t)method); + } +@@ -2510,6 +2534,10 @@ int cppyy_is_publicdata(cppyy_type_t type, cppyy_index_t datamember_index) { + return (int)Cppyy::IsPublicData(type, datamember_index); + } + ++int cppyy_is_protecteddata(cppyy_type_t type, cppyy_index_t datamember_index) { ++ return (int)Cppyy::IsProtectedData(type, datamember_index); ++} ++ + int cppyy_is_staticdata(cppyy_type_t type, cppyy_index_t datamember_index) { + return (int)Cppyy::IsStaticData(type, datamember_index); + } +diff --git a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cpp_cppyy.h b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cpp_cppyy.h +index b22a36fc961..e9fd3b26ab8 100644 +--- bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cpp_cppyy.h ++++ bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cpp_cppyy.h +@@ -205,6 +205,8 @@ namespace Cppyy { + RPY_EXPORTED + bool IsPublicMethod(TCppMethod_t method); + RPY_EXPORTED ++ bool IsProtectedMethod(TCppMethod_t method); ++ RPY_EXPORTED + bool IsConstructor(TCppMethod_t method); + RPY_EXPORTED + bool IsDestructor(TCppMethod_t method); +@@ -227,6 +229,8 @@ namespace Cppyy { + RPY_EXPORTED + bool IsPublicData(TCppScope_t scope, TCppIndex_t idata); + RPY_EXPORTED ++ bool IsProtectedData(TCppScope_t scope, TCppIndex_t idata); ++ RPY_EXPORTED + bool IsStaticData(TCppScope_t scope, TCppIndex_t idata); + RPY_EXPORTED + bool IsConstData(TCppScope_t scope, TCppIndex_t idata); \ No newline at end of file