Search code examples
makefiletargetconditional-variable

Why can't i add to the OBJ's but i can add to the CFLAGS/SRC's in a MAKEFILE


I want two Makefile targets which both create the same targetfile but with some bonus files added to the normal files in the bonus rule (all files inside the final .a though). Which in itself is pretty easy, but i want both rules to not relink. By not relinking i mean not executing the ar command if the prereq-files didn't change. So showing that "Nothing to be done for target" in the terminal is what i want.

I thought about changing the OBJ'S var before calling the same $(NAME) target to get that to happen.

SRC = test1.c
BSRC = test2.c

OBJ = $(SRC:.c=.o) 
BOBJ = $(BSRC:.c=.o)
NAME = libtest.a
CC = gcc

all: $(NAME)

bonus: OBJ += $(BOBJ)
bonus: $(NAME)

$(NAME): $(OBJ)
    ar rcs $@ $^

this will result in:

compile_test> make bonus 
gcc    -c -o test1.o test1.c
ar rcs libtest.a test1.o

i am in confusion about why the first line of the bonus rule isn't working. I can add to the CFLAGS or to the SRC's: e.g.

SRC = test1.c
BSRC = test2.c

OBJ = $(SRC:.c=.o) 
BOBJ = $(BSRC:.c=.o)
NAME = libtest.a
CC = gcc
CFLAGS = -Wall

all: $(NAME)

bonus: SRC += $(BSRC)
bonus: OBJ += $(BOBJ)
bonus: CFLAGS += -g
bonus: $(NAME)

$(NAME): $(OBJ)
    ar rcs $@ $^

$(OBJ): $(SRC)
    $(CC) $(SRC) $(CFLAGS) -c

will run like this:

compile_test> make bonus 
gcc test1.c test2.c -Wall -g -c
ar rcs libtest.a test1.o

so it added to the SRC and to the CFLAG but not to the OBJ. At first i thought it would be something with $(OBJ) beeing a prerequisit of the target, but then after this test adding to SCR (a prerequisite as well) that idea got rewoked. I want to know why i cant add to OBJ.


Solution

  • I think I see; you mean "compiling" where you say "linking" in your question. I will explain what is happening by expanding the variables in your makefile.

    So, after expanding this is what make sees for rules:

    bonus: SRC += test2.c
    bonus: OBJ += test2.o
    bonus: CFLAGS += -g
    bonus: libtest.a
    
    libtest.a: test1.o                    # 1
            ar rcs libtest.a test1.o      # 2
    
    test1.o: test1.c                      # 3
            gcc test1.c test2.c -Wall -c  # 4
    

    At line #1 we can see that $(OBJS) expanded to test1.o, because as described in the docs the target-specific change only applies to recipes, not prerequisites.

    At line #2 we can see that $^ expands to just test1.o because that's the list of prerequisites for this rule.

    At line #3 we can see that $(OBJS) expands to test1.o and $(SRC) expands to test1.c for the same reason as above: target-specific variables are in effect only in recipes not in targets or prerequisites.

    At line #4 we can see that $(SRCS) expands to both test1.c and test2.c, because finally here we're using a target-specific variable setting inside a recipe. But, this rule is wrong because (a) it builds two output files: test1.o and test2.o, but (b) the target test1.o in the rule tells make that this rule only builds test1.o.

    This is the reason make runs the compiler twice if you run make bonus: it tries to build test1.o using your recipe, but it doesn't know that this rule builds two output files so it uses a built-in recipe to also build test2.o.

    Before we can tell you what you need to change, you need to tell us what you actually want your makefile to do when you run make bonus because it's really not clear. Do you want it to build the "normal" libtest.a, which contains only test1.o, and then in addition build an extra test2.o which is not in libtest.a? Or do you want libtest.a to contain both objects if you run make bonus?