public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Michel Lind <salimma@fedoraproject.org>
To: git-commits@fedoraproject.org
Subject: [rpms/folly-rpm-macros] rawhide: Add AGENTS.md: how to maintain the getdeps-based packages
Date: Fri, 25 Sep 2026 08:19:54 GMT	[thread overview]
Message-ID: <179032439495.1.14322170332597261426.rpms-folly-rpm-macros-eeb59a77cc6c@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/folly-rpm-macros
            Branch : rawhide
            Commit : eeb59a77cc6cfb5a48864d6b54d8a57986db6ab3
            Author : Michel Lind <salimma@fedoraproject.org>
            Date   : 2026-09-25T09:14:15+01:00
            Stats  : +314/-0 in 1 file(s)
            URL    : https://src.fedoraproject.org/rpms/folly-rpm-macros/c/eeb59a77cc6cfb5a48864d6b54d8a57986db6ab3?branch=rawhide

            Log:
            Add AGENTS.md: how to maintain the getdeps-based packages

- New AGENTS.md distilling what maintaining cachelib, mcrouter and future
  getdeps packages needs: the static-only model, the spec anatomy and
  version scheme, the vendoring and license workflows, the build
  environment, and the upstream fixes each package carries

Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Michel Lind <salimma@fedoraproject.org>

---
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..ab60bfb
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,314 @@
+# Maintaining getdeps-based Fedora packages
+
+Working notes for the packages built with Meta's `getdeps.py` through the
+`%getdeps_*` macros in this repository: cachelib and mcrouter today, wdt,
+proxygen-derived tools or others later. Written for whoever (or whatever)
+picks this up next; it records the decisions and the traps, not just the
+commands. Package-specific details live in each package's README.
+
+What is true right now (which package is where, which patches have landed,
+what the next tag needs) is deliberately not here but in `../STATUS.md`,
+outside this repository: this file should only change when the macros do.
+
+## 1. The model in one paragraph
+
+Each package builds its upstream project with the project's own
+`build/fbcode_builder/getdeps.py`. Dependencies Fedora ships come from the
+system (`BuildRequires` are generated from the getdeps manifests); the rest of
+the Meta stack (folly, fizz, wangle, mvfst, fbthrift, plus liboqs and whatever
+else the manifests cannot map to a Fedora package) is vendored as `Source1`, a
+tarball made by `getdeps.py vendor` and recorded in
+`vendor/getdeps-vendor.txt`. Everything is linked statically into the
+project's executables; the package ships executables only, no libraries and
+no `-devel`. bundled() Provides, versioned, are generated from the vendor
+manifest; the License tag covers the vendored code and is verified at build
+time.
+
+Why static and no `-devel` (decided 2026-09-18, after trying the alternative):
+a shared-library build links, but every `libfoo_*.so` gets its own copy of the
+stack and the copies re-register the same gflags and folly singletons at
+startup, which gflags treats as fatal. A `-devel` would also need folly and
+fbthrift headers, which Fedora does not ship. So `%getdeps_install` prunes
+libraries, headers, CMake and pkg-config files unconditionally, and there is
+no shared-library mode in the macros.
+
+## 2. Repositories and where changes go
+
+| Thing | Where | Notes |
+|---|---|---|
+| macros, attr/prov, license helper | this repo (`folly-rpm-macros`) | version 46 = first with `%getdeps_*` |
+| package specs | `cachelib`, `mcrouter` dist-git (locally `~/src/fedora/pkgs-meta/folly/<pkg>`) | each has `vendor.sh`, `snapshot.sh`, `getdeps-vendor-licenses.toml`, README |
+| getdeps itself | `build/fbcode_builder/` inside every Meta project's GitHub export; canonical copy in fbsource | CacheLib's copy is the one changes have been developed against |
+| upstream fixes | a branch on Michel's fork of the project, signed off, evidence in the commit message; **no GitHub PR** unless asked | fbthrift maintainers close PRs; Meta engineers import diffs by hand: `curl -L https://github.com/michel-slm/<repo>/commit/<hash>.patch \| hg import -` |
+| Fedora review | spec + SRPM URLs in the bug, koji scratch build as evidence | no dist-git, no `fedpkg new-sources` until the package is (un)retired |
+
+Running the license helper from a checkout of this repository leaves a
+`__pycache__/` behind (the script has no `.py` suffix, so the file is named
+`getdeps_vendor_license_checkcpython-3XX.pyc`); it is not in `.gitignore` and
+has been committed by accident once. Check `git status` before `git add -A`.
+
+Commit conventions: ask before committing, `git commit -s`, `Assisted-by:
+Claude Code:<model-id>` trailer in Fedora repos (no Co-Authored-By), amend
+unpushed commits with `--date=now`. rpmautospec turns the subject and any
+`- ` lines of the body into the changelog; prose below is for details.
+
+## 3. Anatomy of a spec
+
+Read `cachelib.spec` as the reference. The pieces, in order:
+
+- **Version.** Upstream cuts weekly tags `vYYYY.MM.DD.NN`. cachelib has an
+  upstream major (`kCachelibVersion`), so its Version is
+  `<major>.<tag without v>` (`19.2026.09.14.00`); mcrouter has none, so just
+  the tag. A snapshot past the tag appends `^<distance>.<shortcommit>` as
+  `git describe` reports it: the guidelines' `<number>.<revision>` snapshot
+  form (`19.2026.09.14.00^38.ee4c153`). Never lead the snapshot field with
+  the distance without the tag before it: the distance resets at every tag.
+  Cleared with FPC members on 2026-09-24 (Carl George suggested the dotted
+  join, citing ImageMagick's four-part version).
+- **Globals.** `basetag`, and for a snapshot `commit` + `commits` as printed by
+  `./snapshot.sh [ref]` (or from a clone: `git describe --tags --match
+  'v20??.??.??.??' <ref>` gives all three values). `archive_ref`/`archive_dir`
+  follow from them. Do not use the forge macros: they take Source0 from
+  `%tag` whenever it is defined, and they are a nuisance for renaming.
+- **Sources.** `Source0: %{url}/archive/%{archive_ref}/%{name}-%{version}.tar.gz`
+  (GitHub ignores the last path component, so the file is named like
+  Source1). `Source1: %{name}-%{version}-vendor.tar.xz` from `./vendor.sh`.
+  `Source2: getdeps-vendor-licenses.toml`. Keep xz: measured 2026-09-24,
+  zstd -19 saves nothing worth diverging from the Rust/Go convention.
+- **License.** `SourceLicense: <project license>` gated on
+  `%if !0%{?rhel} || 0%{?rhel} >= 10` (rpm 4.19+), then `License:
+  %{shrink: ... }`, one identifier per line, project license first, with the
+  per-project breakdown as comments above. See section 5 for how to derive it.
+- **Full-check gate**, plain rpm on purpose (it runs when the SRPM is built,
+  without this package installed):
+  ```
+  %global vendor_checked_sha512 <sha512 of Source1 from the sources file>
+  %if "%(sha512sum %{SOURCE1} 2>/dev/null | cut -c1-128)" == "%{vendor_checked_sha512}"
+  %bcond_with license_full_check
+  %else
+  %bcond_without license_full_check
+  %endif
+  ```
+  (`%bcond_with`/`%bcond_without`: EPEL 9's rpm lacks the `%bcond name value`
+  form.)
+- **Patches.** See section 8; each needs a comment naming its upstream
+  reference.
+- **`ExclusiveArch: x86_64 aarch64 ppc64le`** in the spec itself. Anything the
+  SRPM step needs (ExclusiveArch, bconds) cannot come from this package:
+  `-srpm-macros` only work when preloaded into every SRPM buildroot.
+- **`BuildRequires: folly-rpm-macros >= 46`** plus `gcc-c++` (or clang under
+  the `toolchain_clang` bcond). Everything else comes from
+  `%getdeps_generate_buildrequires` and `%getdeps_vendor_license_buildrequires`.
+- **Sections:**
+  ```
+  %prep
+  %autosetup -n %{archive_dir} -a1 -p1
+  %getdeps_vendor_prune -c %{SOURCE2}
+  # plus rm -rf of anything else unneeded and oddly licensed (mcrouter: m4/)
+
+  %build
+  %getdeps_vendor_license_check -c %{SOURCE2} -L %{?with_license_full_check:-f}
+  %getdeps_build %{?with_check:-t}
+
+  %install
+  %getdeps_install
+  %getdeps_vendor_license_install -c %{SOURCE2}
+
+  %check
+  %if %{with check}
+  %getdeps_test
+  %endif
+
+  %files -f %{getdeps_vendor_license_filelist}
+  ```
+  The license check sits at the start of `%build`, not in `%prep`: it only
+  needs the unpacked trees, a wrong tag then fails in minutes instead of
+  after the compile, and `%prep` runs once per dynamic-BuildRequires pass,
+  before the detector is installed.
+- **Knobs** (`%global` before use): `getdeps_project`, `getdeps_vendor_dir`,
+  `getdeps_scratch_dir` (defaults under `%_builddir`; never `/tmp`, a tmpfs),
+  `getdeps_job_weight_mib` (3072, fed to `%limit_build`),
+  `getdeps_extra_cmake_defines` (JSON *contents* without braces, applies to
+  every project getdeps builds; cachelib uses it for `LIB_INSTALL_DIR`,
+  `CACHELIB_MAJOR_VERSION`, `CONFIGS_INSTALL_DIR`; mcrouter for
+  `MCROUTER_PACKAGE_VERSION`, which upstream's CMake conversion left at
+  "0.1.0-dev").
+
+## 4. Updating a package to a new weekly tag
+
+1. `basetag` to the tag (comment out `commit`/`commits`). For a snapshot,
+   paste the lines `./snapshot.sh <ref>` prints. Check the project's major if
+   it has one.
+2. Re-examine the patches: drop what the new pinned revisions contain
+   (`vendor/<project>` trees follow `build/deps/github_hashes` when the
+   project ships pins; mcrouter's export gained them on 2026-09-24).
+3. `./vendor.sh` with the generated BuildRequires installed on the host
+   (`python3 build/fbcode_builder/getdeps.py --allow-system-packages
+   install-system-deps --recursive <project>` from the unpacked source lists
+   them), so getdeps vendors exactly what the build will look for. It keeps
+   its temp and scratch on disk, never tmpfs. A tarball vendored with fewer
+   packages installed still builds, it is only larger. Set `GETDEPS_PROJECT`
+   when the getdeps project is not the package name. The manifest it writes
+   has three columns: project, commit (or sha256 for tarballs), version.
+4. Derive the License tag (section 5), update it if the report changed.
+5. Build with `mock -r <target>-getdeps` (section 7). The full license pass
+   runs because `vendor_checked_sha512` no longer matches; on success copy
+   the new tarball's sha512 from `sources` (after `fedpkg new-sources`) into
+   the spec so later builds, koji included, take the cheap path.
+6. Check the result: `rpm -qp --provides <rpm> | grep bundled` shows every
+   vendored project with a version; `rpm -qlp` shows executables, docs and
+   the license tree only; run the executables (`--help` at least; mcrouter
+   starts with an inline `--config-str`, cachebench replays the shipped test
+   configs).
+7. Commit (subject + `- ` bullets = changelog), push, koji: folly-rpm-macros
+   first whenever the spec needs a newer one.
+
+Tuesday is when the weekly tags appear; keep the packit/canary cadence weekly,
+a full build is 45 minutes warm and hours cold.
+
+## 5. Licenses: the model and the tooling
+
+Two lists in `getdeps-vendor-licenses.toml` draw the lines:
+
+- `[getdeps] prune_directories`: code the build does not need at all
+  (fbthrift's Go bindings). `%getdeps_vendor_prune` deletes it in `%prep`:
+  never scanned, never declared, cannot be compiled by accident, invisible to
+  an outside audit such as fedora-review's licensecheck.
+- `[licensing] exclude_directories`: present at build time but not part of
+  the shipped binaries, and *needed* to configure or by the test build: each
+  vendored project's own `build/fbcode_builder/CMake` modules (fizz, wangle,
+  mvfst, fbthrift include `FBBuildOptions` from there; getdeps gives them no
+  other module path), the projects' `cmake/` helper directories (NCSA
+  `CheckAtomic.cmake`), liboqs's `tests/` (its CMakeLists adds it
+  unconditionally) and the liboqs algorithm families `OQS_MINIMAL_BUILD`
+  leaves out. Both scans skip these.
+- Everything else must be covered by the License tag.
+
+Two scanners, because license *files* are not enough: go-vendor-tools reads
+`LICENSE` files (askalono), licensecheck reads every file's header. The
+header scan is what found folly's zlib-licensed `hash/detail/Crc32cDetail.cpp`,
+mvfst's Boost-licensed `quic/common/third-party/{expected,optional}.h`,
+liboqs's MIT-CMU `brg_endian.h` and public-domain AES code, all compiled in.
+To derive the tag, from the unpacked source root with the vendor tarball
+extracted:
+
+```
+awk '{ print "# " $1 " v" $2 }' vendor/getdeps-vendor.txt > vendor/modules.txt
+/usr/lib/rpm/getdeps_vendor_license_check --config getdeps-vendor-licenses.toml --report \
+    --expression "$(go_vendor_license --config getdeps-vendor-licenses.toml report expression -L)"
+```
+
+It prints the expression the check accepts and the files behind each
+identifier the license files alone would not show. `-L` tolerates a project
+whose license file sits in a versioned subdirectory (liboqs, sparse-map).
+
+Details that will come back:
+
+- The check macro's own comment says "for `%check`"; the specs deliberately
+  run it at the start of `%build` instead, see section 3.
+- The full licensecheck pass takes about five minutes and a lot of memory
+  (fedora-review has OOMed laptops); hence the sha512 gate. A failing check
+  ends with "the tag needs: ..." so the fix is a copy.
+- licensecheck is asked for SPDX names but prints its internal name where
+  SPDX has none: the generic `GPL-2` (no only/or-later wording) and
+  `public-domain`. The helper translates those (`GPL-2.0-only`,
+  `LicenseRef-Fedora-Public-Domain`).
+- liboqs: only Kyber/ML-KEM are built (`OQS_MINIMAL_BUILD` in the manifest);
+  the pqcrystals "Public Domain or Apache 2.0" notices are one file in twelve
+  variants, pinned by sha256 in the config as `CC0-1.0 OR Apache-2.0`; the
+  aarch64 Kyber code is CC0-1.0, as in Fedora's own liboqs tag.
+- `[[licensing.licenses]]` overrides exist for license *files* the detector
+  cannot classify; do not use them to widen a file's expression for per-file
+  licenses, the header scan covers that.
+- Everything under `%{_defaultlicensedir}/<pkg>/vendor/...` is installed by
+  `%getdeps_vendor_license_install`, plus `getdeps-vendor.txt` itself.
+
+## 6. bundled() Provides
+
+`getdeps_vendor.attr` runs `getdeps_vendor.prov` over the installed
+`getdeps-vendor.txt`: `bundled(<name>) = <version>` per line, unversioned
+only when getdeps could not determine a version (the guidelines allow it).
+The name is mapped to the Fedora package name where it differs
+(`sparsemap` -> `tsl-sparse-map`; extend the awk table when a new case
+appears). Versions come from `getdeps.py vendor`: `git describe` against the
+nearest tag for git checkouts (deepening the shallow clone as needed), the
+version in the download URL for archives. That getdeps change is on
+michel-slm/CacheLib `getdeps-vendor-record-commit` (94bfa232) and carried
+as a patch in both packages until it lands.
+
+## 7. Build environment (this laptop, mock)
+
+- **ccache**: mock's plugin, 20G, `CCACHE_BASEDIR=/builddir/build/BUILD` so
+  objects are reused across package versions (accepted cost: debuginfo of a
+  cross-version hit keeps the first build's path, fine for local builds, not
+  for official ones, which run in koji anyway). getdeps passes ccache as the
+  CMake C++ launcher on its own; C files bypass it.
+- **Use the dedicated configs** `~/.config/mock/fedora-{rawhide,45,44}-aarch64-getdeps.cfg`
+  (`mock -r fedora-rawhide-aarch64-getdeps`). They only set
+  `config_opts['root']`, so chroot, root cache and ccache live under
+  `-getdeps`, isolated from other builds. `--uniqueext` does **not** do this:
+  the ccache plugin renders its directory from `{{root}}` before the suffix
+  is appended, so a `--uniqueext` build silently uses (and fills) the plain
+  root's cache.
+- **Never run a second mock command against a root while a build is running
+  in it** (not even `--copyin`): mock kills "leftover" processes and can
+  leave the bootstrap chroot's `/dev` busy. Stage files first. Read logs
+  through `/var/lib/mock/<root>/root/...` with sudo instead.
+- **Background builds**: a process started inside `mock --chroot` dies when
+  that mock exits; keep the mock process itself alive (run it in the
+  background) rather than detaching inside.
+- **Aborted or OOM-killed builds** leave stale bind mounts;
+  `~/src/sourcehut/michel-slm/fedora-packaging-scripts/mock-unmount.sh <root>`
+  clears them before the next mock command.
+- **Memory**: systemd-oomd kills the whole tmux scope at 90% swap; four
+  compilers at 3 to 4 GiB each plus a full swap is enough. `--define
+  '_smp_build_ncpus 3'` or drained swap. `%limit_build` prints `-jN` only
+  when memory is the tighter limit (it never sets `_smp_build_ncpus`); the
+  macros read its output, do not use it as a statement.
+- **Disk**: about 25 GB peak per build (8.4 GB installed deps, fbthrift's
+  build tree the largest transient); `/tmp` is tmpfs and getdeps defaults its
+  scratch there unless told otherwise; `vendor.sh` uses `/var/tmp`.
+  `mock --scrub=chroot` on roots not in use, `--scrub=dnf-cache`, and the
+  fedrq cache are the usual reclaims.
+- **Resuming after a failure in `%install` or later**: keep the chroot
+  (`--no-clean`) and, inside it, `rpmbuild -bi --short-circuit` then
+  `rpmbuild -bb --short-circuit`. `-bb --short-circuit` alone skips
+  `%install` and packages a stale BUILDROOT.
+- Result directories append to an existing `build.log`; delete it or read
+  from the last `Executing(%prep)`.
+- After a successful shared-library or static build, check what was
+  produced: `LD_PRELOAD=<lib> /bin/true`, `readelf -lW` (more than one LOAD,
+  GNU_RELRO present), zero "RWX permissions" warnings in the log. That is
+  how the fbthrift `.rodata` bug was found.
+
+## 8. Carried patches
+
+Each package carries upstream fixes its pinned revisions do not have yet, in
+three kinds: to the vendored trees (`vendor/<project>/...`, which `vendor.sh`
+skips when vendoring and `%autosetup` applies), to `build/fbcode_builder`
+(getdeps fixes, applied both when vendoring and in `%prep`), and to the
+project itself. Every patch gets a comment naming the upstream reference, and
+is dropped when a pin moves past the landed commit.
+
+Which patch is at which stage upstream changes constantly, so that list is
+not kept here: see `../STATUS.md`, outside this repository, so that landing a
+patch never means touching folly-rpm-macros.
+
+## 9. EPEL (deferred; Fedora first)
+
+EPEL 10 detects as `centos_stream` so the distro_family work is not needed
+there; blockers are `lz4-static` (absent on EL10, manifest selector), the
+libraries EL vendors that Fedora maps (fmt, gflags, glog, googletest,
+benchmark, fast_float: map them for EL10 and verify by compiling on c10s,
+glog 0.3.5 being the doubtful one), `tsl-sparse-map-devel` in EPEL 10, and
+branches for both packages. EPEL 9 additionally needs the RHEL
+distro_family selectors, gcc-toolset-14 (from the manifest) and a
+`%limit_build` fallback, which belongs in epel-rpm-macros. `SourceLicense`
+is already gated on rpm 4.19.
+
+## 10. Tests
+
+Off by default (`%bcond_with check`); `%getdeps_build -t` builds only the
+top-level project's tests (getdeps needs `--test-dependencies` for the
+stack's, which the macros never pass) and `%getdeps_test` runs them. First
+run will show which need SysV/POSIX shm or NUMA that a chroot lacks.

                 reply	other threads:[~2026-09-25  8:19 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=179032439495.1.14322170332597261426.rpms-folly-rpm-macros-eeb59a77cc6c@fedoraproject.org \
    --to=salimma@fedoraproject.org \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox