问题
I am creating a Ubuntu 12.04 docker image that has gcc 4.8.5. I am getting gcc 4.8.5 source and building it myself. This container will be running on a Ubuntu 18.04 host.
Referencing the code at the bottom, if I don't put this in the dockerfile and run the same commands after starting the container, the build works fine, however if I use RUN instead in the dockerfile, I get the following build error
In file included from /usr/include/stdio.h:28:0,
from ../../../gcc-4.8.5/libgcc/../gcc/tsystem.h:87,
from ../../../gcc-4.8.5/libgcc/libgcc2.c:27:
/usr/include/features.h:324:26: fatal error: bits/predefs.h: No such
file or directory
#include <bits/predefs.h>
^
The problem seems to stem from the ./gcc-4.8.5/configure call. When run inside the container i get:
checking build system type... i686-pc-linux-gnu
When put in the dockerfile i get:
checking build system type... x86_64-unknown-linux-gnu
Can someone fill in my understanding of the RUN in dockerfiles because i feel like i am missing something about how its working. I was under the impression that those commands would run in the previous layer? But it seems like they are running on my host.
## Get gcc 4.8.5 and build it
RUN wget ftp://gcc.gnu.org/pub/gcc/releases/gcc-4.8.5/gcc-4.8.5.tar.gz \
&& tar xzf gcc-4.8.5.tar.gz && \
cd gcc-4.8.5 && \
./contrib/download_prerequisites && \
cd .. && mkdir gccbuild && cd gccbuild && \
../gcc-4.8.5/configure \
--prefix="/opt/gcc" \
--enable-shared --with-system-zlib --enable-threads=posix \
--enable-__cxa_atexit --enable-checking --enable-gnu-indirect-function \
--enable-languages="c,c++" --disable-bootstrap \
&& make all && make install
EDIT:
docker build -t 12.04_builder - < dockerfile
docker run -i -t 12.04_builder
Complete dockerfile:
FROM jnickborys/i386-ubuntu:12.04
RUN apt-get update && \
apt-get install -y \
wget \
build-essential \
libssl-dev \
git \
asciidoc \
libpulse-dev \
libasound2-dev \
libpcsclite-dev
## Get latest cmake that has a 32-bit version
RUN wget https://github.com/Kitware/CMake/releases/download/v3.6.3/cmake-3.6.3-Linux-i386.sh && \
chmod +x cmake-3.6.3-Linux-i386.sh && \
./cmake-3.6.3-Linux-i386.sh --skip-license --prefix=/usr
## Get gcc 4.8.5 and build it
RUN wget ftp://gcc.gnu.org/pub/gcc/releases/gcc-4.8.5/gcc-4.8.5.tar.gz \
&& tar xzf gcc-4.8.5.tar.gz && \
cd gcc-4.8.5 && \
./contrib/download_prerequisites && \
cd .. && mkdir gccbuild && cd gccbuild && \
../gcc-4.8.5/configure \
--prefix="/opt/gcc" \
--enable-shared --with-system-zlib --enable-threads=posix \
--enable-__cxa_atexit --enable-checking --enable-gnu-indirect-function \
--enable-languages="c,c++" --disable-bootstrap
&& make all && make install
回答1:
First of all, a little bit of background: the platform detection script which runs during the build uses uname(1)
utility (thus uname(2)
system call) to identify the hardware it runs on:
root@6e4b69adfd4c:/gcc-4.8.5# grep 'uname -m' config.guess
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
On your 64-bit machine uname -m
returns x86_64
. However, there is a system call which allows overriding this result: personality(2)
. When the process calls personality(2)
, it and its subsequent forks (children) start seeing the fake results when calling uname(2)
. So, there is the possibility to ask the kernel to provide the fake hardware information in uname(2)
.
The base image you use (jnickborys/i386-ubuntu:12.04
) contains the 32-bit binaries and defines the entrypoint /usr/bin/linux32
, which calls personality(PER_LINUX32)
to ask the kernel to pretend that it runs on 32-bit hardware and to return i686
in uname(2)
(this may be checked using docker inspect
and strace
respectively). This makes possible to pretend that the containerized process runs in 32-bit environment.
What is the difference between executing the build in RUN
directive and manually in the container?
When you execute the build in RUN
, Docker does not use the entrypoint to run the commands. It uses what is specified in the SHELL
directive instead (default is /bin/sh -c
). This means that the personality of the shell running the build is not altered, and it (and the child processes) sees the real hardware information - x86_64
, thus, you get x86_64-unknown-linux-gnu
build system type in 32-bit environment and the build fails.
When you run the build manually in the container (e.g. after starting it using docker run -it jnickborys/i386-ubuntu:12.04
and then performing the same steps as in the Dockerfile), the entrypoint is called, thus, the personality is altered, and the kernel starts reporting that it runs on 32-bit hardware (i686
), so you get i686-pc-linux-gnu
build system type, and the build runs correctly.
How to fix this? Depends on what do you want. If your goal is to build gcc for 64-bit environment, just use the 64-bit base image. If you want to build for 32-bit environment, one of your options is to alter the SHELL
being used for RUN
s before these RUN
s:
SHELL ["/usr/bin/linux32", "/bin/sh", "-c"]
This will make Docker execute RUN
s with altered personality, so, the build system type will be detected correctly (i686-pc-linux-gnu
) and the build will succeed. If required, you may change the SHELL
back to /bin/sh -c
after the build.
来源:https://stackoverflow.com/questions/54416100/run-command-in-dockerfile-produces-different-result-than-manually-running-same-c