Home > Enterprise >  Shell variable subsitution in makefile
Shell variable subsitution in makefile

Time:01-11

I would like to have command in Makefile that allows me to change service's target in docker-compose.yaml respectively to target given command parameter. I have something like this:

example:
    @[[ "$${target:-dev}" == @(dev|test|release) ]] || exit 1
    yq -y -i '.services."$(service)".build.target = "$(target)"' docker-compose.yaml

This almost do the job, but I have this parameter to be optional. It should fallback to "dev" when not provided. In second line there is validation which check if given target has allowed value. If target is not specified in command it should fallback to "dev" and it work. But when I try similar syntax it doesn't work in third line. I tried different combination like

yq -y -i '.services."$(service)".build.target = "$(target:-dev)"' docker-compose.yaml
yq -y -i '.services."$(service)".build.target = "${target:-dev}"' docker-compose.yaml
yq -y -i '.services."$(service)".build.target = "${{target:-dev}}"' docker-compose.yaml
yq -y -i '.services."$(service)".build.target = "$(${target:-dev})"' docker-compose.yaml
// etc

But in most cases I got empty string instead of "dev", what's wrong?

CodePudding user response:

The values $(target) is a make variable, not a shell variable. Make variables can't be manipulated using shell constructs like ${foo:-bar}.

If you want to do something like that you have to use make constructs. If you're willing to restrict yourself to GNU make, you can use:

$(or $(target),dev)

If you want to be completely portable you can convert your make variable into a shell variable, then use shell constructs, like this:

target='$(target)'; yq -u -i ".services.\"...\".build.target = \"$${target:-dev}\"" ...

By the way I should point out: make always invokes /bin/sh when running recipes, unless you specifically request that it use a different shell by setting SHELL. This line in your recipe:

@[[ "$${target:-dev}" == @(dev|test|release) ]] || exit 1

is using a bunch of bash-specific features which are definitely NOT portable to the POSIX shell. If your system's /bin/sh just happens to be bash (for example, you're on a Red Hat type system) this will work. But if you try to run this on a system where /bin/sh is a strict POSIX shell, like dash (for example, if you're on a Debian type system including Ubuntu) it will fail.

If your makefile requires recipes to run in bash, not POSIX sh, you need to add:

SHELL := /bin/bash

or similar to it.

  • Related