diff --git a/.github/workflows/root-ci-config/build_root.py b/.github/workflows/root-ci-config/build_root.py index 27e49bb7049fc..8be928158c137 100755 --- a/.github/workflows/root-ci-config/build_root.py +++ b/.github/workflows/root-ci-config/build_root.py @@ -143,7 +143,8 @@ def main(): build_utils.print_warning(f'Failed to download: {err}') args.incremental = False - git_pull("src", args.repository, args.base_ref) + if not args.source_dir: + git_pull("src", args.repository, args.base_ref) benchmark: bool = 'rootbench' in options_dict and options_dict['rootbench'] == "ON" if benchmark: @@ -166,7 +167,10 @@ def main(): # Delete all the .gcda files produces by an artefact. build_utils.remove_file_match_ext(WORKDIR, "gcda") - build(options, args.buildtype) + if args.source_dir: + build(options, args.buildtype, source_dir=args.source_dir) + else: + build(options, args.buildtype) # Build artifacts should only be uploaded for full builds, and only for # "official" branches (master, v?-??-??-patches), i.e. not for pull_request @@ -227,6 +231,7 @@ def parse_args(): parser.add_argument("--pull_repository", default="", help="Url to the pull request incoming repository") parser.add_argument("--head_ref", default=None, help="Ref to feature branch; it may contain a : part") parser.add_argument("--head_sha", default=None, help="Sha of commit that triggered the event") + parser.add_argument("--source_dir", default=None, help="Don't check out. Just use the source dir provided here.") parser.add_argument("--binaries", default="false", help="Whether to create binary artifacts") parser.add_argument("--architecture", default=None, help="Windows only, target arch") parser.add_argument("--repository", default="https://github.com/root-project/root.git", @@ -242,7 +247,7 @@ def parse_args(): args.binaries = args.binaries.lower() in ('yes', 'true', '1', 'on') args.upload_artifacts = args.upload_artifacts.lower() in ('yes', 'true', '1', 'on') - if not args.base_ref: + if not args.base_ref and not args.source_dir: die(os.EX_USAGE, "base_ref not specified") if not args.platform_config: # If nothing special, we take the standard platform configuration, called as the platform @@ -396,8 +401,11 @@ def archive_and_upload(archive_name, prefix): @github_log_group("Configure") -def cmake_configure(options, buildtype): - srcdir = os.path.join(WORKDIR, "src") +def cmake_configure(options, buildtype, **kwargs): + if 'source_dir' in kwargs: + srcdir = kwargs['source_dir'] + else: + srcdir = os.path.join(WORKDIR, "src") builddir = os.path.join(WORKDIR, "build") # Add a private option to make the CI build faster by not changing the @@ -445,16 +453,16 @@ def cmake_build(buildtype): die(result, "Failed to build") -def build(options, buildtype): +def build(options, buildtype, **kwargs): if not os.path.isdir(os.path.join(WORKDIR, "build")): builddir = os.path.join(WORKDIR, "build") - result = subprocess_with_log(f"mkdir {builddir}") + result = subprocess_with_log(f"mkdir -p {builddir}") if result != 0: die(result, "Failed to create build directory") if not os.path.exists(os.path.join(WORKDIR, "build", "CMakeCache.txt")): - cmake_configure(options, buildtype) + cmake_configure(options, buildtype, **kwargs) else: cmake_dump_config() diff --git a/.github/workflows/root-docs-ci.yml b/.github/workflows/root-docs-ci.yml index 1cd64060689c3..76dc6b10f5112 100644 --- a/.github/workflows/root-docs-ci.yml +++ b/.github/workflows/root-docs-ci.yml @@ -1,27 +1,35 @@ name: 'ROOT Docs CI' on: + pull_request: + paths: + - '.github/workflows/root-docs-ci.yml' + - 'documentation/doxygen/**' + branches: + - 'master' - # Allows nightly builds to trigger one run for each branch easily, by - # providing the relevant branch as "default" value here: workflow_call: - inputs: - incremental: - type: boolean - default: true workflow_dispatch: inputs: - incremental: - description: 'Do full build' - type: boolean + eos_upload_directory: + description: 'Make documentation available at /doc/. Leave empty for skipping the upload to eos.' + type: string required: false - default: false - # docu_input: # opportunity: overwrite makeinput.sh with these args - # description: Folders to build documentation for. All folders are built if empty. - # type: string - # default: "" - # required: false + default: 'master' + +env: + PLATFORM: alma10 + DOC_DIR: ${{ case(github.base_ref == null, github.ref_name, 'PullRequest') }} + DOC_LOCATION: /github/home + EOS_DIR_NAME: |- + ${{ case( + inputs.eos_upload_directory != null, inputs.eos_upload_directory, + github.base_ref != null, 'PR', + github.ref_name + ) + }} + TAR_FILE: html${{ case(github.base_ref == null, github.ref_name, '_PullRequest') }}.tar.gz jobs: build-docs: @@ -32,20 +40,14 @@ jobs: - linux - x64 - env: - PLATFORM: alma9 - DOC_DIR: master - DOC_LOCATION: /github/home - BASE_REF: master - WEB_DIR_NAME: master - TAR_NAME: htmlmaster.tar - permissions: contents: read container: - image: registry.cern.ch/root-ci/alma9:buildready + image: registry.cern.ch/root-ci/alma10:buildready options: '--security-opt label=disable --rm' + volumes: + - alma10_ccache_volume:/github/home/.cache/ccache env: OS_APPLICATION_CREDENTIAL_ID: '7f5b64a265244623a3a933308569bdba' OS_APPLICATION_CREDENTIAL_SECRET: ${{ secrets.OS_APPLICATION_CREDENTIAL_SECRET }} @@ -57,96 +59,49 @@ jobs: PYTHONUNBUFFERED: true steps: + - name: Prepare qhelpgenerator + # Qt6's libexec executables are not in the PATH by default + run: | + QHELPGENERATOR=$(which qhelpgenerator) && echo "QHELPGENERATOR=${QHELPGENERATOR}" >> "${GITHUB_ENV}" + PATH=/usr/lib64/qt6/libexec/:${PATH} QHELPGENERATOR=$(which qhelpgenerator) && echo "QHELPGENERATOR=${QHELPGENERATOR}" >> "${GITHUB_ENV}" + - name: Checkout uses: actions/checkout@v6 with: - ref: ${{ env.BASE_REF }} fetch-depth: 0 - 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: Set up directory name and tar filenames - run: | - echo TAR_NAME=html${BASE_REF}.tar >> $GITHUB_ENV - echo DOCDIR_NAME=${BASE_REF} >> $GITHUB_ENV - - # TODO: install latest versions in image on root-ci-images - - name: Install Doxygen 1.10.0 - run : | - mkdir -p ${{ github.workspace }}/doxygen - curl -L https://github.com/doxygen/doxygen/releases/download/Release_1_10_0/doxygen-1.10.0.linux.bin.tar.gz | tar -xz -C ${{ github.workspace }}/doxygen/ --strip-components=1 - echo PATH=$PATH:${{ github.workspace }}/doxygen/bin >> $GITHUB_ENV -# git clone --branch Release_1_12_0 https://github.com/doxygen/doxygen.git -# cd doxygen -# mkdir build -# cd build -# cmake .. -# make -j$(nproc) -# echo PATH=$PATH:${{ github.workspace }}/doxygen/build/bin >> $GITHUB_ENV -# cd ${{ github.workspace }} -# doxygen --version - - - name: Install qhelpgenerator-qt5 - run: | - dnf upgrade -y - dnf install -y qt5-doctools - which qhelpgenerator-qt5 + run: 'if [ -d /py-venv/ROOT-CI/bin/ ]; then . /py-venv/ROOT-CI/bin/activate && echo ${PATH%:*} >> "${GITHUB_PATH}"; fi' - - name: Apply option overrides - env: - OVERRIDES: "testing=Off roottest=Off" - CONFIGFILE: '.github/workflows/root-ci-config/buildconfig/alma9.txt' - shell: bash - run: | - set -x - echo '' >> "$CONFIGFILE" - for ENTRY in $OVERRIDES; do - KEY=$( echo "$ENTRY" | cut -d '=' -f 1 ) - # Add entry to file if not exists, otherwise replace - if grep -q "$KEY=" "$CONFIGFILE"; then - sed -i "s/$KEY=.*\$/$ENTRY/" "$CONFIGFILE" - else - echo "$ENTRY" >> "$CONFIGFILE" - fi - done - cat "$CONFIGFILE" || true - - - name: Build ROOT - Workflow Dispatch - if: github.event_name == 'workflow_dispatch' - env: - INCREMENTAL: ${{ inputs.incremental == 'true' }} + - name: Build ROOT run: ".github/workflows/root-ci-config/build_root.py --buildtype Release --platform ${{ env.PLATFORM }} - --incremental ${{ env.INCREMENTAL }} - --base_ref ${BASE_REF} - --head_ref ${BASE_REF} - --binaries false - --repository ${{ github.server_url }}/${{ github.repository }}" - - - name: Build ROOT - Schedule - if: github.event_name == 'schedule' - run: ".github/workflows/root-ci-config/build_root.py - --buildtype Release - --platform ${{ env.PLATFORM }} - --incremental ${{ inputs.incremental }} - --base_ref ${BASE_REF} - --head_ref ${BASE_REF} - --binaries false - --repository ${{ github.server_url }}/${{ github.repository }}" + --incremental false + --source_dir ${GITHUB_WORKSPACE} + --repository ${{ github.server_url }}/${{ github.repository }} + --overrides testing=Off roottest=Off + --upload_artifacts false + --binaries false" - name: Run Doxygen - working-directory: ${{ env.DOC_LOCATION }} shell: bash run: | - source ROOT-CI/build/bin/thisroot.sh - export DOXYGEN_OUTPUT_DIRECTORY=/github/home/${DOC_DIR} - cd ROOT-CI/src/documentation/doxygen - make -j `nproc --all` + source ${HOME}/ROOT-CI/build/bin/thisroot.sh + export DOXYGEN_OUTPUT_DIRECTORY="/github/home/${DOC_DIR}" + cd documentation/doxygen + make -j "$(nproc --all)" + + - name: Clean temporary files + # These files are used when doxygen runs incrementally + shell: bash + run: | + cd "/github/home/${DOC_DIR}" + rm html/*.md5 + rm html/*.map - name: Create documentation archives working-directory: ${{ env.DOC_LOCATION }} @@ -154,27 +109,19 @@ jobs: run: | pwd ls -l - echo ${DOC_DIR} - echo ${TAR_NAME} ls -l ${DOC_DIR} - tar cf ${TAR_NAME} ${DOC_DIR} - gzip ${TAR_NAME} + tar -caf ${TAR_FILE} ${DOC_DIR} ls -l - #Upload to GitHub as an artifact - - name: Upload tar file for GH + - name: Upload tar file to github if: ${{ !cancelled() }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: - name: ${{env.TAR_NAME}}.gz - path: ${{env.DOC_LOCATION}}/${{env.TAR_NAME}}.gz + name: ${{env.TAR_FILE}} + path: ${{env.DOC_LOCATION}}/${{env.TAR_FILE}} if-no-files-found: error - - - name: Install Kerberos utilities - run: dnf -y install krb5-workstation - - - name: Install XRootD client - run: dnf -y install xrootd-client + archive: false + retention-days: 8 - name: Sync documentation to EOS env: @@ -185,26 +132,29 @@ jobs: EOS_BASE_PATH: /eos/project/r/root-eos/website/ EOS_ENDPOINT: root://eosproject-r.cern.ch KRB5CCNAME: /tmp/krb5cc + if: ${{ env.KRB5REALM != '' && env.EOS_DIR_NAME != null && env.EOS_DIR_NAME != '' }} working-directory: ${{ env.DOC_LOCATION }} # Because of https://github.com/xrootd/xrootd/issues/2235 , the xrd client can copy # only at most 65536 in one go. For this reason, we copy files in batches, dividing # them by extension. Once that is fixed in XRootD, a single command can be used # instead. run: | + echo "Input eos directory: ${EOS_DIR_NAME}" + EOS_DIR_NAME="${EOS_DIR_NAME%-00-patches}" + EOS_DIR_NAME="${EOS_DIR_NAME/-/}" + echo "Uploading to the directory: ${EOS_DIR_NAME}" failure=0 - echo ${RWEBEOS_KT} | base64 -d > ${KT_FILE_NAME} - kinit -p ${{ secrets.KRB5USER }}@${{ secrets.KRB5REALM }} -kt ${KT_FILE_NAME} - cd ${DOC_DIR}/html/ - rm -rf *.map # Intermediate files to create dot graphs - rm -rf *.md5 # As above - xrdcp --parallel 64 -rf ./*.html ${EOS_ENDPOINT}/${EOS_BASE_PATH}/doc/${WEB_DIR_NAME} || failure=1 - rm -rf *.html - xrdcp --parallel 64 -rf ./*.svg ${EOS_ENDPOINT}/${EOS_BASE_PATH}/doc/${WEB_DIR_NAME} || failure=1 - rm -rf *.svg - xrdcp --parallel 64 -rf ./ ${EOS_ENDPOINT}/${EOS_BASE_PATH}/doc/${WEB_DIR_NAME} || failure=1 + echo "${RWEBEOS_KT}" | base64 -d > "${KT_FILE_NAME}" + kinit -p ${{ secrets.KRB5USER }}@${{ secrets.KRB5REALM }} -kt "${KT_FILE_NAME}" + cd "${DOC_DIR}/html/" + xrdcp --parallel 64 -rf ./*.html "${EOS_ENDPOINT}/${EOS_BASE_PATH}/doc/${EOS_DIR_NAME}" || failure=1 + rm -rf ./*.html + xrdcp --parallel 64 -rf ./*.svg "${EOS_ENDPOINT}/${EOS_BASE_PATH}/doc/${EOS_DIR_NAME}" || failure=1 + rm -rf ./*.svg + xrdcp --parallel 64 -rf ./ "${EOS_ENDPOINT}/${EOS_BASE_PATH}/doc/${EOS_DIR_NAME}" || failure=1 cd .. rm -r html - xrdcp --parallel 64 -rf ./ ${EOS_ENDPOINT}/${EOS_BASE_PATH}/doc/${WEB_DIR_NAME} || failure=1 + xrdcp --parallel 64 -rf ./ "${EOS_ENDPOINT}/${EOS_BASE_PATH}/doc/${EOS_DIR_NAME}" || failure=1 cd .. - xrdcp -rf ${TAR_NAME}.gz ${EOS_ENDPOINT}/${EOS_BASE_PATH}/download || failure=1 - exit $failure + xrdcp -rf "${TAR_FILE}" "${EOS_ENDPOINT}/${EOS_BASE_PATH}/download" || failure=1 + exit "$failure" diff --git a/documentation/doxygen/Doxyfile b/documentation/doxygen/Doxyfile index a46142f3c676f..79433c26ee0ec 100644 --- a/documentation/doxygen/Doxyfile +++ b/documentation/doxygen/Doxyfile @@ -1699,7 +1699,7 @@ QHP_SECT_FILTER_ATTRS = # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = qhelpgenerator-qt5 +QHG_LOCATION = $(QHELPGENERATOR) # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To diff --git a/documentation/doxygen/Makefile b/documentation/doxygen/Makefile index 7b22aadb1a11a..1b1a9b5d008a7 100644 --- a/documentation/doxygen/Makefile +++ b/documentation/doxygen/Makefile @@ -77,6 +77,9 @@ doxygen: filter pyzdoc htmlfooter $(call MkDir,$(DOXYGEN_EXAMPLE_PATH)) $(call MkDir,$(DOXYGEN_NOTEBOOK_PATH)) ./makeinput.sh + echo "*** Diff of config file with default template:" + doxygen -x + echo "*** Run doxygen" doxygen bash ./CleanNamespaces.sh gzip $(DOXYGEN_IMAGE_PATH)/ROOT.tag diff --git a/documentation/doxygen/converttonotebook.py b/documentation/doxygen/converttonotebook.py index bb13fb99d0625..f4cb4f6bc701d 100755 --- a/documentation/doxygen/converttonotebook.py +++ b/documentation/doxygen/converttonotebook.py @@ -423,7 +423,7 @@ def findBracedBlock(text, startpos, openingBraceChar): def findStuffBeforeFunc(text, searchStart, searchEnd): - beforeFunctionRe = re.compile("(;|}|//[^\n]*)\s*$", flags = re.MULTILINE) + beforeFunctionRe = re.compile("(;|}|//[^\n]*)\\s*$", flags = re.MULTILINE) try: # Find the last '}' or comment line etc before function definition: lastMatchBeforeFunc = [thisMatch for thisMatch in beforeFunctionRe.finditer(text, searchStart, searchEnd)][-1] @@ -622,7 +622,7 @@ def changeString(matchObject): matchString = matchString.replace("THISISASPACE" , " ") return matchString - newcode = re.sub("#\s\s?\w\s[\w-]\s\w.*", changeString , code) + newcode = re.sub(r"#\s\s?\w\s[\w-]\s\w.*", changeString , code) return newcode def declareNamespace(code):