diff --git a/.github/workflows/archiver.sh b/.github/workflows/archiver.sh index 2e7d35f3e4..a0221fb8ec 100755 --- a/.github/workflows/archiver.sh +++ b/.github/workflows/archiver.sh @@ -55,6 +55,7 @@ echo "VERSION.txt file available on ${topdir}/$(basename ${outfile})" cd ${topdir}/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT for file in $(git ls-tree --name-only HEAD -r); do if [ "${file/acceptance_tests}" != "${file}" ]; then continue; fi # acceptance_tests are not needed for code generation + if [ "${file}" == "VERSION.txt" ]; then continue; fi # skip the committed (non-release) VERSION.txt: the release-ready one generated above must win mkdir -p ${outdir}/$(dirname ${file}) cp -dp ${file} ${outdir}/${file} # preserve symlinks for AUTHORS, COPYING, COPYING.LESSER and COPYRIGHT done diff --git a/.github/workflows/version_master.sh b/.github/workflows/version_master.sh new file mode 100755 index 0000000000..0dadf9273e --- /dev/null +++ b/.github/workflows/version_master.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Copyright (C) 2020-2025 CERN and UCLouvain. +# Licensed under the GNU Lesser General Public License (version 3 or later). +# Created by: D. Massaro (2026) for the MG5aMC CUDACPP plugin. + +# Generate the "non-release" form of CUDACPP_SA_OUTPUT/VERSION.txt for a normal +# commit/push on master (the release form is instead generated by archiver.sh). +# The file records the minimal supported MG5aMC version and the last commit block. +# It is committed back to the repository by version_master.yml, so at any time it +# stores the information about the most recent (non version-update) commit. + +# Path to the top directory of madgraph4gpu +# In the CI this would be simply $(pwd), but allow the script to be run also outside the CI +echo "Executing $0 $*" +topdir=$(cd $(dirname $0)/../..; pwd) + +# Create the VERSION.txt file directly inside the plugin source directory +cd ${topdir}/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT +outfile=VERSION.txt +dateformat='%Y-%m-%d_%H:%M:%S UTC' +rm -f ${outfile} # remove any pre-existing VERSION.txt before regenerating it +touch ${outfile} +echo "mg5_version_minimal = $(cat __init__.py | awk '/minimal_mg5amcnlo_version/{print $3}' | sed 's/(//' | sed 's/)//' | sed 's/,/./g')" >> ${outfile} +echo "" >> ${outfile} +TZ=UTC git --no-pager log -n1 --date=format-local:"${dateformat}" --pretty=format:'commit %h%nAuthor: %an%nAuthorDate: %ad%nCommitter: %cn%nCommitterDate: %cd%nMessage: "%s"%n' >> ${outfile} +python3 -c 'print("="*132)'; cat ${outfile}; python3 -c 'print("="*132)' +echo "VERSION.txt file available on $(pwd)/${outfile}" diff --git a/.github/workflows/version_master.yml b/.github/workflows/version_master.yml new file mode 100644 index 0000000000..b983f7df84 --- /dev/null +++ b/.github/workflows/version_master.yml @@ -0,0 +1,58 @@ +# Copyright (C) 2020-2025 CERN and UCLouvain. +# Licensed under the GNU Lesser General Public License (version 3 or later). +# Created by: D. Massaro (2026) for the MG5aMC CUDACPP plugin. + +#---------------------------------------------------------------------------------------------------------------------------------- + +name: Version (master) + +#---------------------------------------------------------------------------------------------------------------------------------- + +# On every push to master, (re)generate the non-release CUDACPP_SA_OUTPUT/VERSION.txt +# describing the last commit, and commit it back. The auto commit uses '[skip ci]' so +# that it does not re-trigger this workflow (which would otherwise loop forever): as a +# result VERSION.txt always tracks the latest genuine commit on master. +on: + push: + branches: + - master + +#---------------------------------------------------------------------------------------------------------------------------------- + +jobs: + + version: + + runs-on: ubuntu-latest + + # TEMPORARY! this mirrors the repository guard used in archiver.yml + if: | + ( github.repository == 'madgraph5/madgraph4gpu' ) + + steps: + + - name: checkout + uses: actions/checkout@v4 + with: + submodules: 'true' + + - name: create_versiontxt + run: | + echo "Current directory is $(pwd)" + .github/workflows/version_master.sh + + - name: commit_versiontxt + run: | + versiontxt=epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/VERSION.txt + git config user.name github-actions + git config user.email github-actions@github.com + if [ -z "$(git status --porcelain ${versiontxt})" ]; then + echo "No changes detected in ${versiontxt}: nothing to commit" + else + echo "Commit and push ${versiontxt}" + # '[skip ci]' prevents this push from re-triggering the workflow (avoid infinite loop) + git commit -m "Update VERSION.txt [skip ci]" ${versiontxt} + git push + fi + +#---------------------------------------------------------------------------------------------------------------------------------- diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/launch_plugin.py b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/launch_plugin.py index 3bd0c281fc..648cfc4c93 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/launch_plugin.py +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/launch_plugin.py @@ -17,12 +17,14 @@ import internal.extended_cmd as extended_cmd import internal.banner as banner_mod import internal.common_run_interface as common_run_interface + import internal.files as files else: import madgraph.interface.madevent_interface as madevent_interface import madgraph.various.misc as misc import madgraph.interface.extended_cmd as extended_cmd import madgraph.various.banner as banner_mod import madgraph.interface.common_run_interface as common_run_interface + import madgraph.iolibs.files as files class CPPMEInterface(madevent_interface.MadEventCmdShell): def compile(self, *args, **opts): @@ -62,6 +64,70 @@ def compile(self, *args, **opts): else: return misc.compile(nb_core=self.options['nb_core'], *args, **opts) + def do_generate_events(self, *args, **kwargs): + cudacpp_version = os.path.join(self.me_dir, "CUDACPP_VERSION.txt") + if os.path.exists(cudacpp_version): + with open(cudacpp_version, "r") as f: + lines = f.readlines() + logger.info("=================================================") + for line in lines: + logger.info(line.strip()) + logger.info("=================================================") + return super().do_generate_events(*args, **kwargs) + + def do_create_gridpack(self, *args, **kwargs): + """Overload to embed the CUDACPP_VERSION.txt banner into the gridpack. + The banner is not printed here (this runs at gridpack *creation* time); instead + it is packaged inside the tarball and printed at *run* time by run.sh, since a + gridpack run uses GridPackCmd and never goes through this plugin interface.""" + self.embed_cudacpp_version_in_gridpack() + return super().do_create_gridpack(*args, **kwargs) + + # DM - make the CUDACPP_VERSION.txt banner available (and printed) inside gridpacks + def embed_cudacpp_version_in_gridpack(self): + """Prepare the process directory so that make_gridpack packages the CUDACPP banner + and run.sh prints it at runtime. Only files copied into the tarball are touched: + - copy CUDACPP_VERSION.txt into bin/internal/ (bin/ is packaged into madevent/) + - patch bin/internal/Gridpack/run.sh (the template packaged as ./run.sh) to cat it + This must run *before* super().do_create_gridpack(), which invokes make_gridpack.""" + version_src = pjoin(self.me_dir, 'CUDACPP_VERSION.txt') + if not os.path.exists(version_src): + logger.warning('CUDACPP_VERSION.txt not found in %s: the gridpack will not print ' + 'the CUDACPP version banner' % self.me_dir) + return + # 1) copy the banner into a directory that make_gridpack moves into madevent/ + # (make_gridpack packages 'bin' but not the process-root CUDACPP_VERSION.txt) + files.cp(version_src, pjoin(self.me_dir, 'bin', 'internal', 'CUDACPP_VERSION.txt')) + # 2) patch the gridpack run.sh so it prints the banner at runtime (idempotent) + runsh = pjoin(self.me_dir, 'bin', 'internal', 'Gridpack', 'run.sh') + if not os.path.exists(runsh): + logger.warning('%s not found: the gridpack will not print the CUDACPP version banner' + % runsh) + return + marker = '# CUDACPP version banner' + with open(runsh) as fsock: + content = fsock.read() + if marker in content: + return # already patched (e.g. create_gridpack called more than once) + # DIR (=./madevent when the gridpack is unpacked) is defined just above this anchor + anchor = '# For Linux' + banner_block = ( + '%s (printed by the MG5aMC CUDACPP plugin)\n' + 'if [ -f "${DIR}/bin/internal/CUDACPP_VERSION.txt" ]; then\n' + ' echo "================================================="\n' + ' cat "${DIR}/bin/internal/CUDACPP_VERSION.txt"\n' + ' echo "================================================="\n' + 'fi\n\n' % marker + ) + if anchor not in content: + logger.warning('Could not find the expected anchor in %s: the gridpack will not ' + 'print the CUDACPP version banner' % runsh) + return + content = content.replace(anchor, banner_block + anchor, 1) + with open(runsh, 'w') as fsock: + fsock.write(content) + logger.info('Patched %s to print the CUDACPP version banner at gridpack runtime' % runsh) + # Phase-Space Optimization ------------------------------------------------------------------------------------ template_on = \ """#*********************************************************************** diff --git a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/output.py b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/output.py index f2d7189ddd..4b1bf209e9 100644 --- a/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/output.py +++ b/epochX/cudacpp/CODEGEN/PLUGIN/CUDACPP_SA_OUTPUT/output.py @@ -279,9 +279,59 @@ def finalize(self, matrix_element, cmdhistory, MG5options, outputflag): # Additional patching (OM) self.add_madevent_plugin_fct() # Added by OM + # DM - write the CUDACPP_VERSION.txt banner file at the root of the process directory + self.write_cudacpp_version_file() # do not call standard finalize since is this is already done... #return super().finalize(matrix_element, cmdhistory, MG5options, outputflag) + # DM - build the CUDACPP_VERSION.txt file logged at runtime by the cudacpp bridge (see counters.cc/smatrix_multi.f) + def write_cudacpp_version_file(self): + """Write /CUDACPP_VERSION.txt, the 4-line version banner printed at runtime. + Information is gathered from two files: + - PLUGIN/CUDACPP_OUTPUT/VERSION.txt : cudacpp version/tag/commit and the minimal MG5 version supported + - /VERSION : the current MG5aMC version + The plugin VERSION.txt can be in two forms (release / non-release), see the plugin CLAUDE.md.""" + sha_or_tag = 'unknown' + commit_message = '' + mg5_minimal = 'unknown' + mg5_current = 'unknown' + # Parse the plugin VERSION.txt (created by the archiver / master-push workflow) + version_txt = pjoin(PLUGINDIR, 'VERSION.txt') + if os.path.exists(version_txt): + keyval = {} # lines of the form 'key = value' + commit_sha = None + for line in open(version_txt): + if '=' in line: + key, val = line.split('=', 1) + keyval[key.strip()] = val.strip() + elif line.startswith('commit '): # 'commit ' + commit_sha = line.split(None, 1)[1].strip() if len(line.split(None, 1)) > 1 else None + elif line.startswith('Message:'): # 'Message: ""' + commit_message = line.split(':', 1)[1].strip().strip('"') + # : the tagged cudacpp_version if this is a release, else the commit sha + sha_or_tag = keyval.get('cudacpp_version') or commit_sha or 'unknown' + mg5_minimal = keyval.get('mg5_version_minimal', 'unknown') + else: + logger.warning('CUDACPP VERSION.txt not found in %s: CUDACPP_VERSION.txt will be incomplete' % PLUGINDIR) + # Parse the current MG5aMC version + mg5_version_file = pjoin(MG5DIR, 'VERSION') + if os.path.exists(mg5_version_file): + for line in open(mg5_version_file): + if line.strip().startswith('version'): + mg5_current = line.split('=', 1)[1].strip() + break + # Assemble and write the 4-line banner + cudacpp_line = '%s' % sha_or_tag + if commit_message: + cudacpp_line += ' "%s"' % commit_message + banner = [ 'You are using MadGraph5_aMC@NLO + CUDACPP plugin:', + 'CUDACPP version = %s' % cudacpp_line, + 'Minimal MadGraph5 version supported = %s' % mg5_minimal, + 'Current MadGraph5 version = %s' % mg5_current ] + outpath = pjoin(self.dir_path, 'CUDACPP_VERSION.txt') + open(outpath, 'w').write('\n'.join(banner) + '\n') + logger.info('Created CUDACPP_VERSION.txt in %s' % self.dir_path) + # AV (default from OM's tutorial) - overload settings and add a debug printout def modify_grouping(self, matrix_element): """allow to modify the grouping (if grouping is in place)