Compiling Python and LibPython Statically Using Musl-Libc

TL;DR

Just use the setup script.

I’ve successfully tested it on Ubuntu 14.04:64bit using both Gcc and Clang.

Introduction

According to Python’s documentation, it is somewhat tricky to get a static build of libpython and the Python interpreter:

This page is a placeholder until MicahElliott (or anyone) figures out all the steps to make this work.

At least, it seems to me that it is the case on Linux… if you try to do it using Glibc as your C Standard Library.

How about using an alternative Standard Library ?

As you can see on the previous Wikipedia link, there are several implementations of the C Standard Library. In particular, the musl-libc project stands out as being a fairly lightweight and feature-rich implementation. Also, it is MIT-licensed and extremely straightforward to build. For those reasons, I decided to give it a try.

Preparations

First download the Musl and Python sources. I am using musl-1.1.12 and Python-3.5.0 (i.e. latest releases at the time of writing) but the method demonstrated here should work for other versions as well.

Building Musl

First of all, choose a C compiler. The following instructions use Clang, but you can use Gcc if you want.

export CC="clang-3.7"
# export CC="gcc-4.8"

As mentioned earlier, musl is straightforward to build. Just extract the downloaded archive then use the usual ./configure, make, make install commands:

# extract the archive
tar -vxzf musl-1.1.12.tar.gz
cd musl-1.1.12

# configure and build a static version of musl
# feel free to change the prefix to another location
export MUSL_PREFIX="/opt/my_libs/musl/1.1.12/clang/3.7"
./configure --prefix="${MUSL_PREFIX}" --disable-shared
make
make install

Sure enough, this does generate the expected headers and library files under ${MUSL_PREFIX}/include and ${MUSL_PREFIX}/lib, but it also installs a handy compiler wrapper script under ${MUSL_PREFIX}/bin. This script is just a drop-in replacement that basically calls your compiler by telling it to use musl as its C Standard Library.

In my case, I got ${MUSL_PREFIX}/bin/musl-clang which I can use as follows:

# compile and run a simple hello world program
$ musl-clang -static hello-world.c -o hello-world
$ ./hello-world
Hello world!

# check that the program has actually been compiled statically
$ ldd hello-world
    not a dynamic executable

Building Python Using Musl

Now that we have musl and the compiler wrapper, we can build python statically.

First, set the compiler wrapper as the C compiler:

export CC="${MUSL_PREFIX}/bin/musl-clang"
# export CC="${MUSL_PREFIX}/bin/musl-gcc"

Then extract the downloaded Python archive and build using the following configuration:

# extract the archive
tar -vxJf Python-3.5.0.tar.xz
cd Python-3.5.0

# configure and build a static version of Python
# feel free to change the prefix to another location
export PY_PREFIX="/opt/my_libs/python/3.5.0/musl-clang/3.7"
./configure --prefix="${PY_PREFIX}" --disable-shared  \
            LDFLAGS="-static" CFLAGS="-static" CPPFLAGS="-static"
make
make install

An error might occur when running make install. You can ignore it 😉

That’s it! You now have a static libpython along with a statically built Python interpreter under ${PY_PREFIX}.

To test it, you can run the following program:

/* hello-python.c */

#include <Python.h>

int main()
{
    Py_Initialize();
    /* say hello and print the current time */
    PyRun_SimpleString(
        "import time;"
        "t = time.strftime('%H:%M:%S', time.gmtime());"
        "print('Hello world! It is', t);"
    );
    Py_Finalize();
}

Compile it using the compiler wrapper:

# build a Python-enabled C program
$ export CC=${MUSL_PREFIX}/bin/musl-clang
$ $CC -static hello-python.c -o hello-python  \
      -I "${PY_PREFIX}/include/python3.5m"    \
      "${PY_PREFIX}/lib/libpython3.5m.a"

# run the test program
$ ./hello-python
Hello world! It is 22:07:44

# make sure the program is statically linked
$ ldd hello-python
    not a dynamic executable

Conclusion

This article showed a simple method for building static versions of Python and LibPython using musl.

I have also made a script for automating the above steps (download, extraction, compilation…). It allows me to quickly switch between different compilers and versions. You can download it from Github.

However, the result is not exactly perfect: some modules (e.g. tkinter) are not included in the generated files. To enable them, you’ll have to edit Modules/Setup before compiling Python. I haven’t done that because I didn’t need those modules in my application. I might add more information later, but you can check out this StackOverflow post for a starting point on how to do it.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s