Search code examples
makefile

How to use .ONESHELL to assign a value to a simple variable in a Makefile rule?


I have the following recipe:

.ONESHELL:
misc:
  $(eval venvPath := $(shell xdg-user-dir ENVIRONMENTS)/nav)
ifeq ($(wildcard ${venvPath}),)
  python -m venv ${venvPath} \
    && source ${venvPath}/bin/activate \
    && pip install -U pip wheel
  source ${venvPath}/bin/activate \
    && pip install powerline-status
endif
.PHONY: misc

I would like to use the value of the simple variable venvPath. However, after setting its value with $(eval ...), it always remains empty.

How can I use .ONESHELL properly to do this? Or, is there another method?


Solution

  • You have a lot of confusion here. Some of it can be alleviated by following the rule, never use $(eval ...) in a recipe. And similar for using $(shell ...). It's only used in the most obscure situations and only if you really understand how make works at a deep level.

    The simplest way to write this is to use all shell scripting, and no make functions. So for example:

    .ONESHELL:
    misc:
            venvPath=$$(xdg-user-dir ENVIRONMENTS)/nav
            if test -d $$venvPath; then
                python -m venv $$venvPath \
                    && source $$venvPath/bin/activate \
                    && pip install -U pip wheel
                . $$venvPath/bin/activate \
                    && pip install powerline-status
            fi
    .PHONY: misc
    

    The basic reason for the problems you have is that ifeq is a preprocessor statement which is expanded and tested when the makefile is parsed, while the contents of the recipe (the lines indented by TAB) are only executed later, when make tries to build the target.

    So it cannot work to use ifeq to test a variable that is not set until the recipe is invoked.