diff --git a/.github/workflows/cling-test.yml b/.github/workflows/cling-test.yml new file mode 100644 index 0000000000000..fc6a1f6978ee7 --- /dev/null +++ b/.github/workflows/cling-test.yml @@ -0,0 +1,117 @@ +name: 'Cling Test CI' + +on: + pull_request: + paths: + - 'interpreter/cling/**' + - '.github/workflows/cling-test.yml' + +env: + PYTHONUNBUFFERED: true + OS_APPLICATION_CREDENTIAL_ID: '7f5b64a265244623a3a933308569bdba' + OS_APPLICATION_CREDENTIAL_SECRET: ${{ secrets.OS_APPLICATION_CREDENTIAL_SECRET }} + OS_AUTH_TYPE: 'v3applicationcredential' + OS_AUTH_URL: 'https://keystone.cern.ch/v3' + OS_IDENTITY_API_VERSION: 3 + OS_INTERFACE: 'public' + OS_REGION_NAME: 'cern' + GLOBAL_OVERRIDES: 'asserts=ON LLVM_ENABLE_ASSERTIONS=ON' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + check-cling: + # For any event that is not a PR, the CI will always run. In PRs, the CI + # can be skipped if the tag [skip-ci] or [skip ci] is written in the title. + if: | + (github.repository_owner == 'root-project' && github.event_name != 'pull_request') || + (github.event_name == 'pull_request' && !( + contains(github.event.pull_request.title, '[skip-ci]') || + contains(github.event.pull_request.title, '[skip ci]') || + contains(github.event.pull_request.labels.*.name, 'skip ci') + )) + + permissions: + contents: read + + strategy: + fail-fast: false + matrix: + # Specify image + (optional) build option overrides + # + # Available images: https://github.com/root-project/root-ci-images + # Common configs: {Release,Debug,RelWithDebInfo) + # Build options: https://root.cern/install/build_from_source/#all-build-options + include: + - image: alma10 + platform_config: alma10-minimal + + runs-on: + - self-hosted + - linux + - ${{ matrix.architecture == null && 'x64' || matrix.architecture }} + - ${{ matrix.extra-runs-on == null && 'cpu' || matrix.extra-runs-on }} + + name: Cling Tests + + container: + image: registry.cern.ch/root-ci/${{ matrix.image }}:buildready # KEEP IN SYNC WITH env key below + options: --security-opt label=disable --rm ${{ matrix.property == 'gpu' && '--device nvidia.com/gpu=all' || '' }} # KEEP IN SYNC WITH env key below + volumes: + - ${{ matrix.image }}_ccache_volume:/github/home/.cache/ccache + env: + CONTAINER_IMAGE: "registry.cern.ch/root-ci/${{ matrix.image }}:buildready" #KEEP IN SYNC WITH ABOVE + CONTAINER_OPTIONS: "--security-opt label=disable --rm ${{ matrix.property == 'gpu' && '--device nvidia.com/gpu=all' || '' }}" #KEEP IN SYNC WITH ABOVE + + steps: + - name: Configure large ccache + if: ${{ matrix.is_special }} + run: | + ccache -o max_size=5G + ccache -p || true + ccache -s || true + + - name: Configure small ccache + if: ${{ !matrix.is_special }} + run: | + ccache -o max_size=1.5G + ccache -p || true + ccache -s || true + + - name: Set up Python Virtual Env + # if the `if` expr is false, `if` still has exit code 0. + # if the `if` block is entered, the block's exit code becomes the exit + # code of the `if`. + run: 'if [ -d /py-venv/ROOT-CI/bin/ ]; then . /py-venv/ROOT-CI/bin/activate && echo PATH=$PATH >> $GITHUB_ENV; fi' + + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref_name }} + + - name: Pull Request Build + env: + INCREMENTAL: ${{ !contains(github.event.pull_request.labels.*.name, 'clean build') }} + GITHUB_PR_ORIGIN: ${{ github.event.pull_request.head.repo.clone_url }} + CMAKE_GENERATOR: ${{ matrix.cmake_generator }} + OVERRIDES: ${{ join( matrix.overrides, ' ') }} + run: ".github/workflows/root-ci-config/build_root.py + --buildtype RelWithDebInfo + --platform ${{ matrix.image }} + --dockeropts \"$CONTAINER_OPTIONS\" + --incremental $INCREMENTAL + --base_ref ${{ github.base_ref }} + --sha ${{ github.sha }} + --pull_repository ${{ github.event.pull_request.head.repo.clone_url }} + --head_ref refs/pull/${{ github.event.pull_request.number }}/head:${{ github.event.pull_request.head.ref }} + --head_sha ${{ github.event.pull_request.head.sha }} + --repository ${{ github.server_url }}/${{ github.repository }} + --overrides ${GLOBAL_OVERRIDES} ${OVERRIDES} + --clingtests_only true + " + + - name: ccache info (post) + run: | + ccache -s || true diff --git a/.github/workflows/root-ci-config/build_root.py b/.github/workflows/root-ci-config/build_root.py index 77f7cc6028edc..0bf996c111264 100755 --- a/.github/workflows/root-ci-config/build_root.py +++ b/.github/workflows/root-ci-config/build_root.py @@ -102,6 +102,9 @@ def main(): ctest_custom_flags = options_dict['ROOT_CTEST_CUSTOM_FLAGS'] options_dict.pop('ROOT_CTEST_CUSTOM_FLAGS') # we do not want a -D called like that + if args.clingtests_only: + options_dict['clingtest'] = "ON" + options = build_utils.cmake_options_from_dict(options_dict) print("Full build options") for key, val in sorted(options_dict.items()): @@ -165,7 +168,7 @@ def main(): # Delete all the .gcda files produces by an artefact. build_utils.remove_file_match_ext(WORKDIR, "gcda") - build(options, args.buildtype) + build(options, args.buildtype, args.clingtests_only) # Build artifacts should only be uploaded for full builds, and only for # "official" branches (master, v?-??-??-patches), i.e. not for pull_request @@ -177,7 +180,7 @@ def main(): if args.binaries: create_binaries(args.buildtype) - if testing: + if testing and not args.clingtests_only: extra_ctest_flags = '' if WINDOWS: extra_ctest_flags += '--repeat until-pass:5 ' @@ -191,7 +194,7 @@ def main(): if args.coverage: create_coverage_xml() - if testing and ctest_returncode != 0: + if testing and not args.clingtests_only and ctest_returncode != 0: handle_test_failure(ctest_returncode) print_trace() @@ -220,6 +223,7 @@ def parse_args(): parser.add_argument("--dockeropts", default=None, help="Extra docker options, if any") parser.add_argument("--incremental", default="false", help="Do incremental build") parser.add_argument("--buildtype", default="Release", help="Release|Debug|RelWithDebInfo") + parser.add_argument("--clingtests_only", default="false", help="Run only clingtests") parser.add_argument("--coverage", default="false", help="Create Coverage report in XML") parser.add_argument("--sha", default=None, help="sha that triggered the event") parser.add_argument("--base_ref", default=None, help="Ref to target branch") @@ -236,6 +240,7 @@ def parse_args(): # Set argument to True if matched args.incremental = args.incremental.lower() in ('yes', 'true', '1', 'on') + args.clingtests_only = args.clingtests_only.lower() in ('yes', 'true', '1', 'on') args.coverage = args.coverage.lower() in ('yes', 'true', '1', 'on') args.binaries = args.binaries.lower() in ('yes', 'true', '1', 'on') @@ -429,20 +434,23 @@ def dump_requested_config(options): @github_log_group("Build") -def cmake_build(buildtype): +def cmake_build(buildtype, clingtests_only = False): generator_flags = "-- '-verbosity:minimal'" if WINDOWS else "" parallel_jobs = "4" if WINDOWS else str(os.cpu_count()) builddir = os.path.join(WORKDIR, "build") - result = subprocess_with_log(f""" - cmake --build '{builddir}' --config '{buildtype}' --parallel '{parallel_jobs}' {generator_flags} - """) + command = f""" + cmake --build '{builddir}' --config '{buildtype}' --parallel '{parallel_jobs}' {generator_flags}""" + if clingtests_only: + command += " --target check-cling" + + result = subprocess_with_log(command) if result != 0: die(result, "Failed to build") -def build(options, buildtype): +def build(options, buildtype, clingtests_only = False): if not os.path.isdir(os.path.join(WORKDIR, "build")): builddir = os.path.join(WORKDIR, "build") result = subprocess_with_log(f"mkdir {builddir}") @@ -457,7 +465,7 @@ def build(options, buildtype): dump_requested_config(options) - cmake_build(buildtype) + cmake_build(buildtype, clingtests_only) @github_log_group("Create binary packages") diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index a0638cec307bd..a23c08a3432cb 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -6,6 +6,7 @@ on: branches: - '**' paths-ignore: + - '**' - 'doc/**' - 'documentation/**' - 'README/ReleaseNotes/**' diff --git a/interpreter/cling/include/cling/Interpreter/RuntimePrintValue.h b/interpreter/cling/include/cling/Interpreter/RuntimePrintValue.h index 43d9dffd72f88..5ce504e0c768c 100644 --- a/interpreter/cling/include/cling/Interpreter/RuntimePrintValue.h +++ b/interpreter/cling/include/cling/Interpreter/RuntimePrintValue.h @@ -15,6 +15,7 @@ #endif #include +#include #if __cplusplus >= 201703L #include @@ -152,7 +153,12 @@ namespace cling { #ifdef __cpp_lib_source_location CLING_LIB_EXPORT - std::string printValue(const std::source_location* location); + std::string printValue(const std::source_location* location) { + cling::ostrstream strm; + strm << location->file_name() << ":" << location->line() << ":" + << location->function_name(); + return strm.str().str(); + } #endif // cling::Value diff --git a/interpreter/cling/lib/Interpreter/Transaction.cpp b/interpreter/cling/lib/Interpreter/Transaction.cpp index d2e7f6f4705d7..f4ca51a5dad9f 100644 --- a/interpreter/cling/lib/Interpreter/Transaction.cpp +++ b/interpreter/cling/lib/Interpreter/Transaction.cpp @@ -168,9 +168,18 @@ namespace cling { // declaration, because each time Sema believes a vtable is used it emits // that callback. // For reference (clang::CodeGen::CodeGenModule::EmitVTable). - if (oldDCI.m_Call != kCCIHandleVTable - && oldDCI.m_Call != kCCIHandleCXXImplicitFunctionInstantiation) + if (oldDCI.m_Call != kCCIHandleVTable && + oldDCI.m_Call != kCCIHandleCXXImplicitFunctionInstantiation) { + // Allow duplicates for inline/constexpr functions + if (oldDCI == DCI && DCI.m_DGR.isSingleDecl()) { + if (const FunctionDecl* FD = + dyn_cast_or_null(DCI.m_DGR.getSingleDecl())) { + if (FD->isInlined()) + continue; + } + } assert(oldDCI != DCI && "Duplicates?!"); + } } // We want to assert there is only one wrapper per transaction. checkForWrapper = true; diff --git a/interpreter/cling/lib/Interpreter/ValuePrinter.cpp b/interpreter/cling/lib/Interpreter/ValuePrinter.cpp index 56e5dbfaeb5dc..effc7b915398a 100644 --- a/interpreter/cling/lib/Interpreter/ValuePrinter.cpp +++ b/interpreter/cling/lib/Interpreter/ValuePrinter.cpp @@ -42,12 +42,6 @@ #include "llvm/Support/Format.h" #include -#if __cplusplus >= 202002L -#include -#endif -#ifdef __cpp_lib_source_location -#include -#endif #include using namespace cling; @@ -557,16 +551,6 @@ namespace cling { return toUnicode(Val, 'L', 'x'); } -#ifdef __cpp_lib_source_location - CLING_LIB_EXPORT - std::string printValue(const std::source_location* location) { - cling::ostrstream strm; - strm << location->file_name() << ":" << location->line() << ":" - << location->function_name(); - return strm.str().str(); - } -#endif - } // end namespace cling namespace { diff --git a/interpreter/cling/test/CodeGeneration/ValidDuplicateInstantiation.cpp b/interpreter/cling/test/CodeGeneration/ValidDuplicateInstantiation.cpp new file mode 100644 index 0000000000000..a258387df3d85 --- /dev/null +++ b/interpreter/cling/test/CodeGeneration/ValidDuplicateInstantiation.cpp @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// CLING - the C++ LLVM-based InterpreterG :) +// +// This file is dual-licensed: you can choose to license it under the University +// of Illinois Open Source License or the GNU Lesser General Public License. See +// LICENSE.TXT for details. +//------------------------------------------------------------------------------ + +// RUN: %cling %s -Xclang -verify 2>&1 | FileCheck %s + +// Check that explicit instantiation of a template after an implicit +// instantiation of a constexpr member does not trigger an assertion. + +extern "C" int printf(const char* fmt, ...); + +template struct Box { + constexpr Box() : {} +}; + +Box b; // Trigger implicit instantiation +template class Box; // Explicit instantiation, forces re-instantiation + +printf("Ran without assertions\n"); // CHECK: Ran without assertions + +// expected-no-diagnostics