libddwaf Ruby bindings

libddwaf-rb is library exposing the libddwaf C++ library to Ruby, packaging it in a multiplatform gem.

For the libddwaf implementation, see this repository:

Rake tasks

Outline

A typical workflow is as follows:

rake fetch    # fetch prebuilt libddwaf binaries tarball in vendor/libddwaf
rake extract  # extract downloaded tarball in vendor/libddwaf
rake spec     # run rspec
rake binary   # build the gem

Note that each depends on the previous one, but fetch and extract are lazy, which proves useful to produce manual builds.

Platform selection

By default the above will automatically use the local Ruby platform.

Since libddwaf binary builds are available upstream, it's possible to build gems for any platform on any other platform. To that end fetch, extract, and binary can take an argument to specify the Ruby platform for which these operations should apply:

rake fetch[x86_64-linux-musl]
rake extract[x86_64-linux-musl]
rake binary[x86_64-linux-musl]

Of course you can't force the platform for rspec since that requires running code; see the Docker section below for ways to achieve that.

Note that zsh gives special meaning to brackets, therefore you may need to quote the argument:

rake 'fetch[x86_64-linux-musl]'

Available platforms are:

x86_64-linux-musl   # Alpine build: targets musl-based Linux
x86_64-linux-gnu    # Debian build: targets glibc-based Linux
x86_64-linux        # Portable build: targets multiple linux libc
x86_64-darwin       # Darwin build: targets macOS
aarch64-linux-musl  # Same as above, for ARMv8
aarch64-linux-gnu   # Same as above, for ARMv8
aarch64-linux       # Same as above, for ARMv8
arm64-darwin        # Same as above, for Apple Silicon
java                # JRuby build, universal

Note: since it is not (yet) possible to package gems for the java Ruby platform any other way than java, it has to package all the native architectures.

In addition, options can be specified for the portable build:

rake binary[x86_64-linux:gnu+musl]  # Combined build: combine musl and glibc builds, selecting one at runtime
rake binary[x86_64-linux:llvm]      # Hybrid build: linked to llvm static libs and built against a musl sysroot

See upstream libddwaf for details about the hybrid portable build.

Testing with Docker

Unless using Docker for Mac, remember to enable foreign CPU emulation via QEMU:

# aarch64 on x86_64 hardware
docker run --privileged --rm tonistiigi/binfmt --install arm64
# x86_64 on aarch64 hardware
docker run --privileged --rm tonistiigi/binfmt --install amd64

Then you can substitute e.g --platform linux/x86_64 with --platform linux/aarch64 below.

GNU (Debian)

# this is too old for aarch64
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.1 sh -c 'rm -fv Gemfile.lock && gem install bundler -v "~> 1.17" && bundle install && bundle exec rake spec'
# these are fine for aarch64
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.2 sh -c 'rm -fv Gemfile.lock && gem install bundler -v "~> 1.17" && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.3 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.4 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.5 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.6 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.7 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.0 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.1 sh -c 'rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'

musl (Alpine)

# these are too old for aarch64
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.1-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler -v "~> 1.17" && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.2-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler -v "~> 1.17" && bundle install && bundle exec rake spec'
# these are fine for aarch64
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.3-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.4-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.5-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.6-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:2.7-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.0-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.1-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" ruby:3.1-alpine sh -c 'apk update && apk add build-base git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'

JRuby

# these are too old for aarch64
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" jruby:9.2.8.0 sh -c 'apt-get update && apt-get install -y build-essential git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" jruby:9.3.0.0 sh -c 'apt-get update && apt-get install -y build-essential git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'
# this is fine for aarch64
docker run --rm -it --platform linux/x86_64 -v "${PWD}":"${PWD}" -w "${PWD}" jruby:9.3.4.0 sh -c 'apt-get update && apt-get install -y build-essential git && rm -fv Gemfile.lock && gem install bundler:2.2.22 && bundle install && bundle exec rake spec'