When installing Rust toolchain in Docker, Bash `so

2019-08-31 05:42发布

问题:

I am trying to create a docker image that will setup a Linux environment for building Rust projects. Here is my Dockerfile so far:

FROM ubuntu:16.04

# Update default packages
RUN apt-get update

# Get Ubuntu packages
RUN apt-get install -y \
    build-essential \
    curl

# Update new packages
RUN apt-get update

# Get Rust
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y

The last thing I need to do is configure Rust, so that I can use cargo. The documentation says to use

source $HOME/.cargo/env

but when I try that in a RUN command in a Dockerfile, it says source is not recognized. Another option I found was to use

RUN /bin/bash -c "source ~/.cargo/env"

This does not error, but when I run my container, cargo is not a recognized command.

Either approach works from Bash when I have the container open, but I would like this to be automated as part of the image.

How can I integrate this into my Dockerfile?

回答1:

You have to add the sourcing inside the .bashrc.

This works:

FROM ubuntu:16.04

# Update default packages
RUN apt-get update

# Get Ubuntu packages
RUN apt-get install -y \
    build-essential \
    curl

# Update new packages
RUN apt-get update

# Get Rust
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y

RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc


回答2:

I think you may be misunderstanding what source does. This built-in command tells the current shell to load the following code (almost) as if it were being run at the current prompt (you can also use source inside other scripts). It's basically an "include file here" command. It's mostly used to set up your environment (PATH, LIBPATH, and other shell functions), not to do real work.

Running "source" in a RUN command is thus (almost always) useless. It will load up the cargo environment and then exit, thus losing all the environment changes.

This leaves you with two basic options. One is to do as michael_bitard suggests, and get it added to your .bashrc. This will mean that all commands forevermore in that container (by that user) will have the environment set up. If you only need this for setup, then it pollutes your shell environment at runtime.

The second option is to basically run the source as part of every RUN command that needs it in the rest of your Dockerfile. RUN bash -c 'source $HOME/.cargo/env; command goes here for example. This is more work on each RUN line, but the environment will be explicitly there when you need it, and not when you don't.

Most of the time, the first option is what you want. Rarely you want the second. That is, sometimes you only need this environment for setup purposes, and you don't want it to persist - this is rare, though I've had that scenario come up a few times.



回答3:

source (or .) is a shell built in command - it runs the commands from the file given as parameter in the current shell. This is useful for configuring your current environment (aliases, functions and environment variables) configured in a script does not apply to the parent shell that started it, with source those can be configured. This is why RUN fails - there is no executable called source. (It might work with the shell form of RUN or with SHELL)

To run a command with items set up from that file, you should be able to get away with:

RUN bash -c 'source ~/.cargo/env; cargo <whatever>'

If you want the variables to be applied to a shell inside the container, you need to add the source command to one of the bash files that is sourced when the shell is started, e.g. ~/.bashrc or ~/.bash_profile (depending on whether a login shell is started or not) or (on most distributions) a .sh file in /etc/profile.d/

One way to do this would be the following in your Dockerfile:

RUN ln -s $HOME/.cargo/env /etc/profile.d/cargo_env.sh