Quick and dirty way to build PhantomJS2 from source
A blog post about building the all-new PhantomJS2 for Linux until the official builds are available
Overview
PhantomJS 2 is just around the corner, but official Linux binaries are yet to be made available. In the meantime I needed some of the new functionality so I couldn't wait until the errors were fixed, so I compiled one from source. Being a Java/Javascript/Ruby developer, compiling C/C++ programs from source was always a black box for me. I needed to install a lot of libraries to my system which I most likely never use again, just to produce a binary that actually does not need any of those stuff. As I recently got familiar with Docker, it seems like there is a solution to these issues.
In short, I wanted a script that:
- compiles the source and produces the binary
- is possibly a one-liner
- does not litter around various libraries
- cleans up after itself
- does not have any dependencies besides Docker
Steps
Setup and cleanup
The first step is to create a new container based on Ubuntu, execute some commands and remove it. Docker and Bash have powerful support for this:
sudo docker run --name phantomjs-compile ubuntu /bin/bash -c "echo abc"; sudo docker rm phantomjs-compile
With --name, we can name a container, and then we can reference it without first having to look up the container id.
Fetching the dependencies and compiling
The next thing is to actually setting up and the compiling inside the container. I've started from the official build docs, just added make, git and libqt5webkit5-dev as they were needed for the base Ubuntu image. Also, the build have a confirmation prompt, but luckily that can be supplied from the CLI with --confirm. The commands are:
apt-get update
apt-get install -y g++ flex bison gperf ruby perl libsqlite3-dev libfontconfig1-dev libicu-dev libfreetype6 libssl-dev libpng-dev libjpeg-dev make git libqt5webkit5-dev
git clone git://github.com/ariya/phantomjs.git
cd phantomjs
git checkout 2.0
./build.sh --confirm
To make it a one-liner, simply add && between the commands:
apt-get update &&
apt-get install -y g++ flex bison gperf ruby perl libsqlite3-dev libfontconfig1-dev libicu-dev libfreetype6 libssl-dev libpng-dev libjpeg-dev make git libqt5webkit5-dev &&
git clone git://github.com/ariya/phantomjs.git &&
cd phantomjs &&
git checkout 2.0 &&
./build.sh --confirm
Copying the resulting binary
Docker have several ways to copy a file from the container to the host (there is a COPY command, and the container can also mount a volume of the host), unfortunately they are not portable. Luckily there is a method that works with relative paths, and that is stream redirection. The basic idea is that we redirect all stderr streams to stdout (so there will be no stderr data), and read the binary to the stderr, and finally on the host redirect the stderr to a file. As the binary is located as bin/phantomjs, the resulting commands are:
apt-get update 2>&1 &&
apt-get install -y g++ flex bison gperf ruby perl libsqlite3-dev libfontconfig1-dev libicu-dev libfreetype6 libssl-dev libpng-dev libjpeg-dev make git libqt5webkit5-dev 2>&1 &&
git clone git://github.com/ariya/phantomjs.git 2>&1 &&
cd phantomjs 2>&1 &&
git checkout 2.0 2>&1 &&
./build.sh --confirm 2>&1 &&
cat bin/phantomjs 1>&2
And the redirection to file:
2> phantomjs
Result
The only step now is to combine and compress all the above into a single line, which can be copy-pasted to any system with Docker:
sudo docker run --name phantomjs-compile ubuntu /bin/bash -c "apt-get update 2>&1 && apt-get install -y g++ flex bison gperf ruby perl libsqlite3-dev libfontconfig1-dev libicu-dev libfreetype6 libssl-dev libpng-dev libjpeg-dev make git libqt5webkit5-dev 2>&1 && git clone git://github.com/ariya/phantomjs.git 2>&1 && cd phantomjs 2>&1 && git checkout 2.0 2>&1 && ./build.sh --confirm 2>&1 && cat bin/phantomjs 1>&2" 2> phantomjs; sudo docker rm phantomjs-compile
The actual compilation will take quite some time, on my machine it was ~ 1 - 1.5 hours. The binary works fine, and it will make a good substitution until the official build arrive. The only thing the process leaves is an Ubuntu image, but that's something people may generally want to have around. If you don't need it, delete it with a simple sudo docker rmi ubuntu.
The good thing with this approach is that it will work with any other compilation too. Just figure out the exact sequence of commands, add the above tricks, and you have a fully automated build script. Remember, that you can pull out multiple files, just tar them inside the container and untar on the host.
For a reference, if you want to start a fresh new Ubuntu as a playground, use:
sudo docker run -i -t ubuntu /bin/bash