Beautiful Makefiles with Wildcards
Tags: make, makefiles, softwaredevelopment, ci
Published: 2020-05-22 18:47:24

---
title: Beautiful Makefiles with Wildcards
published: true
date: 2020-05-22 18:30:14 UTC
tags: Make, Makefiles, Software Development, Continuous Integration
canonical_url: https://www.robg3d.com/2020/05/beautiful-makefiles-with-wildcards/
---

Every single project we build includes a Makefile as a task runner. Every. Single. One.

Why? Because it allows someone to jump into a codebase and start working with the same set of tools and commands as everyone else.

Want to know how to install, build, test, deploy, and see what else you can do?Just open `Makefile`.

This may not be so important if you spend all your time on one monolith, but if you’re jumping around to many services or clients, it’s a lifesaver.

It’s also _so, so nice_ for open source projects, I’m surprised GitHub doesn’t suggest it.

In this post and future ones, we’ll go over some of the tricks we’ve learned building Makefiles in several dozen separate projects.

Today’s lesson: **Wildcards (%)**

## Dynamic Arguments with Wildcards (%)

Many popular languages include some (Make-inspired) script runner, so most commands look something like this:

```
migrate:
    bundle exec rake migrate
install:
    yarn install
runserver:
    python ./manage.py runserver
```

That’s nice, but what about when you want to supply arguments to one of those CLI commands.For example, how can you run `bundle exec rake migrate[50]` to migrate to version 50, rather than latest?

**_Wildcards, that’s how!_** For migrations, we can add a new command to migrate to a specific version:

```
migrate-to-%:
    bundle exec rake migrate[$(*)]
```

Now if you want to migrate _to_ a specific version, you can run:

```
$ make migrate-to-50
Migrating to version 50...
```

Well, we think that’s pretty cool, but what else can wildcards do?

## Declarative Argument Dependencies

The other way to configure Makefiles is with environment variables.Let’s say we use a consistent `STACK` environment variable for working with deployed Cloudformation stacks (as we actually do).

It can be pretty annoying to debug the errors if you forget to set STACK:

```
logs:
    awslogs get -w /ecs/$(STACK)_MyService
```

That can be invoked (correctly) as `awslogs get -w /ecs/development_MyService` or (incorrectly) as `awslogs get -w /ecs/_MyService`. If it’s missing, your error will be about some unknown service or wrong file path, rather than about a missing parameter.

Well, we have a special command we’ve been using for years that we’ll share with you now:

```
guard-%:
    if [-z '${${*}}']; then echo 'ERROR: variable $* not set' && exit 1; fi
```

It’s maybe our favorite two lines of code in the world.

Now, you can rewrite the `logs` target like this, which is also more declarative and clearer about its dependencies:

```
guard-%:
    if [-z '${${*}}']; then echo 'ERROR: variable $* not set' && exit 1; fi
logs: guard-STACK
    awslogs get -w /ecs/$(STACK)_MyService
```

Finally, if you invoke `logs` without `STACK` set, you get a nice error:

```
$ make logs
ERROR: variable STACK not set
make: *** [guard-STACK] Error 1
```

## Wait, there’s more!

There are a couple other wildcard-based tricks we still have in store, as well as some more Makefile goodness.

_This was originally posted on my consultancy’s blog, at [https://lithic.tech/blog/2020-05/makefile-wildcards](https://lithic.tech/blog/2020-05/makefile-wildcards). If you have any questions, please leave a comment or [get in touch](https://lithic.tech)!_