Fix installation and build on current Ubuntu and arm64 - #264
Conversation
These headers declare uint64_t, uint32_t and friends without including <cstdint>, relying on some other header to pull it in transitively. libstdc++ stopped providing it through unrelated headers, so from GCC 13 on the build fails with "'uint64_t' does not name a type". The rule applied here is include-what-you-use: every header in the core tree that names a fixed-width integer type includes <cstdint> itself, whether or not it happens to compile today, so that compiling does not depend on what other headers happen to pull in. ecosystem/ is a separate Bazel workspace excluded via .bazelignore and is not touched here; its one affected header is noted in a separate issue.
Abseil 20211102.0 declares fixed-width integer types without including
<cstdint>, so it stops compiling from GCC 13 on:
absl/strings/internal/str_format/extension.h:34:33: error:
enum class FormatConversionChar : uint8_t;
found ':' in nested-name-specifier, expected '::'
20240722.2 builds with the Bazel pinned in .bazelversion. 20250127.0 and later
select on @rules_cc//cc/compiler, which the rules_cc bundled with Bazel 6.0.0
does not provide, and fail during analysis; releases between the two were not
tested.
The tree's only direct Abseil labels are absl/status and absl/status:statusor,
and the bump needed no ResilientDB source changes.
The first zlib URL now 404s because zlib.net serves only the current release
from the top level and moves older ones under fossils/. The build survives on
the bazel-mirror fallback but logs a download failure on every fetch.
zlib's ./configure normally rewrites zconf.h to set this. Bazel compiles the
sources directly, so zconf.h skips <unistd.h> and the gz* sources call
write(), close() and lseek() undeclared:
external/net_zlib_zlib/gzwrite.c:89:20: error: implicit declaration of
function 'write'; did you mean 'fwrite'?
GCC 14 turned implicit function declarations into errors, and the -w already in
this file does not suppress them.
Upgrading zlib does not help: 1.2.12 and 1.3.2 both fail without the define and
both build with it, because the guard in zconf.h depends on the build
configuration rather than on the release. The version pin is therefore left
alone.
getopt_long returns int and signals the end of the options with -1, but the
result was stored in a char. GCC on Linux/AArch64 defaults plain char to
unsigned, so -1 became 255, the loop condition never became false, and the
tool spun at 100% CPU inside getopt_long without ever reaching the network
code:
#0 getopt_long (argc=11, ..., options="h", ...) at ./posix/getopt1.c:33
#1 main ()
Every option-style invocation hung on arm64 as a result, which is the interface
the README documents, while the replicas reported "socket recv:0". The older
positional get/set path returns before this loop is reached and is unaffected.
GCC defaults char to signed on x86-64, which is why this was never seen there.
The only arm64 build in CI is the post-merge Docker image job, which compiles
this tool on an Ubuntu 20.04 base and never runs it, so the spin was invisible
there too.
The three sibling tools in this repository already declare this variable as
int.
protobuf was fetched with git_repository(tag = "v3.10.0"). A tag can be moved to a different commit upstream, which would silently change what is built and is not detectable from the workspace. It is now an http_archive of the release tarball with a sha256, which also removes the need for a git binary during the fetch. hedron_compile_commands, duckdb, pybind11_bazel and rules_boost had no sha256 at all, so their contents were trusted on every fetch. duckdb even carried a comment asking for the hash to be filled in. bazel_skylib was never declared by this project. Abseil needs config_setting_group from lib/selects.bzl, which the skylib that protobuf 3.10 pins does not provide, so the build only worked because rules_foreign_cc_dependencies() happens to register 1.0.3 before protobuf_deps() runs. 1.0.3 is pinned explicitly here: it is the version that already wins today, so resolution is unchanged, only deliberate.
On a current Ubuntu several steps failed but the script kept going, and the final apt-get succeeded so the whole run still exited 0; the failure only surfaced later as a confusing build error. It now sets -eu and stops at the first problem. Bazel was installed from the Bazel apt repository, whose Release file declares "Architectures: amd64" and has no arm64 index. On arm64 apt selected bazel:amd64 and then could not satisfy g++:amd64. The fallback path compiled Bazel from source, which fails on GCC 14+ because Bazel 6.0.0 vendors a zlib that calls write()/close() undeclared. Bazelisk replaces both: it is pinned to v1.29.0 and verified against the SHA-256 published with that release, then reads .bazelversion and fetches the same Bazel 6.0.0 as before, so the pinned version is unchanged. This is the approach Docker/Dockerfile_mac already uses. Other fixes: - apt-key is deprecated on 22.04 and 24.04 and no longer exists on 26.04. The keyring line above it already does the same job, so the call is gone. - The script is run as root in containers and by cloud-init, where sudo is usually absent. sudo is now used only when not already root. - build-essential was installed only on the fallback path, so a normal run never got a C++ compiler. - python3.10-dev was hardcoded; python3-dev already follows the system. - hooks/pre-push does not exist in this repository, so the unconditional rm and ln -s left a dangling symlink. The link is now made only if the hook exists. - The shebang was below the licence header and therefore inert, while ecosystem/deployment/ansible runs this script with sh. It is now #!/bin/sh on line 1 and the script is POSIX (shellcheck -s sh and checkbashisms are clean). - Bazelisk is downloaded as the invoking user into a temporary directory and verified before sudo install writes it, so an interrupted transfer cannot leave a truncated binary in /usr/local/bin. - The script cds to its own directory so Bazelisk reads this checkout's .bazelversion rather than whatever is in the caller's working directory. - A trapped signal whose handler does not exit lets the script continue: the old trap cleaned up on INT/TERM and then carried on. Signals now exit. Note on architecture support: Bazelisk publishes only linux-amd64 and linux-arm64, so the script now stops with a clear message on anything else. The previous source-compilation fallback was not architecture-limited in principle, though it does not complete on any tested release.
|
For release planning context: #245, which this PR closes, is on the Release v1.13.0 Planning milestone, but this PR is not attached to it. @msadoghi since you added #245 to that milestone, would you like this tagged for v1.13.0 as well? I don't have permission to set it myself. Happy to split the PR up if the six commits are easier to review separately, or to rebase if anything has moved underneath it. |
|
Thanks for helping trigger the CI! |
Added to our next milestone as requested. |
The Abseil bump in 4efc7cd (20211102.0 -> 20240722.2) deletes absl/base/internal/thread_annotations.h, the header that defined the unprefixed thread-safety annotations (GUARDED_BY on line 64 of the 20211102.0 release). block_manager.h used GUARDED_BY while including no thread-annotations header of its own -- its only Abseil include was absl/status/status.h -- so the macro reached it transitively via cord.h -> cordz_info.h -> absl/base/thread_annotations.h. In 20240722.2 that last header no longer defines the unprefixed spelling, so the annotation is parsed as a declaration: ./platform/consensus/ordering/poc/pow/block_manager.h:96:52: error: ISO C++ forbids declaration of 'GUARDED_BY' with no type The current spelling is ABSL_GUARDED_BY, from absl/base/thread_annotations.h; both expand to __attribute__((guarded_by(x))). That header still arrives transitively, so the rename alone would compile, but it is now included directly and the target takes the @com_google_absl//absl/base:core_headers dependency that owns it, so the annotation no longer rides on Abseil's internal include graph. This was the only live use in the core tree; the three remaining occurrences of these macros sit inside comments. //service/kv:kv_service does not depend on poc/pow, so building that target alone -- as this branch's earlier checks did -- never reached the failure; both PR-gating workflows build `...`, where it broke. With this commit `bazel build ...` completes on ubuntu-24.04 with gcc-11 11.5.0 and Python 3.10.20 on amd64, and in the same environment on arm64.
|
Pushed What broke: the Abseil bump in this PR (20211102.0 -> 20240722.2) deletes I missed it locally because my earlier checks built is empty — that target never reaches the PoW code. Both PR-gating workflows build Verified before pushing this time:
The two Actions runs are this repo's unmodified On the Same counts as the run on this PR. It is pre-existing and unrelated — this PR does not touch |
Closes #245
The problem
The README states
OS Requirements: Ubuntu 20+and tells users to run./INSTALL.sh. I could not get that to work on Ubuntu 24.04 on eitherarchitecture, nor on 26.04/arm64, and on arm64 it fails before Bazel is
installed at all.
Reproduction, on a machine that has
sudo,curlandwget, which is what anormal Ubuntu install has:
On
masterthis prints a series of failures and then:Several commands failed, but the script has no
set -eand kept going. Thefinal
apt-get install python3-devsucceeded, so the run exits 0 and reportssuccess. Bazel is not installed. The failure surfaces later as a confusing
error from a build that cannot start.
What actually breaks
1. Bazel cannot be installed on arm64.
INSTALL.shadds the Bazel aptrepository with
[arch=amd64]. That repository'sReleasefile declaresArchitectures: amd64and has no arm64 index, so apt selectsbazel:amd64andthen cannot satisfy
g++:amd64:The script then falls back to compiling Bazel 6.0.0 from source, and that fails
too, for a different reason on each release. Bazel 6.0.0 vendors dependencies
about as old as this repository's own, so it hits the same problems:
On 24.04 (GCC 13), while compiling Bazel's bundled Abseil:
On 26.04 (GCC 15), while compiling Bazel's bundled zlib:
Two smaller things in the same run:
apt-keyis deprecated on 22.04 and 24.04and no longer exists on 26.04, where it reports
apt-key: command not found(the keyring line above it already does the same job); and
python3.10-devishardcoded, so on 24.04 the run also reports
E: Unable to locate package python3.10-dev.2. Abseil 20211102.0 does not compile with GCC 13+. It declares fixed-width
integer types without including
<cstdint>. This is the same failure thebundled copy inside Bazel produces above.
3. 53 headers in the core tree have the same problem. They name
uint64_tand friends and rely on another header to provide
<cstdint>. (ecosystem/isa separate Bazel workspace excluded via
.bazelignore; its one affected headeris covered in the GraphQL issue below.)
4. The vendored zlib misses
<unistd.h>../configurenormally setsZ_HAVE_UNISTD_Hinzconf.h; Bazel compiles the sources directly, so thegz*sources callwrite()/close()/lseek()undeclared. GCC 14 turned thatinto an error.
5.
kv_service_toolsspins forever on arm64.getopt_longreturnsintand signals the end of the options with
-1, but the result was stored in achar. GCC on Linux/AArch64 defaults plaincharto unsigned, so-1became255, the loop condition never became false, and the tool sat at 100% CPUinside
getopt_longwhile the replicas reportedsocket recv:0:This affects every option-style invocation, which is the interface the README
documents. The older positional
get/set/getvalues/getrangepath returnsbefore this loop and is unaffected. GCC defaults
charto signed on x86-64,which is why it was never seen there.
Why these fixes and not the obvious ones
zlib is not upgraded. The obvious fix is a version bump, and it does not
work. Compiling
gzwrite.cwith GCC 15:-DZ_HAVE_UNISTD_HThe guard in
zconf.hdepends on the build configuration rather than on therelease, so the pin is left alone and the define is added instead.
Abseil is not moved to the newest release. 20250127.0 and later select on
@rules_cc//cc/compiler, which the rules_cc bundled with Bazel 6.0.0 does notprovide; the build fails during analysis. 20240722.2 built successfully in the
matrix below. The tree's only direct Abseil labels are
absl/statusandabsl/status:statusor(common/BUILD:21), and the bump needed no ResilientDBsource changes.
Bazelisk is not a new idea.
Docker/Dockerfile_mac:39already installsBazel this way for arm64, though unpinned. It reads
.bazelversion, so thepinned Bazel 6.0.0 is unchanged; only how it is obtained differs. Here it is
pinned to v1.29.0 and verified against the SHA-256 published with that release
before being installed.
All 53 headers get
<cstdint>, not only the ones that break today. Everychanged header directly names a fixed-width integer type. Some fail now and
others currently compile through a transitive include; adding the direct
include makes that explicit rather than dependent on what other headers happen
to pull in. Taking each header's own
#include <...>lines and compiling themagainst a
uint64_tdeclaration, 43 of the 53 cannot obtain the type that wayand 10 can.
Verification
Each row was run from a clean container:
./INSTALL.sh, thenbazel build //service/kv:kv_service.22.04 is the oldest release tested here; the README also lists 20.04, which I
did not test. Every run reports the Bazel 6.0.0 pinned in
.bazelversion.The path the README describes also completes on 26.04/arm64, which it did not
before:
shellcheck -s sh(0.10.0),checkbashismsanddash -nare clean onINSTALL.sh.clang-format --dry-run --Werrorreports no header that wasclean on
masterand is not clean here.Two additions to the apt list deserve a word:
ca-certificatesis required forthe curl download of Bazelisk, and
default-jre-headlesspluszip/unzipare what
dev/check-licenseneeds to run Apache RAT — Bazel itself ships itsown JDK and does not need them.
Why CI did not catch any of this
build.yml:45andut.yml:45pin Python 3.10, andbuild.yml:62andut.yml:59pinCC=gcc-11, which is what Ubuntu 22.04 ships — so the GCC13/14 failures cannot appear in the PR-gating workflows. The one arm64 build
that does exist is
build-push.yml's Docker image job: it runs post-merge onmaster only, is skipped when Docker Hub credentials are absent, uses an Ubuntu
20.04 base (old toolchain, so the compiler failures cannot appear there
either), and builds
kv_service_toolswithout ever running it, so the getoptspin stays invisible too.
No workflow is changed or added here. Coverage that uses the distribution's own
toolchain would stop this recurring, but that is a decision about CI policy and
runner budget rather than part of this fix, and it is easier to discuss on its
own. Happy to send it separately.
A note on architecture support
Bazelisk publishes only
linux-amd64andlinux-arm64, so the rewrittenscript stops with a clear message on any other Debian architecture rather than
failing part way through. The previous source-compilation fallback was not
architecture-limited in principle, although it does not complete on any release
I tested. If ppc64el or s390x matter, that needs a different way of obtaining
Bazel and I have not attempted it.
Deliberately not included
bazel build ...still fails, because pybind11 2.6.2 does not compileagainst Python 3.11+.
//service/kv:kv_servicehas no pybind11 in itsdependency graph and is unaffected. Filed as [BUG] pybind11 2.6.2 does not build against Python 3.11+, so
bazel build ...fails on current Ubuntu #262.ecosystem/graphql/WORKSPACE:224fetches this project withgit_repository(branch = "master"), so a GraphQL build uses remote masterrather than the checkout and none of these fixes reach it. Changing how a
subproject depends on its parent is an architectural decision for the
maintainers. Filed as ecosystem/graphql builds against remote master rather than the checkout it lives in #263.
Docker/Dockerfile,INSTALL/README.mdand the Ansible role eachinstall Bazel their own way, one of them still using
apt-key. Consolidatingthem is a separate change.
rapidjson-devandprotobuf-compilerappear to have no consumer leftin the core tree, but they predate this change and removing them is unrelated
cleanup.
Commits
The dependency pinning in commit 5 is independent of the rest:
protobufmovesoff a mutable git tag, four archives that had no
sha256get one, andbazel_skylibis pinned to the 1.0.3 that already wins today so that thechoice is deliberate rather than a side effect of macro ordering. Drop that
commit if it is out of scope for this PR; nothing else depends on it.