Update: 5 july 2015 – See updated version of dockerfile at the end of post with new NDK / SSL / clang toolchain.

If you never heard of Docker be sure to check it out as fast as possible. There are lot of publications out there. At first it looks like another virtualisation software but it is actually more like new paradigm. Someone may call it very advanced chroot, someone may call virtual containers with version control and building scripts, and so on. I like it as the idea of application-centric containers – your application can keep whole operating system as a coating and its making perfect separation from outside influence. As well you can easily reproduce production process environment at another location. It makes virtualisation easy and fun.

Almost everything can be done inside containers now. Recently i had to recompile curl for Android as static lib using latest NDK toolchain. Its not so complicated to do on your local machine (if it is not Windows) but now there is a more clean way to do this time-wasting operation. You can go to digital ocean, create droplet with Docker and using Dockerfile from the end of this post compile it while drinking coffee.

FROM field is describing source OS image. You can specify version of ubuntu or choose something else.

1

2

3

4

5

6

7

8

9

# Install compilation tools

RUN apt-get update&&apt-get install-y\

automake\

build-essential\

wget\

p7zip-full\

bash\

curl

As next step there is installation of compilation tools and some utils. Note that each command inside docker file produces intermediate image which is use like cache if you run your docker file again. This saves you a lot of time when you are tuning or recreating your container image with different options.

1

2

3

4

5

6

7

# Download SDK / NDK

RUN mkdir/Android&&cd Android&&mkdir output

WORKDIR/Android

RUN wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz

RUN wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin

Here we download SDK / NDK using official links from Google – you can edit it to more recent versions. Also we create /Android folder and use it as WORKDIR. Any new command will run in this dir.

1

2

3

4

5

# Extracting ndk/sdk

RUN tar-xvzf android-sdk_r23.0.2-linux.tgz&&\

chmoda+xandroid-ndk-r10c-linux-x86_64.bin&&\

7zxandroid-ndk-r10c-linux-x86_64.bin

Extraction of NDK produces more then 4.5GB (for now). Docker aims to make containers as light as possible so watch out for free space at your storage as images can consume it pretty fast if don’t clean up them.

1

2

3

4

5

6

# Set ENV variables

ENV ANDROID_HOME/Android/android-sdk-linux

ENV NDK_ROOT/Android/android-ndk-r10c

ENV PATH$PATH:$ANDROID_HOME/tools

ENV PATH$PATH:$ANDROID_HOME/platform-tools

ENV command is setting environment variable. Note that you can’t use export command as you do in sh scripts to set local compilation variables because every command in Dockerfile produces new image. Here we just set SDK/NDK folders.

1

2

3

4

5

6

7

# Make stand alone toolchain (Modify platform / arch here)

RUN mkdir=toolchain-arm&&bash$NDK_ROOT/build/tools/make-standalone-toolchain.sh--verbose--platform=android-19--install-dir=toolchain-arm--arch=arm--toolchain=arm-linux-androideabi-4.9--system=linux-x86_64

ENV TOOLCHAIN/Android/toolchain-arm

ENV SYSROOT$TOOLCHAIN/sysroot

ENV PATH$PATH:$TOOLCHAIN/bin:$SYSROOT/usr/local/bin

As next step we create stand alone compilation toolchain using script from NDK. As parameters you select platform and arch. This is example is for ARM but I’m sure you will modify it with easy to make several builds for couple of arms and x86. After creation of toolchain we add it to PATH env variable.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

# Configure toolchain path

ENV ARCH armv7

ENV CROSS_COMPILE arm-linux-androideabi

ENV CC arm-linux-androideabi-gcc

ENV CXX arm-linux-androideabi-g++

ENV AR arm-linux-androideabi-ar

ENV ASarm-linux-androideabi-as

ENV LD arm-linux-androideabi-ld

ENV RANLIB arm-linux-androideabi-ranlib

ENV NM arm-linux-androideabi-nm

ENV STRIP arm-linux-androideabi-strip

ENV CHOST arm-linux-androideabi

ENV CPPFLAGS-std=c++11

Here we set ENV variables for cross-compilation. CC – main C compiler.

1

2

3

4

5

6

7

8

9

# download, configure and make Zlib

RUN curl-Ohttp://zlib.net/zlib-1.2.8.tar.gz && \

tar-xzf zlib-1.2.8.tar.gz&&\

mv zlib-1.2.8zlib

RUN cd zlib&&./configure--static&&\

make&&\

ls-hs.&&\

cp libz.a/Android/output

Here we download last version of ZLib (you can change the link to latest version), extract, configure and make it using –static parameter. And we doing it using newly created android toolchain. As last step we put result libz.a into /Android/output folder where we will put all the results.

The most important step – here we download curl. Configure script of curl has a lot of parameters. I don’t use SSL at the moment so i don’t need to compile OpenSSL (may be i will add it later here). The most important params here are –host=arm-linux-androideabi –target=arm-linux-androideabi as they identify cross-compilation for configuration script.

Also here we setup compilation options CFLAGS / LDFLAGS. You can tune this to your content.

Note – if something goes wrong during ./configure run you can append “|| cat config.log” to the end of line to see your errors during the building of container image.

1

2

3

4

# To get the results run container with output folder

# Example: docker run -v HOSTFOLDER:/output --rm=true IMAGENAME

ENTRYPOINT cp-r/Android/output/*/output

Last step is to set ENTRYPOINT. We have to provide the result of our compilation to host OS (or we can easily make some web server and host it – but i decided to go first way).

To build container put Dockerfile into some folder on host OS and run

1

docker build-tandroid/curl.

android/curl here is just a name i gave to new created image. The process will consume some time. You can see the list of created images using command: docker images

RUN wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin

# Extracting ndk/sdk

RUN tar-xvzf android-sdk_r23.0.2-linux.tgz&&\

chmoda+xandroid-ndk-r10c-linux-x86_64.bin&&\

7zxandroid-ndk-r10c-linux-x86_64.bin

# Set ENV variables

ENV ANDROID_HOME/Android/android-sdk-linux

ENV NDK_ROOT/Android/android-ndk-r10c

ENV PATH$PATH:$ANDROID_HOME/tools

ENV PATH$PATH:$ANDROID_HOME/platform-tools

# Make stand alone toolchain (Modify platform / arch here)

RUN mkdir=toolchain-arm&&bash$NDK_ROOT/build/tools/make-standalone-toolchain.sh--verbose--platform=android-19--install-dir=toolchain-arm--arch=arm--toolchain=arm-linux-androideabi-4.9--system=linux-x86_64

So this small script creates all your compilation environment and does the job!

As you can see if something changes this Dockerfile gives you ability to recompile your libs using different NDK or toolchain pretty fast. Now you can think how to expand this solution to make automatic building of your projects on server side (daily builds). Docker also gives some new freedom for distributed architectures. At the moment i look at CoreOs as perfect environment for containers to live in. But this is subject of separate thread.

I just put new Dockerfile on github which contains build section for OpenSSL library (libssl.a / lib crypto.a). So it’s pretty easy to build lib curl with SSL which supports HTTPS protocol. Also Docker file uses latest NDK with clang3.6 as building toolchain. This version is for armv7, but i’m sure it not so complicated to change it to other ARCH.

Some important lines from my current Application.mk / Android.mk files (just for example – yours might be different):

Thanks for the response.
For now, we are trying to use Android.mk (instead of gradle) & have same values set as above (instead of include folder we have copied .h files to our source folder).
But we get following error:
jni/curlrules.h:143:41: error: size of array ‘__curl_rule_01__’ is negative
jni/curlrules.h:153:53: error: size of array ‘__curl_rule_02__’ is negative

Did you face something similar?

Victor Laskin

You, probably, dont have curlbuild.h configured. Check that it has #define CURL_SIZEOF_LONG 4 near the bottom.

Rakesh Desai

Yes, we got similar feedback from a forum post & accordingly changed CURL_SIZEOF_LONG to 4.
This resolved the error.
Thanks a lot.

Guest

Sorry to bother again.
With the change in value for SIZEOF_LONG, compilation went thr’ but linking gives error as ‘incompatible target’ & then ‘undefined reference’ errors.
I think we seem to have some issue with architecture flag.

Do you think the architecture should be 32 bit instead?
I tried to build libcurl with –host=arm-32-linux-androideabi in ./configure but still objdump gives architecture as 64 bit.

Rakesh Desai

Pasting as a separate query as earlier comment might not have reached Victor.

Sorry to bother again.
With the change in value for SIZEOF_LONG,
compilation went thr’ but linking gives error as ‘incompatible target’
& then ‘undefined reference’ errors.
I think we seem to have some issue with architecture flag.

Please note that I am running the commands directly in Linux VM & not using docker container.
I have Ubuntu 12.04.2 LTS (GNU/Linux 3.5.0-23-generic x86_64) on my VM.
Are you using 32-bit Linux?

It seems that we face issue because of mismatch of 32-bit &

64-bit.
In ./configure is the the build param correct as –build=x86_64-unknown-linux-gnu?

Victor Laskin

Then something is wrong with your ENV settings / paths (and you are not actually cross-compiling). Using docker you could avoid such problems and this post was aimed to show this profit as well. Container has 64bit system inside.

Rakesh Desai

okay, I will try to use docker script.

We are trying to use complied libcurl.a with Android SDK on Windows platform. But I believe that should be fine?

Victor Laskin

yes. If you already compiled something using NDK on Windows there should not be any troubles.

Rakesh Desai

I compiled libcurl.a with docker script & that seem to work fine.

Output of objdump also matches with your output.
Thanks a lot.

This version is compiled for arm architecture & we use Windows machine for Android development which uses x86 emulator. Hence now I’ll try to modify docker script to target x86 arch.

Rakesh Desai

While trying to compile libcurl for X86 modifying docker script suitably, I get following error:

Its linker flag to include lib that is missing inside .a (if that lib exists in your building environment)

Rakesh Desai

As we use SSL, I am trying to compile libcurl with OpenSSL with two modifications to the Doocker script.
1) Added openssl to the tools list as below:
RUN apt-get update && apt-get install -y
automake
build-essential
wget
p7zip-full
bash
curl
openssl

With this I don’t get any error & libcurl does get compiled. However size of compiled version (compared to non-ssl version) does not change & when I try to use it gives ‘unsupported protocol’ error.
As I am not sure where openssl gets installed in the image, I have given default path in –with-ssl switch.
Looks like this path is not correct.
Can you suggest what could be the correct path for –with-ssl?

Victor Laskin

You need to staticly cross-compile open-ssl lib from sources the same way you are compiling curl.

I am new to docker. I installed docker and I built the docker file. I can see the image in my repo (android/curl latest f4b6bd643336). How do I use the static lib? Is it only available within docker container?