62

Is there a way to reassign Makefile variable value inside of the target body?

What I am trying to do is to add some extra flags for debug compilation:

%.erl: %.beam
    $(ERLC) $(ERLFLAGS) -o ebin $<

test: clean debug_compile_flag compile compile_test

debug_compile:
    $(ERLCFLAGS) += -DTEST

So if I invoke test target I would like to clean up my environment, add some new flags (like -DTEST to the existing ones), compile the whole code once again (first sources, then test modules).

I do not want to copy/paste the code for compiling with some new flags set since there is a lot of logic put here and there.

Is there some easy way to redefine the variable value so I can reuse the existing code?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
paulgray
  • 623
  • 1
  • 5
  • 4

6 Answers6

101

Yes, there is an easy way to do it, and without rerunning Make. Use a target-specific variable value:

test: clean debug_compile

debug_compile: ERLCFLAGS += -DTEST
debug_compile: compile compile_test;
Beta
  • 96,650
  • 16
  • 149
  • 150
  • 3
    Is the order of execution guarantied here? Or could a make -j2 screw things up? – Marenz May 29 '11 at 19:46
  • 6
    [Docs](http://www.gnu.org/software/make/manual/make.html#Target_002dspecific): "Be aware that a given prerequisite will only be built once per invocation of make, at most. If the same file is a prerequisite of multiple targets, and each of those targets has a different value for the same target-specific variable, then the first target to be built will cause that prerequisite to be built and the prerequisite will inherit the target-specific value from the first target. It will ignore the target-specific values from any other targets." – ntc2 Sep 19 '13 at 22:34
53

Another answer is here: Define make variable at rule execution time.

For the lazy, you can have rules like the following (FLAG and DEBUG are my variables):

.DBG:
    $(eval FLAG += $(DEBUG))
Community
  • 1
  • 1
Ian Ooi
  • 1,635
  • 1
  • 15
  • 19
  • 1
    I have a lot of problems with `$(eval xxx)`, with tons of strange side-effects . It doesn't seem to work as well as a "real" Makefile variable assignment. – Cyan Feb 02 '17 at 23:59
  • 1
    I've had an issue with `eval` function - I got empty values for my variables because variable defined with eval gets it's value in the beginning of the target execution, not when this line is reached. For example, if you are creating some files in the beginning of the target, and later try to fill some variable using `eval FILES=$(shell ls)`, your FILES var will be empty – Vadim Kotov Jun 05 '17 at 13:31
  • Inside the target I believe you need $(eval FILES=$(shell ls)) but I've had some issues with my makefiles depending on versions of make. – Ian Ooi Jul 05 '17 at 19:24
  • 1
    More problematically, consider https://pastebin.com/L1gYhHk5 . If you go with `make a b`, Make will complain that *bar* is needed. If you then `touch bar`, then the `echo` in the `b` execution will print `baz`: the computation of the dependency and the execution of the body are made at different points. – Michaël Dec 11 '17 at 16:46
7

Here is the solution I use:

PASSWORD = abc123

main: sub
    @echo "in main" $(PASSWORD)

sub:
    @echo "in sub" $(PASSWORD)
    $(eval PASSWORD=qwerty)
    @echo "in sub" $(PASSWORD)

If you run make main then the output is:

in sub abc123
in sub qwerty
in main qwerty

You can see that the original value "abc123" is overwritten in the sub and the new value "qwerty" is visible at the main level.

Csongor Halmai
  • 3,239
  • 29
  • 30
0

To override on the command line try something like:

make prefix=<path to new dir> install

This won't change Makefile, but will alter the variable.

David García Bodego
  • 1,058
  • 3
  • 13
  • 21
placid chat
  • 179
  • 10
  • Letting what calls the make command decide by changing the variable on invocation feels like a great solution for simple use cases, like the example provided. I'd say for anything complex it would be better to code it in, but I like the simplicity of this solution! – perpetuallynotfini Nov 08 '20 at 09:19
-2

I wanted to add a target in a makefile to run tests, which implied recompiling the source code with some debug flags. Ian's answer: https://stackoverflow.com/a/15561911/ was the only solution that worked.

Here's the Makefile I came up with, which guaranties the order of execution when running make tests:

TARGET     = a.out

CC         = g++
GENERIC_F  = -Wall -Wextra -I. -Idoctest/doctest/

CFLAGS     = -O0 -std=c++11 $(GENERIC_F)
DEBUG_MODE = -DDEBUG

LINKER     = g++
LFLAGS     = $(GENERIC_F) -lm

SRCDIR     = src
OBJDIR     = build
BINDIR     = bin

SOURCES    = $(wildcard $(SRCDIR)/*.cc)
INCLUDES   = $(wildcard $(SRCDIR)/*.h)
OBJECTS    = $(SOURCES:$(SRCDIR)/%.cc=$(OBJDIR)/%.o)
rm         = rm -f

.PHONY: clear_screen tests extend_cflags

$(BINDIR)/$(TARGET): $(OBJECTS) $(INCLUDES)
    $(LINKER) $(OBJECTS) $(LFLAGS) -o $@
    @echo -e "Linking complete!\n"

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.cc $(INCLUDES)
    @mkdir -p $(OBJDIR) $(BINDIR)
    $(CC) $(CFLAGS) -c $< -o $@
    @echo -e "Compiled "$<" successfully!\n"

.PHONY: clean
clean:
    @$(rm) $(OBJECTS)
    @echo "Cleanup complete!"

.PHONY: remove
remove: clean
    @$(rm) $(BINDIR)/$(TARGET)
    @echo "Executable removed!"

clear_screen:
    @clear

extend_cflags:
    $(eval CFLAGS += $(DEBUG_MODE))

tests: | remove extend_cflags $(BINDIR)/$(TARGET) clear_screen
    @$(BINDIR)/$(TARGET)
Alberto Chiusole
  • 2,204
  • 2
  • 16
  • 26
-9

Edit: As explained by Beta in the other answer, it is possible.


No. There is no way to do this in the Makefile. You can however change the value of a variable on the make command line. If you rewrite your Makefile as follows:

ERLCFLAGS += $(ERLCFLAGSADDED)

%.erl: %.beam
    $(ERLC) $(ERLCFLAGS) -o ebin $<

test: clean compile compile_test

Then, you can invoke make to perform your tests using:

make ERLCFLAGSADDED=-DTEST test
Community
  • 1
  • 1
Didier Trosset
  • 36,376
  • 13
  • 83
  • 122
  • Yeah, I solved the problem as you suggested, running submake in the debug_compile: ERLC_FLAGS=$(ERLC_DEBUG_FLAGS) $(MAKE) compile Thanks! – paulgray Apr 26 '10 at 09:39
  • Oh yes, great. I didn't thought about this submake invocation. – Didier Trosset Apr 26 '10 at 11:36
  • The submake invocation is still useful if you want to run a target multiple times with different values of the flags. See docs I quoted in comment below. – ntc2 Sep 20 '13 at 00:00