diff --git a/.gitignore b/.gitignore index 2e1fa7846f..2f3f11fd77 100755 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ results/* # Executables and build files /gambit build/* +build_*/ *_standalone # Generated cmake files diff --git a/BUILD_OPTIONS.md b/BUILD_OPTIONS.md index 1840613b9c..5ca243fc2c 100644 --- a/BUILD_OPTIONS.md +++ b/BUILD_OPTIONS.md @@ -26,6 +26,15 @@ For a more complete list of cmake variables, take a look in the file `CMakeCache -DBits="CosmoBit;DarkBit" # typical cosmology project +# Register Bits' module functors and the backend functors with the +# Core at link time instead of compiling their rollcall headers into +# the Core: LINK_TIME_REGISTRATION (On|Off, default Off) +# Editing a Bit's rollcall header then recompiles only that Bit's +# objects plus a relink, rather than the Core's largest translation +# units. See doc/link_time_registration.md. +-DLINK_TIME_REGISTRATION=On + + # List the FlexibleSUSY models to build: BUILD_FS_MODELS # The names of the available FlexibleSUSY models correspond to # the subdirectories in diff --git a/Backends/include/gambit/Backends/backend_info.hpp b/Backends/include/gambit/Backends/backend_info.hpp index b6d80907d7..412fe1cf5c 100644 --- a/Backends/include/gambit/Backends/backend_info.hpp +++ b/Backends/include/gambit/Backends/backend_info.hpp @@ -132,6 +132,12 @@ namespace Gambit /// Given a backend and a true version (with periods), return the safe version str safe_version_from_version (str, str) const; + /// Check whether a backend safe version (no periods) has been registered yet + bool has_safe_version (const str&, const str&) const; + + /// Check whether a backend true version (with periods) has been registered yet + bool has_version (const str&, const str&) const; + /// Link a backend's version and safe version void link_versions(str, str, str); diff --git a/Backends/include/gambit/Backends/ini_functions.hpp b/Backends/include/gambit/Backends/ini_functions.hpp index bd3f2173af..ba58883b1c 100644 --- a/Backends/include/gambit/Backends/ini_functions.hpp +++ b/Backends/include/gambit/Backends/ini_functions.hpp @@ -69,6 +69,14 @@ namespace Gambit /// Set the classloading requirements of a given functor. int set_classload_requirements(module_functor_common&, str, str, str); + /// Apply any deferred classloading requirements whose backend version + /// information has become available. + void process_deferred_classload_requirements(); + + /// Raise an error for any classloading requirement that is still unfulfilled + /// once static initialisation is over. + void check_deferred_classload_requirements(); + namespace Backends { diff --git a/Backends/registration/Backends_registration.cpp b/Backends/registration/Backends_registration.cpp new file mode 100644 index 0000000000..bbb30cf0ad --- /dev/null +++ b/Backends/registration/Backends_registration.cpp @@ -0,0 +1,56 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// the GAMBIT backends. +/// +/// This file expands the generated +/// backend_rollcall.hpp (i.e. every frontend +/// header) in the in-core macro context, exactly +/// as gambit.hpp used to do inside the Core, but +/// from within a translation unit owned by the +/// Backends directory. All backend functor +/// definitions, BackendIniBit module functors, +/// classloading bookkeeping and registration +/// calls produced by the backend macros are +/// therefore compiled into this object file, and +/// the registrations happen during static +/// initialisation, before main(). +/// +/// Unlike the per-Bit registration TUs, no +/// harvester change is involved: gambit.hpp +/// simply skips its #include of +/// backend_rollcall.hpp when the global +/// LINK_TIME_REGISTRATION compile definition is +/// set, and this TU includes it instead. +/// +/// This file is only compiled into the main +/// gambit executable, and only when the CMake +/// option LINK_TIME_REGISTRATION is ON. It must +/// NOT be compiled into the Backends object +/// library: standalone executables expand +/// backend_rollcall.hpp from their own main +/// translation unit via standalone_module.hpp +/// (with STANDALONE defined), and would suffer +/// duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Backends/backend_rollcall.hpp" + +#endif diff --git a/Backends/src/backend_info.cpp b/Backends/src/backend_info.cpp index a12e29b1ab..89364268a4 100644 --- a/Backends/src/backend_info.cpp +++ b/Backends/src/backend_info.cpp @@ -24,6 +24,7 @@ #include "gambit/cmake/cmake_variables.hpp" #include "gambit/Backends/backend_info.hpp" +#include "gambit/Backends/ini_functions.hpp" #include "gambit/Utils/util_functions.hpp" #include "gambit/Utils/python_interpreter.hpp" #include "gambit/Logs/logger.hpp" @@ -226,11 +227,32 @@ namespace Gambit return safe_version_map.at(be).second.at(v); } + /// Check whether a backend safe version (no periods) has been registered yet + bool Backends::backend_info::has_safe_version (const str& be, const str& sv) const + { + auto it = safe_version_map.find(be); + return it != safe_version_map.end() and it->second.first.find(sv) != it->second.first.end(); + } + + /// Check whether a backend true version (with periods) has been registered yet + bool Backends::backend_info::has_version (const str& be, const str& v) const + { + auto it = safe_version_map.find(be); + return it != safe_version_map.end() and it->second.second.find(v) != it->second.second.end(); + } + /// Link a backend's version and safe version void Backends::backend_info::link_versions(str be, str v, str sv) { safe_version_map[be].first[sv] = v; safe_version_map[be].second[v] = sv; + // A new backend version is available, so try to fulfil any classloading + // requirements that were deferred because this backend had not yet been + // registered. (With link-time registration, the static-initialisation + // order of module and backend registration translation units is + // unspecified, so a module's NEEDS_CLASSES_FROM may run before the + // backend it refers to has registered its versions.) + process_deferred_classload_requirements(); } /// Override a backend's config file location diff --git a/Backends/src/ini_functions.cpp b/Backends/src/ini_functions.cpp index 30d5578002..5b31c68c69 100644 --- a/Backends/src/ini_functions.cpp +++ b/Backends/src/ini_functions.cpp @@ -372,26 +372,108 @@ namespace Gambit } + /// A classloading requirement that could not be applied immediately, because + /// the backend it refers to had not yet registered its versions. This happens + /// with link-time registration, where the static-initialisation order of the + /// module and backend registration translation units is unspecified. + struct deferred_classload_request + { + module_functor_common* f; + str be; + str verstr; + str default_ver; + }; + + /// Pending classloading requirements (function-local static so that it is + /// safe to access during static initialisation). + std::vector& deferred_classload_requests() + { + static std::vector requests; + return requests; + } + + /// Try to apply a classloading requirement to a functor. Returns false + /// (without side effects on the functor) if the backend has not yet + /// registered all the version information that the requirement refers to. + bool try_set_classload_requirements(module_functor_common& f, const str& be, const str& verstr, const str& default_ver) + { + // Split up the passed version string into individual versions + std::vector versions = Utils::delimiterSplit(verstr, ","); + // First make sure every needed version is known, so that the requirement + // is applied either completely or not at all. + for (const str& v : versions) + { + if (v == "default") + { + if (not Backends::backendInfo().has_safe_version(be, default_ver)) return false; + } + else + { + if (not Backends::backendInfo().has_version(be, v)) return false; + } + } + // Add each version individually as required for classloading + for (str v : versions) + { + // Retrieve the version corresponding to the default if needed + if (v == "default") v = Backends::backendInfo().version_from_safe_version(be, default_ver); + // Retrieve the safe version corresponding to this version + str sv = Backends::backendInfo().safe_version_from_version(be, v); + // Set the requirement in the functor + f.setRequiredClassloader(be,v,sv); + } + return true; + } + /// Set the classloading requirements of a given functor. int set_classload_requirements(module_functor_common& f, str be, str verstr, str default_ver) { try { - // Split up the passed version string into individual versions - std::vector versions = Utils::delimiterSplit(verstr, ","); - // Add each version individually as required for classloading - for (auto it = versions.begin() ; it != versions.end(); ++it) + // If the backend's versions are not all registered yet, record the + // requirement passively; backend_info::link_versions retries the queue + // every time a backend registers a new version. + if (not try_set_classload_requirements(f, be, verstr, default_ver)) { - // Retrieve the version corresponding to the default if needed - if (*it == "default") *it = Backends::backendInfo().version_from_safe_version(be, default_ver); - // Retrieve the safe version corresponding to this version - str sv = Backends::backendInfo().safe_version_from_version(be, *it); - // Set the requirement in the functor - f.setRequiredClassloader(be,*it,sv); + deferred_classload_requests().push_back({&f, be, verstr, default_ver}); } } catch (std::exception& e) { ini_catch(e); } return 0; } + /// Apply any deferred classloading requirements whose backend version + /// information has become available. Called from + /// backend_info::link_versions whenever a backend registers a version. + void process_deferred_classload_requirements() + { + auto& requests = deferred_classload_requests(); + for (auto it = requests.begin(); it != requests.end(); ) + { + if (try_set_classload_requirements(*(it->f), it->be, it->verstr, it->default_ver)) + { + it = requests.erase(it); + } + else ++it; + } + } + + /// Raise an error for any classloading requirement that is still unfulfilled + /// once all backends have had the chance to register (i.e. once static + /// initialisation is over). Called from the Core before functor activation. + void check_deferred_classload_requirements() + { + for (const auto& request : deferred_classload_requests()) + { + std::ostringstream msg; + msg << "The classloading requirement NEEDS_CLASSES_FROM(" << request.be + << ", " << request.verstr << ") of module function " + << request.f->origin() << "::" << request.f->name() + << " refers to a backend version that never registered itself." + << "\nEither the backend \"" << request.be << "\" is not known to" + << "\nGAMBIT at all, or the requested versions do not exist."; + backend_error().raise(LOCAL_INFO, msg.str()); + } + } + } diff --git a/CMakeLists.txt b/CMakeLists.txt index 230dfe3b67..f9fad61601 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -610,6 +610,43 @@ include(cmake/externals.cmake) string (REPLACE ";" "," itch_with_commas "${itch}") +# Link-time (self-registering) module registration. Bits compile their own +# in-core rollcall macro expansions into a per-Bit registration translation +# unit (/registration/_registration.cpp) that is linked only into the gambit +# executable, instead of having their rollcall headers #included into the Core via the +# generated module_rollcall.hpp. Editing a Bit's rollcall header then +# recompiles only that Bit's objects (plus a relink), not the Core. +option(LINK_TIME_REGISTRATION "Register Bits' module functors and the backend functors with the Core at link time instead of compiling their rollcall headers into the Core" OFF) +set(LINK_TIME_REGISTRATION_BITS "") +set(LINK_TIME_REGISTRATION_COMPONENTS "") +if(LINK_TIME_REGISTRATION) + # Bits using link-time registration. A Bit can only be listed here if it has + # a registration translation unit at + # /registration/_registration.cpp. + foreach(bit ColliderBit CosmoBit DarkBit DecayBit ExampleBit_A ExampleBit_B FlavBit NeutrinoBit ObjectivesBit PrecisionBit SpecBit) + if(";${GAMBIT_BITS};" MATCHES ";${bit};") + list(APPEND LINK_TIME_REGISTRATION_BITS ${bit}) + endif() + endforeach() + set(LINK_TIME_REGISTRATION_COMPONENTS ${LINK_TIME_REGISTRATION_BITS}) + # The backends follow the same pattern, but are not a Bit: the generated + # backend_rollcall.hpp (all frontend headers) is expanded in a single + # registration TU instead of being #included into the Core by gambit.hpp. + # The harvesters are unaffected; gambit.hpp skips the include via the + # LINK_TIME_REGISTRATION compile definition. + if(EXISTS "${PROJECT_SOURCE_DIR}/Backends/") + list(APPEND LINK_TIME_REGISTRATION_COMPONENTS Backends) + endif() +endif() +if(LINK_TIME_REGISTRATION_COMPONENTS) + message("${BoldYellow}-- Link-time registration enabled for: ${LINK_TIME_REGISTRATION_COMPONENTS}${ColourReset}") + add_definitions(-DLINK_TIME_REGISTRATION=1) +endif() +if(LINK_TIME_REGISTRATION_BITS) + string(REPLACE ";" "," ltr_bits_with_commas "${LINK_TIME_REGISTRATION_BITS}") + set(MODULE_HARVESTER_EXTRA_ARGS -r ${ltr_bits_with_commas}) +endif() + if(EXISTS "${PROJECT_SOURCE_DIR}/Elements/") add_gambit_custom(module_harvest modules_harvested MODULE_HARVESTER MODULE_HARVESTER_FILES ${itch_with_commas}) endif() diff --git a/ColliderBit/examples/functors_for_CBS.cpp b/ColliderBit/examples/functors_for_CBS.cpp new file mode 100644 index 0000000000..cbc99e61db --- /dev/null +++ b/ColliderBit/examples/functors_for_CBS.cpp @@ -0,0 +1,93 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Explicit functor template class +/// instantiations needed by standalone program +/// CBS. +/// +/// This file was automatically generated by +/// standalone_facilitator.py. Do not modify. +/// The content is harvested from the rollcall +/// headers registered in module_rollcall.hpp +/// and the types registered in +/// types_rollcall.hpp. +/// +/// ********************************************* +/// +/// Authors: +/// +/// \author The GAMBIT Collaboration +/// \date 09:56AM on June 12, 2026 +/// +/// ********************************************* + +#include "gambit/Elements/functor_definitions.hpp" +#include "gambit/Elements/types_rollcall.hpp" +#include "gambit/Backends/backend_functor_types.hpp" + +namespace Gambit +{ + // Non-module types + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor>; + template class module_functor; + template class module_functor>; + template class module_functor>; + template class module_functor>; + template class module_functor>; + template class module_functor; + // Module types + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; + template class module_functor; +} + +// Define standalone versions of functor signal helpers (that do nothing) +namespace Gambit +{ + namespace FunctorHelp + { + void check_for_shutdown_signal(module_functor_common&) {} + bool emergency_shutdown_begun() { return false; } + void entering_multithreaded_region(module_functor_common&) {} + void leaving_multithreaded_region(module_functor_common&) {} + } +} diff --git a/ColliderBit/registration/ColliderBit_registration.cpp b/ColliderBit/registration/ColliderBit_registration.cpp new file mode 100644 index 0000000000..f883f511c3 --- /dev/null +++ b/ColliderBit/registration/ColliderBit_registration.cpp @@ -0,0 +1,59 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// ColliderBit. +/// +/// This file expands ColliderBit's rollcall +/// header tree (including the sub-rollcall +/// headers and the generated +/// ColliderBit_models_rollcall.hpp) in the +/// in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits ColliderBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the ColliderBit object +/// library: standalone executables (CBS) obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// The conditional-compilation guards used in the +/// ColliderBit rollcall headers (HAVE_PYBIND11, +/// EXCLUDE_HEPMC, EXCLUDE_YODA) all come from the +/// generated cmake_variables.hpp, so this TU sees +/// exactly the same configuration as the module's +/// own object files and the legacy in-core +/// expansion. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/ColliderBit/ColliderBit_rollcall.hpp" + +#endif diff --git a/Core/include/gambit/Core/gambit.hpp b/Core/include/gambit/Core/gambit.hpp index a8c64c56c7..f6feda5ddd 100644 --- a/Core/include/gambit/Core/gambit.hpp +++ b/Core/include/gambit/Core/gambit.hpp @@ -25,7 +25,12 @@ #include "gambit/Utils/stream_overloads.hpp" #include "gambit/Models/model_rollcall.hpp" #include "gambit/Elements/equivalency_singleton.hpp" -#include "gambit/Backends/backend_rollcall.hpp" +#ifndef LINK_TIME_REGISTRATION + /* With link-time registration, the backend functors are registered from + Backends/registration/Backends_registration.cpp instead of being compiled + into the Core here. */ + #include "gambit/Backends/backend_rollcall.hpp" +#endif #include "gambit/Core/depresolver.hpp" #include "gambit/Core/yaml_parser.hpp" #include "gambit/Core/likelihood_container.hpp" diff --git a/Core/src/core.cpp b/Core/src/core.cpp index 8d9f1bd3a2..2b5f6998cf 100644 --- a/Core/src/core.cpp +++ b/Core/src/core.cpp @@ -42,6 +42,7 @@ #include "gambit/Core/cli_help_text.hpp" #include "gambit/Core/error_handlers.hpp" #include "gambit/Core/yaml_description_database.hpp" +#include "gambit/Backends/ini_functions.hpp" #include "gambit/ScannerBit/plugin_loader.hpp" #include "gambit/Utils/stream_overloads.hpp" #include "gambit/Utils/util_functions.hpp" @@ -241,6 +242,9 @@ namespace Gambit /// that is supposed to be provided by a backend that is AWOL. void gambit_core::accountForMissingClasses() const { + // First raise an error for any classloading requirement that referred to a + // backend version that never registered itself during static initialisation. + check_deferred_classload_requirements(); // Create a map of all the registered backends that are connected and fully functional (including factories for classloading) std::map> working_bes; // Start by looping over all registered backends diff --git a/CosmoBit/registration/CosmoBit_registration.cpp b/CosmoBit/registration/CosmoBit_registration.cpp new file mode 100644 index 0000000000..11e33c24ba --- /dev/null +++ b/CosmoBit/registration/CosmoBit_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// CosmoBit. +/// +/// This file expands CosmoBit's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits CosmoBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the CosmoBit object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/CosmoBit/CosmoBit_rollcall.hpp" + +#endif diff --git a/DarkBit/registration/DarkBit_registration.cpp b/DarkBit/registration/DarkBit_registration.cpp new file mode 100644 index 0000000000..db50ed1208 --- /dev/null +++ b/DarkBit/registration/DarkBit_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// DarkBit. +/// +/// This file expands DarkBit's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits DarkBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the DarkBit object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/DarkBit/DarkBit_rollcall.hpp" + +#endif diff --git a/DecayBit/registration/DecayBit_registration.cpp b/DecayBit/registration/DecayBit_registration.cpp new file mode 100644 index 0000000000..3c157f2fad --- /dev/null +++ b/DecayBit/registration/DecayBit_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// DecayBit. +/// +/// This file expands DecayBit's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits DecayBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the DecayBit object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/DecayBit/DecayBit_rollcall.hpp" + +#endif diff --git a/Elements/include/gambit/Elements/module_macros_incore_defs.hpp b/Elements/include/gambit/Elements/module_macros_incore_defs.hpp index dee0c4bf91..ff4a354a7b 100644 --- a/Elements/include/gambit/Elements/module_macros_incore_defs.hpp +++ b/Elements/include/gambit/Elements/module_macros_incore_defs.hpp @@ -72,6 +72,12 @@ #include "gambit/Utils/exceptions.hpp" #include "gambit/Utils/python_interpreter.hpp" #include "gambit/Backends/backend_singleton.hpp" +/* Declares set_classload_requirements and set_backend_rule_for_model, used by the + CORE_CLASSLOAD_NEEDED and CORE_BE_MODEL_RULE macros below. In the legacy path this + header arrives implicitly because gambit.hpp includes backend_rollcall.hpp before + module_rollcall.hpp; link-time registration TUs include nothing beforehand, so the + in-core macro machinery must be self-contained. */ +#include "gambit/Backends/ini_functions.hpp" #include "gambit/Models/claw_singleton.hpp" #include "gambit/Models/safe_param_map.hpp" #ifndef STANDALONE diff --git a/Elements/scripts/module_harvester.py b/Elements/scripts/module_harvester.py index 8bce380917..14f0250d33 100644 --- a/Elements/scripts/module_harvester.py +++ b/Elements/scripts/module_harvester.py @@ -58,15 +58,24 @@ def main(argv): # Lists of modules to exclude; anything starting with one of these strings is excluded. exclude_modules=set([]) + # Modules that register their functors with the Core at link time, from their own + # registration translation unit. These are harvested as normal (types, diagnostics, + # standalone pickles, etc.), but their rollcall headers are not included in + # module_rollcall.hpp, so they are not compiled into the Core. + link_time_modules=set([]) + # Handle command line options verbose = False try: - opts, args = getopt.getopt(argv,"vx:",["verbose","exclude-modules="]) + opts, args = getopt.getopt(argv,"vx:r:",["verbose","exclude-modules=","link-time-registration="]) except getopt.GetoptError: print('Usage: module_harvestor.py [flags]') print(' flags:') print(' -v : More verbose output') print(' -x module1,module2,... : Exclude module1, module2, etc.') + print(' -r module1,module2,... : Omit rollcall headers of module1, module2, etc.') + print(' from module_rollcall.hpp; these modules register') + print(' their functors with the Core at link time.') sys.exit(2) for opt, arg in opts: if opt in ('-v','--verbose'): @@ -74,6 +83,8 @@ def main(argv): print('module_harvester.py: verbose=True') elif opt in ('-x','--exclude-modules'): exclude_modules.update(neatsplit(",",arg)) + elif opt in ('-r','--link-time-registration'): + link_time_modules.update(neatsplit(",",arg)) exclude_header = exclude_modules module_rollcall_headers=set([]) module_type_headers=set([]) @@ -112,7 +123,7 @@ def main(argv): equiv_classes = get_type_equivalencies(equiv_ns) # Get list of rollcall header files to search - module_rollcall_headers.update(retrieve_rollcall_headers(verbose,".",exclude_header)) + module_rollcall_headers.update(retrieve_rollcall_headers(verbose,".",exclude_header,link_time_modules=link_time_modules)) rollcall_headers.update(module_rollcall_headers) # Get list of module type header files to search module_type_headers.update(retrieve_module_type_headers(verbose,".",exclude_header)) diff --git a/Elements/scripts/standalone_facilitator.py b/Elements/scripts/standalone_facilitator.py index 88cac56bf0..16df5337a4 100644 --- a/Elements/scripts/standalone_facilitator.py +++ b/Elements/scripts/standalone_facilitator.py @@ -99,11 +99,11 @@ def main(argv): { \n\ // Non-module types \n\ template class module_functor; \n" - for t in returned_types["non_module"]: + for t in sorted(returned_types["non_module"]): towrite += " template class module_functor<"+t+">;\n" if all_types: towrite += " // Module types\n" - for t in all_types: towrite += " template class module_functor<"+t+">;\n" + for t in sorted(all_types): towrite += " template class module_functor<"+t+">;\n" else: towrite += " // No module-specific types required.\n" towrite += "}\n\n\ diff --git a/ExampleBit_A/registration/ExampleBit_A_registration.cpp b/ExampleBit_A/registration/ExampleBit_A_registration.cpp new file mode 100644 index 0000000000..8c7860783d --- /dev/null +++ b/ExampleBit_A/registration/ExampleBit_A_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// ExampleBit_A. +/// +/// This file expands ExampleBit_A's rollcall +/// header in the in-core macro context, exactly +/// as the generated module_rollcall.hpp would do +/// inside the Core, but from within a translation +/// unit owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits ExampleBit_A's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the ExampleBit_A object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/ExampleBit_A/ExampleBit_A_rollcall.hpp" + +#endif diff --git a/ExampleBit_B/registration/ExampleBit_B_registration.cpp b/ExampleBit_B/registration/ExampleBit_B_registration.cpp new file mode 100644 index 0000000000..b1d4a5cb83 --- /dev/null +++ b/ExampleBit_B/registration/ExampleBit_B_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// ExampleBit_B. +/// +/// This file expands ExampleBit_B's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits ExampleBit_B's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the ExampleBit_B object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/ExampleBit_B/ExampleBit_B_rollcall.hpp" + +#endif diff --git a/FlavBit/registration/FlavBit_registration.cpp b/FlavBit/registration/FlavBit_registration.cpp new file mode 100644 index 0000000000..8beef3cd4a --- /dev/null +++ b/FlavBit/registration/FlavBit_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// FlavBit. +/// +/// This file expands FlavBit's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits FlavBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the FlavBit object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/FlavBit/FlavBit_rollcall.hpp" + +#endif diff --git a/NeutrinoBit/registration/NeutrinoBit_registration.cpp b/NeutrinoBit/registration/NeutrinoBit_registration.cpp new file mode 100644 index 0000000000..b50d8b2563 --- /dev/null +++ b/NeutrinoBit/registration/NeutrinoBit_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// NeutrinoBit. +/// +/// This file expands NeutrinoBit's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits NeutrinoBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the NeutrinoBit object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/NeutrinoBit/NeutrinoBit_rollcall.hpp" + +#endif diff --git a/ObjectivesBit/registration/ObjectivesBit_registration.cpp b/ObjectivesBit/registration/ObjectivesBit_registration.cpp new file mode 100644 index 0000000000..058b534281 --- /dev/null +++ b/ObjectivesBit/registration/ObjectivesBit_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// ObjectivesBit. +/// +/// This file expands ObjectivesBit's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits ObjectivesBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the ObjectivesBit object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/ObjectivesBit/ObjectivesBit_rollcall.hpp" + +#endif diff --git a/PrecisionBit/registration/PrecisionBit_registration.cpp b/PrecisionBit/registration/PrecisionBit_registration.cpp new file mode 100644 index 0000000000..7b3a32b333 --- /dev/null +++ b/PrecisionBit/registration/PrecisionBit_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// PrecisionBit. +/// +/// This file expands PrecisionBit's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits PrecisionBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the PrecisionBit object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/PrecisionBit/PrecisionBit_rollcall.hpp" + +#endif diff --git a/SpecBit/registration/SpecBit_registration.cpp b/SpecBit/registration/SpecBit_registration.cpp new file mode 100644 index 0000000000..3d8733521d --- /dev/null +++ b/SpecBit/registration/SpecBit_registration.cpp @@ -0,0 +1,48 @@ +// GAMBIT: Global and Modular BSM Inference Tool +// ********************************************* +/// \file +/// +/// Link-time registration translation unit for +/// SpecBit. +/// +/// This file expands SpecBit's rollcall header +/// in the in-core macro context, exactly as the +/// generated module_rollcall.hpp would do inside +/// the Core, but from within a translation unit +/// owned by the module itself. All functor +/// definitions and registration calls produced by +/// the in-core macros are therefore compiled into +/// this object file, and the registrations happen +/// during static initialisation, before main(). +/// +/// This file is only compiled into the main gambit +/// executable, and only when the CMake option +/// LINK_TIME_REGISTRATION is ON (in which case the +/// module harvester omits SpecBit's rollcall +/// header from module_rollcall.hpp). It must NOT +/// be compiled into the SpecBit object +/// library: standalone executables obtain +/// equivalent functor definitions from their own +/// main translation unit via standalone_module.hpp, +/// and would suffer duplicate-symbol errors. +/// +/// ********************************************* +/// +/// Authors (add name and date if you modify): +/// +/// \author The GAMBIT Collaboration +/// \date 2026 Jun +/// +/// ********************************************* + +#ifdef LINK_TIME_REGISTRATION + + /* The static members defined by static_members.hpp (pulled in via the in-core + macros) are provided by the main gambit translation unit; defining them here + too would break the link with duplicate definitions. */ + #define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS 1 + + #include "gambit/Elements/module_macros_incore.hpp" + #include "gambit/SpecBit/SpecBit_rollcall.hpp" + +#endif diff --git a/Utils/include/gambit/Utils/static_members.hpp b/Utils/include/gambit/Utils/static_members.hpp index a5209d6939..0261217c57 100644 --- a/Utils/include/gambit/Utils/static_members.hpp +++ b/Utils/include/gambit/Utils/static_members.hpp @@ -21,6 +21,13 @@ #include "gambit/Utils/threadsafe_rng.hpp" #include "gambit/Utils/exceptions.hpp" +// This header *defines* static members, so it must contribute them from exactly +// one translation unit per executable (the main program TU). Translation units +// that need the in-core rollcall macros but must not define the static members +// (e.g. the per-Bit link-time registration TUs, which link alongside the main +// gambit TU) define GAMBIT_NO_STATIC_MEMBER_DEFINITIONS before including this. +#ifndef GAMBIT_NO_STATIC_MEMBER_DEFINITIONS + namespace Gambit { @@ -32,5 +39,7 @@ namespace Gambit } +#endif //#ifndef GAMBIT_NO_STATIC_MEMBER_DEFINITIONS + #endif //#ifndef __static_members_hpp__ diff --git a/Utils/scripts/harvesting_tools.py b/Utils/scripts/harvesting_tools.py index e2fef76e04..ab45795b8a 100644 --- a/Utils/scripts/harvesting_tools.py +++ b/Utils/scripts/harvesting_tools.py @@ -567,11 +567,17 @@ def find_and_harvest_headers(header_set, fullheadlist, exclude_set, dir_exclude_ new_headers, fullheadlist, new_exclude_set, dir_exclude_set, verbose=verbose) -def retrieve_rollcall_headers(verbose, install_dir, excludes, retrieve_excluded=False): +def retrieve_rollcall_headers(verbose, install_dir, excludes, retrieve_excluded=False, link_time_modules=set()): """Search the source tree to determine which modules are present, and write a module_rollcall header if the GAMBIT Core exists. If the option `retrieve_excluded` is set to true, it will search for excluded modules. This feature is used for the diagnostic system. + + Modules listed in `link_time_modules` are still harvested (for functor types, + diagnostics, printers, etc.), but their rollcall headers are omitted from the + include list in module_rollcall.hpp: such modules register their functors with + the Core at link time, from their own registration translation unit, rather + than being compiled into the Core. """ rollcall_headers = [] core_exists = False @@ -605,7 +611,7 @@ def retrieve_rollcall_headers(verbose, install_dir, excludes, retrieve_excluded= ".*?/include/", "", os.path.relpath(os.path.join(root, name), install_dir)) rollcall_headers += [rel_name] if core_exists and not retrieve_excluded: - make_module_rollcall(rollcall_headers, verbose) + make_module_rollcall(rollcall_headers, verbose, link_time_modules) return rollcall_headers @@ -900,8 +906,13 @@ def update_only_if_different(existing, candidate, verbose=True): if verbose: print( "\033[1;33m Updated "+re.sub("\\.\\/","",existing)+"\033[0m" ) -def make_module_rollcall(rollcall_headers, verbose): - """Create the module_rollcall header in the Core directory""" +def make_module_rollcall(rollcall_headers, verbose, link_time_modules=set()): + """Create the module_rollcall header in the Core directory. + + Rollcall headers of modules in `link_time_modules` are not included; those + modules compile their own in-core macro expansions into a registration + translation unit that self-registers with the Core at static-init time. + """ towrite = """// GAMBIT: Global and Modular BSM Inference Tool // ********************************************* /// \\file @@ -938,15 +949,20 @@ def make_module_rollcall(rollcall_headers, verbose): """ for h in sorted(rollcall_headers): - towrite += '#include \"{0}\"\n'.format(h) + h_module = neatsplit('\\/',h)[1] + if h_module in link_time_modules: + towrite += '// {0} registers at link time; its rollcall header is not compiled into the Core.\n'.format(h_module) + else: + towrite += '#include \"{0}\"\n'.format(h) towrite += "\n#endif // defined __module_rollcall_hpp__\n" - # Don't touch any existing file unless it is actually different from what we will create + # Don't touch any existing file unless it is actually different from what we will create, + # so that the Core is not needlessly recompiled. if not os.path.isdir("./scratch/build_time"): os.makedirs("./scratch/build_time") header = "./Core/include/gambit/Core/module_rollcall.hpp" candidate = "./scratch/build_time/module_rollcall.hpp.candidate" with open(candidate,"w") as f: f.write(towrite) - update_only_if_different(header, candidate) + update_only_if_different(header, candidate, verbose=False) if verbose: print("Found GAMBIT Core. Generated module_rollcall.hpp.\n") diff --git a/cmake/contrib.cmake b/cmake/contrib.cmake index 08db830efb..4556d15a15 100644 --- a/cmake/contrib.cmake +++ b/cmake/contrib.cmake @@ -171,9 +171,18 @@ set(LHEF_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/contrib/LHEF") include_directories("${LHEF_INCLUDE_DIR}") #contrib/HepMC3; include only if ColliderBit is in use. +# An explicit -DWITH_HEPMC=OFF is honoured even when ColliderBit is in use, for +# environments where the HepMC download is impossible; HepMC-dependent functions +# in ColliderBit, CBS, and HepMC-dependent backends are then disabled. if(";${GAMBIT_BITS};" MATCHES ";ColliderBit;") - message(" ColliderBit included, so HepMC is included too") - set(WITH_HEPMC ON) + if(DEFINED WITH_HEPMC AND NOT WITH_HEPMC) + message("${BoldRed} WITH_HEPMC=OFF requested: building ColliderBit without HepMC.${ColourReset}") + set(WITH_HEPMC OFF) + set(WITH_HEPMC_USER_DISABLED TRUE) + else() + message(" ColliderBit included, so HepMC is included too") + set(WITH_HEPMC ON) + endif() else() set(WITH_HEPMC OFF) message("${BoldCyan} X ColliderBit is not in use: excluding HepMC from GAMBIT configuration.${ColourReset}") @@ -232,10 +241,17 @@ if(NOT EXCLUDE_HEPMC) add_contrib_clean_and_nuke(${name} ${HEPMC_PATH} clean) endif() -#contrib/YODA; include if ColliderBit is in, don't otherwise +#contrib/YODA; include if ColliderBit is in, don't otherwise. +# As for HepMC above, an explicit -DWITH_YODA=OFF is honoured. if(";${GAMBIT_BITS};" MATCHES ";ColliderBit;") - message(" ColliderBit included, so YODA is included too") - set(WITH_YODA ON) + if(DEFINED WITH_YODA AND NOT WITH_YODA) + message("${BoldRed} WITH_YODA=OFF requested: building ColliderBit without YODA.${ColourReset}") + set(WITH_YODA OFF) + set(WITH_YODA_USER_DISABLED TRUE) + else() + message(" ColliderBit included, so YODA is included too") + set(WITH_YODA ON) + endif() else() set(WITH_YODA OFF) message("${BoldCyan} X ColliderBit is not in use: excluding YODA from GAMBIT configuration.${ColourReset}") @@ -521,14 +537,24 @@ if(";${GAMBIT_BITS};" MATCHES ";ColliderBit;") if(NOT EXCLUDE_RESTFRAMES) add_dependencies(contrib restframes) endif() - # contrib depends on HepMC + # contrib depends on HepMC, unless the user explicitly disabled it if(EXCLUDE_HEPMC) - message(FATAL_ERROR "\nColliderBit needs HepMC3. Either use -DWITH_HEPMC=ON or ditch ColliderBit with -Ditch=\"ColliderBit\".") + if(WITH_HEPMC_USER_DISABLED) + message("${BoldRed} Proceeding without HepMC at the user's explicit request; HepMC-dependent ColliderBit functions are disabled.${ColourReset}") + else() + message(FATAL_ERROR "\nColliderBit needs HepMC3. Either use -DWITH_HEPMC=ON or ditch ColliderBit with -Ditch=\"ColliderBit\".") + endif() + else() + add_dependencies(contrib hepmc) endif() - add_dependencies(contrib hepmc) - # contrib depends on YODA + # contrib depends on YODA, unless the user explicitly disabled it if(EXCLUDE_YODA) - message(FATAL_ERROR "\nColliderBit needs YODA. Either use -DWITH_YODA=ON or ditch ColliderBit with -Ditch=\"ColliderBit\".") + if(WITH_YODA_USER_DISABLED) + message("${BoldRed} Proceeding without YODA at the user's explicit request; YODA-dependent ColliderBit functions are disabled.${ColourReset}") + else() + message(FATAL_ERROR "\nColliderBit needs YODA. Either use -DWITH_YODA=ON or ditch ColliderBit with -Ditch=\"ColliderBit\".") + endif() + else() + add_dependencies(contrib yoda) endif() - add_dependencies(contrib yoda) endif() diff --git a/cmake/executables.cmake b/cmake/executables.cmake index 8412dc8ced..d9c2a5ab3b 100644 --- a/cmake/executables.cmake +++ b/cmake/executables.cmake @@ -50,8 +50,19 @@ if(EXISTS "${PROJECT_SOURCE_DIR}/Core/") if (NOT EXCLUDE_YODA) set(gambit_XTRA ${gambit_XTRA} ${YODA_LDFLAGS}) endif() + # Registration translation units for components using link-time registration + # (the Bits, and the backends as a whole). These compile the in-core + # expansion of each component's rollcall header(s), and are linked only into + # the gambit executable: standalone executables get equivalent functor + # definitions from their own main translation unit (via standalone_module.hpp), + # so these sources must not be added to the components' own object libraries. + set(GAMBIT_LTR_SOURCES "") + foreach(component ${LINK_TIME_REGISTRATION_COMPONENTS}) + list(APPEND GAMBIT_LTR_SOURCES ${PROJECT_SOURCE_DIR}/${component}/registration/${component}_registration.cpp) + endforeach() add_gambit_executable(${PROJECT_NAME} "${gambit_XTRA}" SOURCES ${PROJECT_SOURCE_DIR}/Core/src/gambit.cpp + ${GAMBIT_LTR_SOURCES} ${GAMBIT_ALL_COMMON_OBJECTS} ${GAMBIT_BIT_OBJECTS} $