Wednesday, March 25, 2020

Some arch-querying examples for building

Most likely when building, be it container images or standalone binaries, you don't want a separate set of scripts or Makefiles or Dockerfiles, or whatever you're using, for each architecture you want to target (e.g. amd64 or ppc64le). I was asked by some new team-members to provide examples of ways to get the architecture of the system on your scripts are running. It was a short little list of things I've used and seen over the years, so I thought it might be nice to share publicly.

-------------------------

The first example is using shell and uname in a Makefile. Under the lint target, you can see that linting is only done for amd64.

.PNONY: lint
lint:
ifeq ($(ARCH), amd64)
@git diff-tree --check $(shell git hash-object -t tree /dev/null) HEAD $(shell ls -d * | grep -v cfc-files)
@yamllint .
@docker run --rm -v $(shell pwd):/data -w /data $(ANSIBLE_IMAGE) ansible-playbook -e cluster/config.yaml playbook/install.yaml --syntax-check
endif

But where did `ARCH` come from? In this use-case, which I've lifted from an internal project, there's an included file called Configfile that gets the arch using `uname`. You could just as easily put that into the top of your Makefile.
ARCH ?= $(shell uname -m | sed 's/x86_64/amd64/g')
ifeq ($(ARCH), amd64)
DOCKER_FLAG ?= Dockerfile
else
DOCKER_FLAG ?= Dockerfile.$(ARCH)
You can see in this example that this project *does* have a separate Dockerfile for each target arch, which is fine. (However, you can just maintain one if you use multi-arch images, and/or build-args).

You can also see that there's a substitution done for x86_64. Most of the time using those interchangably is fine. There are most likely similar cases for ARM variants, so this is a good thing to keep in mind and keep your case statements cleaner later on.

---------------------------

My  next example is a trick I stole from the nvidia-docker maintainers that lets you get an if-statement into a Dockerfile. Docker intentionally excludes conditional logic in Dockerfiles so that your images are the same from build to build. So use this example with great caution, and keep your images consistent.


RUN set -eux; \
     \
     arch="$(uname -m)"; \
          case "${arch##*-}" in \
          x86_64 | amd64) ARCH='amd64' ;; \
          ppc64el | ppc64le) ARCH='ppc64le' ;; \
         *) echo "unsupported architecture"; exit 1 ;; \
     esac; \
wget -nv -O - https://storage.googleapis.com/golang/go${GO_VERSION}.linux-${ARCH}.tar.gz \
| tar -C /usr/local -xz

I have this in a Dockerfile of my own that I use it to set up a build environment. As you can see, it's a way to use a URL that has a hard-coded architecure.


-----------------------------

So that's it! If you have any of your own fun tricks for your projects, please share them!