Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
FROM debian:stretch AS stage_build
Comment thread
trzecieu marked this conversation as resolved.

# ------------------------------------------------------------------------------

# Supports only 1.38.40+, accpets also '-upstream' variants
ARG EMSCRIPTEN_VERSION=1.38.43
ARG EMSDK_CHANGESET=master

# ------------------------------------------------------------------------------

# NOTE: Any change of following variables should be reflected in ./entrypoint file
ENV EMSDK /emsdk_portable
ENV EM_DATA ${EMSDK}/.data
ENV EM_CONFIG ${EMSDK}/.emscripten
ENV EM_CACHE ${EM_DATA}/cache
ENV EM_PORTS ${EM_DATA}/ports

# ------------------------------------------------------------------------------

RUN echo "## Start building" \
\
&& echo "## Update and install packages" \
&& apt-get -qq -y update \
&& apt-get -qq install -y --no-install-recommends \
libxml2 \
wget \
git-core \
ca-certificates \
build-essential \
file \
python python-pip \
python3 python3-pip \
\
&& echo "## Done"

RUN echo "## Get EMSDK" \
&& git clone https://github.com/emscripten-core/emsdk.git ${EMSDK} \
&& cd ${EMSDK} && git reset --hard ${EMSDK_CHANGESET} \
\
&& ./emsdk.py update-tags \
&& echo "## Done"

RUN echo "## Install Emscripten" \
&& cd ${EMSDK} \
&& ./emsdk install ${EMSCRIPTEN_VERSION} \
\
&& echo "## Done"

# This generates configuration that contains all valid paths according to installed SDK
RUN cd ${EMSDK} \
&& echo "## Generate standard configuration" \
\
&& ./emsdk activate ${EMSCRIPTEN_VERSION} --embedded \
&& ./emsdk construct_env > /dev/null \
&& cat ${EMSDK}/emsdk_set_env.sh \
\
# remove wrongly created entry with EM_CACHE, variable will be picked up from ENV
&& sed -i -e "/EM_CACHE/d" ${EMSDK}/emsdk_set_env.sh \
# add a link to tools like asm2wasm in a system path
# asm2wasm (and friends might be places either in ./upstream of ./fastcomp folder, hence detection is needed)
&& printf "export PATH=$(dirname $(find . -name asm2wasm -exec readlink -f {} +)):\$PATH\n" >> ${EMSDK}/emsdk_set_env.sh \
\
&& echo "## Done"

# Create a structure and make mutable folders accessible for r/w
RUN cd ${EMSDK} \
&& echo "## Create .data structure" \
&& for mutable_dir in ${EM_DATA} ${EM_PORTS} ${EM_CACHE} ${EMSDK}/zips ${EMSDK}/tmp; do \
mkdir -p ${mutable_dir}; \
chmod -R 777 ${mutable_dir}; \
done \
\
&& echo "## Done"

# Create an entrypoint that activates Emscripten SDK and helps running this image as non-root user
COPY entrypoint ${EMSDK}/

# Internal test suite of tools that this image provides
COPY test_dockerimage.sh ${EMSDK}/


# Create symbolic links for critical Emscripten Tools
# This is important for letting people using Emscripten in Dockerfiles without activation
# As each Emscripten release is placed to a different folder (i.e. /emsdk_portable/emscripten/tag-1.38.31)
RUN echo "## Create symbolic links" \
&& . ${EMSDK}/emsdk_set_env.sh \
\
&& mkdir -p ${EMSDK}/llvm ${EMSDK}/emscripten ${EMSDK}/binaryen \
\
&& ln -s $(dirname $(which node))/.. ${EMSDK}/node/current \
&& ln -s $(dirname $(which clang))/.. ${EMSDK}/llvm/clang \
&& ln -s $(dirname $(which emcc)) ${EMSDK}/emscripten/sdk \
\
&& ln -s $(dirname $(which asm2wasm)) ${EMSDK}/binaryen/bin \
\
&& echo "## Done"

# Clean up emscripten installation and strip some symbols
RUN echo "## Aggresive optimization: Remove debug symbols" \
&& apt-get -qq -y update && apt-get -qq install -y --no-install-recommends \
binutils \
&& . ${EMSDK}/emsdk_set_env.sh \
# Remove debugging symbols from embedded node (extra 7MB)
&& strip -s `which node` \
# Tests consume ~80MB disc space
&& rm -fr ${EMSDK}/llvm/clang/emscripten/tests \
# strip out symbols from clang (~extra 50MB disc space)
&& find ${EMSDK}/llvm/clang/bin -type f -exec strip -s {} + || true \
&& find ${EMSDK}/llvm/clang/fastcomp/bin -type f -exec strip -s {} + || true \
&& echo "## Done"

# Populate Emscripten SDK cache with libc++, to improve further compilation times.
RUN echo "## Pre-populate cache" \
Comment thread
trzecieu marked this conversation as resolved.
&& . ${EMSDK}/emsdk_set_env.sh \
\
&& embuilder.py build SYSTEM \
\
&& mkdir -p /tmp/emscripten_test \
&& cd /tmp/emscripten_test \
\
&& printf '#include <iostream>\nint main(){std::cout << "HELLO FROM DOCKER C++"<<std::endl;return 0;}' > test.cpp \
&& em++ --std=c++11 test.cpp -o test.js -s WASM=0 && node test.js \
&& em++ --std=c++11 -g3 test.cpp -o test.js -s WASM=0 && node test.js \
&& em++ --std=c++11 test.cpp -o test.js -s WASM=1 && node test.js \
\
&& cd / \
&& rm -fr /tmp/emscripten_test \
\
# some files were created, and we need to make sure that those can be accessed by non-root people
&& chmod -R 777 ${EM_DATA} \
\
# cleanup
&& find ${EMSDK} -name "*.pyc" -exec rm {} \; \
\
&& echo "## Done"

# ------------------------------------------------------------------------------
# -------------------------------- STAGE DEPLOY --------------------------------
# ------------------------------------------------------------------------------

FROM debian:buster-slim AS stage_deploy

COPY --from=stage_build /emsdk_portable /emsdk_portable
Comment thread
trzecieu marked this conversation as resolved.
Outdated

# Fallback in case Emscripten isn't activated.
# This will let use tools offered by this image inside other Docker images (sub-stages) or with custom / no entrypoint
ENV EMSDK /emsdk_portable
ENV EMSCRIPTEN=${EMSDK}/emscripten/sdk

ENV EM_DATA ${EMSDK}/.data
ENV EM_CONFIG ${EMSDK}/.emscripten
ENV EM_CACHE ${EM_DATA}/cache
ENV EM_PORTS ${EM_DATA}/ports

# Fallback in case Emscripten isn't activated
# Expose Major tools to system PATH, so that emcc, node, asm2wasm etc can be used without activation
ENV PATH="${EMSDK}:${EMSDK}/emscripten/sdk:${EMSDK}/llvm/clang/bin:${EMSDK}/node/current/bin:${EMSDK}/binaryen/bin:${PATH}"

# Use entrypoint that's coming from emscripten-slim image. It sets all required system paths and variables
ENTRYPOINT ["/emsdk_portable/entrypoint"]


# ------------------------------------------------------------------------------
# Create a 'standard` 1000:1000 user
# Thanks to that this image can be executed as non-root user and created files will not require root access level on host machine
# Please note that this solution even if widely spread (i.e. Node.js uses it) is far from perfect as user 1000:1000 might not exist on
# host machine, and in this case running any docker image will cause other random problems (mostly due `$HOME` pointing to `/`)
# This extra user works nicely with entrypoint provided in `/emsdk_portable/entrypoint` as it detects case explained before.
RUN echo "## Create emscripten user (1000:1000)" \
&& groupadd --gid 1000 emscripten \
&& useradd --uid 1000 --gid emscripten --shell /bin/bash --create-home emscripten \
\
&& echo "## Done"

# ------------------------------------------------------------------------------

RUN echo "## Update and install packages" \
# mitigate problem with create symlink to man for base debian image
&& mkdir -p /usr/share/man/man1/ \
\
&& apt-get -qq -y update && apt-get -qq install -y --no-install-recommends \
libxml2 \
ca-certificates \
python3 \
python3-pip \
wget \
curl \
zip \
unzip \
git \
ssh-client \
build-essential \
make \
ant \
libidn11 \
cmake \
openjdk-11-jre-headless \
\
# Standard Cleanup on Debian images
&& apt-get -y clean \
&& apt-get -y autoclean \
&& apt-get -y autoremove \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /var/cache/debconf/*-old \
&& rm -rf /usr/share/doc/* \
&& rm -rf /usr/share/man/?? \
&& rm -rf /usr/share/man/??_* \
&& echo "## Done"

# ------------------------------------------------------------------------------

RUN echo "## Internal Testing of image (activated)" \
&& . ${EMSDK}/emsdk_set_env.sh \
&& ${EMSDK}/test_dockerimage.sh \
\
&& echo "## Done"

RUN echo "## Internal Testing of image (not-activated)" \
&& ${EMSDK}/test_dockerimage.sh \
\
&& echo "## Done"

# ------------------------------------------------------------------------------
# Copy this Dockerimage into image, so that it will be possible to recreate it later
COPY Dockerfile /emsdk_portable/dockerfiles/emscripten-core/emsdk/

LABEL maintainer="kontakt@trzeci.eu" \
org.label-schema.name="emscripten" \
org.label-schema.description="The official container with Emscripten SDK" \
org.label-schema.url="https://emscripten.org" \
org.label-schema.vcs-url="https://github.com/emscripten-core/emsdk" \
org.label-schema.docker.dockerfile="/docker/Dockerfile"

# ------------------------------------------------------------------------------
83 changes: 83 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
### About this Dockerfile
... to be added
Comment thread
trzecieu marked this conversation as resolved.
Outdated

### Usage

Simple usage of this container to compile a hello-world
```bash
# create helloworld.cpp
cat << EOF > helloworld.cpp
#include <iostream>
int main() {
std::cout << "Hello World!" << std::endl;
return 0;
}
EOF
```

```bash
# compile with docker image
docker run \
--rm \
-v $(pwd):$(pwd) \
-u $(id -u):$(id -g) \
emscripten/emscripten \
emcc helloworld.cpp -o helloworld.js

# execute on host machine
node helloworld.js
```

Teardown of compilation command:

|part|description|
|---|---|
|`docker run`| A standard command to run a command in a container|
|`--rm`|remove a container after execution (optimization)|
|`-v $(pwd):$(pwd)`|Mounting current folder from the host system into mirrored path on the container<br>TIP: This helps to investigate possible problem as we preserve exactly the same paths like in host. In such case modern editors (like Sublime, Atom, VS Code) let us to CTRL+Click on a problematic file |
|`-u $(id -u):$(id -g)`| Run the container as a non-root user with the same UID and GID as local user. Hence all files produced by this are accessible to non-root users|
|`emscripten/emscripten`|Get the latest tag of this container|
|`emcc helloworld.cpp -o helloworld.js`|Execute `emcc` command with following arguments inside container, effectively compile our source code|



### Building Dockerimage

This image requires to specify following build arguments:
Comment thread
trzecieu marked this conversation as resolved.

| arg | description |
| --- | --- |
| `EMSCRIPTEN_VERSION` | One of released version of Emscripten. For example `1.38.45`<br/> Can be used with `-upstream` variant like: `1.38.45-upstream`<br /> Minimal supported version is **1.38.40**|

```bash
docker build \
--build-arg=EMSCRIPTEN_VERSION=1.38.43-upstream \
--tag test \
.
```

### Extending

If your project uses packages that this image doesn't provide you might want to:
* Contribute to this repo: Maybe your dependency is either non-intrusive or could be useful for other people
* Create custom image that bases on this image

1. create own Dockerfile that holds:
```dockerfile
# Point at any base image that you find suitable to extend.
FROM emscripten/emscripten:1.38.25

# Install required tools that are useful for your project i.e. python2
RUN apt update && apt install -y python python-pip

```
2. build it
```shell
docker build -t extended_emscripten .
```

3. test
```shell
docker run --rm extended_emscripten python --version
# Python 2.7.16
```
27 changes: 27 additions & 0 deletions docker/entrypoint
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/sh

# In case when mapped user id by `docker run -u` is not created inside docker image
# The `$HOME` variable points to `/` - which prevents any tool to write to, as it requires root access
# In such case we set `$HOME` to `/tmp` as it should r/w for everyone

if [ "$HOME" = "/" ] ; then
export HOME=/tmp
fi

# In case of running as root, use `umask` to reduce problem of file permission on host
if [ "$(id -g)" = "0" ] && [ "$(id -u)" = "0" ] ;
then
umask 0000
fi

# Export this image specific Environment variables
# Those variables are important to use dedicated folder for all cache and predefined config file
export EM_CONFIG=/emsdk_portable/.emscripten
export EM_CACHE=/emsdk_portable/.data/cache
export EM_PORTS=/emsdk_portable/.data/ports

# Activate Emscripten SDK
. ${EMSDK}/emsdk_set_env.sh

# Evaluate a command that's coming after `docker run` / `docker exec`
"$@"
17 changes: 17 additions & 0 deletions docker/test_dockerimage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
set -e

which asm2wasm
which llvm-ar
which emsdk
node --version
npm --version
python3 --version
pip3 --version
Comment thread
trzecieu marked this conversation as resolved.
em++ --version
emcc --version
java -version
cmake --version

# cleanup after test
find ${EMSDK} -name "*.pyc" -exec rm {} \;