Running writefreely 0.7 on Arm

What was necessary to make cross-compiling work for newer WriteFreely versions with SQLite support.
Last updated:

This is a follow-up on The expected tutorial: How to install WriteFreely on a Raspberry pi 3 in 10 steps.

I did it! I finally got WriteFreely to run on my Arm server (check out Scaleways baremetal cloud servers).

It wasn't so easy because with 512MB of RAM I couldn't simply download and build the source on my webserver. Only solution: Cross compiling. Easy especially in Go, right?

If you read the article linked in the beginning you know how easy it could be. But as the article already mentions in an update, since Version 0.6 it is not working anymore because of the new SQLite dependency (newest version as of writing this article is 0.7).

With a bit of research I figured out what to do to make it work anyhow. There are two solutions. A quick (and slightly dirty) one for people who don't need SQLite support and a correct solution that needs a tad more effort.

Quick solution: remove SQLite support

SQLite support makes problems with the cross compiling because it needs some C code to be compiled. Before figuring out how to make this working with the otherwise super easy Go cross compiling, removing the feature might be a viable quick fix. For this, simply change or remove all occurences of sqlite in the Makefile:

diff --git a/Makefile b/Makefile
index 5950dfd..032fd0c 100644
--- a/Makefile
+++ b/Makefile
@@ -13,25 +13,25 @@ IMAGE_NAME=writeas/writefreely
 all : build
 build: assets deps
-	cd cmd/writefreely; $(GOBUILD) -v -tags='sqlite'
+	cd cmd/writefreely; $(GOBUILD) -v
 build-linux: deps
 	@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 		$(GOGET) -u; \
-	xgo --targets=linux/amd64, -dest build/ $(LDFLAGS) -tags='sqlite' -out writefreely ./cmd/writefreely
+	xgo --targets=linux/amd64, -dest build/ $(LDFLAGS) -out writefreely ./cmd/writefreely
 build-windows: deps
 	@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 		$(GOGET) -u; \
-	xgo --targets=windows/amd64, -dest build/ $(LDFLAGS) -tags='sqlite' -out writefreely ./cmd/writefreely
+	xgo --targets=windows/amd64, -dest build/ $(LDFLAGS) -out writefreely ./cmd/writefreely
 build-darwin: deps
 	@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 		$(GOGET) -u; \
-	xgo --targets=darwin/amd64, -dest build/ $(LDFLAGS) -tags='sqlite' -out writefreely ./cmd/writefreely
+	xgo --targets=darwin/amd64, -dest build/ $(LDFLAGS) -out writefreely ./cmd/writefreely
 build-docker :
 	$(DOCKERCMD) build -t $(IMAGE_NAME):latest -t $(IMAGE_NAME):$(GITREV) .
@@ -40,11 +40,11 @@ test:
 	$(GOTEST) -v ./...
 run: dev-assets
-	$(GOINSTALL) -tags='sqlite' ./...
+	$(GOINSTALL) ./...
 	$(BINARY_NAME) --debug
 deps :
-	$(GOGET) -tags='sqlite' -v ./...
+	$(GOGET) -v ./...
 install : build
 	cmd/writefreely/$(BINARY_NAME) --gen-keys
@@ -77,10 +77,10 @@ ui : force_look
 	cd less/; $(MAKE) $(MFLAGS)
 assets : generate
-	go-bindata -pkg writefreely -ignore=\\.gitignore schema.sql sqlite.sql
+	go-bindata -pkg writefreely -ignore=\\.gitignore schema.sql
 dev-assets : generate
-	go-bindata -pkg writefreely -ignore=\\.gitignore -debug schema.sql sqlite.sql
+	go-bindata -pkg writefreely -ignore=\\.gitignore -debug schema.sql
 generate :
 	@hash go-bindata > /dev/null 2>&1; if [ $$? -ne 0 ]; then \

Now just go on as described in the original article and it should work:

env GOARCH=arm GOARM=7 go get

The correct solution

To get WriteFreely cross compiled with SQLite support, a C cross compiler is needed. Void Linux, the distribution of my choice, offers a bunch of packages for all kind of architectures. They are called for example cross-armv7l-linux-gnueabihf (ARMv7), cross-arm-linux-gnueabihf (ARMv6) or cross-arm-linux-gnueabi (ARMv5). I found similar packages in AUR (for Arch Linux).

As soon as the corresponding cross compiler is found, go can be told to use it:

env CGO_ENABLED=1 CC=armv7l-linux-gnueabihf-gcc GOOS=linux GOARCH=arm GOARM=7 make

The environment variables used are:

CGO_ENABLED=1 should be obvious. It tells Go to enable the C compilation.

CC=armv... tells Go which C compiler to use. Usually this would be just gcc. In this case it is the name of the cross compiler. Please set it to the compiler for your target platform. I'm going to use ARMv7 examples here. It is the name of a directory found in /usr/, eg /usr/armv7l-linux-gnueabihf. Initially that failed for me though because it expected to find a file ./lib/ which ended up in another subfolder /usr/. So I cheated a bit and did:

# You might not need to do this on your platform.
sudo ln -s /usr/armv7l-linux-gnueabihf/usr/lib /usr/armv7l-linux-gnueabihf/lib

GOOS=linux GOARCH=arm are the same as in the original article.

GOARM=7 is optional, even on an actual ARMv7. It enables some register optimizations that only work on ARMv7.

And finally make is called. This is short for make all which should do everything necessary.

Not all files are necessary to be transferred to the Server or RaspberryPi. What I did after some experimentation was:

# after building everything create a package
mkdir writefreely-arm
cp -r templates pages static writefreely-arm
mkdir writefreely-arm/keys # fun fact: key generation crashes without this
cp cmd/writefreely/writefreely writefreely-arm
tar cvzf writefreely-arm.tgz writefreely-arm

# copy that package to the server
scp writefreely-arm.tgz you@yourserver.tld:~

# ssh into the server and unpack everything
ssh you@yourserver.tld
tar czf writefreely-arm.tgz
cd writefreely-arm

# generate config, keys and database
./writefreely -config # starts interactive configuration

# This should lead you through all necessary steps
# like filling the config, generating keys, generating database tables
# run `./writefreely --help` to learn more if something is missing.

Now ./writefreely should run an empty blog at the specified port.

Have fun!