Home:Professional:Building Subversion for MyCloud NAS

Recently I purchased a Western Digital MyCloud EX2 Ultra NAS appliance to replace an aging custom-built Linux server. Since that server had been hosting (among other things) my personal Subversion repository, I needed to find a replacement home for it. While moving my personal Web site to cloud hosting made sense, I didn't want my personal source control there. As the MyCloud NAS is essentially just an embedded Linux server anyway, putting Subversion there made a lot of sense to me.

Unfortunately it doesn't come with a Subversion server built in, so I set out to build it myself. This was a bit of a learning process; I'm taking note of it here in case it may be useful for anybody else trying to build software to run on it.

  1. Obtaining a Toolchain
    1. Download
    2. Extract
    3. Open-Source Packages
    4. Toolchain
    5. 64-bit Host
    6. Environment
  2. Building the Software
    1. Expat and Zlib
    2. Apache Portable Runtime (APR)
    3. Apache Portable Runtime Utilities (APR-Util)
    4. Subversion
  3. Debugging
  4. Conclusion

Obtaining a Toolchain

You need a Linux environment to host the cross-building toolchain to the NAS appliance target; I used Ubuntu 20.04. I did naively try installing a Linaro package first, but that failed to produce reliably-functioning binaries. Obviously there are settings that need to match that I'm unaware of.

Download

Instead, you can just download everything you need from Western Digital's support Web site. Since the MyCloud is mostly (if not entirely) open source/GPL software, all of this is supposed to be made available. I found the package I needed by searching for my exact model, firmware version (which you can read off the device's admin Web page), then under a section titled “WD My Cloud EX2 Ultra GPL Source Code”; in my case, as WDMyCloud_EX2Ultra_GPL_v5.16.105_20210728.tar.gz.

Extract

Extract the package somewhere and you will find inside the two main subdirectories needed for this project:

Open-Source Packages

Most of the archives inside here contain an xbuild.sh script that can be used to build the package with the correct configuration settings, presumably contributed by Western Digital themselves. In this case just executing ./xbuild.sh build is enough to build the package correctly.

Some, however, have an sbbuild.sh script instead. These refer to a no-longer-supported project called ‘Scratchbox’, which apparently would have provided some way of simulating the target environment enough for the Autoconf scripts to detect the proper configuration settings. Note that configure runs in and detects properties of the host environment, which may not be the correct ones for software that runs in the target environment. Presence of a Scratchbox-type build script seems a good indication that the package will need some extra careful attention.

Toolchain

This directory has another archive nested inside that contains the tools and libraries needed for the cross-build; in my case this was armv7-marvell-linux-gnueabi-softfp_i686_64K_Dev_20131002.tar.gz. Extract this in place, and go one level inside. Observe that bin/ contains tools and libraries that run on the host, whereas the arm-marvell-linux-gnueabi/ (or whatever the case may be) subdirectory has things that run on the target. Importantly, this has the debugger server in libc/usr/bin/gdbserver which was absolutely invaluable in solving the various build problems that I ran into.

64-bit Host

The toolchain is comprised of 32-bit executables. On my 64-bit Ubuntu, I therefore needed the 32-bit Standard C libraries for these to work:
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386

Environment

Source the source.me script in the current shell (by executing . source.me). This sets up environment variables (notably PATH, MY_PREFIX, TARGET_HOST) which are used by the build scripts. Note here that MY_PREFIX is actually the DESTDIR concatenated with the PREFIX.

Building the Software

While the goal is Subversion, this has prerequites which need to be built first.

Expat and Zlib

Use the archives provided inside Open_Source_packages. For each, extract the archive and go inside. Since both these packages have the ‘simple’ cross-build scripts, build by executing ./xbuild.sh build. This builds and installs into a staging directory called _xinstall.

Apache Portable Runtime (APR)

Again, use the provided archive. Note that this is a Scratchbox build, which means that we have to be more careful about checking what Autotools picks up and hand-holding the build. If the Autotools configuration script is well-written, detected configurations are saved in cache variables. These can then be passed explicitly to the configuration script, providing a way to pass target environment settings which the configuration script can't automatically detect, or can't detect correctly.

Through trial and error I found the correct configuration to be:

./configure \
	--prefix=$MY_PREFIX --host=$TARGET_HOST --with-gnu-ld \
	CFLAGS='-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -DNO_LINGCLOSE -DSO_LINGER' \
	ac_cv_file__dev_zero=yes \
	ac_cv_func_setpgrp_void=yes \
	apr_cv_tcp_nodelay_with_cork=yes \
	ac_cv_sizeof_struct_iovec=1 \
	ac_cv_sizeof_pid_t=4
That last one in particular (4-byte PIDs) took a serious investment in Assembly-level debugging to find. The lesson for me is that it would have been worth the extra time and effort to double-check whether the autodetected host configurations actually matched the target, and not just assuming that a successful compile meant everything was OK.

Next, try building:

make
This will fail right away because it's compiling a tool that is used in the build itself; and that shouldn't be cross-compiled. I suppose that an extremely well-written configuration script might have been able to distinguish between artifacts that run on the host rather than the target and adjust accordingly. In this case, I just worked around it by manually executing the commands needed to build the tool: this requires replacing the arm-marvell-linux-gnueabi-gcc argument to --mode=compile or --mode=link with plain gcc.

However, it turns out that you must first must create a directory so that the Makefile doesn't just rebuild another bad version right on top of it again (this seems like a Makefile bug to me):

mkdir make_tools_dir
The four modified commands that I had to execute manually look something like
/bin/bash /some-path-to/WDMyCloud_Ex2Ultra_GPL_v2.30.193_20180502/Open_Source_packages/apr-1.5.1/libtool \
	--silent --mode=compile gcc \
	... \
	-o tools/gen_test_char.lo -c tools/gen_test_char.c && touch tools/gen_test_char.lo
/bin/bash /some-path-to/WDMyCloud_Ex2Ultra_GPL_v2.30.193_20180502/Open_Source_packages/apr-1.5.1/libtool \
	--silent --mode=link gcc \
	... \
	-o tools/gen_test_char tools/gen_test_char.lo \
	...
/some-path-to/WDMyCloud_Ex2Ultra_GPL_v2.30.193_20180502/Open_Source_packages/apr-1.5.1/build/mkdir.sh \
	include/private
tools/gen_test_char > include/private/apr_escape_test_char.h
After this, you should be able to once again make and have it complete the build. Then
make install
as usual to install to the staging area.

All this shows that cross-building isn't always just a ‘fire-and-forget’ no-brainer; once in a while you do have to get down and dirty into the build process.

Apache Portable Runtime Utilities (APR-Util)

This is also a Scratchbox build, but turns out to be much simpler. Note that we use the APR and Expat we built just before:
./configure \
	--prefix=$MY_PREFIX --host=$TARGET_HOST \
	--with-apr=$MY_PREFIX/bin/apr-1-config \
	--with-expat=$MY_PREFIX \
	CFLAGS='-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -DNO_LINGCLOSE -DSO_LINGER'
make
make install

Subversion

Now we're ready to build the actual goal of this exercise. In this case, since Subversion isn't part of the stock software that's preinstalled on the appliance we have to download the source ourselves. While you might put the source anywhere you want, for simplicity and consistency I prefer to install the build into the same staging area.

While SQLite is actually preinstalled on the NAS, the version (3.6.14) isn't sufficiently recent for Subversion (which needs at least 3.7.12). This is another reminder of the importance of carefully reading and checking the build instructions, or risking a much harder slog debugging a bad build later on.

Subversion happens to support downloading a SQLite ‘amalgamation’ directly inside and building it there; I just used the latest available stable release:

curl 'https://www.sqlite.org/2018/sqlite-amalgamation-3240000.zip' >sqlite-amalgamation-3240000.zip
unzip sqlite-amalgamation-3071501.zip
mv sqlite-amalgamation-3240000/ sqlite-amalgamation
The Autoconf configuration script tries to compile a program to detect whether it's running on Mach-O; however, this again fails because the program is compiled for the target environment and won't run. For this case I created this patch which modifies the Autoconf check macro into an Autoconf cacheable variable that we can set to avoid the check altogether. Apply the patch and regenerate the configuration script:
patch -p0 <subversion-mach-O.patch
autoconf
Now the package can be configured and built:
./configure \
	--prefix=$MY_PREFIX \
	--host=$TARGET_HOST --build=x86_64-linux-gnu \
	--with-apr=$MY_PREFIX/bin/apr-1-config \
	--with-apr-util=$MY_PREFIX/bin/apu-1-config \
	--with-zlib=$MY_PREFIX \
	--enable-optimize --disable-nls \
	CFLAGS='-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -DNO_LINGCLOSE -DSO_LINGER' \
	LDFLAGS='-Wl,--dynamic-linker=/lib/ld-linux.so.3 -Wl,-rpath-link=$MY_TARGET/lib' \
	ac_cv_svn_lib_macho_iterate=no
make
make install
The specification of the path to the dynamic linker is required because the appliance doesn't have it in the default place that's built in to the linker. I do get plenty of “libtool: warning: '.../libexpat.la' seems to be moved” errors, but I ignored them and they don't seem to affect the build.

Debugging

Clearly all this didn't simply come together perfectly on the first try— far from it. Thankfully, the toolchain includes a GDB client and server that are immensely helpful. Refer to the appropriate documentation topics on “remote debugging” if needed. Obviously you have to add -g -O0 to the CFLAGS where appropriate.

Conclusion

Cross-building is not the simple and smooth process that we've come to expect from regular host-targeted builds; it requires much more careful attention and willingness to debug and analyze.

All pages under this domain © Copyright 1999-2018 by: Ben Hekster