From fdf8393a2bf735bc080cf5a4e34aa7e18ac45198 Mon Sep 17 00:00:00 2001 From: Daniel Hilst Selli <392820+dhilst@users.noreply.github.com> Date: Thu, 5 Mar 2026 08:17:23 -0300 Subject: [PATCH] fix: Add scripts & patches for building in mock for EL10 Signed-off-by: Daniel Hilst Selli <392820+dhilst@users.noreply.github.com> --- BUILD.md | 265 ++++++ atftp/atftp.spec | 22 +- atftp/dfsg-3-argz-inline-redefinition.diff | 11 + atftp/dfsg-3-pthread-self-include.diff | 11 + elilo/elilo-gnu-efi-strncpy-conflict.patch | 32 + elilo/elilo-xcat.spec | 26 + elilo/mockbuild.pl | 346 ++++++++ goconserver/mockbuild.pl | 398 +++++++++ grub2-xcat/mockbuild.pl | 362 ++++++++ ...0016-el10-gcc14-missing-intf-getters.patch | 13 + ipmitool/ipmitool.spec | 3 +- ipmitool/mockbuild.pl | 357 ++++++++ mockbuild-all.pl | 840 ++++++++++++++++++ mockbuild-perl-packages.pl | 557 ++++++++++++ ...Lv2_client_method-with-OpenSSL-1.1.0.patch | 33 + ...-building-on-Perl-without-dot-in-INC.patch | 11 + ...ay-0.72-openssl3-sslv3-method-compat.patch | 21 + perl-Crypt-SSLeay/perl-Crypt-SSLeay.spec | 390 ++++++++ perl-HTTP-Async/perl-HTTP-Async.spec | 10 +- perl-IO-Tty/IO-Tty.spec | 11 +- perl-Net-HTTPS-NB/perl-Net-HTTPS-NB.spec | 8 +- perl-Sys-Virt/Sys-Virt.spec | 18 +- syslinux/mockbuild.pl | 399 +++++++++ syslinux/syslinux-6.03-multibootif.patch | 53 ++ syslinux/syslinux-xcat.spec | 71 +- xnba/ipxe-binutils-2.41-compat.patch | 274 ++++++ xnba/xnba-undi.spec | 12 +- 27 files changed, 4518 insertions(+), 36 deletions(-) create mode 100644 BUILD.md create mode 100644 atftp/dfsg-3-argz-inline-redefinition.diff create mode 100644 atftp/dfsg-3-pthread-self-include.diff create mode 100644 elilo/elilo-gnu-efi-strncpy-conflict.patch create mode 100755 elilo/mockbuild.pl create mode 100755 goconserver/mockbuild.pl create mode 100755 grub2-xcat/mockbuild.pl create mode 100644 ipmitool/0016-el10-gcc14-missing-intf-getters.patch create mode 100755 ipmitool/mockbuild.pl create mode 100755 mockbuild-all.pl create mode 100755 mockbuild-perl-packages.pl create mode 100644 perl-Crypt-SSLeay/Crypt-SSLeay-0.72-Do-not-use-SSLv2_client_method-with-OpenSSL-1.1.0.patch create mode 100644 perl-Crypt-SSLeay/Crypt-SSLeay-0.72-Fix-building-on-Perl-without-dot-in-INC.patch create mode 100644 perl-Crypt-SSLeay/Crypt-SSLeay-0.72-openssl3-sslv3-method-compat.patch create mode 100644 perl-Crypt-SSLeay/perl-Crypt-SSLeay.spec create mode 100755 syslinux/mockbuild.pl create mode 100644 syslinux/syslinux-6.03-multibootif.patch create mode 100644 xnba/ipxe-binutils-2.41-compat.patch diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..43fd88f --- /dev/null +++ b/BUILD.md @@ -0,0 +1,265 @@ +# Build Guide (`mockbuild-all.pl`) + +This guide explains how to use `mockbuild-all.pl` to build, validate, and package xCAT dependencies and optional xCAT packages into a unified EL10 repository layout. +It also documents the operational flags for controlling build, install-check, collection, and packaging behavior. + +# Purpose + +`mockbuild-all.pl` is the top-level build orchestrator for generating a **unified xCAT repository**. +It builds required dependency RPMs and, by default, xCAT RPMs, then assembles: + +- a binary RPM repo tree with repodata +- an SRPM repo tree with repodata +- one binary repo tarball and one SRPM repo tarball + +# Historical Context + +Historically, the deployment flow used separate repositories: + +- `xcat-core` for xCAT packages +- `xcat-dep` for dependency packages + +The current flow produces a single **unified `xcat` repository** containing all required packages together. + +# Placeholder Conventions + +This guide uses the following placeholders consistently: + +- ``: xcat-dep repository root (example: `/root/xcat-dep`) +- ``: xCAT source root (example: `/xcat-source-code`) +- ``: output of `uname -m` (for example: `x86_64`, `ppc64le`) +- ``: `ID` field from `/etc/os-release` (for example: `rhel`, `rocky`) +- ``: integer major release from `VERSION_ID` in `/etc/os-release` (for example: `10`) +- ``: `+epel--` +- ``: build run identifier (auto-generated if omitted) + +# Build Pipeline Overview + +`mockbuild-all.pl` orchestrates these build components in parallel: + +- `/elilo/mockbuild.pl` +- `/grub2-xcat/mockbuild.pl` +- `/ipmitool/mockbuild.pl` +- `/syslinux/mockbuild.pl` +- `/goconserver/mockbuild.pl` +- `/mockbuild-perl-packages.pl` +- `/buildrpms.pl` (unless `--skip-xcat` is set) + +Each build path uses `mock` for chroot isolation. Top-level steps are parallelized by `mockbuild-all.pl`, and perl dependency builds are also parallelized internally by `mockbuild-perl-packages.pl`. + +`mockbuild-all.pl` does more than building RPMs. In a default run it performs these stages: + +1. Optional chroot cleanup (`--scrub-all-chroots`) +2. Parallel build execution +3. Optional install/smoke checks inside child builders (disabled with `--skip-install`) +4. Binary RPM collection into `repo//` +5. Source RPM collection into `repo-src/` +6. `createrepo --update` on both repo trees +7. Tarball creation for both repo trees +8. Summary generation (`summary.txt`) + +# Skip and Control Flags + +Use these flags to skip specific operations: + +- `--skip-install` + - Skips install/smoke checks performed by child builder scripts after RPM build. +- `--skip-xcat` + - Skips `/buildrpms.pl` (xCAT package build step). +- `--skip-xcat-dep` + - Skips non-perl xcat-dep package builders (`elilo`, `grub2-xcat`, `ipmitool-xcat`, `syslinux-xcat`, `goconserver`). +- `--skip-perl` + - Skips `/mockbuild-perl-packages.pl`. +- `--skip-build` + - Skips all build steps; only runs collection/repo/tarball stages from existing artifact roots. +- `--skip-createrepo` + - Skips `createrepo --update`. +- `--skip-tarball` + - Skips tarball creation for both binary and SRPM repos. +- `--scrub-all-chroots` + - Runs `mock -r --scrub=all` before build and collection. +- `--collect-dir ` + - Adds extra artifact roots to the collection phase (repeatable). +- `--dry-run` + - Prints planned actions without executing them. + +# Prerequisites + +- Run as `root`. +- `mockbuild-all.pl` and package sources present under ``. +- xCAT sources present under ``. + +Install baseline tooling: + +```bash +dnf -y install perl perl-Parallel-ForkManager mock createrepo tar rpm-build rpmdevtools dnf-plugins-core wget git +``` + +If you will build xCAT packages (that is, you will **not** use `--skip-xcat`), install xCAT build dependencies: + +```bash +cd +perl buildrpms.pl --install_deps +``` + +Validate mock config availability: + +```bash +mock -r --print-root-path +``` + +# Target Resolution + +`--target` is optional. + +If `--target` is omitted, `mockbuild-all.pl` derives `` from: + +- `/etc/os-release` (`ID`, `VERSION_ID`) +- `uname -m` + +Equivalent derivation: + +```text + = +epel-- +``` + +`` is also passed to `mock` as the chroot/config identifier: + +```text +mock -r ... +``` + +# Build Full Unified Repository (xCAT + Dependencies) + +Use this mode to build dependency packages and xCAT packages together. + +```bash +cd /root/xcat-dep +perl ./mockbuild-all.pl \ + --repo-root /root/xcat-dep \ + --xcat-source /root/xcat-dep/xcat-source-code \ + --scrub-all-chroots +``` + +Notes: + +- Install/smoke checks run by default inside child builders. +- Add `--skip-install` to skip those checks. +- `` is optional; when omitted it is timestamp-based. + +# Build Unified Repository Without xCAT (`--skip-xcat`) + +Use this mode to build dependency packages only and skip invoking `/root/xcat-dep/xcat-source-code/buildrpms.pl`. + +```bash +cd /root/xcat-dep +perl ./mockbuild-all.pl \ + --repo-root /root/xcat-dep \ + --xcat-source /root/xcat-dep/xcat-source-code \ + --scrub-all-chroots \ + --skip-xcat \ + --skip-install +``` + +Important behavior: + +- `--skip-xcat` skips the xCAT build step, but collection still scans: + - `/dist//rpms` +- If that path already has xCAT RPMs, they are included in the resulting unified repo. + +# Common Build Modes + +Full unified repo (xCAT + dependencies, with install/smoke checks): + +```bash +cd +perl ./mockbuild-all.pl \ + --repo-root \ + --xcat-source \ + --scrub-all-chroots +``` + +Full unified repo (xCAT + dependencies, skip install/smoke checks): + +```bash +cd +perl ./mockbuild-all.pl \ + --repo-root \ + --xcat-source \ + --scrub-all-chroots \ + --skip-install +``` + +Dependency-only repo (skip xCAT package build): + +```bash +cd +perl ./mockbuild-all.pl \ + --repo-root \ + --xcat-source \ + --scrub-all-chroots \ + --skip-xcat \ + --skip-install +``` + +Collection-only pass from existing build artifacts: + +```bash +cd +perl ./mockbuild-all.pl \ + --repo-root \ + --xcat-source \ + --skip-build +``` + +# Output Artifacts and Paths + +For each run: + +- Binary repo path: + - `/build-output/mockbuild-all//repo//` +- SRPM repo path: + - `/build-output/mockbuild-all//repo-src/` +- Run summary: + - `/build-output/mockbuild-all//summary.txt` +- Binary repo tarball: + - `/build-output/mockbuild-all/mockbuild-all--.tar.gz` +- SRPM repo tarball: + - `/build-output/mockbuild-all/mockbuild-all---srpm.tar.gz` + +# Architecture Requirements + +`mockbuild-all.pl` derives architecture from the build host: + +- ` = $(uname -m)` + +Because of this, to build `ppc64le` artifacts you must run `mockbuild-all.pl` on a Power host where: + +- `uname -m` returns `ppc64le` +- a matching mock config exists for `` (for example `rocky+epel-10-ppc64le`) + +In short: build `ppc64le` packages on a Power machine. + +# Validation Commands + +```bash +cat /build-output/mockbuild-all//summary.txt +ls -1 /build-output/mockbuild-all//repo// +ls -1 /build-output/mockbuild-all//repo-src/ +ls -1 /build-output/mockbuild-all/mockbuild-all--.tar.gz +ls -1 /build-output/mockbuild-all/mockbuild-all---srpm.tar.gz +find /build-output/mockbuild-all//build-logs -type f | sort +``` + +# Common Issues + +- `Missing xCAT build script: .../buildrpms.pl` + - Ensure `` points to a valid xCAT source tree. +- `WARN: missing dep builder script, skipping: ...` + - Sync the repository; one or more package build scripts are missing. +- `mock target not found` + - Validate with `mock -r --print-root-path` and install the required mock config packages. + +# References + +- [mock project repository](https://github.com/rpm-software-management/mock) diff --git a/atftp/atftp.spec b/atftp/atftp.spec index 92a4513..346de63 100644 --- a/atftp/atftp.spec +++ b/atftp/atftp.spec @@ -5,6 +5,7 @@ Version: 0.7 Release: 10 License: GPL Vendor: Linux Networx Inc. +%undefine _hardened_build Source: atftp_0.7.dfsg.orig.tar.gz Source1: tftpd Patch: atftp_0.7.dfsg-3.diff @@ -12,6 +13,11 @@ Patch1: dfsg-3-to-multicast.diff Patch2: dfsg-3-bigfiles.diff Patch3: dfsg-3-to-winpaths.diff Patch4: dfsg-3-mclistfix.diff +Patch5: dfsg-3-pthread-self-include.diff + +BuildRequires: gcc +BuildRequires: make +BuildRequires: glibc-devel Buildroot: /var/tmp/atftp-buildroot Packager: Allen Reese Conflicts: tftp-server @@ -38,11 +44,25 @@ files using the TFTP protocol. %prep %setup -n atftp-0.7.dfsg -%patch -p1 +%patch0 -p1 %patch1 -p1 %patch2 -p1 %patch3 -p1 %patch4 -p1 +%patch5 -p1 +# Avoid argz_next redefinition with modern glibc extern inline semantics. +sed -i '/^#include $/a\ +#ifdef __USE_EXTERN_INLINES\ +#undef __USE_EXTERN_INLINES\ +#endif' argz.c +# Disable glibc extern-inline argz bodies to prevent multiple definitions. +sed -i 's/^#ifdef __USE_EXTERN_INLINES/#if 0/' argz.h +# Force Strncpy to have a linkable external definition with modern GCC. +sed -i 's/^inline char \*Strncpy(/char *Strncpy(/' tftp_def.h +sed -i 's/^inline char \*Strncpy(/char *Strncpy(/' tftp_def.c +# Force tftpd_clientlist_ready to have a linkable external definition. +sed -i 's/^inline void tftpd_clientlist_ready(/void tftpd_clientlist_ready(/' tftpd.h +sed -i 's/^inline void tftpd_clientlist_ready(/void tftpd_clientlist_ready(/' tftpd_list.c %build diff --git a/atftp/dfsg-3-argz-inline-redefinition.diff b/atftp/dfsg-3-argz-inline-redefinition.diff new file mode 100644 index 0000000..3977c90 --- /dev/null +++ b/atftp/dfsg-3-argz-inline-redefinition.diff @@ -0,0 +1,11 @@ +diff -Naur atftp-0.7.dfsg/argz.c atftp-0.7.dfsg-fixed/argz.c +--- atftp-0.7.dfsg/argz.c ++++ atftp-0.7.dfsg-fixed/argz.c +@@ -41,6 +41,9 @@ + #include ++#ifdef __USE_EXTERN_INLINES ++#undef __USE_EXTERN_INLINES ++#endif + #include "argz.h" + + char * argz_next (const char *argz, size_t argz_len, const char *entry) diff --git a/atftp/dfsg-3-pthread-self-include.diff b/atftp/dfsg-3-pthread-self-include.diff new file mode 100644 index 0000000..fd6ad1d --- /dev/null +++ b/atftp/dfsg-3-pthread-self-include.diff @@ -0,0 +1,11 @@ +diff -Naur atftp-0.7.dfsg/logger.c atftp-0.7.dfsg-fixed/logger.c +--- atftp-0.7.dfsg/logger.c ++++ atftp-0.7.dfsg-fixed/logger.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include "logger.h" + diff --git a/elilo/elilo-gnu-efi-strncpy-conflict.patch b/elilo/elilo-gnu-efi-strncpy-conflict.patch new file mode 100644 index 0000000..bf2f582 --- /dev/null +++ b/elilo/elilo-gnu-efi-strncpy-conflict.patch @@ -0,0 +1,32 @@ +--- elilo/strops.h.orig 2003-08-19 13:47:46.000000000 -0300 ++++ elilo/strops.h 2026-03-02 13:08:16.755263536 -0300 +@@ -27,7 +27,6 @@ + #define __STROPS_H__ + + extern CHAR16 *StrChr(IN const CHAR16 *s, const CHAR16 c); +-extern CHAR16 *StrnCpy(OUT CHAR16 *dst, IN const CHAR16 *src, UINTN count); + extern CHAR8 *StrnXCpy(OUT CHAR8 *dst, IN const CHAR16 *src, UINTN count); + + extern CHAR8 *strtok_simple(CHAR8 *in, CHAR8 c); +--- elilo/strops.c.orig 2010-11-09 21:11:07.000000000 -0200 ++++ elilo/strops.c 2026-03-02 13:08:16.755263536 -0300 +@@ -36,19 +36,6 @@ + return (CHAR16 *)s; + } + +-CHAR16 * +-StrnCpy(OUT CHAR16 *dst, IN const CHAR16 *src, IN UINTN size) +-{ +- CHAR16 *res = dst; +- +- while (size && size-- && (*dst++ = *src++) != CHAR_NULL); +- /* +- * does the null padding +- */ +- while (size && size-- > 0) *dst++ = CHAR_NULL; +- +- return res; +-} + + CHAR8 * + StrnXCpy(OUT CHAR8 *dst, IN const CHAR16 *src, IN UINTN size) diff --git a/elilo/elilo-xcat.spec b/elilo/elilo-xcat.spec index 844eab1..f0ab44f 100644 --- a/elilo/elilo-xcat.spec +++ b/elilo/elilo-xcat.spec @@ -17,6 +17,15 @@ BuildArch: noarch Source0: elilo-3.14-source.tar.gz Patch1: elilo-xcat.patch Patch2: elilo-big-bzimage-limit.patch +Patch3: elilo-gnu-efi-strncpy-conflict.patch +Source4: elilo-xcat-3.14-6.noarch.rpm +BuildRequires: gcc +BuildRequires: make +BuildRequires: cpio +%if "%{_host_cpu}" != "ppc64le" +BuildRequires: gnu-efi +BuildRequires: gnu-efi-devel +%endif %description elilo with patches from the xCAT team. Most significantly, adds iPXE usage to the network support @@ -26,18 +35,35 @@ elilo with patches from the xCAT team. Most significantly, adds iPXE usage to t %setup -n elilo %patch1 -p1 %patch2 -p1 +%patch3 -p1 +# EL10 gnu-efi installs linker scripts and crt0 objects under /usr/lib. +sed -i 's|^GNUEFILIB[[:space:]]*=.*|GNUEFILIB = /usr/lib|' Make.defaults +sed -i 's|^EFILIB[[:space:]]*=.*|EFILIB = /usr/lib|' Make.defaults +sed -i 's|^EFICRT0[[:space:]]*=.*|EFICRT0 = /usr/lib|' Make.defaults +%if "%{_host_cpu}" == "ppc64le" +# On ppc64le, reuse the prebuilt EFI payload from the tracked noarch package. +mkdir -p prebuilt +rpm2cpio %{SOURCE4} | (cd prebuilt && cpio -idm --quiet) +test -f prebuilt/tftpboot/xcat/elilo-x64.efi +%endif %build rm -rf %{buildroot} +%if "%{_host_cpu}" != "ppc64le" make +%endif %install mkdir -p %{buildroot}/tftpboot/xcat +%if "%{_host_cpu}" == "ppc64le" +cp prebuilt/tftpboot/xcat/elilo-x64.efi %{buildroot}/tftpboot/xcat/elilo-x64.efi +%else cp elilo.efi %{buildroot}/tftpboot/xcat/elilo-x64.efi +%endif %post diff --git a/elilo/mockbuild.pl b/elilo/mockbuild.pl new file mode 100755 index 0000000..267d1f1 --- /dev/null +++ b/elilo/mockbuild.pl @@ -0,0 +1,346 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(dirname); +use File::Copy qw(copy); +use File::Path qw(make_path remove_tree); +use Getopt::Long qw(GetOptions); + +my $script_dir = abs_path(dirname(__FILE__)); +my $repo_root = abs_path("$script_dir/.."); +my $pkg_dir = "$repo_root/elilo"; +my $spec_file = "$pkg_dir/elilo-xcat.spec"; + +my $source_url = 'https://downloads.sourceforge.net/project/elilo/elilo/elilo-3.14/elilo-3.14-all.tar.gz'; +my $source_file = ''; +my $work_dir = '/tmp/elilo-xcat-mockbuild'; +my $mock_cfg = ''; +my $mock_uniqueext = ''; +my $result_dir = "$repo_root/build-output/list3/elilo-xcat"; +my $log_dir = "$repo_root/build-logs/list3/elilo-xcat"; +my $skip_install = 0; + +GetOptions( + 'source-url=s' => \$source_url, + 'source-file=s' => \$source_file, + 'work-dir=s' => \$work_dir, + 'mock-cfg=s' => \$mock_cfg, + 'mock-uniqueext=s' => \$mock_uniqueext, + 'result-dir=s' => \$result_dir, + 'log-dir=s' => \$log_dir, + 'skip-install!' => \$skip_install, +) or die usage(); + +die "Run as root (current uid=$>)\n" if $> != 0; +die "Missing spec file: $spec_file\n" if !-f $spec_file; + +for my $bin (qw(wget mock rpmbuild rpm dnf file bash grep)) { + run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); +} + +my ($version, @spec_assets) = parse_spec($spec_file); +die "Could not parse Version from $spec_file\n" if !$version; + +if (!$source_file) { + $source_file = "elilo-$version-source.tar.gz"; +} + +my $source_path = "$pkg_dir/$source_file"; +my $arch = capture('uname -m'); +if (!$mock_cfg) { + my $os_id = capture(q{bash -lc 'source /etc/os-release; echo $ID'}); + $mock_cfg = "${os_id}+epel-10-${arch}"; +} +my $mock_uniqueext_opt = $mock_uniqueext ne '' + ? ' --uniqueext ' . sh_quote($mock_uniqueext) + : ''; + +print_step("Configuration"); +print "repo_root: $repo_root\n"; +print "pkg_dir: $pkg_dir\n"; +print "work_dir: $work_dir\n"; +print "result_dir: $result_dir\n"; +print "log_dir: $log_dir\n"; +print "mock_cfg: $mock_cfg\n"; +print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; +print "source_url: $source_url\n"; +print "source_file:$source_file\n"; +print "skip_install: $skip_install\n"; + +make_path($result_dir); +make_path($log_dir); + +print_step("Mock config check"); +run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); + +print_step("Download upstream source"); +run("wget --spider " . sh_quote($source_url)); +run("wget -O " . sh_quote($source_path) . " " . sh_quote($source_url)); +normalize_source_archive($source_path, $version, $work_dir); + +print_step("Verify spec assets"); +for my $asset (@spec_assets) { + my $path = "$pkg_dir/$asset"; + die "Missing required spec asset: $path\n" if !-f $path; +} +print "Verified " . scalar(@spec_assets) . " Source/Patch assets from spec.\n"; + +print_step("Stage files for patch-application check"); +remove_tree($work_dir) if -d $work_dir; +my $prep_top = "$work_dir/prep"; +for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) { + make_path("$prep_top/$d"); +} +copy($spec_file, "$prep_top/SPECS/elilo-xcat.spec") + or die "Failed to copy spec to prep topdir: $!\n"; +for my $asset (@spec_assets) { + copy("$pkg_dir/$asset", "$prep_top/SOURCES/$asset") + or die "Failed to copy $asset into prep SOURCES: $!\n"; +} + +print_step("Apply patches in %prep"); +my $prep_log = "$log_dir/prep.log"; +run( + "rpmbuild --define " . sh_quote("_topdir $prep_top") . + " -bp --nodeps " . sh_quote("$prep_top/SPECS/elilo-xcat.spec") . + " > " . sh_quote($prep_log) . " 2>&1" +); +my $patch_count = capture( + "grep -c '^Patch #' " . sh_quote($prep_log) . " || true" +); +print "Patch application check passed. Applied patches: $patch_count\n"; + +print_step("Build SRPM with mock"); +my $srpm_out = "$work_dir/srpm"; +make_path($srpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --buildsrpm --spec " . sh_quote($spec_file) . + " --sources " . sh_quote($pkg_dir) . + " --resultdir " . sh_quote($srpm_out) +); + +my @srpms = sort glob("$srpm_out/elilo-xcat-*.src.rpm"); +die "SRPM not generated in $srpm_out\n" if !@srpms; +my $srpm = $srpms[-1]; +print "SRPM: $srpm\n"; + +print_step("Rebuild RPM with mock"); +my $rpm_out = "$work_dir/rpm"; +make_path($rpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --rebuild " . sh_quote($srpm) . + " --resultdir " . sh_quote($rpm_out) +); + +my @all_rpms = sort glob("$rpm_out/*.rpm"); +die "No RPMs generated in $rpm_out\n" if !@all_rpms; + +my $main_rpm = ''; +for my $rpm (@all_rpms) { + next if $rpm =~ /\.src\.rpm$/; + my $name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($rpm)); + my $rarch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($rpm)); + if ($name eq 'elilo-xcat' && $rarch eq 'noarch') { + $main_rpm = $rpm; + last; + } +} +die "Could not find main elilo-xcat noarch RPM in $rpm_out\n" if !$main_rpm; + +print_step("Verify generated RPM"); +my $rpm_name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($main_rpm)); +my $rpm_arch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($main_rpm)); +die "Unexpected RPM name: $rpm_name\n" if $rpm_name ne 'elilo-xcat'; +die "Unexpected RPM arch: $rpm_arch (expected noarch)\n" if $rpm_arch ne 'noarch'; +run( + "rpm -qpl " . sh_quote($main_rpm) . + " | grep -Fx /tftpboot/xcat/elilo-x64.efi >/dev/null" +); +print "Verified RPM name/arch/payload: $main_rpm\n"; + +print_step("Copy artifacts and logs"); +for my $rpm (@all_rpms) { + copy($rpm, $result_dir) or die "Failed to copy $rpm to $result_dir: $!\n"; +} +for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$rpm_out/$log"; + next if !-f $src; + copy($src, "$log_dir/$log") + or die "Failed to copy $src to $log_dir: $!\n"; +} + +if (!$skip_install) { + print_step("Install RPM and run smoke tests"); + run("dnf -y install " . sh_quote($main_rpm)); + + my $efi_file = '/tftpboot/xcat/elilo-x64.efi'; + die "Missing installed EFI binary: $efi_file\n" if !-f $efi_file; + + my $file_log = "$log_dir/smoke-file.log"; + my $qf_log = "$log_dir/smoke-rpm-qf.log"; + my $rc_file = run_capture_rc("file $efi_file", $file_log); + my $rc_qf = run_capture_rc("rpm -qf $efi_file", $qf_log); + + die "Smoke check failed: file returned $rc_file\n" if $rc_file != 0; + die "Smoke check failed: rpm -qf returned $rc_qf\n" if $rc_qf != 0; + + my $file_out = slurp($file_log); + my $qf_out = slurp($qf_log); + + die "EFI file signature check failed:\n$file_out\n" + if $file_out !~ /(EFI application|PE32\+ executable)/i; + die "Installed file is not owned by elilo-xcat:\n$qf_out\n" + if $qf_out !~ /^elilo-xcat-/m; + + my $summary = "$log_dir/smoke-summary.txt"; + open my $sfh, '>', $summary or die "Cannot write $summary: $!\n"; + print {$sfh} "efi_file=$efi_file\n"; + print {$sfh} "rc_file=$rc_file\n"; + print {$sfh} "rc_qf=$rc_qf\n"; + close $sfh; +} + +print_step("Completed"); +print "Main RPM: $main_rpm\n"; +print "Artifacts: $result_dir\n"; +print "Logs: $log_dir\n"; +exit 0; + +sub usage { + return <<"USAGE"; +Usage: $0 [options] + --source-url URL Upstream tarball URL (default: $source_url) + --source-file FILE Source filename stored in elilo/ (default: inferred from spec version) + --work-dir PATH Temporary work dir (default: $work_dir) + --mock-cfg NAME Mock config (default: +epel-10-) + --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds + --result-dir PATH Output RPM/SRPM directory (default: $result_dir) + --log-dir PATH Log directory (default: $log_dir) + --skip-install Skip dnf install + smoke tests +USAGE +} + +sub parse_spec { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot open spec $path: $!\n"; + + my $version = ''; + my @assets; + while (my $line = <$fh>) { + if ($line =~ /^Version:\s*(\S+)/) { + $version = $1; + } + if ($line =~ /^(?:Source|Patch)\d*:\s*(\S+)/) { + my $asset = $1; + push @assets, $asset; + } + } + close $fh; + + @assets = map { + my $v = $_; + $v =~ s/%\{version\}/$version/g; + $v =~ s/%\{ver\}/$version/g; + $v; + } @assets; + + return ($version, @assets); +} + +sub normalize_source_archive { + my ($archive, $version, $work_base) = @_; + + my $has_elilo = capture( + "tar -tzf " . sh_quote($archive) . + " | grep -E '^(\\./)?elilo/' | head -n1 || true" + ); + return if $has_elilo ne ''; + + my $nested = capture( + "tar -tzf " . sh_quote($archive) . + " | grep -E '^(\\./)?elilo-$version-source\\.tar\\.gz\$' | head -n1 || true" + ); + die "Downloaded archive does not contain elilo source payload: $archive\n" + if $nested eq ''; + + my $normalize_dir = "$work_base/source-normalize"; + remove_tree($normalize_dir) if -d $normalize_dir; + make_path($normalize_dir); + + run( + "tar -xzf " . sh_quote($archive) . + " -C " . sh_quote($normalize_dir) . + " " . sh_quote($nested) + ); + + my $nested_rel = $nested; + $nested_rel =~ s{^\./}{}; + my $nested_path = "$normalize_dir/$nested_rel"; + die "Failed to extract nested source archive: $nested_path\n" + if !-f $nested_path; + + copy($nested_path, $archive) + or die "Failed to normalize source archive $archive: $!\n"; + + my $recheck = capture( + "tar -tzf " . sh_quote($archive) . + " | grep -E '^(\\./)?elilo/' | head -n1 || true" + ); + die "Normalized source archive still missing elilo top-level tree: $archive\n" + if $recheck eq ''; +} + +sub print_step { + my ($msg) = @_; + print "\n== $msg ==\n"; +} + +sub sh_quote { + my ($s) = @_; + $s =~ s/'/'"'"'/g; + return "'$s'"; +} + +sub run { + my ($cmd) = @_; + print "+ $cmd\n"; + my $rc = system($cmd); + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\n"; + } +} + +sub capture { + my ($cmd) = @_; + print "+ $cmd\n"; + my $out = `$cmd`; + my $rc = $?; + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\nOutput:\n$out\n"; + } + chomp $out; + return $out; +} + +sub run_capture_rc { + my ($cmd, $log_file) = @_; + my $full = "$cmd > " . sh_quote($log_file) . " 2>&1"; + print "+ $full\n"; + my $rc = system($full); + return $rc == -1 ? 255 : ($rc >> 8); +} + +sub slurp { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot read $path: $!\n"; + local $/; + my $content = <$fh>; + close $fh; + return $content; +} diff --git a/goconserver/mockbuild.pl b/goconserver/mockbuild.pl new file mode 100755 index 0000000..8ee86ad --- /dev/null +++ b/goconserver/mockbuild.pl @@ -0,0 +1,398 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(dirname basename); +use File::Copy qw(copy); +use File::Path qw(make_path remove_tree); +use Getopt::Long qw(GetOptions); + +my $script_dir = abs_path(dirname(__FILE__)); +my $repo_root = abs_path("$script_dir/.."); +my $pkg_dir = "$repo_root/goconserver"; +my $work_dir = '/tmp/goconserver-mockbuild'; +my $mock_cfg = ''; +my $mock_uniqueext = ''; +my $result_dir = ''; +my $log_dir = ''; +my $src_rpm = ''; +my $src_rpm_url = ''; +my $skip_install = 0; + +GetOptions( + 'work-dir=s' => \$work_dir, + 'mock-cfg=s' => \$mock_cfg, + 'mock-uniqueext=s' => \$mock_uniqueext, + 'result-dir=s' => \$result_dir, + 'log-dir=s' => \$log_dir, + 'src-rpm=s' => \$src_rpm, + 'src-rpm-url=s' => \$src_rpm_url, + 'skip-install!' => \$skip_install, +) or die usage(); + +die "Run as root (current uid=$>)\n" if $> != 0; +die "Missing package directory: $pkg_dir\n" if !-d $pkg_dir; + +for my $bin (qw(mock rpmbuild rpm dnf ldd bash grep tar wget sha256sum cut)) { + run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); +} +my $has_bsdtar = command_exists('bsdtar'); +if (!$has_bsdtar) { + for my $bin (qw(rpm2cpio cpio)) { + run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); + } +} + +my $arch = capture('uname -m'); +if (!$mock_cfg) { + my $os_id = capture(q{bash -lc 'source /etc/os-release; echo $ID'}); + $mock_cfg = "${os_id}+epel-10-${arch}"; +} +my $mock_uniqueext_opt = $mock_uniqueext ne '' + ? ' --uniqueext ' . sh_quote($mock_uniqueext) + : ''; + +if (!$result_dir) { + $result_dir = "$repo_root/build-output/list5/goconserver/$arch"; +} +if (!$log_dir) { + $log_dir = "$repo_root/build-logs/list5/goconserver/$arch"; +} + +make_path($result_dir); +make_path($log_dir); +make_path($work_dir); + +if ($src_rpm_url) { + my $dst = "$work_dir/" . basename($src_rpm_url); + print_step("Download source RPM"); + run("wget --spider " . sh_quote($src_rpm_url)); + run("wget -O " . sh_quote($dst) . " " . sh_quote($src_rpm_url)); + my $sha = capture("sha256sum " . sh_quote($dst) . " | cut -d ' ' -f1"); + open my $mfh, '>', "$log_dir/source-rpm.txt" + or die "Cannot write $log_dir/source-rpm.txt: $!\n"; + print {$mfh} "url=$src_rpm_url\n"; + print {$mfh} "path=$dst\n"; + print {$mfh} "sha256=$sha\n"; + close $mfh; + $src_rpm = $dst; +} + +if (!$src_rpm) { + my @candidates; + push @candidates, glob("$repo_root/build-output/list5/goconserver/$arch/goconserver-*.src.rpm"); + push @candidates, glob("$repo_root/goconserver-build-$arch/results/srpm/goconserver-*.src.rpm"); + push @candidates, glob("$repo_root/goconserver-build-$arch/results/rpm/goconserver-*.src.rpm"); + @candidates = sort @candidates; + $src_rpm = $candidates[-1] if @candidates; +} +die "Could not locate goconserver source RPM. Use --src-rpm or --src-rpm-url.\n" + if !$src_rpm || !-f $src_rpm; + +print_step("Configuration"); +print "repo_root: $repo_root\n"; +print "pkg_dir: $pkg_dir\n"; +print "work_dir: $work_dir\n"; +print "result_dir: $result_dir\n"; +print "log_dir: $log_dir\n"; +print "arch: $arch\n"; +print "mock_cfg: $mock_cfg\n"; +print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; +print "src_rpm: $src_rpm\n"; +print "skip_install:$skip_install\n"; +print "extractor: " . ($has_bsdtar ? 'bsdtar' : 'rpm2cpio|cpio') . "\n"; + +print_step("Mock config check"); +run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); + +print_step("Extract source RPM contents"); +my $stage_dir = "$work_dir/stage"; +remove_tree($stage_dir) if -d $stage_dir; +make_path($stage_dir); +if ($has_bsdtar) { + run("bsdtar -xf " . sh_quote($src_rpm) . " -C " . sh_quote($stage_dir)); +} else { + my $extract_cmd = + "cd " . sh_quote($stage_dir) . + " && rpm2cpio " . sh_quote($src_rpm) . + " | cpio -idm --quiet"; + run( + "bash -lc " . + sh_quote($extract_cmd) + ); +} + +my @specs = sort glob("$stage_dir/*.spec"); +die "Expected exactly one spec in $stage_dir, found " . scalar(@specs) . "\n" + if @specs != 1; +my $spec_file = $specs[0]; + +my ($version, @assets) = parse_spec($spec_file); +die "Could not parse Version from $spec_file\n" if !$version; +die "No Source/Patch assets parsed from $spec_file\n" if !@assets; + +for my $asset (@assets) { + my $path = "$stage_dir/$asset"; + die "Missing extracted spec asset: $path\n" if !-f $path; +} + +my @repack = grep { /goconserver-repack-.*\.tar\.gz$/ } @assets; +die "Expected goconserver-repack source tar from spec assets.\n" if !@repack; +my $repack_tar = "$stage_dir/$repack[0]"; + +print_step("Verify repack payload"); +for my $required ( + '/usr/bin/goconserver', + '/usr/bin/congo', + '/usr/lib/systemd/system/goconserver.service', + '/etc/goconserver/server.conf' +) { + my $rel = substr($required, 1); + run( + "tar -tzf " . sh_quote($repack_tar) . + " | grep -F " . sh_quote("./$rel") . + " >/dev/null" + ); +} + +print_step("Run %prep check"); +my $prep_top = "$work_dir/prep"; +remove_tree($prep_top) if -d $prep_top; +for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) { + make_path("$prep_top/$d"); +} +copy($spec_file, "$prep_top/SPECS/" . basename($spec_file)) + or die "Failed to copy spec for prep check: $!\n"; +for my $asset (@assets) { + copy("$stage_dir/$asset", "$prep_top/SOURCES/$asset") + or die "Failed to copy $asset for prep check: $!\n"; +} +run( + "rpmbuild --define " . sh_quote("_topdir $prep_top") . + " -bp --nodeps " . sh_quote("$prep_top/SPECS/" . basename($spec_file)) . + " > " . sh_quote("$log_dir/prep.log") . " 2>&1" +); + +print_step("Build SRPM with mock"); +my $srpm_out = "$work_dir/srpm"; +remove_tree($srpm_out) if -d $srpm_out; +make_path($srpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --buildsrpm --spec " . sh_quote($spec_file) . + " --sources " . sh_quote($stage_dir) . + " --resultdir " . sh_quote($srpm_out) . + " > " . sh_quote("$log_dir/mock-buildsrpm.log") . " 2>&1" +); + +my @srpms = sort glob("$srpm_out/goconserver-*.src.rpm"); +die "SRPM not generated in $srpm_out\n" if !@srpms; +my $built_srpm = $srpms[-1]; +print "SRPM: $built_srpm\n"; + +print_step("Rebuild RPM with mock"); +my $rpm_out = "$work_dir/rpm"; +remove_tree($rpm_out) if -d $rpm_out; +make_path($rpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --rebuild " . sh_quote($built_srpm) . + " --resultdir " . sh_quote($rpm_out) . + " > " . sh_quote("$log_dir/mock-rebuild.log") . " 2>&1" +); + +my @all_rpms = sort glob("$rpm_out/*.rpm"); +die "No RPMs generated in $rpm_out\n" if !@all_rpms; + +my $main_rpm = ''; +for my $rpm (@all_rpms) { + next if $rpm =~ /\.src\.rpm$/; + my $name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($rpm)); + my $rarch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($rpm)); + if ($name eq 'goconserver' && $rarch eq $arch) { + $main_rpm = $rpm; + last; + } +} +die "Could not find main goconserver RPM in $rpm_out\n" if !$main_rpm; + +print_step("Verify generated RPM"); +my $rpm_name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($main_rpm)); +my $rpm_arch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($main_rpm)); +die "Unexpected main RPM name: $rpm_name\n" if $rpm_name ne 'goconserver'; +die "Unexpected main RPM arch: $rpm_arch (expected $arch)\n" if $rpm_arch ne $arch; +for my $required ( + '/usr/bin/goconserver', + '/usr/bin/congo', + '/usr/lib/systemd/system/goconserver.service' +) { + run("rpm -qpl " . sh_quote($main_rpm) . " | grep -Fx " . sh_quote($required) . " >/dev/null"); +} + +print_step("Copy artifacts and logs"); +for my $rpm (@all_rpms) { + copy($rpm, $result_dir) or die "Failed to copy $rpm to $result_dir: $!\n"; +} +copy($built_srpm, $result_dir) or die "Failed to copy $built_srpm to $result_dir: $!\n"; + +for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$rpm_out/$log"; + next if !-f $src; + copy($src, "$log_dir/$log") or die "Failed to copy $src to $log_dir: $!\n"; +} +for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$srpm_out/$log"; + next if !-f $src; + copy($src, "$log_dir/srpm-$log") or die "Failed to copy $src to $log_dir: $!\n"; +} + +if (!$skip_install) { + print_step("Install RPM and run smoke tests"); + run("dnf -y install " . sh_quote($main_rpm)); + + my $bin_main = '/usr/bin/goconserver'; + my $bin_cli = '/usr/bin/congo'; + die "Missing installed binary: $bin_main\n" if !-x $bin_main; + die "Missing installed binary: $bin_cli\n" if !-x $bin_cli; + + my $rc_help_main = run_capture_rc("$bin_main -h", "$log_dir/smoke-goconserver-help.log"); + my $rc_help_cli = run_capture_rc("$bin_cli -h", "$log_dir/smoke-congo-help.log"); + my $rc_ldd_main = run_capture_rc("ldd $bin_main", "$log_dir/smoke-goconserver-ldd.log"); + my $rc_ldd_cli = run_capture_rc("ldd $bin_cli", "$log_dir/smoke-congo-ldd.log"); + + die "goconserver -h returned unexpected rc=$rc_help_main\n" + if $rc_help_main != 0 && $rc_help_main != 1; + die "congo -h returned unexpected rc=$rc_help_cli\n" + if $rc_help_cli != 0 && $rc_help_cli != 1; + die "ldd goconserver returned rc=$rc_ldd_main\n" if $rc_ldd_main != 0; + die "ldd congo returned rc=$rc_ldd_cli\n" if $rc_ldd_cli != 0; + + my $help_main = slurp("$log_dir/smoke-goconserver-help.log"); + my $help_cli = slurp("$log_dir/smoke-congo-help.log"); + my $ldd_main = slurp("$log_dir/smoke-goconserver-ldd.log"); + my $ldd_cli = slurp("$log_dir/smoke-congo-ldd.log"); + + die "goconserver help output missing usage text\n" + if $help_main !~ /usage|goconserver/i; + die "congo help output missing usage text\n" + if $help_cli !~ /usage|congo/i; + die "goconserver ldd output missing libc\n" + if $ldd_main !~ /libc/; + die "congo ldd output missing libc\n" + if $ldd_cli !~ /libc/; + + open my $sfh, '>', "$log_dir/smoke-summary.txt" + or die "Cannot write $log_dir/smoke-summary.txt: $!\n"; + print {$sfh} "binary_main=$bin_main\n"; + print {$sfh} "binary_cli=$bin_cli\n"; + print {$sfh} "rc_help_main=$rc_help_main\n"; + print {$sfh} "rc_help_cli=$rc_help_cli\n"; + print {$sfh} "rc_ldd_main=$rc_ldd_main\n"; + print {$sfh} "rc_ldd_cli=$rc_ldd_cli\n"; + close $sfh; +} + +print_step("Completed"); +print "Main RPM: $main_rpm\n"; +print "Artifacts: $result_dir\n"; +print "Logs: $log_dir\n"; +exit 0; + +sub usage { + return <<"USAGE"; +Usage: $0 [options] + --src-rpm PATH Source RPM containing goconserver.spec + repack tar + --src-rpm-url URL Optional URL to download source RPM before build + --work-dir PATH Temporary work dir (default: $work_dir) + --mock-cfg NAME Mock config (default: +epel-10-) + --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds + --result-dir PATH Output RPM/SRPM directory (default: build-output/list5/goconserver/) + --log-dir PATH Log directory (default: build-logs/list5/goconserver/) + --skip-install Skip dnf install + smoke tests +USAGE +} + +sub parse_spec { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot open spec $path: $!\n"; + + my $version = ''; + my @assets; + while (my $line = <$fh>) { + if ($line =~ /^Version:\s*(\S+)/) { + $version = $1; + } + if ($line =~ /^(?:Source|Patch)\d*:\s*(\S+)/) { + my $asset = $1; + push @assets, $asset; + } + } + close $fh; + + @assets = map { + my $v = $_; + $v =~ s/%\{version\}/$version/g; + $v =~ s/%\{ver\}/$version/g; + $v; + } @assets; + + return ($version, @assets); +} + +sub command_exists { + my ($bin) = @_; + my $rc = system("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); + return $rc == 0 ? 1 : 0; +} + +sub print_step { + my ($msg) = @_; + print "\n== $msg ==\n"; +} + +sub sh_quote { + my ($s) = @_; + $s =~ s/'/'"'"'/g; + return "'$s'"; +} + +sub run { + my ($cmd) = @_; + print "+ $cmd\n"; + my $rc = system($cmd); + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\n"; + } +} + +sub capture { + my ($cmd) = @_; + print "+ $cmd\n"; + my $out = `$cmd`; + my $rc = $?; + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\nOutput:\n$out\n"; + } + chomp $out; + return $out; +} + +sub run_capture_rc { + my ($cmd, $log_file) = @_; + my $full = "$cmd > " . sh_quote($log_file) . " 2>&1"; + print "+ $full\n"; + my $rc = system($full); + return $rc == -1 ? 255 : ($rc >> 8); +} + +sub slurp { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot read $path: $!\n"; + local $/; + my $content = <$fh>; + close $fh; + return $content; +} diff --git a/grub2-xcat/mockbuild.pl b/grub2-xcat/mockbuild.pl new file mode 100755 index 0000000..366a3f9 --- /dev/null +++ b/grub2-xcat/mockbuild.pl @@ -0,0 +1,362 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(dirname basename); +use File::Copy qw(copy); +use File::Path qw(make_path remove_tree); +use Getopt::Long qw(GetOptions); + +my $script_dir = abs_path(dirname(__FILE__)); +my $repo_root = abs_path("$script_dir/.."); +my $pkg_dir = "$repo_root/grub2-xcat"; +my $spec_file = "$pkg_dir/grub2-xcat.spec"; +my $recompile_dir = "$repo_root/grub2-xcat.recompile"; + +my $resource_mode = 'reuse-grub2-res'; +my $upstream_url = 'https://mirror.stream.centos.org/10-stream/BaseOS/source/tree/Packages/grub2-2.12-37.el10.src.rpm'; +my $upstream_file = ''; +my $work_dir = '/tmp/grub2-xcat-mockbuild'; +my $mock_cfg = ''; +my $mock_uniqueext = ''; +my $result_dir = "$repo_root/build-output/list3/grub2-xcat"; +my $log_dir = "$repo_root/build-logs/list3/grub2-xcat"; +my $skip_install = 0; +my $skip_upstream_download = 0; + +GetOptions( + 'resource-mode=s' => \$resource_mode, + 'upstream-url=s' => \$upstream_url, + 'upstream-file=s' => \$upstream_file, + 'work-dir=s' => \$work_dir, + 'mock-cfg=s' => \$mock_cfg, + 'mock-uniqueext=s' => \$mock_uniqueext, + 'result-dir=s' => \$result_dir, + 'log-dir=s' => \$log_dir, + 'skip-install!' => \$skip_install, + 'skip-upstream-download!' => \$skip_upstream_download, +) or die usage(); + +die "Run as root (current uid=$>)\n" if $> != 0; +die "Missing spec file: $spec_file\n" if !-f $spec_file; +die "Missing recompile dir: $recompile_dir\n" if !-d $recompile_dir; +die "Invalid --resource-mode '$resource_mode'\n" + if $resource_mode ne 'reuse-grub2-res' && $resource_mode ne 'regenerate-from-el10-srcrpm'; +die "Mode regenerate-from-el10-srcrpm is not supported in this script yet; use reuse-grub2-res on x86_64 first.\n" + if $resource_mode eq 'regenerate-from-el10-srcrpm'; + +for my $bin (qw(wget mock rpmbuild rpm dnf file bash tar sha256sum grep cmp cut)) { + run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); +} + +my ($version, @spec_assets) = parse_spec($spec_file); +die "Could not parse Version from $spec_file\n" if !$version; + +if (!$upstream_file) { + $upstream_file = basename($upstream_url); +} +die "Could not derive upstream file name from URL: $upstream_url\n" if !$upstream_file; +my $upstream_path = "$recompile_dir/$upstream_file"; + +my $arch = capture('uname -m'); +if (!$mock_cfg) { + my $os_id = capture(q{bash -lc 'source /etc/os-release; echo $ID'}); + $mock_cfg = "${os_id}+epel-10-${arch}"; +} +my $mock_uniqueext_opt = $mock_uniqueext ne '' + ? ' --uniqueext ' . sh_quote($mock_uniqueext) + : ''; + +print_step("Configuration"); +print "repo_root: $repo_root\n"; +print "pkg_dir: $pkg_dir\n"; +print "spec_file: $spec_file\n"; +print "recompile_dir: $recompile_dir\n"; +print "resource_mode: $resource_mode\n"; +print "work_dir: $work_dir\n"; +print "result_dir: $result_dir\n"; +print "log_dir: $log_dir\n"; +print "mock_cfg: $mock_cfg\n"; +print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; +print "upstream_url: $upstream_url\n"; +print "upstream_file: $upstream_file\n"; +print "skip_install: $skip_install\n"; +print "skip_upstream_download: $skip_upstream_download\n"; + +make_path($result_dir); +make_path($log_dir); + +print_step("Mock config check"); +run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); + +if (!$skip_upstream_download) { + print_step("Download upstream source RPM"); + run("wget --spider " . sh_quote($upstream_url)); + run("wget -O " . sh_quote($upstream_path) . " " . sh_quote($upstream_url)); + my $sha = capture("sha256sum " . sh_quote($upstream_path) . " | cut -d ' ' -f1"); + my $meta_file = "$log_dir/upstream-source.txt"; + open my $mfh, '>', $meta_file or die "Cannot write $meta_file: $!\n"; + print {$mfh} "url=$upstream_url\n"; + print {$mfh} "file=$upstream_path\n"; + print {$mfh} "sha256=$sha\n"; + close $mfh; + print "Downloaded upstream source rpm: $upstream_path\n"; + print "SHA256: $sha\n"; +} + +print_step("Verify grub2 resource payload"); +my $resource_tar = "$pkg_dir/grub2-res.tar.gz"; +die "Missing required resource tarball: $resource_tar\n" if !-f $resource_tar; +run( + "tar -tzf " . sh_quote($resource_tar) . + " | grep -F 'powerpc-ieee1275/' >/dev/null" +); +run( + "tar -tzf " . sh_quote($resource_tar) . + " | grep -F 'powerpc-ieee1275/core.elf' >/dev/null" +); + +print_step("Verify spec assets"); +for my $asset (@spec_assets) { + my $path = "$pkg_dir/$asset"; + die "Missing required spec asset: $path\n" if !-f $path; +} +print "Verified " . scalar(@spec_assets) . " Source/Patch assets from spec.\n"; + +print_step("Stage files for prep check"); +remove_tree($work_dir) if -d $work_dir; +my $prep_top = "$work_dir/prep"; +for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) { + make_path("$prep_top/$d"); +} +copy($spec_file, "$prep_top/SPECS/grub2-xcat.spec") + or die "Failed to copy spec to prep topdir: $!\n"; +for my $asset (@spec_assets) { + copy("$pkg_dir/$asset", "$prep_top/SOURCES/$asset") + or die "Failed to copy $asset into prep SOURCES: $!\n"; +} + +print_step("Apply %prep flow"); +my $prep_log = "$log_dir/prep.log"; +run( + "rpmbuild --define " . sh_quote("_topdir $prep_top") . + " -bp --nodeps " . sh_quote("$prep_top/SPECS/grub2-xcat.spec") . + " > " . sh_quote($prep_log) . " 2>&1" +); +my $patch_count = capture("grep -c '^Patch #' " . sh_quote($prep_log) . " || true"); +print "Prep dry run passed. Applied patches: $patch_count\n"; + +print_step("Build SRPM with mock"); +my $srpm_out = "$work_dir/srpm"; +make_path($srpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --buildsrpm --spec " . sh_quote($spec_file) . + " --sources " . sh_quote($pkg_dir) . + " --resultdir " . sh_quote($srpm_out) +); + +my @srpms = sort glob("$srpm_out/grub2-xcat-*.src.rpm"); +die "SRPM not generated in $srpm_out\n" if !@srpms; +my $srpm = $srpms[-1]; +print "SRPM: $srpm\n"; + +print_step("Rebuild RPM with mock"); +my $rpm_out = "$work_dir/rpm"; +make_path($rpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --rebuild " . sh_quote($srpm) . + " --resultdir " . sh_quote($rpm_out) +); + +my @all_rpms = sort glob("$rpm_out/*.rpm"); +die "No RPMs generated in $rpm_out\n" if !@all_rpms; + +my $main_rpm = ''; +for my $rpm (@all_rpms) { + next if $rpm =~ /\.src\.rpm$/; + my $name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($rpm)); + my $rarch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($rpm)); + if ($name eq 'grub2-xcat' && $rarch eq 'noarch') { + $main_rpm = $rpm; + last; + } +} +die "Could not find main grub2-xcat noarch RPM in $rpm_out\n" if !$main_rpm; + +print_step("Verify generated RPM"); +my $rpm_name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($main_rpm)); +my $rpm_arch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($main_rpm)); +die "Unexpected RPM name: $rpm_name\n" if $rpm_name ne 'grub2-xcat'; +die "Unexpected RPM arch: $rpm_arch (expected noarch)\n" if $rpm_arch ne 'noarch'; +run( + "rpm -qpl " . sh_quote($main_rpm) . + " | grep -F '/tftpboot/boot/grub2/powerpc-ieee1275/' >/dev/null" +); +run( + "rpm -qpl " . sh_quote($main_rpm) . + " | grep -Fx /tftpboot/boot/grub2/powerpc-ieee1275/core.elf >/dev/null" +); +print "Verified RPM name/arch/payload: $main_rpm\n"; + +print_step("Copy artifacts and logs"); +for my $rpm (@all_rpms) { + copy($rpm, $result_dir) or die "Failed to copy $rpm to $result_dir: $!\n"; +} +copy($srpm, $result_dir) or die "Failed to copy $srpm to $result_dir: $!\n"; +for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$rpm_out/$log"; + next if !-f $src; + copy($src, "$log_dir/$log") + or die "Failed to copy $src to $log_dir: $!\n"; +} +for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$srpm_out/$log"; + next if !-f $src; + copy($src, "$log_dir/srpm-$log") + or die "Failed to copy $src to $log_dir: $!\n"; +} + +if (!$skip_install) { + print_step("Install RPM and run smoke tests"); + run("dnf -y install " . sh_quote($main_rpm)); + + my $core = '/tftpboot/boot/grub2/powerpc-ieee1275/core.elf'; + my $grub2ppc = '/tftpboot/boot/grub2/grub2.ppc'; + die "Missing installed core image: $core\n" if !-f $core; + die "Missing installed post script output: $grub2ppc\n" if !-f $grub2ppc; + + my $file_core_log = "$log_dir/smoke-file-core.log"; + my $file_ppc_log = "$log_dir/smoke-file-grub2ppc.log"; + my $qf_log = "$log_dir/smoke-rpm-qf.log"; + + my $rc_file_core = run_capture_rc("file $core", $file_core_log); + my $rc_file_ppc = run_capture_rc("file $grub2ppc", $file_ppc_log); + my $rc_qf = run_capture_rc("rpm -qf $core", $qf_log); + my $rc_cmp = run_capture_rc("cmp -s $core $grub2ppc", "$log_dir/smoke-cmp.log"); + + die "Smoke check failed: file core returned $rc_file_core\n" if $rc_file_core != 0; + die "Smoke check failed: file grub2.ppc returned $rc_file_ppc\n" if $rc_file_ppc != 0; + die "Smoke check failed: rpm -qf returned $rc_qf\n" if $rc_qf != 0; + die "Smoke check failed: core.elf and grub2.ppc differ (cmp rc=$rc_cmp)\n" if $rc_cmp != 0; + + my $core_out = slurp($file_core_log); + my $qf_out = slurp($qf_log); + + die "Core image signature check failed:\n$core_out\n" + if $core_out !~ /ELF|data/i; + die "Installed core image is not owned by grub2-xcat:\n$qf_out\n" + if $qf_out !~ /^grub2-xcat-/m; + + my $summary = "$log_dir/smoke-summary.txt"; + open my $sfh, '>', $summary or die "Cannot write $summary: $!\n"; + print {$sfh} "core=$core\n"; + print {$sfh} "grub2ppc=$grub2ppc\n"; + print {$sfh} "rc_file_core=$rc_file_core\n"; + print {$sfh} "rc_file_ppc=$rc_file_ppc\n"; + print {$sfh} "rc_qf=$rc_qf\n"; + print {$sfh} "rc_cmp=$rc_cmp\n"; + close $sfh; +} + +print_step("Completed"); +print "Main RPM: $main_rpm\n"; +print "Artifacts: $result_dir\n"; +print "Logs: $log_dir\n"; +exit 0; + +sub usage { + return <<"USAGE"; +Usage: $0 [options] + --resource-mode MODE Build mode: reuse-grub2-res|regenerate-from-el10-srcrpm (default: $resource_mode) + --upstream-url URL Upstream grub2 source RPM URL (default: $upstream_url) + --upstream-file FILE Source RPM filename stored in grub2-xcat.recompile/ (default: basename of URL) + --work-dir PATH Temporary work dir (default: $work_dir) + --mock-cfg NAME Mock config (default: +epel-10-) + --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds + --result-dir PATH Output RPM/SRPM directory (default: $result_dir) + --log-dir PATH Log directory (default: $log_dir) + --skip-upstream-download Skip wget of upstream source RPM + --skip-install Skip dnf install + smoke tests +USAGE +} + +sub parse_spec { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot open spec $path: $!\n"; + + my $version = ''; + my @assets; + while (my $line = <$fh>) { + if ($line =~ /^Version:\s*(\S+)/) { + $version = $1; + } + if ($line =~ /^(?:Source|Patch)\d*:\s*(\S+)/) { + my $asset = $1; + push @assets, $asset; + } + } + close $fh; + + @assets = map { + my $v = $_; + $v =~ s/%\{version\}/$version/g; + $v =~ s/%\{ver\}/$version/g; + $v; + } @assets; + + return ($version, @assets); +} + +sub print_step { + my ($msg) = @_; + print "\n== $msg ==\n"; +} + +sub sh_quote { + my ($s) = @_; + $s =~ s/'/'"'"'/g; + return "'$s'"; +} + +sub run { + my ($cmd) = @_; + print "+ $cmd\n"; + my $rc = system($cmd); + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\n"; + } +} + +sub capture { + my ($cmd) = @_; + print "+ $cmd\n"; + my $out = `$cmd`; + my $rc = $?; + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\nOutput:\n$out\n"; + } + chomp $out; + return $out; +} + +sub run_capture_rc { + my ($cmd, $log_file) = @_; + my $full = "$cmd > " . sh_quote($log_file) . " 2>&1"; + print "+ $full\n"; + my $rc = system($full); + return $rc == -1 ? 255 : ($rc >> 8); +} + +sub slurp { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot read $path: $!\n"; + local $/; + my $content = <$fh>; + close $fh; + return $content; +} diff --git a/ipmitool/0016-el10-gcc14-missing-intf-getters.patch b/ipmitool/0016-el10-gcc14-missing-intf-getters.patch new file mode 100644 index 0000000..f99abed --- /dev/null +++ b/ipmitool/0016-el10-gcc14-missing-intf-getters.patch @@ -0,0 +1,13 @@ +diff --git a/include/ipmitool/ipmi_intf.h b/include/ipmitool/ipmi_intf.h +index 063fdd5..fcf5937 100644 +--- a/include/ipmitool/ipmi_intf.h ++++ b/include/ipmitool/ipmi_intf.h +@@ -262,6 +262,8 @@ struct ipmi_intf { + struct ipmi_intf * ipmi_intf_load(char * name); + void ipmi_intf_print(struct ipmi_intf_support * intflist); + ++uint16_t ipmi_intf_get_max_request_data_size(struct ipmi_intf * intf); ++uint16_t ipmi_intf_get_max_response_data_size(struct ipmi_intf * intf); + void ipmi_intf_session_set_hostname(struct ipmi_intf * intf, char * hostname); + void ipmi_intf_session_set_username(struct ipmi_intf * intf, char * username); + void ipmi_intf_session_set_password(struct ipmi_intf * intf, char * password); diff --git a/ipmitool/ipmitool.spec b/ipmitool/ipmitool.spec index 1f14f77..84f2a0f 100644 --- a/ipmitool/ipmitool.spec +++ b/ipmitool/ipmitool.spec @@ -23,6 +23,7 @@ Patch12: 0012-CVE-2020-5208.patch Patch13: 0013-quanta-oem-support.patch Patch14: 0014-lanplus-cipher-retry.patch Patch15: 0015-lanplus-Cleanup.-Refix-6dec83ff-fix-be2c0c4b.patch +Patch16: 0016-el10-gcc14-missing-intf-getters.patch Patch80: ipmitool-%{version}-saneretry.patch Patch82: ipmitool-%{version}-rflash.patch @@ -73,6 +74,7 @@ fi %patch13 -p1 %patch14 -p1 %patch15 -p1 +%patch16 -p1 %patch80 -p1 %patch82 -p1 %patch83 -p1 @@ -475,4 +477,3 @@ fi * Sun Mar 30 2003 1.0-1 - Initial release. - diff --git a/ipmitool/mockbuild.pl b/ipmitool/mockbuild.pl new file mode 100755 index 0000000..b34af08 --- /dev/null +++ b/ipmitool/mockbuild.pl @@ -0,0 +1,357 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(dirname basename); +use File::Copy qw(copy); +use File::Path qw(make_path remove_tree); +use Getopt::Long qw(GetOptions); + +my $script_dir = abs_path(dirname(__FILE__)); +my $repo_root = abs_path("$script_dir/.."); +my $pkg_dir = "$repo_root/ipmitool"; +my $spec_file = "$pkg_dir/ipmitool.spec"; + +my $source_url = 'https://github.com/ipmitool/ipmitool/archive/refs/tags/IPMITOOL_1_8_18.tar.gz'; +my $source_file = ''; +my $work_dir = '/tmp/ipmitool-xcat-mockbuild'; +my $mock_cfg = ''; +my $mock_uniqueext = ''; +my $result_dir = "$repo_root/build-output/list3/ipmitool-xcat"; +my $log_dir = "$repo_root/build-logs/list3/ipmitool-xcat"; +my $skip_install = 0; + +GetOptions( + 'source-url=s' => \$source_url, + 'source-file=s' => \$source_file, + 'work-dir=s' => \$work_dir, + 'mock-cfg=s' => \$mock_cfg, + 'mock-uniqueext=s' => \$mock_uniqueext, + 'result-dir=s' => \$result_dir, + 'log-dir=s' => \$log_dir, + 'skip-install!' => \$skip_install, +) or die usage(); + +die "Run as root (current uid=$>)\n" if $> != 0; +die "Missing spec file: $spec_file\n" if !-f $spec_file; + +for my $bin (qw(wget mock rpmbuild rpm dnf ldd bash)) { + run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); +} + +my ($version, @spec_assets) = parse_spec($spec_file); +die "Could not parse Version from $spec_file\n" if !$version; + +if (!$source_file) { + $source_file = "ipmitool-$version.tar.gz"; +} + +my $source_path = "$pkg_dir/$source_file"; +my $arch = capture('uname -m'); +if (!$mock_cfg) { + my $os_id = capture(q{bash -lc 'source /etc/os-release; echo $ID'}); + $mock_cfg = "${os_id}+epel-10-${arch}"; +} +my $mock_uniqueext_opt = $mock_uniqueext ne '' + ? ' --uniqueext ' . sh_quote($mock_uniqueext) + : ''; + +print_step("Configuration"); +print "repo_root: $repo_root\n"; +print "pkg_dir: $pkg_dir\n"; +print "work_dir: $work_dir\n"; +print "result_dir: $result_dir\n"; +print "log_dir: $log_dir\n"; +print "mock_cfg: $mock_cfg\n"; +print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; +print "source_url: $source_url\n"; +print "source_file:$source_file\n"; +print "skip_install: $skip_install\n"; + +make_path($result_dir); +make_path($log_dir); + +print_step("Mock config check"); +run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); + +print_step("Download upstream source"); +run("wget --spider " . sh_quote($source_url)); +run("wget -O " . sh_quote($source_path) . " " . sh_quote($source_url)); +normalize_source_archive($source_path, $version, $work_dir); + +print_step("Verify spec assets"); +for my $asset (@spec_assets) { + my $path = "$pkg_dir/$asset"; + die "Missing required spec asset: $path\n" if !-f $path; +} +print "Verified " . scalar(@spec_assets) . " Source/Patch assets from spec.\n"; + +print_step("Stage files for patch-application check"); +remove_tree($work_dir) if -d $work_dir; +my $prep_top = "$work_dir/prep"; +for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) { + make_path("$prep_top/$d"); +} +copy($spec_file, "$prep_top/SPECS/ipmitool.spec") + or die "Failed to copy spec to prep topdir: $!\n"; +for my $asset (@spec_assets) { + copy("$pkg_dir/$asset", "$prep_top/SOURCES/$asset") + or die "Failed to copy $asset into prep SOURCES: $!\n"; +} + +print_step("Apply patches in %prep"); +my $prep_log = "$log_dir/prep.log"; +run( + "rpmbuild --define " . sh_quote("_topdir $prep_top") . + " -bp --nodeps " . sh_quote("$prep_top/SPECS/ipmitool.spec") . + " > " . sh_quote($prep_log) . " 2>&1" +); +my $patch_count = capture( + "grep -c '^Patch #' " . sh_quote($prep_log) . " || true" +); +print "Patch application check passed. Applied patches: $patch_count\n"; + +print_step("Build SRPM with mock"); +my $srpm_out = "$work_dir/srpm"; +make_path($srpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --buildsrpm --spec " . sh_quote($spec_file) . + " --sources " . sh_quote($pkg_dir) . + " --resultdir " . sh_quote($srpm_out) +); + +my @srpms = sort glob("$srpm_out/ipmitool-xcat-*.src.rpm"); +die "SRPM not generated in $srpm_out\n" if !@srpms; +my $srpm = $srpms[-1]; +print "SRPM: $srpm\n"; + +print_step("Rebuild RPM with mock"); +my $rpm_out = "$work_dir/rpm"; +make_path($rpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --rebuild " . sh_quote($srpm) . + " --resultdir " . sh_quote($rpm_out) +); + +my @arch_rpms = sort glob("$rpm_out/*.${arch}.rpm"); +die "No architecture RPMs generated in $rpm_out\n" if !@arch_rpms; + +my $main_rpm = ''; +for my $rpm (@arch_rpms) { + my $name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($rpm)); + if ($name eq 'ipmitool-xcat') { + $main_rpm = $rpm; + last; + } +} +die "Could not find main ipmitool-xcat RPM in $rpm_out\n" if !$main_rpm; + +print_step("Verify generated RPM"); +my $rpm_name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($main_rpm)); +my $rpm_arch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($main_rpm)); +die "Unexpected RPM name: $rpm_name\n" if $rpm_name ne 'ipmitool-xcat'; +die "Unexpected RPM arch: $rpm_arch (expected $arch)\n" if $rpm_arch ne $arch; +run( + "rpm -qpl " . sh_quote($main_rpm) . + " | grep -Fx /opt/xcat/bin/ipmitool-xcat >/dev/null" +); +print "Verified RPM name/arch/payload: $main_rpm\n"; + +print_step("Copy artifacts and logs"); +for my $rpm (@arch_rpms) { + copy($rpm, $result_dir) or die "Failed to copy $rpm to $result_dir: $!\n"; +} +copy($srpm, $result_dir) or die "Failed to copy $srpm to $result_dir: $!\n"; +for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$rpm_out/$log"; + next if !-f $src; + copy($src, "$log_dir/$log") + or die "Failed to copy $src to $log_dir: $!\n"; +} + +if (!$skip_install) { + print_step("Install RPM and run smoke tests"); + run("dnf -y install " . sh_quote($main_rpm)); + + my $bin = '/opt/xcat/bin/ipmitool-xcat'; + die "Missing installed binary: $bin\n" if !-x $bin; + + my $help_short_log = "$log_dir/smoke-help-short.log"; + my $help_long_log = "$log_dir/smoke-help-long.log"; + my $version_log = "$log_dir/smoke-version.log"; + my $open_log = "$log_dir/smoke-open-mc-info.log"; + my $ldd_log = "$log_dir/smoke-ldd.log"; + + my $rc_help_short = run_capture_rc("$bin -h", $help_short_log); + my $rc_help_long = run_capture_rc("$bin --help", $help_long_log); + my $rc_version = run_capture_rc("$bin -V", $version_log); + my $rc_open = run_capture_rc("$bin -I open mc info", $open_log); + my $rc_ldd = run_capture_rc("ldd $bin", $ldd_log); + + die "Smoke check failed: -h returned $rc_help_short\n" if $rc_help_short != 0; + die "Smoke check failed: -V returned $rc_version\n" if $rc_version != 0; + die "Smoke check failed: ldd returned $rc_ldd\n" if $rc_ldd != 0; + + my $help_short_out = slurp($help_short_log); + my $help_long_out = slurp($help_long_log); + my $version_out = slurp($version_log); + my $open_out = slurp($open_log); + my $ldd_out = slurp($ldd_log); + + die "Short help output does not contain usage text\n" + if $help_short_out !~ /usage:/i; + die "Long help output does not contain usage text\n" + if $help_long_out !~ /usage:/i; + die "Long help returned unexpected rc=$rc_help_long (expected 0 or 1)\n" + if $rc_help_long != 0 && $rc_help_long != 1; + die "Version output missing expected version string\n" + if $version_out !~ /ipmitool-xcat version \Q$version\E/i; + die "ldd output missing libcrypto dependency\n" + if $ldd_out !~ /libcrypto/; + die "Expected no-IPMI failure did not occur; rc=$rc_open\n" + if $rc_open == 0; + die "No-IPMI probe failed with unexpected output:\n$open_out\n" + if $open_out !~ m{Could not open device|/dev/ipmi}; + + my $summary = "$log_dir/smoke-summary.txt"; + open my $sfh, '>', $summary or die "Cannot write $summary: $!\n"; + print {$sfh} "binary=$bin\n"; + print {$sfh} "rc_help_short=$rc_help_short\n"; + print {$sfh} "rc_help_long=$rc_help_long\n"; + print {$sfh} "rc_version=$rc_version\n"; + print {$sfh} "rc_open=$rc_open\n"; + print {$sfh} "rc_ldd=$rc_ldd\n"; + close $sfh; +} + +print_step("Completed"); +print "Main RPM: $main_rpm\n"; +print "Artifacts: $result_dir\n"; +print "Logs: $log_dir\n"; +exit 0; + +sub usage { + return <<"USAGE"; +Usage: $0 [options] + --source-url URL Upstream tarball URL (default: $source_url) + --source-file FILE Source filename stored in ipmitool/ (default: inferred from spec version) + --work-dir PATH Temporary work dir (default: $work_dir) + --mock-cfg NAME Mock config (default: +epel-10-) + --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds + --result-dir PATH Output RPM/SRPM directory (default: $result_dir) + --log-dir PATH Log directory (default: $log_dir) + --skip-install Skip dnf install + smoke tests +USAGE +} + +sub parse_spec { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot open spec $path: $!\n"; + + my $version = ''; + my @assets; + while (my $line = <$fh>) { + if ($line =~ /^Version:\s*(\S+)/) { + $version = $1; + } + if ($line =~ /^(?:Source|Patch)\d*:\s*(\S+)/) { + my $asset = $1; + push @assets, $asset; + } + } + close $fh; + + @assets = map { + my $v = $_; + $v =~ s/%\{version\}/$version/g; + $v =~ s/%\{ver\}/$version/g; + $v; + } @assets; + + return ($version, @assets); +} + +sub normalize_source_archive { + my ($archive, $version, $work_base) = @_; + + my $normalize_dir = "$work_base/source-normalize"; + remove_tree($normalize_dir) if -d $normalize_dir; + make_path($normalize_dir); + + run("tar -xzf " . sh_quote($archive) . " -C " . sh_quote($normalize_dir)); + + my @entries = grep { $_ !~ m{/\.\.?$} } glob("$normalize_dir/*"); + die "Unexpected archive layout in $archive\n" if @entries != 1; + my $top_path = $entries[0]; + die "Unexpected non-directory top-level entry in $archive: $top_path\n" + if !-d $top_path; + + my $expected_top = "ipmitool-$version"; + my $actual_top = basename($top_path); + if ($actual_top ne $expected_top) { + my $new_path = "$normalize_dir/$expected_top"; + run("rm -rf " . sh_quote($new_path)); + run("mv " . sh_quote($top_path) . " " . sh_quote($new_path)); + } + + # Repack using the expected top-level directory required by the spec. + run( + "tar -C " . sh_quote($normalize_dir) . + " -czf " . sh_quote($archive) . + " " . sh_quote($expected_top) + ); +} + +sub print_step { + my ($msg) = @_; + print "\n== $msg ==\n"; +} + +sub sh_quote { + my ($s) = @_; + $s =~ s/'/'"'"'/g; + return "'$s'"; +} + +sub run { + my ($cmd) = @_; + print "+ $cmd\n"; + my $rc = system($cmd); + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\n"; + } +} + +sub capture { + my ($cmd) = @_; + print "+ $cmd\n"; + my $out = `$cmd`; + my $rc = $?; + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\nOutput:\n$out\n"; + } + chomp $out; + return $out; +} + +sub run_capture_rc { + my ($cmd, $log_file) = @_; + my $full = "$cmd > " . sh_quote($log_file) . " 2>&1"; + print "+ $full\n"; + my $rc = system($full); + return $rc == -1 ? 255 : ($rc >> 8); +} + +sub slurp { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot read $path: $!\n"; + local $/; + my $content = <$fh>; + close $fh; + return $content; +} diff --git a/mockbuild-all.pl b/mockbuild-all.pl new file mode 100755 index 0000000..3c07ee5 --- /dev/null +++ b/mockbuild-all.pl @@ -0,0 +1,840 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Cwd qw(abs_path cwd); +use File::Basename qw(dirname basename); +use File::Copy qw(copy); +use File::Find qw(find); +use File::Path qw(make_path); +use Getopt::Long qw(GetOptions); +use Parallel::ForkManager; +use POSIX qw(strftime); + +my $script_dir = abs_path(dirname(__FILE__)); +my $repo_root = abs_path($script_dir); +my $xcat_src = "$repo_root/xcat-source-code"; +my $output_root = "$repo_root/build-output/mockbuild-all"; +my $target = ''; +my $nproc = 1; +my $parallel_builds; +my $run_id = strftime('%Y%m%d-%H%M%S', localtime); +my $skip_install = 0; +my $skip_build = 0; +my $skip_xcat_dep = 0; +my $skip_perl = 0; +my $skip_xcat = 0; +my $skip_createrepo = 0; +my $skip_tarball = 0; +my $skip_dhcp = 0; +my $scrub_all_chroots = 0; +my $dry_run = 0; +my $dhcp_repo_url = 'https://github.com/VersatusHPC/rpms-dhcp.git'; +my $dhcp_repo_ref = 'master'; +my $dhcp_source_dir = ''; +my @extra_collect_dirs; + +GetOptions( + 'repo-root=s' => \$repo_root, + 'xcat-source=s' => \$xcat_src, + 'output-root=s' => \$output_root, + 'target=s' => \$target, + 'nproc=i' => \$nproc, + 'parallel-builds=i' => \$parallel_builds, + 'run-id=s' => \$run_id, + 'skip-install!' => \$skip_install, + 'skip-build!' => \$skip_build, + 'skip-xcat-dep!' => \$skip_xcat_dep, + 'skip-perl!' => \$skip_perl, + 'skip-xcat!' => \$skip_xcat, + 'skip-createrepo!' => \$skip_createrepo, + 'skip-tarball!' => \$skip_tarball, + 'skip-dhcp!' => \$skip_dhcp, + 'scrub-all-chroots!' => \$scrub_all_chroots, + 'dhcp-repo-url=s' => \$dhcp_repo_url, + 'dhcp-repo-ref=s' => \$dhcp_repo_ref, + 'dhcp-source-dir=s' => \$dhcp_source_dir, + 'collect-dir=s@' => \@extra_collect_dirs, + 'dry-run!' => \$dry_run, +) or die usage(); + +die "Run as root (uid=$>)\n" if $> != 0; +die "--parallel-builds must be >= 1\n" + if defined($parallel_builds) && $parallel_builds < 1; + +$repo_root = abs_path($repo_root); +$xcat_src = resolve_xcat_source($xcat_src, $repo_root); + +my $arch = capture('uname -m'); +my %os = read_os_release('/etc/os-release'); +my $os_id = $os{ID} // ''; +my $version_id = $os{VERSION_ID} // ''; +my ($rel) = $version_id =~ /^(\d+)/; + +die "Could not resolve ID from /etc/os-release\n" if $os_id eq ''; +die "Could not resolve major release from VERSION_ID='$version_id' in /etc/os-release\n" + if !defined($rel) || $rel eq ''; + +if (!$target) { + $target = "${os_id}+epel-${rel}-${arch}"; +} +my $target_rel = parse_target_rel($target); +my $dhcp_auto_enabled = defined($target_rel) && $target_rel eq '10' ? 1 : 0; +my $dhcp_enabled = (!$skip_dhcp && $dhcp_auto_enabled) ? 1 : 0; +my $dhcp_effective_source = ''; +my $dhcp_commit = ''; +my @dhcp_srpm_collect_roots; + +if (!$dhcp_auto_enabled && !$skip_dhcp) { + print "INFO: DHCP build step disabled for non-EL10 target '$target'.\n"; +} + +for my $bin (qw(perl uname createrepo tar find rpm)) { + require_command($bin); +} +require_command('mock') if $scrub_all_chroots; +if ($dhcp_enabled && !$skip_build) { + require_command('git'); + require_command('mock'); + require_command('rpmdev-spectool'); +} + +my $run_root = "$output_root/$run_id"; +my $build_root = "$run_root/build-results"; +my $log_root = "$run_root/build-logs"; +my $repo_dir = "$run_root/repo/$arch"; +my $summary_file = "$run_root/summary.txt"; +my $tarball = "$output_root/mockbuild-all-$target-$run_id.tar.gz"; +my $srpm_repo_dir = "$run_root/repo-src"; +my $srpm_tarball = "$output_root/mockbuild-all-$target-$run_id-srpm.tar.gz"; + +my @dep_builders = ( + { name => 'elilo-xcat', script => "$repo_root/elilo/mockbuild.pl" }, + { name => 'grub2-xcat', script => "$repo_root/grub2-xcat/mockbuild.pl" }, + { name => 'ipmitool-xcat', script => "$repo_root/ipmitool/mockbuild.pl" }, + { name => 'syslinux-xcat', script => "$repo_root/syslinux/mockbuild.pl" }, + { name => 'goconserver', script => "$repo_root/goconserver/mockbuild.pl" }, +); + +my $perl_builder = "$repo_root/mockbuild-perl-packages.pl"; + +die "Missing xCAT build script: $xcat_src/buildrpms.pl\n" + if !$skip_xcat && !-f "$xcat_src/buildrpms.pl"; + +my @active_dep_builders; +for my $b (@dep_builders) { + if (-f $b->{script}) { + push @active_dep_builders, $b; + next; + } + print "WARN: missing dep builder script, skipping: $b->{script}\n"; +} +die "Missing perl builder script: $perl_builder\n" + if !$skip_perl && !$perl_builder; + +if (!$dry_run) { + make_path($build_root, $log_root, $repo_dir, $srpm_repo_dir); +} + +print_step("Configuration"); +print "repo_root: $repo_root\n"; +print "xcat_source: $xcat_src\n"; +print "output_root: $output_root\n"; +print "run_root: $run_root\n"; +print "arch: $arch\n"; +print "os_id: $os_id\n"; +print "version_id: $version_id\n"; +print "rel: $rel\n"; +print "target: $target\n"; +print "target_rel: " . (defined($target_rel) ? $target_rel : 'unknown') . "\n"; +print "nproc: $nproc\n"; +print "parallel_builds: " . (defined($parallel_builds) ? $parallel_builds : 'auto') . "\n"; +print "skip_build: $skip_build\n"; +print "skip_xcat_dep: $skip_xcat_dep\n"; +print "skip_perl: $skip_perl\n"; +print "skip_xcat: $skip_xcat\n"; +print "skip_dhcp: $skip_dhcp\n"; +print "dhcp_enabled: $dhcp_enabled\n"; +print "dhcp_repo_url: $dhcp_repo_url\n"; +print "dhcp_repo_ref: $dhcp_repo_ref\n"; +print "dhcp_source_dir: " . ($dhcp_source_dir ne '' ? $dhcp_source_dir : '(auto)') . "\n"; +print "skip_install: $skip_install\n"; +print "skip_createrepo: $skip_createrepo\n"; +print "skip_tarball: $skip_tarball\n"; +print "scrub_all_chroots:$scrub_all_chroots\n"; +print "dry_run: $dry_run\n"; +print "perl_builder: $perl_builder\n"; +print "tarball: $tarball\n"; +print "srpm_repo_dir: $srpm_repo_dir\n"; +print "srpm_tarball: $srpm_tarball\n"; + +my @collect_roots; + +if ($dhcp_enabled && !$skip_build) { + ($dhcp_effective_source, $dhcp_commit) = prepare_dhcp_source( + run_root => $run_root, + log_root => "$log_root/dhcpd", + repo_url => $dhcp_repo_url, + repo_ref => $dhcp_repo_ref, + source_dir => $dhcp_source_dir, + ); +} elsif ($dhcp_enabled && $skip_build && $dhcp_source_dir ne '') { + $dhcp_effective_source = eval { abs_path($dhcp_source_dir) } || $dhcp_source_dir; + $dhcp_commit = '(skip-build)'; +} + +if ($dhcp_enabled) { + print "dhcp_source: " . ($dhcp_effective_source ne '' ? $dhcp_effective_source : '(pending)') . "\n"; + print "dhcp_commit: " . ($dhcp_commit ne '' ? $dhcp_commit : '(pending)') . "\n"; +} + +if ($scrub_all_chroots) { + run_step( + step => "Scrub all chroots for target $target", + cmd => "mock -r " . sh_quote($target) . " --scrub=all", + log => "$log_root/scrub-all-chroots.log", + ); +} + +if (!$skip_build) { + my @build_steps; + my $build_step_seq = 0; + + if (!$skip_xcat_dep) { + for my $builder (@active_dep_builders) { + my $name = $builder->{name}; + my $script = $builder->{script}; + my $step_result = "$build_root/$name"; + my $step_log = "$log_root/$name"; + my $step_uniqueext = build_mock_uniqueext($run_id, ++$build_step_seq, $name); + my $cmd = join(' ', + 'perl', sh_quote($script), + '--mock-cfg', sh_quote($target), + '--mock-uniqueext', sh_quote($step_uniqueext), + '--result-dir', sh_quote($step_result), + '--log-dir', sh_quote($step_log), + ($skip_install ? '--skip-install' : ()), + ); + push @build_steps, { + id => "xcat-dep:$name", + step => "Build xcat-dep: $name", + cmd => $cmd, + log => "$log_root/$name/run.log", + }; + push @collect_roots, $step_result; + } + } + + if ($dhcp_enabled) { + my $dhcp_uniqueext = build_mock_uniqueext($run_id, ++$build_step_seq, 'dhcpd'); + my $dhcp_build_cmd = join(' && ', + 'mkdir -p build/SRPMS build/RPMS', + 'rpmdev-spectool --get-files --sources ./dhcp.spec', + 'mock -r ' . sh_quote($target) . + ' --uniqueext ' . sh_quote($dhcp_uniqueext) . + ' --buildsrpm --spec ./dhcp.spec --sources . --resultdir ./build/SRPMS', + 'mock -r ' . sh_quote($target) . + ' --uniqueext ' . sh_quote($dhcp_uniqueext) . + ' --rebuild ./build/SRPMS/*.src.rpm --resultdir ./build/RPMS', + ); + push @build_steps, { + id => 'xcat-dep:dhcpd', + step => 'Build xcat-dep: dhcpd', + cmd => $dhcp_build_cmd, + cwd => $dhcp_effective_source, + log => "$log_root/dhcpd/build.log", + }; + push @collect_roots, "$dhcp_effective_source/build/RPMS"; + push @dhcp_srpm_collect_roots, "$dhcp_effective_source/build/SRPMS"; + } + + if (!$skip_perl) { + my $perl_result = "$build_root/perl/$arch"; + my $perl_log = "$log_root/perl/$arch"; + my $perl_uniqueext = build_mock_uniqueext($run_id, ++$build_step_seq, 'perl-list6'); + my $cmd = join(' ', + 'perl', sh_quote($perl_builder), + '--mock-cfg', sh_quote($target), + '--mock-uniqueext', sh_quote($perl_uniqueext), + '--result-dir', sh_quote($perl_result), + '--log-dir', sh_quote($perl_log), + ($skip_install ? '--skip-install' : ()), + ); + push @build_steps, { + id => 'perl', + step => 'Build perl xcat-dep packages', + cmd => $cmd, + log => "$log_root/perl-build.log", + }; + push @collect_roots, $perl_result; + } + + if (!$skip_xcat) { + my $cmd = join(' ', + 'perl', sh_quote("$xcat_src/buildrpms.pl"), + '--target', sh_quote($target), + '--nproc', int($nproc), + '--force', + '--verbose', + '--xcat_dep_path', sh_quote($repo_root), + ); + push @build_steps, { + id => 'xcat', + step => 'Build xCAT packages', + cmd => $cmd, + cwd => $xcat_src, + log => "$log_root/xcat-build.log", + }; + } + + if (@build_steps) { + my $effective_parallel_builds = defined($parallel_builds) + ? $parallel_builds + : scalar(@build_steps); + run_build_steps_parallel( + steps => \@build_steps, + max_processes => $effective_parallel_builds, + ); + } +} + +my $xcat_rpms_dir = "$xcat_src/dist/$target/rpms"; +my $xcat_srpms_dir = "$xcat_src/dist/$target/srpms"; +push @collect_roots, $xcat_rpms_dir; + +if ($skip_build) { + push @collect_roots, + "$repo_root/build-output/list3/elilo-xcat", + "$repo_root/build-output/list3/grub2-xcat", + "$repo_root/build-output/list3/ipmitool-xcat", + "$repo_root/build-output/list3/syslinux-xcat", + "$repo_root/build-output/list5/goconserver/$arch", + "$repo_root/goconserver-build-$arch/results/rpm", + "$repo_root/build-output/list6/perl/$arch", + "$repo_root/perl-list6/$arch"; +} + +if ($dhcp_effective_source ne '') { + push @collect_roots, "$dhcp_effective_source/build/RPMS"; + push @dhcp_srpm_collect_roots, "$dhcp_effective_source/build/SRPMS"; +} + +push @collect_roots, @extra_collect_dirs; +@collect_roots = uniq(@collect_roots); +my @srpm_collect_roots = uniq(@collect_roots, $xcat_srpms_dir, @dhcp_srpm_collect_roots); + +print_step('Collect RPM artifacts'); +print "collection roots:\n"; +print " $_\n" for @collect_roots; + +my ($copied, $skipped_src, $missing_roots) = collect_rpms( + roots => \@collect_roots, + dest_dir => $repo_dir, + dry_run => $dry_run, +); + +if (!$dry_run && $copied == 0) { + die "No binary RPMs were collected. Check build logs and collection roots.\n"; +} + +print_step('Collect source RPM artifacts'); +print "source collection roots:\n"; +print " $_\n" for @srpm_collect_roots; + +my ($copied_srpms, $skipped_non_src, $missing_srpm_roots) = collect_srpms( + roots => \@srpm_collect_roots, + dest_dir => $srpm_repo_dir, + dry_run => $dry_run, +); + +if (!$dry_run && $copied_srpms == 0) { + print "WARN: No source RPMs were collected. SRPM repo and tarball may be empty.\n"; +} + +if (!$skip_createrepo) { + run_step( + step => 'Run createrepo', + cmd => 'createrepo --update ' . sh_quote($repo_dir), + log => "$log_root/createrepo.log", + ); + run_step( + step => 'Run createrepo for SRPM repo', + cmd => 'createrepo --update ' . sh_quote($srpm_repo_dir), + log => "$log_root/createrepo-srpm.log", + ); +} + +if (!$skip_tarball) { + my $cmd = join(' ', + 'tar', '-C', sh_quote($run_root), + '-czf', sh_quote($tarball), + 'repo' + ); + run_step( + step => 'Create tarball', + cmd => $cmd, + log => "$log_root/tarball.log", + ); + my $srpm_cmd = join(' ', + 'tar', '-C', sh_quote($run_root), + '-czf', sh_quote($srpm_tarball), + 'repo-src' + ); + run_step( + step => 'Create SRPM tarball', + cmd => $srpm_cmd, + log => "$log_root/tarball-srpm.log", + ); +} + +if (!$dry_run) { + open my $sfh, '>', $summary_file or die "Cannot write $summary_file: $!\n"; + print {$sfh} "run_root=$run_root\n"; + print {$sfh} "repo_dir=$repo_dir\n"; + print {$sfh} "target=$target\n"; + print {$sfh} "target_rel=" . (defined($target_rel) ? $target_rel : '') . "\n"; + print {$sfh} "arch=$arch\n"; + print {$sfh} "os_id=$os_id\n"; + print {$sfh} "version_id=$version_id\n"; + print {$sfh} "rel=$rel\n"; + print {$sfh} "dhcp_enabled=$dhcp_enabled\n"; + print {$sfh} "dhcp_repo_url=$dhcp_repo_url\n"; + print {$sfh} "dhcp_repo_ref=$dhcp_repo_ref\n"; + print {$sfh} "dhcp_source=" . ($dhcp_effective_source ne '' ? $dhcp_effective_source : '') . "\n"; + print {$sfh} "dhcp_commit=" . ($dhcp_commit ne '' ? $dhcp_commit : '') . "\n"; + print {$sfh} "copied_rpms=$copied\n"; + print {$sfh} "skipped_src_rpms=$skipped_src\n"; + print {$sfh} "missing_collection_roots=$missing_roots\n"; + print {$sfh} "srpm_repo_dir=$srpm_repo_dir\n"; + print {$sfh} "copied_srpms=$copied_srpms\n"; + print {$sfh} "skipped_non_src_rpms=$skipped_non_src\n"; + print {$sfh} "missing_srpm_collection_roots=$missing_srpm_roots\n"; + print {$sfh} "tarball=$tarball\n" if !$skip_tarball; + print {$sfh} "srpm_tarball=$srpm_tarball\n" if !$skip_tarball; + close $sfh; +} + +print_step('Completed'); +print "Collected binary RPMs: $copied\n"; +print "Skipped source RPMs: $skipped_src\n"; +print "Missing roots: $missing_roots\n"; +print "Repo dir: $repo_dir\n"; +print "Collected source RPMs: $copied_srpms\n"; +print "Skipped non-src RPMs: $skipped_non_src\n"; +print "Missing source roots: $missing_srpm_roots\n"; +print "SRPM repo dir: $srpm_repo_dir\n"; +print "DHCP source: $dhcp_effective_source\n" if $dhcp_effective_source ne ''; +print "DHCP ref: $dhcp_repo_ref\n" if $dhcp_enabled; +print "DHCP commit: $dhcp_commit\n" if $dhcp_commit ne ''; +print "Summary: $summary_file\n" if !$dry_run; +print "Tarball: $tarball\n" if !$skip_tarball; +print "SRPM Tarball: $srpm_tarball\n" if !$skip_tarball; +exit 0; + +sub usage { + return <<"USAGE"; +Usage: $0 [options] + +Build xcat-dep and xCAT RPMs, consolidate binary/source artifacts, run createrepo, and create tarballs. + +Options: + --repo-root PATH xcat-dep repository root (default: script directory) + --xcat-source PATH xCAT source root with buildrpms.pl (default: /xcat-source-code) + --output-root PATH Root output directory (default: /build-output/mockbuild-all) + --target NAME Optional unified target in +epel-- format + --nproc N Parallel jobs for buildrpms.pl (default: 1) + --parallel-builds N Max concurrent top-level build steps (default: auto=queued steps) + --run-id ID Run identifier suffix (default: timestamp) + --skip-install Skip install/smoke tests in child builder scripts + --skip-build Skip all build steps and only collect/create repo/tarballs + --skip-xcat-dep Skip xcat-dep mockbuild.pl package steps + --skip-perl Skip perl package build step + --skip-xcat Skip xCAT buildrpms.pl step + --skip-dhcp Skip rpms-dhcp build step + --skip-createrepo Skip createrepo + --skip-tarball Skip binary/SRPM tarball creation + --scrub-all-chroots Run mock -r --scrub=all before build/collect + --dhcp-repo-url URL rpms-dhcp git URL (default: https://github.com/VersatusHPC/rpms-dhcp.git) + --dhcp-repo-ref REF rpms-dhcp git ref/tag/branch to checkout (default: master) + --dhcp-source-dir PATH Local rpms-dhcp source directory override (skip clone/fetch) + --collect-dir PATH Additional directory to scan recursively for RPMs (repeatable) + --dry-run Print planned commands without executing + +Notes: + - Run this script as root on the build host. + - ARCH is derived from: uname -m + - Top-level parallel queue includes xcat-dep mockbuild.pl steps, perl builder, + and xcat-source-code/buildrpms.pl. + - Child mockbuild scripts are invoked with per-step mock --uniqueext values + to avoid lock collisions on the same mock config. + - If --target is omitted, it is deduced from /etc/os-release: + ID + epel + int(VERSION_ID) + ARCH + - DHCP step auto-enables only for EL10 targets, unless explicitly skipped. +USAGE +} + +sub parse_target_rel { + my ($target_name) = @_; + return undef if !defined($target_name) || $target_name eq ''; + return $1 if $target_name =~ /\+epel-(\d+)-/; + return undef; +} + +sub prepare_dhcp_source { + my (%args) = @_; + my $run_root = $args{run_root} // die "prepare_dhcp_source missing run_root\n"; + my $log_root = $args{log_root} // die "prepare_dhcp_source missing log_root\n"; + my $repo_url = $args{repo_url} // die "prepare_dhcp_source missing repo_url\n"; + my $repo_ref = $args{repo_ref} // 'master'; + my $source_dir = $args{source_dir} // ''; + + my $clone_log = "$log_root/clone.log"; + my $source_path; + + if ($source_dir ne '') { + $source_path = eval { abs_path($source_dir) } || $source_dir; + die "DHCP source directory does not exist: $source_path\n" + if !$dry_run && !-d $source_path; + run_step( + step => 'Prepare DHCP source', + cmd => 'echo Using local DHCP source directory: ' . sh_quote($source_path), + log => $clone_log, + ); + } else { + $source_path = "$run_root/sources/rpms-dhcp"; + my $prepare_cmd = join(' && ', + 'mkdir -p ' . sh_quote("$run_root/sources"), + 'if [ -d ' . sh_quote("$source_path/.git") . ' ]; then ' . + 'cd ' . sh_quote($source_path) . ' && ' . + 'git remote set-url origin ' . sh_quote($repo_url) . ' && ' . + 'git fetch --tags origin' . + '; else ' . + 'git clone ' . sh_quote($repo_url) . ' ' . sh_quote($source_path) . + '; fi', + 'cd ' . sh_quote($source_path), + 'git fetch --tags origin', + 'git checkout --force ' . sh_quote($repo_ref), + ); + run_step( + step => 'Prepare DHCP source', + cmd => $prepare_cmd, + log => $clone_log, + ); + } + + if (!$dry_run && !-f "$source_path/dhcp.spec") { + die "Missing dhcp.spec in DHCP source: $source_path/dhcp.spec\n"; + } + + my $commit = ''; + if ($dry_run) { + $commit = '(dry-run)'; + } elsif (-d "$source_path/.git") { + $commit = capture("cd " . sh_quote($source_path) . " && git rev-parse HEAD"); + } else { + $commit = '(local-source-no-git)'; + } + + return ($source_path, $commit); +} + +sub print_step { + my ($msg) = @_; + print "\n== $msg ==\n"; +} + +sub require_command { + my ($cmd) = @_; + run_simple("command -v " . sh_quote($cmd) . " >/dev/null 2>&1"); +} + +sub run_simple { + my ($cmd) = @_; + my $rc = system($cmd); + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\n"; + } +} + +sub capture { + my ($cmd) = @_; + my $out = `$cmd`; + my $rc = $?; + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\n$out\n"; + } + chomp $out; + return $out; +} + +sub run_step { + my (%args) = @_; + my $step = $args{step} // 'Run command'; + my $cmd = $args{cmd} // die "run_step missing cmd\n"; + my $cwd = $args{cwd}; + my $log = $args{log}; + + print_step($step); + print "+ $cmd\n"; + if ($cwd) { + print " (cwd: $cwd)\n"; + } + if ($log) { + print " (log: $log)\n"; + } + + return if $dry_run; + + my $full_cmd = $cmd; + if ($cwd) { + $full_cmd = "cd " . sh_quote($cwd) . " && $cmd"; + } + if ($log) { + my $log_dir = dirname($log); + make_path($log_dir) if !-d $log_dir; + $full_cmd .= " > " . sh_quote($log) . " 2>&1"; + } + + my $rc = system($full_cmd); + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Step failed (rc=$exit): $step\nCommand: $cmd\n"; + } +} + +sub run_build_steps_parallel { + my (%args) = @_; + my $steps = $args{steps} // []; + my $max_processes = $args{max_processes} // 1; + return if !@{$steps}; + + if ($dry_run || $max_processes <= 1 || @{$steps} == 1) { + for my $step (@{$steps}) { + run_step(%{$step}); + } + return; + } + + my $workers = $max_processes; + $workers = scalar(@{$steps}) if $workers > scalar(@{$steps}); + + print_step('Run build steps in parallel'); + print "max_processes: $workers\n"; + print "queued steps:\n"; + print " - $_->{step}\n" for @{$steps}; + + my %failed; + my $pm = Parallel::ForkManager->new($workers); + $pm->run_on_finish( + sub { + my ($pid, $exit_code, $ident, $signal, $core_dump) = @_; + return if $exit_code == 0 && $signal == 0 && !$core_dump; + my $key = defined($ident) ? $ident : "pid:$pid"; + $failed{$key} = { + exit => $exit_code, + signal => $signal, + core_dump => $core_dump ? 1 : 0, + }; + } + ); + + for my $step (@{$steps}) { + my %step_copy = %{$step}; + my $ident = delete $step_copy{id}; + $ident = $step_copy{step} if !defined($ident) || $ident eq ''; + + my $pid = $pm->start($ident); + next if $pid; + + my $ok = eval { + run_step(%step_copy); + 1; + }; + if (!$ok) { + my $err = $@; + $err = "unknown error\n" if !defined($err) || $err eq ''; + print STDERR "ERROR [$ident] $err"; + $pm->finish(1); + } + $pm->finish(0); + } + $pm->wait_all_children; + + if (%failed) { + my @lines; + for my $id (sort keys %failed) { + my $f = $failed{$id}; + push @lines, + "$id (exit=$f->{exit}, signal=$f->{signal}, core_dump=$f->{core_dump})"; + } + die "Parallel build steps failed:\n " . join("\n ", @lines) . "\n"; + } +} + +sub collect_rpms { + my (%args) = @_; + my $roots = $args{roots} // []; + my $dest = $args{dest_dir} // die "collect_rpms missing dest_dir\n"; + my $is_dry = $args{dry_run} ? 1 : 0; + + my %seen; + my $copied = 0; + my $skipped_src = 0; + my $missing_roots = 0; + + for my $root (@{$roots}) { + if (!$root || !-d $root) { + $missing_roots++; + print "WARN: missing collection root: $root\n"; + next; + } + my @rpms; + find( + sub { + return if !-f $_; + return if $_ !~ /\.rpm$/; + push @rpms, $File::Find::name; + }, + $root, + ); + @rpms = sort uniq(@rpms); + for my $rpm (@rpms) { + next if !-f $rpm; + if ($rpm =~ /\.src\.rpm$/) { + $skipped_src++; + next; + } + my $base = basename($rpm); + next if $seen{$base}++; + if ($is_dry) { + print "DRY-RUN copy: $rpm -> $dest/$base\n"; + $copied++; + next; + } + copy($rpm, "$dest/$base") + or die "Failed to copy $rpm to $dest/$base: $!\n"; + $copied++; + } + } + + return ($copied, $skipped_src, $missing_roots); +} + +sub collect_srpms { + my (%args) = @_; + my $roots = $args{roots} // []; + my $dest = $args{dest_dir} // die "collect_srpms missing dest_dir\n"; + my $is_dry = $args{dry_run} ? 1 : 0; + + my %seen; + my $copied = 0; + my $skipped_non_src = 0; + my $missing_roots = 0; + + for my $root (@{$roots}) { + if (!$root || !-d $root) { + $missing_roots++; + print "WARN: missing source collection root: $root\n"; + next; + } + my @rpms; + find( + sub { + return if !-f $_; + return if $_ !~ /\.rpm$/; + push @rpms, $File::Find::name; + }, + $root, + ); + @rpms = sort uniq(@rpms); + for my $rpm (@rpms) { + next if !-f $rpm; + if ($rpm !~ /\.src\.rpm$/) { + $skipped_non_src++; + next; + } + my $base = basename($rpm); + next if $seen{$base}++; + if ($is_dry) { + print "DRY-RUN copy source: $rpm -> $dest/$base\n"; + $copied++; + next; + } + copy($rpm, "$dest/$base") + or die "Failed to copy $rpm to $dest/$base: $!\n"; + $copied++; + } + } + + return ($copied, $skipped_non_src, $missing_roots); +} + +sub build_mock_uniqueext { + my ($run, $seq, $label) = @_; + + my $run_part = defined($run) ? $run : 'run'; + $run_part =~ s/[^A-Za-z0-9_.-]+/-/g; + $run_part =~ s/^-+|-+$//g; + $run_part = 'run' if $run_part eq ''; + $run_part = substr($run_part, -24) if length($run_part) > 24; + + my $label_part = defined($label) ? $label : 'step'; + $label_part =~ s/[^A-Za-z0-9_.-]+/-/g; + $label_part =~ s/^-+|-+$//g; + $label_part = 'step' if $label_part eq ''; + $label_part = substr($label_part, 0, 20) if length($label_part) > 20; + + my $idx = defined($seq) ? int($seq) : 0; + $idx = 0 if $idx < 0; + + return sprintf("mba-%02d-%s-%s", $idx, $run_part, $label_part); +} + +sub resolve_xcat_source { + my ($requested, $root) = @_; + my @candidates = ( + $requested, + "$root/xcat-source-code", + "$root/../xCAT3", + '/home/build/xCAT3', + ); + for my $c (@candidates) { + next if !defined($c) || $c eq ''; + my $abs = eval { abs_path($c) }; + next if !$abs; + return $abs if -f "$abs/buildrpms.pl"; + } + return eval { abs_path($requested) } || $requested; +} + +sub read_os_release { + my ($path) = @_; + my %vals; + open my $fh, '<', $path or die "Cannot open $path: $!\n"; + while (my $line = <$fh>) { + chomp $line; + next if $line =~ /^\s*#/; + next if $line !~ /=/; + my ($k, $v) = split /=/, $line, 2; + $v =~ s/^"(.*)"$/$1/; + $v =~ s/^'(.*)'$/$1/; + $vals{$k} = $v; + } + close $fh; + return %vals; +} + +sub uniq { + my %seen; + return grep { defined($_) && !$seen{$_}++ } @_; +} + +sub sh_quote { + my ($s) = @_; + $s = '' if !defined $s; + $s =~ s/'/'"'"'/g; + return "'$s'"; +} diff --git a/mockbuild-perl-packages.pl b/mockbuild-perl-packages.pl new file mode 100755 index 0000000..e6b60c5 --- /dev/null +++ b/mockbuild-perl-packages.pl @@ -0,0 +1,557 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(dirname basename); +use File::Copy qw(copy); +use File::Path qw(make_path remove_tree); +use Getopt::Long qw(GetOptions); +use Parallel::ForkManager; + +my $repo_root = abs_path(dirname(__FILE__)); +my $work_dir = '/tmp/perl-list6-mockbuild'; +my $mock_cfg = ''; +my $mock_uniqueext = ''; +my $result_dir = ''; +my $log_dir = ''; +my $packages_csv = ''; +my $jobs = 0; +my $skip_install = 0; +my $allow_erasing = 0; + +GetOptions( + 'work-dir=s' => \$work_dir, + 'mock-cfg=s' => \$mock_cfg, + 'mock-uniqueext=s' => \$mock_uniqueext, + 'result-dir=s' => \$result_dir, + 'log-dir=s' => \$log_dir, + 'packages=s' => \$packages_csv, + 'jobs=i' => \$jobs, + 'skip-install!' => \$skip_install, + 'allow-erasing!' => \$allow_erasing, +) or die usage(); + +die "Run as root (current uid=$>)\n" if $> != 0; + +for my $bin (qw(mock rpmbuild rpm dnf perl bash grep)) { + run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); +} + +my $arch = capture('uname -m'); +if (!$mock_cfg) { + my $os_id = capture(q{bash -lc 'source /etc/os-release; echo $ID'}); + $mock_cfg = "${os_id}+epel-10-${arch}"; +} +my $mock_uniqueext_opt = $mock_uniqueext ne '' + ? ' --uniqueext ' . sh_quote($mock_uniqueext) + : ''; +if (!$result_dir) { + $result_dir = "$repo_root/build-output/list6/perl/$arch"; +} +if (!$log_dir) { + $log_dir = "$repo_root/build-logs/list6/perl/$arch"; +} + +my %meta = ( + 'perl-Crypt-SSLeay' => { + mode => 'spec', + pkg_dir => "$repo_root/perl-Crypt-SSLeay", + spec => "$repo_root/perl-Crypt-SSLeay/perl-Crypt-SSLeay.spec", + rpm_name => 'perl-Crypt-SSLeay', + rpm_arch => 'native', + module => 'Crypt::SSLeay', + }, + 'perl-HTML-Form' => { + mode => 'srpm', + pkg_dir => "$repo_root/perl-HTML-Form", + srpm_globs => [ + "$repo_root/perl-HTML-Form/perl-HTML-Form-6.07-4.fc34.src.rpm", + "$repo_root/perl-HTML-Form/perl-HTML-Form-*.src.rpm", + ], + rpm_name => 'perl-HTML-Form', + rpm_arch => 'noarch', + module => 'HTML::Form', + }, + 'perl-HTTP-Async' => { + mode => 'spec', + pkg_dir => "$repo_root/perl-HTTP-Async", + spec => "$repo_root/perl-HTTP-Async/perl-HTTP-Async.spec", + rpm_name => 'perl-HTTP-Async', + rpm_arch => 'noarch', + module => 'HTTP::Async', + }, + 'perl-IO-Stty' => { + mode => 'srpm', + pkg_dir => "$repo_root/perl-IO-Stty", + srpm_globs => [ + "$repo_root/perl-IO-Stty/perl-IO-Stty-0.04-5.fc34.src.rpm", + "$repo_root/perl-IO-Stty/perl-IO-Stty-*.src.rpm", + ], + rpm_name => 'perl-IO-Stty', + rpm_arch => 'noarch', + module => 'IO::Stty', + }, + 'perl-Net-HTTPS-NB' => { + mode => 'spec', + pkg_dir => "$repo_root/perl-Net-HTTPS-NB", + spec => "$repo_root/perl-Net-HTTPS-NB/perl-Net-HTTPS-NB.spec", + rpm_name => 'perl-Net-HTTPS-NB', + rpm_arch => 'noarch', + module => 'Net::HTTPS::NB', + }, + 'perl-Net-Telnet' => { + mode => 'srpm', + pkg_dir => "$repo_root/perl-Net-Telnet", + srpm_globs => [ + "$repo_root/perl-Net-Telnet/perl-Net-Telnet-3.04-16.fc34.src.rpm", + "$repo_root/perl-Net-Telnet/perl-Net-Telnet-*.src.rpm", + ], + rpm_name => 'perl-Net-Telnet', + rpm_arch => 'noarch', + module => 'Net::Telnet', + }, + 'perl-Sys-Virt' => { + mode => 'spec', + pkg_dir => "$repo_root/perl-Sys-Virt", + spec => "$repo_root/perl-Sys-Virt/Sys-Virt.spec", + rpm_name => 'perl-Sys-Virt', + rpm_arch => 'native', + module => 'Sys::Virt', + }, +); + +my @default_order = qw( + perl-Crypt-SSLeay + perl-HTML-Form + perl-HTTP-Async + perl-IO-Stty + perl-Net-HTTPS-NB + perl-Net-Telnet + perl-Sys-Virt +); + +my @packages = @default_order; +if ($packages_csv ne '') { + @packages = grep { $_ ne '' } map { s/^\s+|\s+$//gr } split /,/, $packages_csv; +} + +for my $pkg (@packages) { + die "Unknown package in --packages: $pkg\n" if !exists $meta{$pkg}; +} + +if ($jobs <= 0) { + $jobs = scalar(@packages); +} +$jobs = 1 if $jobs < 1; +if (@packages && $jobs > scalar(@packages)) { + $jobs = scalar(@packages); +} +if (!$skip_install && $jobs > 1) { + print "INFO: --skip-install is disabled; forcing --jobs 1 to avoid host dnf lock contention\n"; + $jobs = 1; +} + +make_path($result_dir); +make_path($log_dir); +make_path($work_dir); + +print_step("Configuration"); +print "repo_root: $repo_root\n"; +print "work_dir: $work_dir\n"; +print "result_dir: $result_dir\n"; +print "log_dir: $log_dir\n"; +print "arch: $arch\n"; +print "mock_cfg: $mock_cfg\n"; +print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; +print "packages: " . join(', ', @packages) . "\n"; +print "jobs: $jobs\n"; +print "skip_install:$skip_install\n"; +print "allow_erasing:$allow_erasing\n"; + +print_step("Mock config check"); +run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); + +my @failed; +my @passed; +my @summary_lines; + +print_step("Build packages"); +print "parallel jobs: $jobs\n"; +my $pm = Parallel::ForkManager->new($jobs); +$pm->run_on_finish( + sub { + my ($pid, $exit_code, $ident) = @_; + my $label = defined $ident ? $ident : "pid=$pid"; + my $state = $exit_code == 0 ? 'PASS' : "FAIL(rc=$exit_code)"; + print "[$label] $state\n"; + } +); + +for my $idx (0 .. $#packages) { + my $pkg = $packages[$idx]; + my $cfg = $meta{$pkg}; + my $pkg_uniqueext = package_uniqueext($mock_uniqueext, $idx + 1, $pkg); + + my $pid = $pm->start($pkg); + next if $pid; + my $ok = build_package( + pkg => $pkg, + cfg => $cfg, + work_dir => $work_dir, + result_dir => $result_dir, + log_dir => $log_dir, + mock_cfg => $mock_cfg, + mock_uniqueext => $pkg_uniqueext, + arch => $arch, + skip_install => $skip_install, + allow_erasing => $allow_erasing, + ); + $pm->finish($ok ? 0 : 1); +} +$pm->wait_all_children; + +for my $pkg (@packages) { + my $status_file = "$log_dir/$pkg/status.txt"; + if (!-f $status_file) { + push @failed, $pkg; + push @summary_lines, "$pkg FAIL missing status file ($status_file)"; + next; + } + my $line = slurp($status_file); + $line =~ s/\r?\n.*$//s; + my ($status, $summary) = split /\t/, $line, 2; + if (!defined $status || !defined $summary || ($status ne 'PASS' && $status ne 'FAIL')) { + $status = 'FAIL'; + $summary = "$pkg FAIL malformed status line in $status_file"; + } + if ($status eq 'PASS') { + push @passed, $pkg; + } else { + push @failed, $pkg; + } + push @summary_lines, $summary; +} + +open my $sfh, '>', "$log_dir/build-summary.txt" + or die "Cannot write $log_dir/build-summary.txt: $!\n"; +print {$sfh} "mock_cfg=$mock_cfg\n"; +print {$sfh} "mock_uniqueext=$mock_uniqueext\n" if $mock_uniqueext ne ''; +print {$sfh} "arch=$arch\n"; +print {$sfh} "result_dir=$result_dir\n"; +print {$sfh} "log_dir=$log_dir\n"; +print {$sfh} "packages=" . join(',', @packages) . "\n"; +print {$sfh} "passed=" . join(',', @passed) . "\n"; +print {$sfh} "failed=" . join(',', @failed) . "\n"; +print {$sfh} "$_\n" for @summary_lines; +close $sfh; + +print_step("Completed"); +print "Passed: " . join(', ', @passed) . "\n" if @passed; +print "Failed: " . join(', ', @failed) . "\n" if @failed; +print "Artifacts: $result_dir\n"; +print "Logs: $log_dir\n"; + +exit(@failed ? 1 : 0); + +sub build_package { + my (%args) = @_; + my $pkg = $args{pkg}; + my $cfg = $args{cfg}; + my $work_dir = $args{work_dir}; + my $result_dir = $args{result_dir}; + my $log_dir = $args{log_dir}; + my $mock_cfg = $args{mock_cfg}; + my $mock_uniqueext = $args{mock_uniqueext}; + my $arch = $args{arch}; + my $skip_install = $args{skip_install}; + my $allow_erasing = $args{allow_erasing}; + + my $pkg_run_dir = "$work_dir/$pkg"; + my $pkg_result = "$result_dir/$pkg"; + my $pkg_log = "$log_dir/$pkg"; + my $status_file = "$pkg_log/status.txt"; + + remove_tree($pkg_run_dir) if -d $pkg_run_dir; + make_path($pkg_run_dir); + make_path($pkg_result); + make_path($pkg_log); + + my $run_log = "$pkg_log/run.log"; + open my $runfh, '>', $run_log or die "Cannot write $run_log: $!\n"; + open STDOUT, '>&', $runfh or die "Cannot redirect stdout to $run_log: $!\n"; + open STDERR, '>&', $runfh or die "Cannot redirect stderr to $run_log: $!\n"; + select(STDOUT); + $| = 1; + + my $mock_uniqueext_opt = ' --uniqueext ' . sh_quote($mock_uniqueext); + print_step("Build $pkg"); + print "mock_uniqueext: $mock_uniqueext\n"; + + my $summary = ''; + my $ok = 0; + + my $srpm_path = ''; + my $rebuild_result = "$pkg_run_dir/rpm"; + make_path($rebuild_result); + + eval { + if ($cfg->{mode} eq 'srpm') { + $srpm_path = select_srpm($cfg->{srpm_globs}); + die "Could not locate source RPM for $pkg\n" if !$srpm_path; + } else { + my $spec = $cfg->{spec}; + die "Missing spec for $pkg: $spec\n" if !-f $spec; + my $source_dir = $cfg->{pkg_dir}; + die "Missing source directory for $pkg: $source_dir\n" if !-d $source_dir; + + my ($version, @assets) = parse_spec($spec); + die "Could not parse Version from $spec\n" if !$version; + for my $asset (@assets) { + my $asset_path = "$source_dir/$asset"; + die "Missing Source/Patch asset for $pkg: $asset_path\n" if !-f $asset_path; + } + + my $prep_top = "$pkg_run_dir/prep"; + for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) { + make_path("$prep_top/$d"); + } + my $prep_spec = "$prep_top/SPECS/" . basename($spec); + copy($spec, $prep_spec) or die "Failed to copy prep spec for $pkg: $!\n"; + for my $asset (@assets) { + copy("$source_dir/$asset", "$prep_top/SOURCES/$asset") + or die "Failed to stage prep asset $asset for $pkg: $!\n"; + } + run( + "rpmbuild --define " . sh_quote("_topdir $prep_top") . + " -bp --nodeps " . sh_quote($prep_spec) . + " > " . sh_quote("$pkg_log/prep.log") . " 2>&1" + ); + + my $srpm_result = "$pkg_run_dir/srpm"; + make_path($srpm_result); + run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --buildsrpm --spec " . sh_quote($spec) . + " --sources " . sh_quote($source_dir) . + " --resultdir " . sh_quote($srpm_result) . + " > " . sh_quote("$pkg_log/mock-buildsrpm.log") . " 2>&1" + ); + my @srpms = sort glob("$srpm_result/*.src.rpm"); + die "No SRPM produced for $pkg in $srpm_result\n" if !@srpms; + $srpm_path = $srpms[-1]; + } + + run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --rebuild " . sh_quote($srpm_path) . + " --resultdir " . sh_quote($rebuild_result) . + " > " . sh_quote("$pkg_log/mock-rebuild.log") . " 2>&1" + ); + + my @rpms = sort glob("$rebuild_result/*.rpm"); + die "No RPMs generated for $pkg in $rebuild_result\n" if !@rpms; + + my $main_rpm = ''; + for my $rpm (@rpms) { + next if $rpm =~ /\.src\.rpm$/; + my $name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($rpm)); + my $rarch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($rpm)); + next if $name ne $cfg->{rpm_name}; + if ($cfg->{rpm_arch} eq 'noarch' && $rarch eq 'noarch') { + $main_rpm = $rpm; + last; + } + if ($cfg->{rpm_arch} eq 'native' && $rarch eq $arch) { + $main_rpm = $rpm; + last; + } + } + die "Could not find main RPM for $pkg in $rebuild_result\n" if !$main_rpm; + + run("rpm -qpl " . sh_quote($main_rpm) . " > " . sh_quote("$pkg_log/payload.list")); + my $payload = slurp("$pkg_log/payload.list"); + die "Empty payload list for $pkg main RPM\n" if $payload !~ m{^/}m; + + for my $rpm (@rpms) { + copy($rpm, $pkg_result) or die "Failed to copy $rpm to $pkg_result: $!\n"; + } + copy($srpm_path, $pkg_result) or die "Failed to copy $srpm_path to $pkg_result: $!\n"; + + for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$rebuild_result/$log"; + next if !-f $src; + copy($src, "$pkg_log/$log") or die "Failed to copy $src to $pkg_log: $!\n"; + } + if (-d "$pkg_run_dir/srpm") { + for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$pkg_run_dir/srpm/$log"; + next if !-f $src; + copy($src, "$pkg_log/srpm-$log") or die "Failed to copy $src to $pkg_log: $!\n"; + } + } + + if (!$skip_install) { + my $install_cmd = "dnf -y install "; + $install_cmd .= "--allowerasing " if $allow_erasing; + run($install_cmd . sh_quote($main_rpm)); + my $module = $cfg->{module}; + my $rc_mod = run_capture_rc("perl -M$module -e 1", "$pkg_log/smoke-perl-module.log"); + die "Perl module import failed for $pkg ($module), rc=$rc_mod\n" if $rc_mod != 0; + } + + $summary = "$pkg PASS main_rpm=" . basename($main_rpm); + $ok = 1; + }; + + if ($@) { + my $err = $@; + chomp $err; + $err =~ s/\s+/ /g; + $summary = "$pkg FAIL $err"; + open my $efh, '>', "$pkg_log/error.txt" or die "Cannot write $pkg_log/error.txt: $!\n"; + print {$efh} "$err\n"; + close $efh; + print "ERROR: $err\n"; + } + + open my $sfh, '>', $status_file or die "Cannot write $status_file: $!\n"; + print {$sfh} (($ok ? 'PASS' : 'FAIL') . "\t$summary\n"); + close $sfh; + return $ok; +} + +sub package_uniqueext { + my ($base, $index, $pkg) = @_; + my $tag = lc $pkg; + $tag =~ s/[^a-z0-9]+/-/g; + $tag =~ s/^-+|-+$//g; + $tag = "pkg$index" if $tag eq ''; + my $prefix = $base ne '' ? $base : "perl-list6-$$"; + my $value = "${prefix}-${index}-${tag}"; + $value =~ s/[^A-Za-z0-9_.-]+/-/g; + return $value; +} + +sub usage { + return <<"USAGE"; +Usage: $0 [options] + --work-dir PATH Temporary work dir (default: $work_dir) + --mock-cfg NAME Mock config (default: +epel-10-) + --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds + --jobs N Number of parallel package workers (default: selected package count) + --result-dir PATH Output directory (default: build-output/list6/perl/) + --log-dir PATH Log directory (default: build-logs/list6/perl/) + --packages LIST Comma-separated subset of packages to build + --skip-install Skip dnf install + perl module import checks + --allow-erasing Allow dnf to erase conflicting packages during install smoke tests +USAGE +} + +sub select_srpm { + my ($globs_ref) = @_; + for my $g (@{$globs_ref}) { + my @matches = sort glob($g); + next if !@matches; + return $matches[-1]; + } + return ''; +} + +sub parse_spec { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot open spec $path: $!\n"; + + my $version = ''; + my %macros; + my @assets; + while (my $line = <$fh>) { + if ($line =~ /^\s*%(?:global|define)\s+([A-Za-z0-9_]+)\s+(.+?)\s*$/) { + my ($k, $v) = ($1, $2); + $v =~ s/\s+#.*$//; + $macros{$k} = $v; + } + if ($line =~ /^Version:\s*(\S+)/i) { + $version = $1; + } + if ($line =~ /^(?:Source|Patch)\d*:\s*(\S+)/i) { + my $asset = $1; + push @assets, $asset; + } + } + close $fh; + + $macros{version} = $version if $version ne ''; + $macros{ver} = $version if $version ne ''; + + @assets = map { + my $v = $_; + for my $i (1 .. 6) { + my $changed = 0; + for my $k (keys %macros) { + my $before = $v; + $v =~ s/%\{$k\}/$macros{$k}/g; + $changed = 1 if $v ne $before; + } + last if !$changed; + } + if ($v =~ m{^[a-zA-Z][a-zA-Z0-9+.-]*://}) { + $v = basename($v); + } + $v =~ s/\?.*$//; + $v; + } @assets; + + return ($version, @assets); +} + +sub print_step { + my ($msg) = @_; + print "\n== $msg ==\n"; +} + +sub sh_quote { + my ($s) = @_; + $s =~ s/'/'"'"'/g; + return "'$s'"; +} + +sub run { + my ($cmd) = @_; + print "+ $cmd\n"; + my $rc = system($cmd); + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\n"; + } +} + +sub capture { + my ($cmd) = @_; + print "+ $cmd\n"; + my $out = `$cmd`; + my $rc = $?; + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\nOutput:\n$out\n"; + } + chomp $out; + return $out; +} + +sub run_capture_rc { + my ($cmd, $log_file) = @_; + my $full = "$cmd > " . sh_quote($log_file) . " 2>&1"; + print "+ $full\n"; + my $rc = system($full); + return $rc == -1 ? 255 : ($rc >> 8); +} + +sub slurp { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot read $path: $!\n"; + local $/; + my $content = <$fh>; + close $fh; + return $content; +} diff --git a/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-Do-not-use-SSLv2_client_method-with-OpenSSL-1.1.0.patch b/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-Do-not-use-SSLv2_client_method-with-OpenSSL-1.1.0.patch new file mode 100644 index 0000000..93dcb49 --- /dev/null +++ b/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-Do-not-use-SSLv2_client_method-with-OpenSSL-1.1.0.patch @@ -0,0 +1,33 @@ +From 1c725e333e9d20b87346fb394a1d01fa5be4fbaf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= +Date: Wed, 12 Oct 2016 10:46:22 +0200 +Subject: [PATCH] Do not use SSLv2_client_method() with OpenSSL >= 1.1.0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +SSLv2 support was removed from OpenSSL 1.1.0. + +CPAN RT#118343 + +Signed-off-by: Petr PísaÅ™ +--- + SSLeay.xs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/SSLeay.xs b/SSLeay.xs +index 1560604..ba0dd24 100644 +--- a/SSLeay.xs ++++ b/SSLeay.xs +@@ -152,7 +152,7 @@ SSL_CTX_new(packname, ssl_version) + ctx = SSL_CTX_new(SSLv3_client_method()); + } + else { +-#ifndef OPENSSL_NO_SSL2 ++#if !defined OPENSSL_NO_SSL2 && OPENSSL_VERSION_NUMBER < 0x10100000L + /* v2 is the default */ + ctx = SSL_CTX_new(SSLv2_client_method()); + #else +-- +2.7.4 + diff --git a/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-Fix-building-on-Perl-without-dot-in-INC.patch b/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-Fix-building-on-Perl-without-dot-in-INC.patch new file mode 100644 index 0000000..7073b10 --- /dev/null +++ b/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-Fix-building-on-Perl-without-dot-in-INC.patch @@ -0,0 +1,11 @@ +diff -up Crypt-SSLeay-0.72/Makefile.PL.orig Crypt-SSLeay-0.72/Makefile.PL +--- Crypt-SSLeay-0.72/Makefile.PL.orig 2017-05-16 13:44:37.511126314 +0200 ++++ Crypt-SSLeay-0.72/Makefile.PL 2017-05-16 13:45:31.141903223 +0200 +@@ -8,6 +8,7 @@ use Getopt::Long qw( GetOptionsFromArray + use Path::Class; + use Try::Tiny; + ++BEGIN { push @INC, '.'; } + use inc::IO::Interactive::Tiny; + + caller diff --git a/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-openssl3-sslv3-method-compat.patch b/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-openssl3-sslv3-method-compat.patch new file mode 100644 index 0000000..b1c2405 --- /dev/null +++ b/perl-Crypt-SSLeay/Crypt-SSLeay-0.72-openssl3-sslv3-method-compat.patch @@ -0,0 +1,21 @@ +--- a/SSLeay.xs ++++ b/SSLeay.xs +@@ -149,14 +149,14 @@ + if(ssl_version == 23) { + ctx = SSL_CTX_new(SSLv23_client_method()); + } + else if(ssl_version == 3) { +- ctx = SSL_CTX_new(SSLv3_client_method()); ++ ctx = SSL_CTX_new(TLS_client_method()); + } + else { + #ifndef OPENSSL_NO_SSL2 + /* v2 is the default */ + ctx = SSL_CTX_new(SSLv2_client_method()); + #else + /* v3 is the default */ +- ctx = SSL_CTX_new(SSLv3_client_method()); ++ ctx = SSL_CTX_new(TLS_client_method()); + #endif + } + EOF \ No newline at end of file diff --git a/perl-Crypt-SSLeay/perl-Crypt-SSLeay.spec b/perl-Crypt-SSLeay/perl-Crypt-SSLeay.spec new file mode 100644 index 0000000..ad4e691 --- /dev/null +++ b/perl-Crypt-SSLeay/perl-Crypt-SSLeay.spec @@ -0,0 +1,390 @@ +# Disable network tests by default +%bcond_with perl_Crypt_SSLeay_enables_network_test + +Name: perl-Crypt-SSLeay +Summary: OpenSSL glue that provides LWP with HTTPS support +Version: 0.72 +Release: 24%{?dist} +License: Artistic 2.0 +Source0: https://cpan.metacpan.org/authors/id/N/NA/NANIS/Crypt-SSLeay-%{version}.tar.gz +# Adapt to OpenSSL 1.1.0, bug #1383756, CPAN RT#118343 +Patch0: Crypt-SSLeay-0.72-Do-not-use-SSLv2_client_method-with-OpenSSL-1.1.0.patch +Patch1: Crypt-SSLeay-0.72-Fix-building-on-Perl-without-dot-in-INC.patch +URL: https://metacpan.org/release/Crypt-SSLeay +BuildRequires: coreutils +BuildRequires: findutils +BuildRequires: gcc +BuildRequires: make +BuildRequires: openssl-devel +BuildRequires: zlib-devel +BuildRequires: perl-interpreter +BuildRequires: perl-devel +BuildRequires: perl-generators +BuildRequires: perl(ExtUtils::CBuilder) >= 0.280205 +BuildRequires: perl(ExtUtils::MakeMaker) +# ExtUtils::MakeMaker::Coverage is useless +BuildRequires: perl(Getopt::Long) +BuildRequires: perl(Path::Class) +BuildRequires: perl(strict) +BuildRequires: perl(warnings) +BuildRequires: pkgconf-pkg-config +# Run-time: +BuildRequires: /etc/pki/tls/certs/ca-bundle.crt +BuildRequires: perl(Carp) +# DynaLoader not needed if XSLoader is available +BuildRequires: perl(Exporter) +BuildRequires: perl(IO::Socket) +BuildRequires: perl(MIME::Base64) +BuildRequires: perl(Socket) +BuildRequires: perl(vars) +BuildRequires: perl(XSLoader) +# Tests: +BuildRequires: perl(Test::More) >= 0.88 +BuildRequires: perl(Try::Tiny) >= 0.19 +# Optional tests: +BuildRequires: perl(Test::Pod) +BuildRequires: perl(Test::Pod::Coverage) +%if %{with perl_Crypt_SSLeay_enables_network_test} +# Network tests: +BuildRequires: perl(constant) +BuildRequires: perl(HTTP::Request) +BuildRequires: perl(LWP::Protocol::https) >= 6.02 +BuildRequires: perl(LWP::UserAgent) +%endif +Requires: perl(:MODULE_COMPAT_%(eval "`perl -V:version`"; echo $version)) +Requires: /etc/pki/tls/certs/ca-bundle.crt +Requires: perl(XSLoader) + +%global __provides_exclude %{?__provides_exclude:__provides_exclude|}^perl\\(DB\\) +%{?perl_default_filter} + +%description +These Perl modules provide support for the HTTPS protocol under the World-Wide +Web library for Perl (LWP), so that a LWP::UserAgent can make HTTPS GET, HEAD, +and POST requests. + +This package contains Net::SSL module which is automatically loaded by +LWP::Protocol::https on HTTPS requests, and provides the necessary SSL glue +for that module to work. + +%prep +%setup -q -n Crypt-SSLeay-%{version} +%patch0 -p1 +%patch1 -p1 +# OpenSSL 3 removed SSLv3 symbols; force TLS method for the old SSLv3 code path. +sed -i 's/SSLv3_client_method()/TLS_client_method()/g' SSLeay.xs + +# Placate rpmlint +chmod -c -x lib/Net/SSL.pm + +%build +if pkg-config openssl ; then + export INC="$CFLAGS `pkg-config --cflags-only-I openssl`" + export LDFLAGS="$LDFLAGS `pkg-config --libs-only-L openssl`" +fi + +perl Makefile.PL --%{!?with_perl_Crypt_SSLeay_enables_network_test:no-}live-tests \ + INC="$INC" LDFLAGS="$LDFLAGS" INSTALLDIRS=vendor NO_PACKLIST=1 \ + OPTIMIZE="%{optflags}" - 0.72-20 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Thu Jun 28 2018 Jitka Plesnikova - 0.72-19 +- Perl 5.28 rebuild + +* Thu Feb 08 2018 Fedora Release Engineering - 0.72-18 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Thu Aug 03 2017 Fedora Release Engineering - 0.72-17 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 0.72-16 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Jun 07 2017 Jitka Plesnikova - 0.72-15 +- Perl 5.26 re-rebuild of bootstrapped packages + +* Mon Jun 05 2017 Petr Pisar - 0.72-14 +- Modernize spec file + +* Mon Jun 05 2017 Jitka Plesnikova - 0.72-13 +- Perl 5.26 rebuild + +* Tue May 16 2017 Jitka Plesnikova - 0.72-12 +- Fix building on Perl without '.' in @INC + +* Sat Feb 11 2017 Fedora Release Engineering - 0.72-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Oct 12 2016 Petr Pisar - 0.72-10 +- Adapt to OpenSSL 1.1.0 (bug #1383756) + +* Sun May 15 2016 Jitka Plesnikova - 0.72-9 +- Perl 5.24 rebuild + +* Thu Feb 04 2016 Fedora Release Engineering - 0.72-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Thu Jun 18 2015 Fedora Release Engineering - 0.72-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Sat Jun 06 2015 Jitka Plesnikova - 0.72-6 +- Perl 5.22 rebuild + +* Fri Aug 29 2014 Jitka Plesnikova - 0.72-5 +- Perl 5.20 rebuild + +* Sun Aug 17 2014 Fedora Release Engineering - 0.72-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jun 07 2014 Fedora Release Engineering - 0.72-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Mon Apr 28 2014 Jitka Plesnikova - 0.72-2 +- Correct License tag, which should be Artistic 2.0 + +* Mon Apr 28 2014 Jitka Plesnikova - 0.72-1 +- 0.72 bump + +* Wed Apr 16 2014 Petr Pisar - 0.64-6 +- Make build script non-interactive +- Update package description +- Specify all dependencies + +* Sat Aug 03 2013 Petr Pisar - 0.64-5 +- Perl 5.18 rebuild + +* Thu Feb 14 2013 Fedora Release Engineering - 0.64-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Aug 8 2012 Paul Howarth - 0.64-3 +- Remove circular BR: perl(Net::SSL) provided by this package +- Placate rpmlint regarding file permissions +- Don't need to remove empty directories from the buildroot + +* Tue Aug 07 2012 Petr Å abata - 0.64-1 +- 0.64 bump + +* Mon Jul 30 2012 Petr Å abata - 0.60-1 +- 0.60 bugfix bump +- Drop command macros and modernize the spec a bit + +* Fri Jul 20 2012 Fedora Release Engineering - 0.58-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Fri Jul 20 2012 Marcela MaÅ¡láňová - 0.58-10 +- Conditionalize ExtUtils::MakeMaker::Coverage + +* Mon Jun 25 2012 Petr Pisar - 0.58-9 +- Perl 5.16 rebuild + +* Fri Jan 13 2012 Fedora Release Engineering - 0.58-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Wed Sep 07 2011 Petr Sabata - 0.58-7 +- Link to the ca-certificates ca-bundle.crt instead of shipping our own, + outdated copy (#734385) + +* Fri Jul 22 2011 Petr Pisar - 0.58-6 +- RPM 4.9 dependency filtering added + +* Wed Jul 20 2011 Petr Sabata - 0.58-5 +- Perl mass rebuild + +* Tue Apr 19 2011 Paul Howarth - 0.58-4 +- Remove buildroot specification and cleaning, not needed for modern rpmbuild +- Use %%{?perl_default_filter} +- Filter the perl(DB) provide in a way that works with rpm >= 4.9 +- Use DESTDIR rather than PERL_INSTALL_ROOT +- Fix line endings on documentation +- Fix upstream source URL +- Fix argument order for find with -depth + +* Tue Feb 08 2011 Fedora Release Engineering - 0.58-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Thu Dec 16 2010 Marcela Maslanova - 0.58-2 +- Rebuild to fix problems with vendorarch/lib (#661697) + +* Wed Sep 1 2010 Petr Sabata - 0.58-1 +- New upstream release, v0.58 +- removing perl-Crypt-SSLeay-0.57-live-tests.patch, fixed in upstream +- removing perl-Crypt-SSLeay-Makefile_ssl1.patch, fixed in upstream + +* Fri Apr 30 2010 Marcela Maslanova - 0.57-17 +- Mass rebuild with perl-5.12.0 + +* Mon Dec 7 2009 Stepan Kasal - 0.57-16 +- rebuild against perl 5.10.1 + +* Wed Nov 25 2009 Marcela MaÅ¡láňová - 0.57-14 +- change Makefile for openssl 1.0, which couldn't be found properly before + +* Fri Aug 21 2009 Tomas Mraz - 0.57-13 +- rebuilt with new openssl + +* Sat Jul 25 2009 Fedora Release Engineering - 0.57-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Thu Feb 26 2009 Fedora Release Engineering - 0.57-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Sat Jan 17 2009 Tomas Mraz - 0.57-10 +- rebuild with new openssl + +* Mon Oct 6 2008 Marcela Maslanova - 0.57-9 +- add examples into doc + +* Wed Sep 24 2008 Marcela Maslanova - 0.57-8 +- fix patches for fuzz + +* Wed Mar 5 2008 Tom "spot" Callaway - 0.57-7 +- rebuild for new perl + +* Tue Feb 19 2008 Fedora Release Engineering - 0.57-6 +- Autorebuild for GCC 4.3 + +* Wed Dec 05 2007 Release Engineering - 0.57-5 +- Rebuild for deps + +* Wed Dec 5 2007 Robin Norwood - 0.57-4 +- Rebuild for new openssl + +* Sat Oct 27 2007 Robin Norwood - 0.57-3 +- Remove unnecessary BR: pkgconfig + +* Fri Oct 26 2007 Robin Norwood - 0.57-2 +- Fix buildroot per package review +- Resolves: bz#226248 + +* Thu Oct 25 2007 Robin Norwood - 0.57-1 +- Update to latest upstream version. +- Remove old patch (patch applied to upstream) +- Several fixes for package review: +- Fixed BuildRequires (added Test::Pod and LWP::UserAgent) +- Apply patch to avoid prompting for input when building Makefile +- Fix defattr line +- Resolves: bz#226248 + +* Mon Aug 27 2007 Robin Norwood - 0.56-2 +- perl(ExtUtils::MakeMaker::Coverage) is now available + +* Mon Aug 13 2007 Robin Norwood - 0.56-1 +- 0.56 is the latest CPAN version, not 0.55 + +* Mon Aug 13 2007 Robin Norwood - 0.55-2 +- Update to latest version from CPAN: 0.55 +- Remove two old patches, update lib64 patch for Makefile.PL changes. + +* Tue Feb 13 2007 Robin Norwood - 0.53-1 +- New version: 0.53 + +* Mon Nov 27 2006 Robin Norwood - 0.51-12 +- Resolves: bug#217138 +- fix a segfault on x86_64 + +* Tue Oct 17 2006 Robin Norwood - 0.51-10 +- Filter out Provides perl(DB) +- bug #205562 + +* Wed Jul 12 2006 Jesse Keating - 0.51-9.2.2.1 +- rebuild + +* Fri Feb 10 2006 Jesse Keating - 0.51-9.2.2 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating - 0.51-9.2.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Fri Feb 03 2006 Jason Vas Dias - 0.51-9.2 +- rebuild for new perl-5.8.8 / gcc / glibc + +* Fri Dec 16 2005 Jesse Keating +- rebuilt for new gcc + +* Fri Dec 16 2005 Jesse Keating +- rebuilt for new gcj + +* Thu Nov 10 2005 Tomas Mraz 0.51-9 +- rebuilt against new openssl +- added missing SSL_library_init() + +* Sat Sep 24 2005 Ville Skyttä 0.51-8 +- Own more installed dirs (#73908). +- Enable rpmbuild's internal dependency generator, drop unneeded dependencies. +- Require perl(:MODULE_COMPAT_*). +- Run tests in the %%check section. +- Fix License, Source0, URL, and Group tags. + +* Wed Mar 30 2005 Warren Togami 0.51-7 +- remove brp-compress + +* Tue Mar 8 2005 Joe Orton 0.51-6 +- rebuild + +* Tue Aug 31 2004 Chip Turner 0.51-5 +- build for FC3 + +* Tue Aug 31 2004 Chip Turner 0.51-4 +- build for RHEL3 U4 + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Tue Mar 02 2004 Elliot Lee +- rebuilt + +* Fri Feb 13 2004 Chip Turner 0.51-1 +- update to upstream 0.51 + +* Thu Jun 05 2003 Elliot Lee +- rebuilt + +* Tue Jan 7 2003 Nalin Dahyabhai +- pass openssl includes to make as INC and ldflags in as LDFLAGS + +* Thu Nov 21 2002 Chip Turner +- patch to support /usr/lib64 before /usr/lib + +* Wed Nov 20 2002 Chip Turner +- rebuild + +* Tue Aug 6 2002 Chip Turner +- automated release bump and build + +* Thu Jun 27 2002 Chip Turner +- description update + +* Tue Jun 25 2002 Chip Turner +- move to 0.39 + +* Wed Jan 09 2002 Tim Powers +- automated rebuild + +* Fri Dec 7 2001 root +- Spec file was autogenerated. diff --git a/perl-HTTP-Async/perl-HTTP-Async.spec b/perl-HTTP-Async/perl-HTTP-Async.spec index e67dfb8..d05264c 100644 --- a/perl-HTTP-Async/perl-HTTP-Async.spec +++ b/perl-HTTP-Async/perl-HTTP-Async.spec @@ -5,16 +5,20 @@ Name: perl-%{upstream_name} Version: %{upstream_version} -Release: 2 +Release: 3%{?dist} Summary: Politely process multiple HTTP requests License: GPL+ or Artistic Group: Development/Perl Url: http://search.cpan.org/dist/%{upstream_name} Source0: http://www.cpan.org/modules/by-module/HTTP/%{upstream_name}-%{upstream_version}.tar.gz -Patch: HTTP-Async-0.30.patch +Patch0: HTTP-Async-0.30.patch BuildArch: noarch +BuildRequires: perl(ExtUtils::MakeMaker) +BuildRequires: make +BuildRequires: perl-generators + %description Although using the conventional 'LWP::UserAgent' is fast and easy it does have some drawbacks - the code execution blocks until the request has been @@ -35,7 +39,7 @@ using 'select' lists. %prep %setup -q -n %{upstream_name}-%{upstream_version} -%patch -p1 +%patch 0 -p1 %build CFLAGS="$RPM_OPT_FLAGS $CFLAGS" %__perl Makefile.PL diff --git a/perl-IO-Tty/IO-Tty.spec b/perl-IO-Tty/IO-Tty.spec index 1341560..4ce1544 100644 --- a/perl-IO-Tty/IO-Tty.spec +++ b/perl-IO-Tty/IO-Tty.spec @@ -14,7 +14,7 @@ name: perl-IO-Tty summary: IO-Tty - Pseudo ttys and constants version: 1.07 -release: 1 +release: 2%{?dist} vendor: Roland Giersig packager: Arix International license: Artistic @@ -24,6 +24,13 @@ buildroot: %{_tmppath}/%{name}-%{version}-%(id -u -n) prefix: %(echo %{_prefix}) source: IO-Tty-1.07.tar.gz +BuildRequires: gcc +BuildRequires: make +BuildRequires: perl-interpreter +BuildRequires: perl-devel +BuildRequires: perl(ExtUtils::MakeMaker) +BuildRequires: perl-generators + %description "IO::Tty" is used internally by "IO::Pty" to create a pseudo-tty. You wouldn't want to use it directly except to import constants, use @@ -80,7 +87,7 @@ CFLAGS="$RPM_OPT_FLAGS" %install [ "%{buildroot}" != "/" ] && rm -rf %{buildroot} -%{makeinstall} `%{__perl} -MExtUtils::MakeMaker -e ' print \$ExtUtils::MakeMaker::VERSION <= 6.05 ? qq|PREFIX=%{buildroot}%{_prefix}| : qq|DESTDIR=%{buildroot}| '` +%{__make} pure_install DESTDIR=%{buildroot} cmd=/usr/share/spec-helper/compress_files [ -x $cmd ] || cmd=/usr/lib/rpm/brp-compress diff --git a/perl-Net-HTTPS-NB/perl-Net-HTTPS-NB.spec b/perl-Net-HTTPS-NB/perl-Net-HTTPS-NB.spec index 831e5e0..1a13e86 100644 --- a/perl-Net-HTTPS-NB/perl-Net-HTTPS-NB.spec +++ b/perl-Net-HTTPS-NB/perl-Net-HTTPS-NB.spec @@ -5,20 +5,22 @@ Name: perl-%{upstream_name} Version: %{upstream_version} -Release: 2 +Release: 3%{?dist} Summary: Non-blocking HTTPS client License: GPL+ or Artistic Group: Development/Perl Url: http://search.cpan.org/dist/%{upstream_name} Source0: http://www.cpan.org/modules/by-module/Net/%{upstream_name}-%{upstream_version}.tar.gz -Patch: Net-HTTPS-NB-0.14.patch +Patch0: Net-HTTPS-NB-0.14.patch BuildRequires: perl(Exporter) BuildRequires: perl(ExtUtils::MakeMaker) BuildRequires: perl(IO::Socket::SSL) >= 0.980.0 BuildRequires: perl(Net::HTTP) BuildRequires: perl(Net::HTTPS) +BuildRequires: make +BuildRequires: perl-generators BuildArch: noarch %description @@ -35,7 +37,7 @@ addition allows non-blocking connect. %prep %setup -q -n %{upstream_name}-%{upstream_version} -%patch -p1 +%patch 0 -p1 %build %__perl Makefile.PL diff --git a/perl-Sys-Virt/Sys-Virt.spec b/perl-Sys-Virt/Sys-Virt.spec index efa35d1..36a16f0 100644 --- a/perl-Sys-Virt/Sys-Virt.spec +++ b/perl-Sys-Virt/Sys-Virt.spec @@ -14,17 +14,24 @@ name: perl-Sys-Virt summary: Sys-Virt - Represent and manage a libvirt hypervisor connection version: 0.2.0 -release: 2 +release: 3%{?dist} vendor: Daniel P. Berrange packager: Arix International license: Artistic group: Applications/CPAN url: http://www.cpan.org buildroot: %{_tmppath}/%{name}-%{version}-%(id -u -n) -buildarch: x86_64 prefix: %(echo %{_prefix}) source: Sys-Virt-0.2.0.tar.gz -patch: Sys-Virt-fixes.patch +patch0: Sys-Virt-fixes.patch + +BuildRequires: gcc +BuildRequires: make +BuildRequires: perl-interpreter +BuildRequires: perl-devel +BuildRequires: perl(ExtUtils::MakeMaker) +BuildRequires: perl-generators +BuildRequires: libvirt-devel %description The Sys::Virt module provides a Perl XS binding to the libvirt @@ -40,10 +47,11 @@ a consistent API. %prep %setup -q -n %{pkgname}-%{version} -%patch +%patch 0 -p0 chmod -R u+w %{_builddir}/%{pkgname}-%{version} %build +export PERL_USE_UNSAFE_INC=1 grep -rsl '^#!.*perl' . | grep -v '.bak$' |xargs --no-run-if-empty \ %__perl -MExtUtils::MakeMaker -e 'MY->fixin(@ARGV)' @@ -57,7 +65,7 @@ CFLAGS="$RPM_OPT_FLAGS" %install [ "%{buildroot}" != "/" ] && rm -rf %{buildroot} -%{makeinstall} `%{__perl} -MExtUtils::MakeMaker -e ' print \$ExtUtils::MakeMaker::VERSION <= 6.05 ? qq|PREFIX=%{buildroot}%{_prefix}| : qq|DESTDIR=%{buildroot}| '` +%{__make} pure_install DESTDIR=%{buildroot} cmd=/usr/share/spec-helper/compress_files [ -x $cmd ] || cmd=/usr/lib/rpm/brp-compress diff --git a/syslinux/mockbuild.pl b/syslinux/mockbuild.pl new file mode 100755 index 0000000..90ddf74 --- /dev/null +++ b/syslinux/mockbuild.pl @@ -0,0 +1,399 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(dirname basename); +use File::Copy qw(copy); +use File::Path qw(make_path remove_tree); +use Getopt::Long qw(GetOptions); + +my $script_dir = abs_path(dirname(__FILE__)); +my $repo_root = abs_path("$script_dir/.."); +my $pkg_dir = "$repo_root/syslinux"; +my $spec_file = "$pkg_dir/syslinux-xcat.spec"; + +my $source_url = 'https://www.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.xz'; +my $source_file = ''; +my $work_dir = '/tmp/syslinux-xcat-mockbuild'; +my $mock_cfg = ''; +my $mock_uniqueext = ''; +my $result_dir = "$repo_root/build-output/list3/syslinux-xcat"; +my $log_dir = "$repo_root/build-logs/list3/syslinux-xcat"; +my $skip_install = 0; +my $skip_upstream_download = 0; + +GetOptions( + 'source-url=s' => \$source_url, + 'source-file=s' => \$source_file, + 'work-dir=s' => \$work_dir, + 'mock-cfg=s' => \$mock_cfg, + 'mock-uniqueext=s' => \$mock_uniqueext, + 'result-dir=s' => \$result_dir, + 'log-dir=s' => \$log_dir, + 'skip-install!' => \$skip_install, + 'skip-upstream-download!' => \$skip_upstream_download, +) or die usage(); + +die "Run as root (current uid=$>)\n" if $> != 0; +die "Missing spec file: $spec_file\n" if !-f $spec_file; + +for my $bin (qw(wget mock rpmbuild rpm dnf file bash grep cut)) { + run("command -v " . sh_quote($bin) . " >/dev/null 2>&1"); +} + +my ($pkg_name, $version, $source_assets_ref, $patch_assets_ref, $all_assets_ref) = parse_spec($spec_file); +my @source_assets = @{$source_assets_ref}; +my @patch_assets = @{$patch_assets_ref}; +my @all_assets = @{$all_assets_ref}; + +die "Could not parse Name/Version from $spec_file\n" if !$pkg_name || !$version; +die "No Source assets found in $spec_file\n" if !@source_assets; + +if (!$source_file) { + $source_file = $source_assets[0]; +} + +my $source_path = "$pkg_dir/$source_file"; +my $arch = capture('uname -m'); +if (!$mock_cfg) { + my $os_id = capture(q{bash -lc 'source /etc/os-release; echo $ID'}); + $mock_cfg = "${os_id}+epel-10-${arch}"; +} +my $mock_uniqueext_opt = $mock_uniqueext ne '' + ? ' --uniqueext ' . sh_quote($mock_uniqueext) + : ''; + +print_step("Configuration"); +print "repo_root: $repo_root\n"; +print "pkg_dir: $pkg_dir\n"; +print "work_dir: $work_dir\n"; +print "result_dir: $result_dir\n"; +print "log_dir: $log_dir\n"; +print "spec_file: $spec_file\n"; +print "pkg_name: $pkg_name\n"; +print "version: $version\n"; +print "mock_cfg: $mock_cfg\n"; +print "mock_uniqueext: " . ($mock_uniqueext ne '' ? $mock_uniqueext : '(none)') . "\n"; +print "source_url: $source_url\n"; +print "source_file:$source_file\n"; +print "skip_install: $skip_install\n"; +print "skip_upstream_download: $skip_upstream_download\n"; + +make_path($result_dir); +make_path($log_dir); + +print_step("Mock config check"); +run("mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . " --print-root-path >/dev/null"); + +if (!$skip_upstream_download) { + print_step("Download upstream source"); + run("wget --spider " . sh_quote($source_url)); + run("wget -O " . sh_quote($source_path) . " " . sh_quote($source_url)); + + my $sha = capture("sha256sum " . sh_quote($source_path) . " | cut -d ' ' -f1"); + my $meta_file = "$log_dir/upstream-source.txt"; + open my $mfh, '>', $meta_file or die "Cannot write $meta_file: $!\n"; + print {$mfh} "url=$source_url\n"; + print {$mfh} "file=$source_path\n"; + print {$mfh} "sha256=$sha\n"; + close $mfh; + print "Downloaded source: $source_path\n"; + print "SHA256: $sha\n"; +} + +print_step("Verify spec assets"); +for my $asset (@all_assets) { + my $path = "$pkg_dir/$asset"; + die "Missing required spec asset: $path\n" if !-f $path; +} +print "Verified Sources=" . scalar(@source_assets) . ", Patches=" . scalar(@patch_assets) . "\n"; + +print_step("Stage files for patch-application check"); +remove_tree($work_dir) if -d $work_dir; +my $prep_top = "$work_dir/prep"; +for my $d (qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)) { + make_path("$prep_top/$d"); +} +copy($spec_file, "$prep_top/SPECS/syslinux-xcat.spec") + or die "Failed to copy spec to prep topdir: $!\n"; +for my $asset (@all_assets) { + copy("$pkg_dir/$asset", "$prep_top/SOURCES/$asset") + or die "Failed to copy $asset into prep SOURCES: $!\n"; +} + +print_step("Apply patches in %prep"); +my $prep_log = "$log_dir/prep.log"; +run( + "rpmbuild --define " . sh_quote("_topdir $prep_top") . + " -bp --nodeps " . sh_quote("$prep_top/SPECS/syslinux-xcat.spec") . + " > " . sh_quote($prep_log) . " 2>&1" +); +my $patch_count = capture("grep -c '^Patch #' " . sh_quote($prep_log) . " || true"); +print "Patch application check passed. Applied patches: $patch_count\n"; + +print_step("Build SRPM with mock"); +my $srpm_out = "$work_dir/srpm"; +make_path($srpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --buildsrpm --spec " . sh_quote($spec_file) . + " --sources " . sh_quote($pkg_dir) . + " --resultdir " . sh_quote($srpm_out) +); + +my @srpms = sort glob("$srpm_out/*.src.rpm"); +die "SRPM not generated in $srpm_out\n" if !@srpms; +my $srpm = $srpms[-1]; +print "SRPM: $srpm\n"; + +print_step("Rebuild RPM with mock"); +my $rpm_out = "$work_dir/rpm"; +make_path($rpm_out); +run( + "mock -r " . sh_quote($mock_cfg) . $mock_uniqueext_opt . + " --rebuild " . sh_quote($srpm) . + " --resultdir " . sh_quote($rpm_out) +); + +my @all_rpms = sort glob("$rpm_out/*.rpm"); +die "No RPMs generated in $rpm_out\n" if !@all_rpms; + +my $xcat_rpm = ''; +my $main_rpm = ''; +for my $rpm (@all_rpms) { + next if $rpm =~ /\.src\.rpm$/; + my $name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($rpm)); + my $rarch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($rpm)); + if ($name eq 'syslinux-xcat' && $rarch eq 'noarch') { + $xcat_rpm = $rpm; + } + if ($name eq 'syslinux' && $rarch eq $arch) { + $main_rpm = $rpm; + } +} +die "Could not find main syslinux-xcat noarch RPM in $rpm_out\n" if !$xcat_rpm; + +print_step("Verify generated RPM"); +my $rpm_name = capture("rpm -qp --qf '%{NAME}' " . sh_quote($xcat_rpm)); +my $rpm_arch = capture("rpm -qp --qf '%{ARCH}' " . sh_quote($xcat_rpm)); +die "Unexpected RPM name: $rpm_name\n" if $rpm_name ne 'syslinux-xcat'; +die "Unexpected RPM arch: $rpm_arch (expected noarch)\n" if $rpm_arch ne 'noarch'; +run( + "rpm -qpl " . sh_quote($xcat_rpm) . + " | grep -F '/opt/xcat/share/xcat/netboot/syslinux/' >/dev/null" +); +run( + "rpm -qpl " . sh_quote($xcat_rpm) . + " | grep -Fx /opt/xcat/share/xcat/netboot/syslinux/pxelinux.0 >/dev/null" +); +print "Verified RPM name/arch/payload: $xcat_rpm\n"; + +print_step("Copy artifacts and logs"); +for my $rpm (@all_rpms) { + copy($rpm, $result_dir) or die "Failed to copy $rpm to $result_dir: $!\n"; +} +copy($srpm, $result_dir) or die "Failed to copy $srpm to $result_dir: $!\n"; +for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$rpm_out/$log"; + next if !-f $src; + copy($src, "$log_dir/$log") + or die "Failed to copy $src to $log_dir: $!\n"; +} +for my $log (qw(build.log root.log state.log hw_info.log installed_pkgs.log)) { + my $src = "$srpm_out/$log"; + next if !-f $src; + copy($src, "$log_dir/srpm-$log") + or die "Failed to copy $src to $log_dir: $!\n"; +} + +if (!$skip_install) { + print_step("Install RPM(s) and run smoke tests"); + run("dnf -y install " . sh_quote($xcat_rpm)); + if ($main_rpm) { + run("dnf -y install " . sh_quote($main_rpm)); + } + + my $pxe_file = '/opt/xcat/share/xcat/netboot/syslinux/pxelinux.0'; + die "Missing installed PXE file: $pxe_file\n" if !-f $pxe_file; + + my $file_log = "$log_dir/smoke-file.log"; + my $qf_log = "$log_dir/smoke-rpm-qf.log"; + my $rc_file = run_capture_rc("file $pxe_file", $file_log); + my $rc_qf = run_capture_rc("rpm -qf $pxe_file", $qf_log); + + die "Smoke check failed: file returned $rc_file\n" if $rc_file != 0; + die "Smoke check failed: rpm -qf returned $rc_qf\n" if $rc_qf != 0; + + my $qf_out = slurp($qf_log); + die "Installed file is not owned by syslinux-xcat:\n$qf_out\n" + if $qf_out !~ /^syslinux-xcat-/m; + + my $syslinux_help_log = "$log_dir/smoke-syslinux-help.log"; + if (-x '/usr/bin/syslinux') { + my $rc_help = run_capture_rc("/usr/bin/syslinux --help", $syslinux_help_log); + my $help_out = slurp($syslinux_help_log); + die "syslinux --help returned unexpected rc=$rc_help\n" + if $rc_help != 0 && $rc_help != 1; + die "syslinux --help output missing expected usage text\n" + if $help_out !~ /usage|syslinux/i; + } + + my $summary = "$log_dir/smoke-summary.txt"; + open my $sfh, '>', $summary or die "Cannot write $summary: $!\n"; + print {$sfh} "pxe_file=$pxe_file\n"; + print {$sfh} "rc_file=$rc_file\n"; + print {$sfh} "rc_qf=$rc_qf\n"; + print {$sfh} "main_rpm_installed=" . ($main_rpm ? 1 : 0) . "\n"; + close $sfh; +} + +print_step("Completed"); +print "syslinux-xcat RPM: $xcat_rpm\n"; +print "Artifacts: $result_dir\n"; +print "Logs: $log_dir\n"; +exit 0; + +sub usage { + return <<"USAGE"; +Usage: $0 [options] + --source-url URL Upstream tarball URL (default: $source_url) + --source-file FILE Source filename stored in syslinux/ (default: inferred from spec) + --work-dir PATH Temporary work dir (default: $work_dir) + --mock-cfg NAME Mock config (default: +epel-10-) + --mock-uniqueext TXT Optional mock --uniqueext suffix to isolate concurrent builds + --result-dir PATH Output RPM/SRPM directory (default: $result_dir) + --log-dir PATH Log directory (default: $log_dir) + --skip-upstream-download Skip wget download step + --skip-install Skip dnf install + smoke tests +USAGE +} + +sub parse_spec { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot open spec $path: $!\n"; + + my %macros; + my $name = ''; + my $version = ''; + my @sources; + my @patches; + + while (my $line = <$fh>) { + if ($line =~ /^\s*%(?:define|global)\s+([A-Za-z0-9_]+)\s+(.+?)\s*$/) { + my ($k, $v) = (lc($1), $2); + $v =~ s/\s+#.*$//; + $macros{$k} = $v; + next; + } + if ($line =~ /^Name:\s*(\S+)/) { + $name = $1; + $macros{name} = $name; + next; + } + if ($line =~ /^Version:\s*(\S+)/) { + $version = $1; + $macros{version} = $version; + next; + } + if ($line =~ /^Source\d*:\s*(\S+)/) { + push @sources, $1; + next; + } + if ($line =~ /^Patch\d*:\s*(\S+)/) { + push @patches, $1; + next; + } + } + close $fh; + + $name = expand_macros($name, \%macros); + $macros{name} = $name if $name; + $version = expand_macros($version, \%macros); + $macros{version} = $version if $version; + + @sources = map { normalize_asset(expand_macros($_, \%macros)) } @sources; + @patches = map { normalize_asset(expand_macros($_, \%macros)) } @patches; + + my @assets = (@sources, @patches); + return ($name, $version, \@sources, \@patches, \@assets); +} + +sub expand_macros { + my ($text, $macros) = @_; + return '' if !defined $text; + + for (1..30) { + my $prev = $text; + $text =~ s/%\{([^}]+)\}/ + exists $macros->{lc($1)} ? $macros->{lc($1)} : "%{$1}" + /ge; + last if $text eq $prev; + } + return $text; +} + +sub normalize_asset { + my ($asset) = @_; + $asset //= ''; + $asset =~ s/^["']//; + $asset =~ s/["']$//; + $asset =~ s/\?.*$//; + + if ($asset =~ m{^[A-Za-z][A-Za-z0-9+.-]*://}) { + $asset =~ s{.*/}{}; + } + $asset =~ s{^\./}{}; + return $asset; +} + +sub print_step { + my ($msg) = @_; + print "\n== $msg ==\n"; +} + +sub sh_quote { + my ($s) = @_; + $s =~ s/'/'"'"'/g; + return "'$s'"; +} + +sub run { + my ($cmd) = @_; + print "+ $cmd\n"; + my $rc = system($cmd); + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\n"; + } +} + +sub capture { + my ($cmd) = @_; + print "+ $cmd\n"; + my $out = `$cmd`; + my $rc = $?; + if ($rc != 0) { + my $exit = $rc == -1 ? 255 : ($rc >> 8); + die "Command failed (rc=$exit): $cmd\nOutput:\n$out\n"; + } + chomp $out; + return $out; +} + +sub run_capture_rc { + my ($cmd, $log_file) = @_; + my $full = "$cmd > " . sh_quote($log_file) . " 2>&1"; + print "+ $full\n"; + my $rc = system($full); + return $rc == -1 ? 255 : ($rc >> 8); +} + +sub slurp { + my ($path) = @_; + open my $fh, '<', $path or die "Cannot read $path: $!\n"; + local $/; + my $content = <$fh>; + close $fh; + return $content; +} diff --git a/syslinux/syslinux-6.03-multibootif.patch b/syslinux/syslinux-6.03-multibootif.patch new file mode 100644 index 0000000..70fce59 --- /dev/null +++ b/syslinux/syslinux-6.03-multibootif.patch @@ -0,0 +1,53 @@ +--- a/com32/mboot/mboot.c 2014-10-06 09:29:01.000000000 -0700 ++++ b/com32/mboot/mboot.c 2026-03-02 00:00:00.000000000 +0000 +@@ -93,9 +93,18 @@ static int get_modules(char **argv, struct module_data **mdp) + char **argp, **argx; + struct module_data *mp; + int rv; ++ int firstmod = 1; + int module_count = 1; + int arglen; + const char module_separator[] = "---"; ++ com32sys_t reg; ++ const char *bootifstr = ""; ++ ++ reg.eax.w[0] = 0x000f; /* Get IPAPPEND strings */ ++ __intcall(0x22, ®, ®); ++ if (!(reg.eflags.l & EFLAGS_CF)) ++ bootifstr = MK_PTR(reg.es, ++ *(uint16_t *)MK_PTR(reg.es, reg.ebx.w[0] + 2)); + + for (argp = argv; *argp; argp++) { + if (!strcmp(*argp, module_separator)) +@@ -130,15 +139,29 @@ static int get_modules(char **argv, struct module_data **mdp) + arglen += strlen(*argx) + 1; + + if (arglen == 0) { +- mp->cmdline = strdup(""); ++ if (firstmod) { ++ firstmod = 0; ++ mp->cmdline = strdup(bootifstr); ++ } else { ++ mp->cmdline = strdup(""); ++ } + } else { + char *p; ++ if (firstmod) ++ arglen += strlen(bootifstr) + 1; ++ + mp->cmdline = p = malloc(arglen); + for (; *argp && strcmp(*argp, module_separator); argp++) { + p = stpcpy(p, *argp); + *p++ = ' '; + } +- *--p = '\0'; ++ if (firstmod) { ++ firstmod = 0; ++ p = stpcpy(p, bootifstr); ++ *p = '\0'; ++ } else { ++ *--p = '\0'; ++ } + } + mp++; + if (*argp) diff --git a/syslinux/syslinux-xcat.spec b/syslinux/syslinux-xcat.spec index 55deee8..37a9de6 100644 --- a/syslinux/syslinux-xcat.spec +++ b/syslinux/syslinux-xcat.spec @@ -1,30 +1,34 @@ # -*- rpm -*- -%define RPMVERSION 3.86 -%define VERSION 3.86 +%define RPMVERSION 6.03 +%define VERSION 6.03 Summary: Kernel loader which uses a FAT, ext2/3 or iso9660 filesystem or a PXE network Name: syslinux Version: %{RPMVERSION} -Release: 2 +Release: 1 License: GPL Group: System/Boot -Source0: ftp://ftp.kernel.org/pub/linux/utils/boot/syslinux/%{name}-%{VERSION}.tar.bz2 -Patch0: syslinux-3.86-multibootif.patch -ExclusiveArch: i386 i486 i586 i686 athlon pentium4 x86_64 +Source0: https://www.kernel.org/pub/linux/utils/boot/syslinux/%{name}-%{VERSION}.tar.xz +Patch0: syslinux-6.03-multibootif.patch +ExclusiveArch: i386 i486 i586 i686 athlon pentium4 x86_64 ppc64le Packager: H. Peter Anvin Buildroot: %{_tmppath}/%{name}-%{VERSION}-root -BuildPrereq: nasm >= 0.98.39, perl Autoreq: 0 -BuildRequires: mtools, libc.so.6()(64bit) +BuildRequires: nasm >= 2.03, perl +BuildRequires: mtools, libc.so.6()(64bit), python3 +BuildRequires: git +BuildRequires: libuuid-devel %define my_cc gcc # NOTE: extlinux belongs in /sbin, not in /usr/sbin, since it is typically # a system bootloader, and may be necessary for system recovery. %define _sbindir /sbin +%ifnarch ppc64le %package devel Summary: Development environment for SYSLINUX add-on modules Group: Development/Libraries Requires: syslinux +%endif %description SYSLINUX is a suite of bootloaders, currently supporting DOS FAT @@ -32,10 +36,12 @@ filesystems, Linux ext2/ext3 filesystems (EXTLINUX), PXE network boots (PXELINUX), or ISO 9660 CD-ROMs (ISOLINUX). It also includes a tool, MEMDISK, which loads legacy operating systems from these media. +%ifnarch ppc64le %description devel The SYSLINUX boot loader contains an API, called COM32, for writing sophisticated add-on modules. This package contains the libraries necessary to compile such modules. +%endif %package extlinux Summary: The EXTLINUX bootloader, for booting the local system. @@ -58,24 +64,43 @@ booting in the /opt/xcat/share/xcat/netboot/syslinux directory. %prep %setup -q -n syslinux-%{VERSION} %patch0 -p1 +# Source tarballs do not include .git metadata; skip submodule refresh in that case. +sed -i 's/^[[:space:]]*git submodule update --init$/[ -d .git ] \&\& git submodule update --init || :/' efi/clean-gnu-efi.sh +# GCC/glibc compatibility for major()/minor() declarations. +sed -i '/#include /a #include ' extlinux/main.c +# GCC14 strict prototype fix in COM32 syslinux debug helper. +sed -i '/#include /a #include ' com32/lib/syslinux/debug.c +# GCC >= 10 defaults to -fno-common; legacy syslinux code expects common symbols. +for f in mk/lib.mk mk/com32.mk mk/elf.mk mk/embedded.mk; do \ + sed -i '/fno-strict-aliasing/a GCCOPT += $(call gcc_ok,-fcommon,)' "$f"; \ +done %build -make CC='%{my_cc}' clean -make CC='%{my_cc}' -C com32 -make CC='%{my_cc}' installer -make CC='%{my_cc}' -C sample tidy +make CC='%{my_cc}' PYTHON=python3 clean +make CC='%{my_cc}' PYTHON=python3 installer %install rm -rf %{buildroot} -make CC='%{my_cc}' install-all \ +%ifarch ppc64le +make CC='%{my_cc}' PYTHON=python3 install \ INSTALLROOT=%{buildroot} BINDIR=%{_bindir} SBINDIR=%{_sbindir} \ LIBDIR=%{_libdir} DATADIR=%{_datadir} \ MANDIR=%{_mandir} INCDIR=%{_includedir} \ - TFTPBOOT=/opt/xcat/share/xcat/netboot/syslinux EXTLINUXDIR=/boot/extlinux -rm %{buildroot}/usr/share/syslinux/dosutil/copybs.com -rm %{buildroot}/usr/share/syslinux/dosutil/eltorito.sys -rm %{buildroot}/usr/share/syslinux/dosutil/mdiskchk.com -make CC='%{my_cc}' -C sample tidy + INSTALLSUBDIRS= +%else +make CC='%{my_cc}' PYTHON=python3 install \ + INSTALLROOT=%{buildroot} BINDIR=%{_bindir} SBINDIR=%{_sbindir} \ + LIBDIR=%{_libdir} DATADIR=%{_datadir} \ + MANDIR=%{_mandir} INCDIR=%{_includedir} +%endif +make CC='%{my_cc}' PYTHON=python3 netinstall \ + INSTALLROOT=%{buildroot} TFTPBOOT=/opt/xcat/share/xcat/netboot/syslinux +if make -n extbootinstall >/dev/null 2>&1; then \ + make CC='%{my_cc}' PYTHON=python3 extbootinstall \ + INSTALLROOT=%{buildroot} EXTLINUXDIR=/boot/extlinux; \ +else \ + mkdir -p %{buildroot}/boot/extlinux; \ +fi mkdir -p %{buildroot}/etc ( cd %{buildroot}/etc && ln -s ../boot/extlinux/extlinux.conf . ) @@ -94,9 +119,17 @@ rm -rf %{buildroot} %{_datadir}/syslinux/*.bin %{_datadir}/syslinux/*.0 %{_datadir}/syslinux/memdisk +%ifnarch ppc64le +%{_datadir}/syslinux/dosutil/* +%endif +%{_datadir}/syslinux/diag/* +%{_datadir}/syslinux/efi32 +%{_datadir}/syslinux/efi64 +%ifnarch ppc64le %files devel %{_datadir}/syslinux/com32 +%endif %files extlinux %{_sbindir}/extlinux @@ -255,7 +288,7 @@ fi * Wed Jul 12 2000 Prospector - automatic rebuild -* Thu Jul 06 2000 Trond Eivind Glomsrød +* Thu Jul 06 2000 Trond Eivind Glomsrod - use %%{_tmppath} - change application group (Applications/Internet doesn't seem right to me) diff --git a/xnba/ipxe-binutils-2.41-compat.patch b/xnba/ipxe-binutils-2.41-compat.patch new file mode 100644 index 0000000..ac625fc --- /dev/null +++ b/xnba/ipxe-binutils-2.41-compat.patch @@ -0,0 +1,274 @@ +diff -urN xnba-1.21.1/src/arch/x86/core/patch_cf.S xnba-1.21.1.mod/src/arch/x86/core/patch_cf.S +--- xnba-1.21.1/src/arch/x86/core/patch_cf.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/core/patch_cf.S 2026-03-02 16:08:49.051016121 -0300 +@@ -23,8 +23,8 @@ + FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text +- .arch i386 + .code16 ++ .arch i386 + + /**************************************************************************** + * Set/clear CF on the stack as appropriate, assumes stack is as it should +diff -urN xnba-1.21.1/src/arch/x86/core/stack.S xnba-1.21.1.mod/src/arch/x86/core/stack.S +--- xnba-1.21.1/src/arch/x86/core/stack.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/core/stack.S 2026-03-02 16:08:49.051016121 -0300 +@@ -1,6 +1,5 @@ + FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +- .arch i386 + + #ifdef __x86_64__ + #define STACK_SIZE 8192 +diff -urN xnba-1.21.1/src/arch/x86/core/stack16.S xnba-1.21.1.mod/src/arch/x86/core/stack16.S +--- xnba-1.21.1/src/arch/x86/core/stack16.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/core/stack16.S 2026-03-02 16:08:49.051016121 -0300 +@@ -1,6 +1,5 @@ + FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +- .arch i386 + + /**************************************************************************** + * Internal stack +diff -urN xnba-1.21.1/src/arch/x86/drivers/net/undiisr.S xnba-1.21.1.mod/src/arch/x86/drivers/net/undiisr.S +--- xnba-1.21.1/src/arch/x86/drivers/net/undiisr.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/drivers/net/undiisr.S 2026-03-02 16:08:49.051016121 -0300 +@@ -11,8 +11,8 @@ + #define PIC2_ICR 0xa0 + + .text +- .arch i386 + .code16 ++ .arch i386 + + .section ".text16", "ax", @progbits + .globl undiisr +diff -urN xnba-1.21.1/src/arch/x86/interface/pcbios/e820mangler.S xnba-1.21.1.mod/src/arch/x86/interface/pcbios/e820mangler.S +--- xnba-1.21.1/src/arch/x86/interface/pcbios/e820mangler.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/interface/pcbios/e820mangler.S 2026-03-02 16:08:49.051016121 -0300 +@@ -24,8 +24,8 @@ + FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text +- .arch i386 + .code16 ++ .arch i386 + + #define SMAP 0x534d4150 + +diff -urN xnba-1.21.1/src/arch/x86/interface/pxe/pxe_entry.S xnba-1.21.1.mod/src/arch/x86/interface/pxe/pxe_entry.S +--- xnba-1.21.1/src/arch/x86/interface/pxe/pxe_entry.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/interface/pxe/pxe_entry.S 2026-03-02 16:08:49.051016121 -0300 +@@ -26,7 +26,6 @@ + + #include + +- .arch i386 + + /**************************************************************************** + * !PXE structure +diff -urN xnba-1.21.1/src/arch/x86/prefix/bootpart.S xnba-1.21.1.mod/src/arch/x86/prefix/bootpart.S +--- xnba-1.21.1/src/arch/x86/prefix/bootpart.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/bootpart.S 2026-03-02 16:08:49.054349513 -0300 +@@ -6,7 +6,6 @@ + #define STACK_SIZE 0x2000 + + .text +- .arch i386 + .section ".prefix", "awx", @progbits + .code16 + +diff -urN xnba-1.21.1/src/arch/x86/prefix/dskprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/dskprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/dskprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/dskprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -25,7 +25,6 @@ + .equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ + + .org 0 +- .arch i386 + .text + .section ".prefix", "ax", @progbits + .code16 +diff -urN xnba-1.21.1/src/arch/x86/prefix/exeprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/exeprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/exeprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/exeprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -37,7 +37,6 @@ + #define PSP_CMDLINE_START 0x81 + + .text +- .arch i386 + .org 0 + .code16 + .section ".prefix", "awx", @progbits +diff -urN xnba-1.21.1/src/arch/x86/prefix/hdprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/hdprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/hdprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/hdprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -3,7 +3,6 @@ + #include + + .text +- .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 +diff -urN xnba-1.21.1/src/arch/x86/prefix/libprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/libprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/libprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/libprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -26,7 +26,6 @@ + + #include + +- .arch i386 + + /* Image compression enabled */ + #define COMPRESS 1 +diff -urN xnba-1.21.1/src/arch/x86/prefix/lkrnprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/lkrnprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/lkrnprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/lkrnprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -5,8 +5,8 @@ + #define BZI_LOAD_HIGH_ADDR 0x100000 + + .text +- .arch i386 + .code16 ++ .arch i386 + .section ".prefix", "ax", @progbits + .globl _lkrn_start + _lkrn_start: +diff -urN xnba-1.21.1/src/arch/x86/prefix/mbr.S xnba-1.21.1.mod/src/arch/x86/prefix/mbr.S +--- xnba-1.21.1/src/arch/x86/prefix/mbr.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/mbr.S 2026-03-02 16:08:49.054349513 -0300 +@@ -1,7 +1,6 @@ + FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text +- .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 +diff -urN xnba-1.21.1/src/arch/x86/prefix/mromprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/mromprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/mromprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/mromprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -42,8 +42,8 @@ + #include "pciromprefix.S" + + .text +- .arch i386 + .code16 ++ .arch i386 + + /* Obtain access to payload by exposing the expansion ROM BAR at the + * address currently used by a suitably large memory BAR on the same +diff -urN xnba-1.21.1/src/arch/x86/prefix/nbiprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/nbiprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/nbiprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/nbiprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -3,8 +3,8 @@ + #include + + .text +- .arch i386 + .code16 ++ .arch i386 + .section ".prefix", "ax", @progbits + .org 0 + +diff -urN xnba-1.21.1/src/arch/x86/prefix/nullprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/nullprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/nullprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/nullprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -2,7 +2,6 @@ + + .org 0 + .text +- .arch i386 + + .section ".prefix", "ax", @progbits + .code16 +diff -urN xnba-1.21.1/src/arch/x86/prefix/pxeprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/pxeprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/pxeprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/pxeprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -12,7 +12,6 @@ + #define PXE_HACK_EB54 0x0001 + + .text +- .arch i386 + .org 0 + .code16 + +diff -urN xnba-1.21.1/src/arch/x86/prefix/rawprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/rawprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/rawprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/rawprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -9,7 +9,6 @@ + FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text +- .arch i386 + .org 0 + .code16 + +diff -urN xnba-1.21.1/src/arch/x86/prefix/romprefix.S xnba-1.21.1.mod/src/arch/x86/prefix/romprefix.S +--- xnba-1.21.1/src/arch/x86/prefix/romprefix.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/romprefix.S 2026-03-02 16:08:49.054349513 -0300 +@@ -56,7 +56,6 @@ + + .text + .code16 +- .arch i386 + .section ".prefix", "ax", @progbits + .globl _rom_start + _rom_start: +diff -urN xnba-1.21.1/src/arch/x86/prefix/undiloader.S xnba-1.21.1.mod/src/arch/x86/prefix/undiloader.S +--- xnba-1.21.1/src/arch/x86/prefix/undiloader.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/undiloader.S 2026-03-02 16:08:49.054349513 -0300 +@@ -4,7 +4,6 @@ + + .text + .code16 +- .arch i386 + .section ".prefix", "ax", @progbits + + /* UNDI loader +diff -urN xnba-1.21.1/src/arch/x86/prefix/unlzma.S xnba-1.21.1.mod/src/arch/x86/prefix/unlzma.S +--- xnba-1.21.1/src/arch/x86/prefix/unlzma.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/unlzma.S 2026-03-02 16:08:49.054349513 -0300 +@@ -44,7 +44,6 @@ + */ + + .text +- .arch i486 + .section ".prefix.lib", "ax", @progbits + + #ifdef CODE16 +diff -urN xnba-1.21.1/src/arch/x86/prefix/usbdisk.S xnba-1.21.1.mod/src/arch/x86/prefix/usbdisk.S +--- xnba-1.21.1/src/arch/x86/prefix/usbdisk.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/prefix/usbdisk.S 2026-03-02 16:08:49.054349513 -0300 +@@ -3,7 +3,6 @@ + #include + + .text +- .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 +diff -urN xnba-1.21.1/src/arch/x86/transitions/liba20.S xnba-1.21.1.mod/src/arch/x86/transitions/liba20.S +--- xnba-1.21.1/src/arch/x86/transitions/liba20.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/transitions/liba20.S 2026-03-02 16:08:49.054349513 -0300 +@@ -24,7 +24,6 @@ + + FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + +- .arch i386 + + /**************************************************************************** + * test_a20_short, test_a20_long +diff -urN xnba-1.21.1/src/arch/x86/transitions/libkir.S xnba-1.21.1.mod/src/arch/x86/transitions/libkir.S +--- xnba-1.21.1/src/arch/x86/transitions/libkir.S 2021-09-23 14:29:02.000000000 -0300 ++++ xnba-1.21.1.mod/src/arch/x86/transitions/libkir.S 2026-03-02 16:08:49.054349513 -0300 +@@ -32,7 +32,6 @@ + #define BOCHSBP xchgw %bx, %bx + + .text +- .arch i386 + .section ".text16", "awx", @progbits + .code16 + diff --git a/xnba/xnba-undi.spec b/xnba/xnba-undi.spec index f85675a..4455b16 100644 --- a/xnba/xnba-undi.spec +++ b/xnba/xnba-undi.spec @@ -20,6 +20,13 @@ Patch2: ipxe-machyp.patch Patch3: ipxe-xnbaclass.patch Patch4: ipxe-dhcp.patch Patch5: ipxe-verbump.patch +Patch6: ipxe-binutils-2.41-compat.patch + +BuildRequires: make +BuildRequires: gcc +BuildRequires: binutils +BuildRequires: perl +BuildRequires: xz-devel %description The xCAT Network Boot Agent is a slightly modified version of iPXE. It provides enhanced boot features for any UNDI compliant x86 host. This includes iSCSI, http/ftp downloads, and iPXE script based booting. @@ -32,14 +39,15 @@ The xCAT Network Boot Agent is a slightly modified version of iPXE. It provides %patch3 -p1 %patch4 -p1 %patch5 -p1 +%patch6 -p1 %build rm -rf %{buildroot} cd src -make bin/undionly.kkpxe -make bin-x86_64-efi/snponly.efi +make NO_WERROR=1 bin/undionly.kkpxe +make NO_WERROR=1 bin-x86_64-efi/snponly.efi %install