Skip to content

Pin conda sysroot_linux-64 to a version that exists, and stop deleting committed lockfiles on a failed solve - #2374

Open
cwssemi wants to merge 3 commits into
ucb-bar:mainfrom
cwssemi:sysroot-glibc-availability
Open

Pin conda sysroot_linux-64 to a version that exists, and stop deleting committed lockfiles on a failed solve#2374
cwssemi wants to merge 3 commits into
ucb-bar:mainfrom
cwssemi:sysroot-glibc-availability

Conversation

@cwssemi

@cwssemi cwssemi commented Aug 26, 2026

Copy link
Copy Markdown

changelog:fixed
Please Backport

Related PRs / Issues:

fixes ucb-bar/chipyard#2373. ucb-bar/chipyard#2271 introduced the adaptation, ucb-bar/chipyard#2241 is the issue it was written for, and ucb-bar/chipyard#2174 is the source of both the 2.34 pin and the VCS constraint behind it.

Type of change:

  • Bug fix
  • New feature
  • Other enhancement

Impact:

  • RTL change
  • Software change (RISC-V software)
  • Build system change
  • Other

Contributor Checklist:

  • Did you set main as the base branch?
  • Is this PR's title suitable for inclusion in the changelog and have you added a changelog:<topic> label?
  • Did you state the type-of-change/impact?
  • Did you delete any extraneous prints/debugging code?
  • Did you mark the PR with a changelog: label?
  • (If applicable) Did you update the conda .conda-lock.yml file if you updated the conda requirements file?
  • (If applicable) Did you add documentation for the feature?
  • (If applicable) Did you add a test demonstrating the PR?
  • (If applicable) Did you mark the PR as Please Backport?

CI Help:
Add the following labels to modify the CI for a set of features.
Generally, a label added only affect subsequent changes to the PR (i.e. new commits, force pushing, closing/reopening).
See ci:* for full list of labels:

  • ci:fpga-deploy - Run FPGA-based E2E testing
  • ci:local-fpga-buildbitstream-deploy - Build local FPGA bitstreams for platforms that are released
  • ci:disable - Disable CI

Summary

scripts/build-setup.sh adapts the conda sysroot_linux-64 pin to the host, and the adaptation has
three separable problems. On Ubuntu 22.04 the first one makes a fresh ./build-setup.sh fail
outright, and the second deletes a committed lockfile on the way out.

What is wrong

1. The pin is set to a version that may not exist. The script writes the host glibc into the pin
verbatim, and conda-forge publishes sysroot_linux-64 for only six glibc versions — 2.12, 2.12.2,
2.17, 2.28, 2.34, 2.39. On a host with glibc 2.35 the solve cannot be satisfied:

The following packages are not available from current channels:
  - sysroot_linux-64=2.35
...
build-setup.sh: Build script failed with exit code 1 at step 1: Conda environment setup

That is Ubuntu 22.04, and Debian 12 (2.36) fails the same way. Note this also means
ucb-bar/chipyard#2271 does not fix the case it was written for: ucb-bar/chipyard#2241 came from
Ubuntu 20.04, whose glibc 2.31 is likewise unpublished, so that reporter moves from a build failure
to a solve failure.

2. The guard never guards. DEFAULT_GLIBC is read with awk -F= '{print $2}', which returns
everything after the first = — including the trailing comment on the pinned line:

committed line:  - sysroot_linux-64=2.34 # need to be close to system glibc for VCS compatibility
DEFAULT_GLIBC:  [2.34 # need to be close to system glibc for VCS compatibility]
SYS_GLIBC:      [2.35]

SYS_GLIBC is a bare version, so the two never compare equal — not even on a host at exactly the
pinned 2.34. The rewrite-and-relock branch is taken on every fresh clone, which means the
committed lockfiles are never what a fresh clone installs.

3. A failed solve deletes a committed file. generate-conda-lockfiles.sh does rm -rf $LOCKFILE
before the conda-lock that replaces it, so problem 1 leaves the tree missing a committed lockfile
and the user cannot simply retry — the fallback was destroyed by the attempt to improve on it. Both
lockfiles are regenerated in one loop, full first, so --use-lean-conda does not avoid it: the lean
setup loses the non-lean lockfile it was never going to install.

What this changes

1/3 — read the pin without its trailing comment. Strip #… before the compare, and move the
comment onto its own line with a note that the value is read by grep. Either alone fixes today; the
parser fix is what stops a re-added comment silently re-arming it.

2/3 — generate lockfiles to a temp path, move on success. With a trap to remove the temporary
file on any exit. A failed solve now leaves the committed lockfiles exactly as they were.

3/3 — clamp instead of assign. The sysroot is a build-target floor: it must be the host
glibc, and ≥ 2.33 for the VCS shared libraries that motivated the 2.34 pin
(ucb-bar/chipyard#2174) — it never had to equal the host glibc, and the comment on the pinned
line says as much. Select the greatest published sysroot ≤ host glibc, and warn instead of proceeding
silently when that lands below the VCS floor:

host glibc distro before after
2.31 Ubuntu 20.04, Debian 11 2.31 → solve fails 2.28 + VCS-floor warning
2.34 RHEL/Rocky 9 rewrite + relock 2.34, no-op
2.35 Ubuntu 22.04 2.35 → solve fails 2.34, no-op
2.36 Debian 12 2.36 → solve fails 2.34
2.39 Ubuntu 24.04, Fedora 40 rewrite + relock 2.39

A host that cannot be served at all — glibc older than 2.12, or no ldd — now keeps the committed
pin and warns, rather than writing an unusable value into the requirements file.

Version comparison is a small version_le in scripts/utils.sh, numeric per component so 2.9 sorts
below 2.10 and 2.12 below 2.12.2.

How it was tested

  • Parsing: the extraction returns 2.34 on the committed file, on the fixed file, and on a file
    where a trailing comment has been re-added.
  • Lockfile generation, with a stubbed conda-lock: on a forced solve failure both committed
    lockfiles are untouched and no temporary file is left behind. The current script, run as a control
    in the same harness, deletes the full lockfile.
  • The clamp, against the real code path with ldd stubbed: every row of the table above, plus
    glibc 2.11 and an absent ldd. Unstubbed on a glibc 2.35 host the whole block is now a no-op
    no rewrite, no .bak, no relock — so the committed lockfile installs as authored.
  • Lockfiles unaffected: content_hash_for_platform("linux-64") computed with conda-lock 2.5.7
    over the four requirement files is identical before and after this PR, so no lockfile regeneration
    is needed. (The same computation reproduces the hash committed in 1.14.0's lockfile from 1.14.0's
    requirement files, as a check on the method.)

Not tested: a real conda solve on a 2.31 or 2.39 host — I have neither. The "before" column for
those rows follows from the published sysroot list rather than from a run.

Reproducing problem 1 — one trap worth knowing: it does not reproduce by re-running in the same
tree, because the first run's sed rewrote the pinned line without its comment, after which the
comparison behaves as intended. Start from git checkout -- conda-reqs/.

build-setup.sh extracts the pinned sysroot version with

    grep -i "sysroot_linux-64=" conda-reqs/chipyard-base.yaml | awk -F= '{print $2}'

which returns everything after the first '=', so on the committed file
DEFAULT_GLIBC is

    2.34 # need to be close to system glibc for VCS compatibility

while SYS_GLIBC is a bare version such as 2.35. The two can never compare
equal -- not even on a host whose glibc is exactly the pinned 2.34 -- so the
mismatch branch is taken on every fresh clone: the pin is rewritten and the
lockfiles are regenerated unconditionally. The committed lockfiles are
therefore never what a fresh clone installs, which defeats the purpose of
committing them.

Fixed from both sides, because either alone leaves a trap:

  - strip a trailing comment before the compare, so the parser no longer
    depends on the line having none;
  - move the comment onto its own line, so the intent is visible to the next
    person editing the file, and note there that the value is read by grep.

This is the only line in chipyard-base.yaml that is both an inline trailing
comment and read by a shell parser rather than by conda, which is why no
other comment in the file causes trouble.
generate-conda-lockfiles.sh removes each committed lockfile before the solve
that is meant to replace it:

    rm -rf $LOCKFILE
    conda-lock ... --lockfile $LOCKFILE

Under `set -e` a failed solve therefore leaves the tree missing a file that
is committed to the repository, and the user cannot simply retry: the
fallback the script would have had -- install the committed lockfile -- was
destroyed by the attempt to improve on it. Recovering needs a `git checkout`
the error message gives no hint of.

Both lockfiles are regenerated in one loop, full before lean, so this is not
avoided by --use-lean-conda: the full solve runs first, and a lean setup that
fails there loses the non-lean lockfile it was never going to install.

Generate beside the target instead and move into place only once conda-lock
has succeeded, with a trap to remove the temporary file on any exit. The
temporary name keeps the .conda-lock.yml suffix that conda-lock requires.

Verified with a stubbed conda-lock: on a forced solve failure the committed
lockfiles are untouched and no temporary file is left behind, where the
current script deletes the full lockfile; on success both are replaced.
build-setup.sh writes the host glibc into the sysroot pin verbatim:

    sed -i.bak "s/^\([[:space:]]*-\s*sysroot_linux-64=\).*/\1$SYS_GLIBC/" ...

but conda-forge publishes sysroot_linux-64 for only six glibc versions --
2.12, 2.12.2, 2.17, 2.28, 2.34 and 2.39 -- and nothing checks that the value
written is one of them. On a host whose glibc is not published the solve
cannot be satisfied and step 1 fails outright:

    The following packages are not available from current channels:
      - sysroot_linux-64=2.35

That is Ubuntu 22.04, and also Debian 12 (2.36). It is not a rare corner:
ucb-bar#2241, the issue the adaptation was written for, was reported from Ubuntu
20.04, whose glibc 2.31 is likewise unpublished -- so that reporter is moved
from a build failure to a conda solve failure rather than being fixed.

The sysroot is a build-target floor, not a mirror of the host: it needs to be
<= the host glibc so the result runs, and >= 2.33 for the VCS shared
libraries that motivated the 2.34 pin in ucb-bar#2174. Equality was never required,
and the comment on the pinned line says as much.

Select the greatest published sysroot that is <= the host glibc, and warn
rather than silently proceeding when that falls below the VCS floor. Hosts
that cannot be served at all (glibc older than 2.12, or ldd absent) keep the
committed pin and get a warning instead of having an unusable value written
into the file.

  host glibc  distro                  before          after
  2.31        Ubuntu 20.04            2.31, fails     2.28 + VCS warning
  2.34        RHEL/Rocky 9            relock          2.34, no-op
  2.35        Ubuntu 22.04            2.35, fails     2.34, no-op
  2.36        Debian 12               2.36, fails     2.34
  2.39        Ubuntu 24.04            relock          2.39

This builds on the parse fix in the preceding commit and wants to land with
it: without that, DEFAULT_GLIBC still carries the trailing comment, so the
comparison never matches and the lockfiles are regenerated on every clone
even when the clamp selects the value already pinned.

version_le compares dotted versions numerically, so 2.9 sorts below 2.10 and
2.12 below 2.12.2. Every row above was exercised against the real code path
with ldd stubbed, along with glibc 2.11 and an absent ldd.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant