@@ -70,14 +70,112 @@ RUN set -eux; \
7070 "$PY" -c "import pytantan; from pytantan.lib import RepeatFinder, default_scoring_matrix; print('pytantan smoke test OK', pytantan.__version__)"
7171
7272# Pre-generate an activation script so the final image doesn't need pixi.
73+ # The post-hook PATH re-prepend keeps /opt/glimmerhmm/bin ahead of
74+ # /app/.pixi/envs/default/bin even after `pixi shell-hook` activation
75+ # prepends the pixi env — so our generic-baseline glimmerhmm shadows the
76+ # bioconda v3 build at runtime. See the glimmerhmm-build stage for why.
7377RUN mkdir -p /app/bin && \
7478 { echo '#!/bin/bash' ; \
7579 echo 'set -e' ; \
7680 pixi shell-hook --shell bash; \
81+ echo 'export PATH="/opt/glimmerhmm/bin:$PATH"' ; \
7782 echo 'exec "$@"' ; \
7883 } > /app/bin/entrypoint.sh && \
7984 chmod +x /app/bin/entrypoint.sh
8085
86+ # ---------------------------------------------------------------------------
87+ # Stage 1b: glimmerhmm — compile glimmerhmm from upstream source with a
88+ # generic x86_64 baseline so it runs on every host (incl. Rosetta 2).
89+ # The bioconda glimmerhmm 3.0.4 build sets -march=x86-64-v3 (AVX2/BMI2)
90+ # and SIGILLs under Rosetta 2 on Apple Silicon / pre-Haswell x86_64
91+ # (the same root cause as augustus and pytantan). glimmerhmm is NOT in
92+ # Ubuntu apt, so we build it ourselves here.
93+ # Install layout matches bioconda's: bin/{glimmerhmm,glimmhmm.pl,trainGlimmerHMM}
94+ # + share/glimmerhmm/{train/*,trained_dir/*}. The trainGlimmerHMM perl
95+ # script locates its support binaries via $RealBin/../share/glimmerhmm/train
96+ # (the same upstream patch applied by both bioconda and this recipe).
97+ # The bioconda glimmerhmm in the pixi env is kept as a shadowed fallback
98+ # (and is what runs on osx-arm64 native dev) — see the PATH order in the
99+ # final stage and the comment in pixi.toml.
100+ # ---------------------------------------------------------------------------
101+ FROM --platform=linux/amd64 ubuntu:${UBUNTU_VERSION} AS glimmerhmm-build
102+
103+ ENV DEBIAN_FRONTEND=noninteractive \
104+ LANG=C.UTF-8 \
105+ LC_ALL=C.UTF-8
106+
107+ RUN apt-get update && \
108+ apt-get install -y --no-install-recommends \
109+ build-essential \
110+ ca-certificates \
111+ wget \
112+ binutils && \
113+ rm -rf /var/lib/apt/lists/*
114+
115+ ARG GLIMMERHMM_VERSION=3.0.4
116+ ARG GLIMMERHMM_SHA256=43e321792b9f49a3d78154cbe8ddd1fb747774dccb9e5c62fbcc37c6d0650727
117+
118+ WORKDIR /build
119+ RUN wget -q https://ccb.jhu.edu/software/glimmerhmm/dl/GlimmerHMM-${GLIMMERHMM_VERSION}.tar.gz && \
120+ echo "${GLIMMERHMM_SHA256} GlimmerHMM-${GLIMMERHMM_VERSION}.tar.gz" | sha256sum -c - && \
121+ tar -xzf GlimmerHMM-${GLIMMERHMM_VERSION}.tar.gz
122+
123+ WORKDIR /build/GlimmerHMM
124+
125+ # Same upstream fixes the bioconda recipe applies (makefile typos +
126+ # self-locating perl entry points). Without these the `all` target in
127+ # train/makefile names "escoreSTOP2" / "rfapp" and clean targets a
128+ # nonexistent "trainGlimmerHMM", and the perl entry points don't find
129+ # their support binaries when invoked via PATH.
130+ RUN sed -i 's|^escoreSTOP2:|scoreSTOP2:|g' train/makefile && \
131+ sed -i 's|^rfapp:|erfapp:|g' train/makefile && \
132+ sed -i 's| trainGlimmerHMM||g' train/makefile && \
133+ sed -i 's|all: build-icm|all: misc.o build-icm.o build-icm-noframe.o build-icm|g' train/makefile && \
134+ sed -i '1 s|^.*$|#!/usr/bin/env perl|g' train/trainGlimmerHMM && \
135+ sed -i 's|FindBin;|FindBin qw($RealBin);|g' train/trainGlimmerHMM && \
136+ sed -i 's|$FindBin::Bin;|"$RealBin/../share/glimmerhmm/train";|g' train/trainGlimmerHMM && \
137+ sed -i '1 s|^.*$|#!/usr/bin/env perl|g' bin/glimmhmm.pl
138+
139+ # -O3 matches the bioconda recipe; -march=x86-64 -mtune=generic gives the
140+ # v1 baseline that every Rosetta-2-emulated CPU can execute.
141+ ENV CFLAGS="-O3 -march=x86-64 -mtune=generic -Wno-format -Wno-deprecated-declarations -Wno-unused-variable -Wno-unused-but-set-variable -Wno-comment" \
142+ CXXFLAGS="-O3 -march=x86-64 -mtune=generic -Wno-format -Wno-deprecated-declarations -Wno-unused-variable -Wno-unused-but-set-variable -Wno-comment"
143+
144+ RUN make -C sources CC=g++ CFLAGS="${CXXFLAGS}" -j"$(nproc)" && \
145+ make -C train clean && \
146+ make -C train all C=gcc CC=g++ CFLAGS="${CXXFLAGS}" -j"$(nproc)"
147+
148+ # Install into /opt/glimmerhmm with the same bin/ + share/ layout
149+ # bioconda uses, so trainGlimmerHMM's $RealBin/../share/... lookup
150+ # still resolves once the tree is copied into the final stage.
151+ RUN mkdir -p /opt/glimmerhmm/bin /opt/glimmerhmm/share/glimmerhmm/train && \
152+ install -m 0755 bin/glimmhmm.pl sources/glimmerhmm train/trainGlimmerHMM /opt/glimmerhmm/bin/ && \
153+ install -m 0755 train/build-icm train/build-icm-noframe train/build1 train/build2 train/erfapp /opt/glimmerhmm/share/glimmerhmm/train/ && \
154+ install -m 0755 train/falsecomp train/findsites train/karlin train/score train/score2 /opt/glimmerhmm/share/glimmerhmm/train/ && \
155+ install -m 0755 train/scoreATG train/scoreATG2 train/scoreSTOP train/scoreSTOP2 train/splicescore /opt/glimmerhmm/share/glimmerhmm/train/ && \
156+ cp -f train/*.pm /opt/glimmerhmm/share/glimmerhmm/train/ && \
157+ cp -Rf trained_dir /opt/glimmerhmm/share/glimmerhmm/
158+
159+ # Verify no AVX/AVX2/AVX512 instructions slipped into the compiled
160+ # binaries (defense-in-depth — same check pytantan uses). Plain SSE2
161+ # (xmm only, non-VEX) is the x86_64 baseline and is fine.
162+ RUN set -eux; \
163+ BAD="" ; \
164+ for bin in /opt/glimmerhmm/bin/glimmerhmm \
165+ /opt/glimmerhmm/share/glimmerhmm/train/build1 \
166+ /opt/glimmerhmm/share/glimmerhmm/train/build2 \
167+ /opt/glimmerhmm/share/glimmerhmm/train/build-icm \
168+ /opt/glimmerhmm/share/glimmerhmm/train/build-icm-noframe; do \
169+ hits=$(objdump -d -M intel --no-show-raw-insn "$bin" 2>/dev/null \
170+ | grep -Ec '\b (ymm[0-9]+|zmm[0-9]+|vpbroadcast|vextracti128|vinserti128|vfmadd|vpermd|vpgatherdd)\b ' || true); \
171+ echo "$bin -> $hits AVX/AVX2/AVX512 hits" ; \
172+ if [ "$hits" -gt 0 ]; then BAD="${BAD} ${bin}" ; fi; \
173+ done; \
174+ if [ -n "$BAD" ]; then \
175+ echo "ERROR: AVX-bearing binaries in glimmerhmm:$BAD" ; \
176+ exit 1; \
177+ fi
178+
81179# ---------------------------------------------------------------------------
82180# Stage 2: dbs — download/build the funannotate2 databases (minus BUSCO)
83181# ---------------------------------------------------------------------------
@@ -130,6 +228,15 @@ RUN apt-get update && \
130228COPY --from=build /app/.pixi/envs/default /app/.pixi/envs/default
131229COPY --from=build /app/bin/entrypoint.sh /app/bin/entrypoint.sh
132230
231+ # Self-compiled glimmerhmm (generic x86_64 baseline). Layered onto PATH
232+ # ahead of /app/.pixi/envs/default/bin so it shadows the bioconda binary
233+ # at runtime — the bioconda glimmerhmm in the pixi env is built with
234+ # -march=x86-64-v3 (AVX2/BMI2) and SIGILLs under Rosetta 2. See the
235+ # glimmerhmm-build stage above for the rationale; the pixi-env glimmerhmm
236+ # remains in the image as a shadowed fallback (and is what runs natively
237+ # on osx-arm64 outside docker).
238+ COPY --from=glimmerhmm-build /opt/glimmerhmm /opt/glimmerhmm
239+
133240# Pre-built databases (~3 GB; BUSCO lineages download at runtime)
134241COPY --from=dbs /opt/funannotate2_db /opt/funannotate2_db
135242
@@ -139,7 +246,7 @@ COPY --from=dbs /opt/funannotate2_db /opt/funannotate2_db
139246# installs new_species.pl, optimize_augustus.pl, etc.
140247ENV FUNANNOTATE2_DB=/opt/funannotate2_db \
141248 AUGUSTUS_CONFIG_PATH=/usr/share/augustus/config \
142- PATH=/app/.pixi/envs/default/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
249+ PATH=/opt/glimmerhmm/bin:/ app/.pixi/envs/default/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
143250
144251WORKDIR /data
145252
0 commit comments