@@ -512,6 +512,182 @@ jobs:
512512 MIN_CMAKE_VERSION : ${{ inputs.min_cmake_version }}
513513 MAX_CXX_STDS_PER_JOB : ${{ inputs.max_cxx_stds_per_job }}
514514
515+ - name : Determine library name
516+ run : |
517+ set -x
518+ if [[ -z "$SELF" ]]; then
519+ mkdir meta
520+ wget -q -O meta/libraries.json "https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/${GITHUB_SHA}/meta/libraries.json" && \
521+ wget -q -O get_libname.py "https://raw.githubusercontent.com/boostorg/boost-ci/${WORKFLOW_SHA}/ci/get_libname.py" && \
522+ SELF=$(BOOST_CI_SRC_FOLDER="$PWD" python3 get_libname.py) || SELF='(failed to extract)'
523+ fi
524+ echo "SELF=$SELF" >> "$GITHUB_ENV"
525+ env :
526+ SELF : ${{inputs.library_folder}}
527+ WORKFLOW_SHA : ${{job.workflow_sha}}
528+
529+ - name : Generate CI summary
530+ run : |
531+ import json
532+ import os
533+ import re
534+
535+ def merge_cxxstd_duplicates(entries):
536+ """Merge consecutive entries identical except for 'cxxstd', concatenating cxxstd.
537+ Works because jobs split by C++ std are emitted consecutively by the matrix generator."""
538+ merged = []
539+ for e in entries:
540+ if merged:
541+ prev = merged[-1]
542+ if {k: v for k, v in prev.items() if k != 'cxxstd'} == {k: v for k, v in e.items() if k != 'cxxstd'}:
543+ a, b = prev.get('cxxstd', ''), e.get('cxxstd', '')
544+ prev['cxxstd'] = f"{a},{b}" if a and b else (a or b)
545+ continue
546+ merged.append(e)
547+ return merged
548+ def load_from_env(name):
549+ value = os.environ.get(name)
550+ return merge_cxxstd_duplicates(json.loads(value)['include']) if value else []
551+ def sort_key(entry):
552+ # Natural sort for compiler
553+ compiler = tuple(int(p) if p.isdigit() else p
554+ for p in re.findall(r'\D+|\d+', entry.get('compiler', entry.get('toolset')))
555+ )
556+ return (compiler, entry.get('name') or ' ') # Named entries after non-named with same compiler
557+ def compiler_name(toolset):
558+ result = toolset.replace('-', ' ').upper()
559+ result = result.replace('CLANG', 'Clang')
560+ result = result.replace('ICPX', 'Intel OneAPI')
561+ return {'msvc-14.3': 'Visual Studio 2022',
562+ 'msvc-14.5': 'Visual Studio 2026',
563+ 'clang-win': 'Clang',
564+ }.get(toolset, result)
565+
566+ summary = "# Tested Configurations\n\n"
567+
568+ windows_entries = load_from_env('WINDOWS_MATRIX_ENTRIES')
569+ mingw_entries = load_from_env('MINGW_MATRIX_ENTRIES')
570+ cmake_entries = load_from_env('CMAKE_MATRIX_ENTRIES')
571+ posix_entries = load_from_env('POSIX_MATRIX_ENTRIES')
572+ linux_entries = [e for e in posix_entries if 'ubuntu' in e.get('os', '') or 'ubuntu' in e.get('container', '')]
573+ macos_entries = [e for e in posix_entries if 'macos' in e.get('os', '')]
574+
575+ # B2 build options (apply to all B2 jobs)
576+ if posix_entries or windows_entries or mingw_entries:
577+ options = []
578+ library_folder = os.environ.get('LIBRARY_FOLDER')
579+ if not library_folder:
580+ library_folder = os.environ.get('SELF') + " (default)"
581+ options.append(("Library folder", library_folder))
582+ options.append(("Variant", os.environ.get('B2_VARIANT') or '[default]'))
583+ options.append(("Link", os.environ.get('B2_LINK') or '[default]'))
584+ defines = os.environ.get('B2_DEFINES')
585+ if defines:
586+ options.append(("Defines", defines))
587+ targets = os.environ.get('B2_TARGETS')
588+ if targets:
589+ options.append(("Targets", targets))
590+ b2_headers_flags = os.environ.get('B2_HEADERS_FLAGS')
591+ if b2_headers_flags:
592+ options.append(("Extra flags for `b2 headers`", b2_headers_flags))
593+ parallelism = os.environ.get('B2_JOBS')
594+ if parallelism:
595+ options.append(("Parallelism (`-j`)", parallelism))
596+ summary += "## B2 Build Options\n\n"
597+ summary += "| Option | Value |\n"
598+ summary += "|--------|-------|\n"
599+ for name, value in options:
600+ summary += f"| {name} | {value} |\n"
601+ summary += "\n"
602+
603+ if linux_entries:
604+ summary += "## Linux\n\n"
605+ summary += "| Job | Compiler | C++ std | Arch |\n"
606+ summary += "|-----|----------|---------|------|\n"
607+ for e in sorted(linux_entries, key=sort_key):
608+ compiler = e['compiler']
609+ stdlib = e.get('stdlib', '')
610+ name = e.get('name')
611+ if name:
612+ if stdlib:
613+ compiler += f" ({stdlib})"
614+ else:
615+ name = compiler_name(compiler)
616+ if stdlib:
617+ name += f" ({stdlib})"
618+ cxxstd = e.get('cxxstd', '[default]')
619+ arch = e.get('address-model', '')
620+ summary += f"| {name} | {compiler} | {cxxstd} | {arch} |\n"
621+ summary += "\n"
622+
623+ if macos_entries:
624+ summary += "## macOS\n\n"
625+ summary += "| Job | Compiler | C++ std | OS |\n"
626+ summary += "|-----|----------|---------|----|\n"
627+ for e in sorted(macos_entries, key=sort_key):
628+ compiler = e['compiler']
629+ name = e.get('name', compiler.replace('-', ' ').capitalize())
630+ cxxstd = e.get('cxxstd', '[default]')
631+ summary += f"| {name} | {compiler} | {cxxstd} | {e['os'].replace('-', ' ').upper()} |\n"
632+ summary += "\n"
633+
634+ if windows_entries or mingw_entries:
635+ summary += "## Windows\n\n"
636+ summary += "| Job | Toolset | C++ std | Arch |\n"
637+ summary += "|-----|---------|---------|------|\n"
638+ for e in sorted(windows_entries, key=sort_key):
639+ toolset = e['toolset']
640+ cxxstd = e.get('cxxstd', '[default]')
641+ arch = e.get('address-model', '')
642+ name = e.get('name')
643+ if not name:
644+ if e.get('target-os') == 'cygwin':
645+ name = 'Cygwin'
646+ else:
647+ name = ''
648+ name += ' ' + compiler_name(toolset)
649+ if 'arm' in e['os'] and 'arm' not in name.lower():
650+ name = 'ARM: ' + name
651+ summary += f"| {name} | {toolset} | {cxxstd} | {arch} |\n"
652+
653+ if mingw_entries:
654+ for e in mingw_entries:
655+ sys = e['sys']
656+ compiler = e['compiler']
657+ cxxstd = e.get('cxxstd', '[default]')
658+ arch = e.get('address-model', sys.upper().replace('MINGW', '')) # Default: MINGW64 -> 64
659+ summary += f"| {sys} | {compiler} | {cxxstd} | {arch} |\n"
660+ summary += "\n"
661+
662+ if cmake_entries:
663+ summary += "## CMake\n\n"
664+ summary += "| Generator | Build Type | Shared | OS | |\n"
665+ summary += "|-----------|------------|--------|----|-|\n"
666+
667+ for c in cmake_entries:
668+ summary += f"| {c['generator']} | {c['build_type']} | {c['build_shared']} | {c['os']} |"
669+ cmake_version = c.get('cmake_version')
670+ if cmake_version:
671+ summary += f" CMake {cmake_version} "
672+ summary += "|\n"
673+
674+ with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as fh:
675+ fh.write(summary)
676+ shell : python
677+ env :
678+ POSIX_MATRIX_ENTRIES : ${{steps.generate-matrix.outputs.posix-matrix}}
679+ WINDOWS_MATRIX_ENTRIES : ${{steps.generate-matrix.outputs.windows-matrix}}
680+ MINGW_MATRIX_ENTRIES : ${{steps.generate-matrix.outputs.mingw-matrix}}
681+ CMAKE_MATRIX_ENTRIES : ${{steps.generate-matrix.outputs.cmake-matrix}}
682+ MIN_CMAKE_VERSION : ${{inputs.min_cmake_version}}
683+ B2_JOBS : ${{inputs.parallelism}}
684+ B2_HEADERS_FLAGS : ${{inputs.b2_headers_flags}}
685+ B2_VARIANT : ${{inputs.b2_variant}}
686+ B2_LINK : ${{inputs.b2_link}}
687+ B2_DEFINES : ${{inputs.b2_defines}}
688+ B2_TARGETS : ${{inputs.b2_targets}}
689+ LIBRARY_FOLDER : ${{inputs.library_folder}}
690+
515691 posix :
516692 if : ${{needs.generate-job-matrix.outputs.posix-matrix}}
517693 needs : generate-job-matrix
@@ -1060,184 +1236,3 @@ jobs:
10601236 - name : Run CMake install tests w/ B2 installed library
10611237 run : ci/test_installed_cmake_config.sh
10621238 env : {BOOST_CI_INSTALLED_BY: B2}
1063-
1064- ci-summary :
1065- needs : generate-job-matrix
1066- runs-on : ubuntu-latest
1067- if : always()
1068- steps :
1069- - name : Determine library name
1070- run : |
1071- set -x
1072- if [[ -z "$SELF" ]]; then
1073- mkdir meta
1074- wget -q -O meta/libraries.json "https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/${GITHUB_SHA}/meta/libraries.json" && \
1075- wget -q -O get_libname.py "https://raw.githubusercontent.com/boostorg/boost-ci/${WORKFLOW_SHA}/ci/get_libname.py" && \
1076- SELF=$(BOOST_CI_SRC_FOLDER="$PWD" python3 get_libname.py) || SELF='(failed to extract)'
1077- fi
1078- echo "SELF=$SELF" >> "$GITHUB_ENV"
1079- env :
1080- SELF : ${{inputs.library_folder}}
1081- WORKFLOW_SHA : ${{job.workflow_sha}}
1082-
1083- - name : Generate CI summary
1084- run : |
1085- import json
1086- import os
1087- import re
1088-
1089- def merge_cxxstd_duplicates(entries):
1090- """Merge consecutive entries identical except for 'cxxstd', concatenating cxxstd.
1091- Works because jobs split by C++ std are emitted consecutively by the matrix generator."""
1092- merged = []
1093- for e in entries:
1094- if merged:
1095- prev = merged[-1]
1096- if {k: v for k, v in prev.items() if k != 'cxxstd'} == {k: v for k, v in e.items() if k != 'cxxstd'}:
1097- a, b = prev.get('cxxstd', ''), e.get('cxxstd', '')
1098- prev['cxxstd'] = f"{a},{b}" if a and b else (a or b)
1099- continue
1100- merged.append(e)
1101- return merged
1102- def load_from_env(name):
1103- value = os.environ.get(name)
1104- return merge_cxxstd_duplicates(json.loads(value)['include']) if value else []
1105- def sort_key(entry):
1106- # Natural sort for compiler
1107- compiler = tuple(int(p) if p.isdigit() else p
1108- for p in re.findall(r'\D+|\d+', entry.get('compiler', entry.get('toolset')))
1109- )
1110- return (compiler, entry.get('name') or ' ') # Named entries after non-named with same compiler
1111- def compiler_name(toolset):
1112- result = toolset.replace('-', ' ').upper()
1113- result = result.replace('CLANG', 'Clang')
1114- result = result.replace('ICPX', 'Intel OneAPI')
1115- return {'msvc-14.3': 'Visual Studio 2022',
1116- 'msvc-14.5': 'Visual Studio 2026',
1117- 'clang-win': 'Clang',
1118- }.get(toolset, result)
1119-
1120- summary = "# Tested Configurations\n\n"
1121-
1122- windows_entries = load_from_env('WINDOWS_MATRIX_ENTRIES')
1123- mingw_entries = load_from_env('MINGW_MATRIX_ENTRIES')
1124- cmake_entries = load_from_env('CMAKE_MATRIX_ENTRIES')
1125- posix_entries = load_from_env('POSIX_MATRIX_ENTRIES')
1126- linux_entries = [e for e in posix_entries if 'ubuntu' in e.get('os', '') or 'ubuntu' in e.get('container', '')]
1127- macos_entries = [e for e in posix_entries if 'macos' in e.get('os', '')]
1128-
1129- # B2 build options (apply to all B2 jobs)
1130- if posix_entries or windows_entries or mingw_entries:
1131- options = []
1132- library_folder = os.environ.get('LIBRARY_FOLDER')
1133- if not library_folder:
1134- library_folder = os.environ.get('SELF') + " (default)"
1135- options.append(("Library folder", library_folder))
1136- options.append(("Variant", os.environ.get('B2_VARIANT') or '[default]'))
1137- options.append(("Link", os.environ.get('B2_LINK') or '[default]'))
1138- defines = os.environ.get('B2_DEFINES')
1139- if defines:
1140- options.append(("Defines", defines))
1141- targets = os.environ.get('B2_TARGETS')
1142- if targets:
1143- options.append(("Targets", targets))
1144- b2_headers_flags = os.environ.get('B2_HEADERS_FLAGS')
1145- if b2_headers_flags:
1146- options.append(("Extra flags for `b2 headers`", b2_headers_flags))
1147- parallelism = os.environ.get('B2_JOBS')
1148- if parallelism:
1149- options.append(("Parallelism (`-j`)", parallelism))
1150- summary += "## B2 Build Options\n\n"
1151- summary += "| Option | Value |\n"
1152- summary += "|--------|-------|\n"
1153- for name, value in options:
1154- summary += f"| {name} | {value} |\n"
1155- summary += "\n"
1156-
1157- if linux_entries:
1158- summary += "## Linux\n\n"
1159- summary += "| Job | Compiler | C++ std | Arch |\n"
1160- summary += "|-----|----------|---------|------|\n"
1161- for e in sorted(linux_entries, key=sort_key):
1162- compiler = e['compiler']
1163- stdlib = e.get('stdlib', '')
1164- name = e.get('name')
1165- if name:
1166- if stdlib:
1167- compiler += f" ({stdlib})"
1168- else:
1169- name = compiler_name(compiler)
1170- if stdlib:
1171- name += f" ({stdlib})"
1172- cxxstd = e.get('cxxstd', '[default]')
1173- arch = e.get('address-model', '')
1174- summary += f"| {name} | {compiler} | {cxxstd} | {arch} |\n"
1175- summary += "\n"
1176-
1177- if macos_entries:
1178- summary += "## macOS\n\n"
1179- summary += "| Job | Compiler | C++ std | OS |\n"
1180- summary += "|-----|----------|---------|----|\n"
1181- for e in sorted(macos_entries, key=sort_key):
1182- compiler = e['compiler']
1183- name = e.get('name', compiler.replace('-', ' ').capitalize())
1184- cxxstd = e.get('cxxstd', '[default]')
1185- summary += f"| {name} | {compiler} | {cxxstd} | {e['os'].replace('-', ' ').upper()} |\n"
1186- summary += "\n"
1187-
1188- if windows_entries or mingw_entries:
1189- summary += "## Windows\n\n"
1190- summary += "| Job | Toolset | C++ std | Arch |\n"
1191- summary += "|-----|---------|---------|------|\n"
1192- for e in sorted(windows_entries, key=sort_key):
1193- toolset = e['toolset']
1194- cxxstd = e.get('cxxstd', '[default]')
1195- arch = e.get('address-model', '')
1196- name = e.get('name')
1197- if not name:
1198- if e.get('target-os') == 'cygwin':
1199- name = 'Cygwin'
1200- else:
1201- name = ''
1202- name += ' ' + compiler_name(toolset)
1203- if 'arm' in e['os'] and 'arm' not in name.lower():
1204- name = 'ARM: ' + name
1205- summary += f"| {name} | {toolset} | {cxxstd} | {arch} |\n"
1206-
1207- if mingw_entries:
1208- for e in mingw_entries:
1209- sys = e['sys']
1210- compiler = e['compiler']
1211- cxxstd = e.get('cxxstd', '[default]')
1212- arch = e.get('address-model', sys.upper().replace('MINGW', '')) # Default: MINGW64 -> 64
1213- summary += f"| {sys} | {compiler} | {cxxstd} | {arch} |\n"
1214- summary += "\n"
1215-
1216- if cmake_entries:
1217- summary += "## CMake\n\n"
1218- summary += "| Generator | Build Type | Shared | OS | |\n"
1219- summary += "|-----------|------------|--------|----|-|\n"
1220-
1221- for c in cmake_entries:
1222- summary += f"| {c['generator']} | {c['build_type']} | {c['build_shared']} | {c['os']} |"
1223- cmake_version = c.get('cmake_version')
1224- if cmake_version:
1225- summary += f" CMake {cmake_version} "
1226- summary += "|\n"
1227-
1228- with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as fh:
1229- fh.write(summary)
1230- shell : python
1231- env :
1232- POSIX_MATRIX_ENTRIES : ${{needs.generate-job-matrix.outputs.posix-matrix}}
1233- WINDOWS_MATRIX_ENTRIES : ${{needs.generate-job-matrix.outputs.windows-matrix}}
1234- MINGW_MATRIX_ENTRIES : ${{needs.generate-job-matrix.outputs.mingw-matrix}}
1235- CMAKE_MATRIX_ENTRIES : ${{needs.generate-job-matrix.outputs.cmake-matrix}}
1236- MIN_CMAKE_VERSION : ${{inputs.min_cmake_version}}
1237- B2_JOBS : ${{inputs.parallelism}}
1238- B2_HEADERS_FLAGS : ${{inputs.b2_headers_flags}}
1239- B2_VARIANT : ${{inputs.b2_variant}}
1240- B2_LINK : ${{inputs.b2_link}}
1241- B2_DEFINES : ${{inputs.b2_defines}}
1242- B2_TARGETS : ${{inputs.b2_targets}}
1243- LIBRARY_FOLDER : ${{inputs.library_folder}}
0 commit comments