--- title: KiCad compilation through Docker date: 2019-09-10 18:30:00 --- While trying to compile [KiCad](http://kicad-pcb.org/download/debian/) from the latest sources, I had problems installing the large number of required dependencies with the correct versions on my two computers. I already know of a tool that allows building a system image that is in a known state and can be executed independently of the host system : Docker. In this article, I will summarize the different steps I took to compile KiCad from the sources into an AppImage that can then be copied onto the different systems that I use for my keyboard project. ## Packaging the dependencies The first step is to package the dependencies needed for the compilation into a Docker image that can then be used. The KiCad website lists the required packages for Debian, we will thus start from this list to build our image. Some of the dependencies required have been updated. The final list I obtained through trial and error was the following. ``` FROM debian:10 # Install kicad deps RUN apt-get update -q && \ apt-get install --no-upgrade -qqy \ git cmake build-essential curl ccache \ libcurl4 libcurl4-gnutls-dev \ libboost-dev libboost-test-dev libboost-filesystem-dev libboost-regex-dev \ liboce-foundation-dev liboce-ocaf-dev \ ca-certificates libssl-dev \ libngspice0-dev \ libglew-dev libglm-dev swig \ libcairo2-dev doxygen graphviz \ python3-wxgtk4.0 \ libwxgtk3.0-dev libwxgtk3.0-gtk3-dev python3 python3-dev \ && \ apt-get clean && \ apt-get purge && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* ``` This Dockerfile will allow us to build a base system that allows us to then manually compile the sources through Docker. The following command can be executed from a clone KiCad repository on your computed containing the sources. We will create a build directory from inside the container, configure the build and compile the sources to check that everything needed has been packaged. ```none host $ docker run --rm -ti -v $(pwd):/kicad 196c68cf5e05 /bin/bash ctnr $ cd kicad/ ctnr $ mkdir build ctnr $ cmake \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr \ -DKICAD_SCRIPTING_PYTHON3=on \ -DKICAD_SCRIPTING_WXPYTHON_PHOENIX=on \ ../ ctnr $ make -j16 ``` You should get no error during the compilation, otherwise it means that some dependency must be updated or installed. I followed the different errors to build the list of dependencies given above. ## Generating an AppImage [AppImage](https://appimage.org/) builds everything needed for the execution into one executable, allowing me to copy a single file to a new computer and not having to worry about installing any dependency and thus testing the generated executable quickly. I extended the Docker image to also include the necessary tools for building a basic AppImage : ``` FROM debian:10 # Install kicad deps RUN apt-get update -q && \ apt-get install --no-upgrade -qqy \ git cmake build-essential curl ccache \ libcurl4 libcurl4-gnutls-dev \ libboost-dev libboost-test-dev libboost-filesystem-dev libboost-regex-dev \ liboce-foundation-dev liboce-ocaf-dev \ ca-certificates libssl-dev \ libngspice0-dev \ libglew-dev libglm-dev swig \ libcairo2-dev doxygen graphviz \ python3-wxgtk4.0 \ libwxgtk3.0-dev libwxgtk3.0-gtk3-dev python3 python3-dev \ && \ apt-get clean && \ apt-get purge && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Download and install linuxdeploy tool RUN curl -O -J -L https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage && \ chmod +x linuxdeploy-x86_64.AppImage && \ mv linuxdeploy-x86_64.AppImage /usr/bin/linuxdeploy && \ curl -O -J -L https://raw.githubusercontent.com/TheAssassin/linuxdeploy-plugin-conda/master/linuxdeploy-plugin-conda.sh && \ mv linuxdeploy-plugin-conda.sh /usr/bin/linuxdeploy-plugin-conda && \ chmod +x /usr/bin/linuxdeploy-plugin-conda ``` The first `linuxdeploy` tools allows us to easily build an AppImage. The second `linuxdeploy-plugin-conda` eases working with packaging applications that needs Python. We can build the new Docker image and execute it again on the folder used previously. As we used a volume, our previous build will still be available allowing us to continue directly with the packaging. ``` host $ docker run --rm -ti -v $(pwd):/kicad cae4d304d730 /bin/bash ctnr $ cd /kicad/build ctrn $ make install DESTDIR=AppDir ``` This will install our compiled sources into an AppDir directory. We will now need to also copy some Python dependencies that will be required and not automatically handled : ``` ctnr $ mkdir -p AppDir/usr/lib/python3/dist-packages/ ctnr $ cp -r /usr/lib/python3/dist-packages/wx \ /usr/lib/python3/dist-packages/wxPython-4.0.4.egg-info \ AppDir/usr/lib/python3/dist-packages/ ``` Then, we also need to update our KiCad binary so that the packaged Python will be used and not the system one. For that, we will replace the KiCad binary with a script that will simply extract the path at which the AppImage will be mounted and export the `PYTHON_PATH` variable accordingly. ``` ctnr $ mv AppDir/usr/bin/kicad AppDir/usr/bin/kicad_bin ctnr $ cat << "EOF" > AppDir/usr/bin/kicad #!/bin/sh HERE="$(dirname "$(readlink -f "${0}")")/../../" export PYTHON_PATH="${HERE}"/usr/lib/python3:${PYTHON_PATH} exec "${HERE}/usr/bin/kicad_bin" "$@" EOF ctnr $ chmod +x AppDir/usr/bin/kicad ``` Now that everything is ready, we can package the AppDir into an AppImage using `linuxdeploy`. ``` ctnr $ LD_LIBRARY_PATH=$(pwd)/AppDir/usr/lib/x86_64-linux-gnu/ linuxdeploy \ --appimage-extract-and-run \ --appdir AppDir \ -d $(pwd)/AppDir/usr/share/applications/kicad.desktop \ --output appimage ``` This will generate a `KiCad-*-x86_64.AppImage` file in your build folder that can be executed on your host OS without having to install any dependency. It will not be possible to execute the AppImage from inside the container for multiple reasons, first of which that FUSE is needed to mount the AppImage content and is not available in a container. Second, an X Server will be needed to start the application and is also not available in the container. ## Build script Building manually without having to worry about dependencies is great but we can do better by simply scripting the different steps so that everything happens in one command line. Once the steps have been verified, we can put them in a build script `build-docker.sh` that we include in the Docker image that will be called when starting the container. ``` #!/bin/bash cd "$1" BUILD_TYPE="$2" if [ -z "$BUILD_TYPE" ]; then BUILD_TYPE="Debug" fi mkdir build cd build rm -rf AppDir cmake \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ -DCMAKE_INSTALL_PREFIX=/usr \ -DKICAD_SCRIPTING_PYTHON3=on \ -DKICAD_SCRIPTING_WXPYTHON_PHOENIX=on \ ../ make -j$(nproc) --output-sync if [ $? -ne 0 ]; then exit 10 fi make install DESTDIR=AppDir mkdir -p AppDir/usr/lib/python3/dist-packages/ cp -r /usr/lib/python3/dist-packages/wx \ /usr/lib/python3/dist-packages/wxPython-4.0.4.egg-info \ AppDir/usr/lib/python3/dist-packages/ mv AppDir/usr/bin/kicad AppDir/usr/bin/kicad_bin cat << "EOF" > AppDir/usr/bin/kicad #!/bin/sh HERE="$(dirname "$(readlink -f "${0}")")/../../" export PYTHON_PATH="${HERE}"/usr/lib/python3:${PYTHON_PATH} exec "${HERE}/usr/bin/kicad_bin" "$@" EOF chmod +x AppDir/usr/bin/kicad LD_LIBRARY_PATH=$(pwd)/AppDir/usr/lib/x86_64-linux-gnu/ linuxdeploy \ --appimage-extract-and-run \ --appdir AppDir \ -d $(pwd)/AppDir/usr/share/applications/kicad.desktop \ --output appimage mv KiCad*.AppImage ../KiCad-$(git describe).AppImage ``` ``` FROM debian:10 # Install kicad deps RUN apt-get update -q && \ apt-get install --no-upgrade -qqy \ git cmake build-essential curl ccache \ libcurl4 libcurl4-gnutls-dev \ libboost-dev libboost-test-dev libboost-filesystem-dev libboost-regex-dev \ liboce-foundation-dev liboce-ocaf-dev \ ca-certificates libssl-dev \ libngspice0-dev \ libglew-dev libglm-dev swig \ libcairo2-dev doxygen graphviz \ python3-wxgtk4.0 \ libwxgtk3.0-dev libwxgtk3.0-gtk3-dev python3 python3-dev \ && \ apt-get clean && \ apt-get purge && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Download and install linuxdeploy tool RUN curl -O -J -L https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage && \ chmod +x linuxdeploy-x86_64.AppImage && \ mv linuxdeploy-x86_64.AppImage /usr/bin/linuxdeploy && \ curl -O -J -L https://raw.githubusercontent.com/TheAssassin/linuxdeploy-plugin-conda/master/linuxdeploy-plugin-conda.sh && \ mv linuxdeploy-plugin-conda.sh /usr/bin/linuxdeploy-plugin-conda && \ chmod +x /usr/bin/linuxdeploy-plugin-conda COPY build-docker.sh /build-docker.sh ENTRYPOINT ["/bin/bash", "/build-docker.sh"] CMD ["/kicad"] ``` We can then simply build an AppImage from the latest sources with just one command line, without having to worry about updating our host system with the latest dependencies : `docker run --rm -it -v $(pwd):/kicad kicad-builder-docker kicad Release`