4

This is a make file I have wrote with the current goal of turning all c++ files in a directory into object files. My issue is that it always executes g++ first then any other commands second.

CC=g++
CPPFLAGS=`pkg-config --cflags --libs gtkmm-3.0`
SRCS=$(wildcard classes/source/*.cc)
PROGS=$(patsubst %.cc,%,$(SRCS))
CLASS_HEADERS=classes/
all: $(PROGS)
    mkdir -p build/release/objs 
%: %.cc
    $(CC) -I$(CLASS_HEADERS) -c $< -o build/release/objs/$(@F).o $(CPPFLAGS)

results in:

g++ -Iclasses/ -c classes/source/helloworld.cc -o build/release/objs/helloworld.o `pkg-config --cflags --libs gtkmm-3.0`
mkdir -p build/release/objs 

Thanks

Russeree
  • 45
  • 4
  • 1
    Provide another dependency target for `build/release/objs`, and place that before `$(PROGS)`. – πάντα ῥεῖ May 16 '16 at 04:09
  • 1
    The rules for `all` say: make sure all the `$(PROGS)` are up to date; when that's done, running the `mkdir` will finish the job of creating `all`. You need something more like `all: objdir $(PROGS)` where the rule for `objdir` might be: `objdir: ; mkdir -p build/release/objs` (where you'd use a newline and tab in place of the semicolon, though the notation shown does actually work on a single line). This is just a longer version of what @πάνταῥεῖ said. – Jonathan Leffler May 16 '16 at 04:16
  • Jonathan Leffler you have solved my problem, and provided a great explanation to help me better understand how make works. – Russeree May 16 '16 at 04:28

3 Answers3

4

The simplest fix is to always issue mkdir before using the target directory. If using the -p option, there's really no harm in executing this command redundantly.

%: %.cc
   mkdir -p build/release/objs
   $(CC) -I$(CLASS_HEADERS) -c $< -o build/release/objs/$(@F).o $(CPPFLAGS)

As noted in my answer to GNU make's -j option: Although it may sometimes work, it is not correct to simply add a mkdir target as an initial dependency in your existing all dependency list. In principle, these could be executed in any order (so mkdir wouldn't necessarily be first). If you want to take the dependency-based approach, you have to start doing more complicated stuff. For example using make recursively:

all: do_mkdir
   $(MAKE) $(progs)

do_mkdir:
   mkdir -p build/release/objs
Community
  • 1
  • 1
Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
1

Possible solution could be creating a target for the object directory and place it before the build target:

......
OBJ_DIR := build/release/objs

all: create_obj_dir $(PROGS)

create_obj_dir:
    mkdir -p  $(OBJ_DIR)

%: %.cc
    $(CC) -I$(CLASS_HEADERS) -c $< -o $(OBJ_DIR)/$(@F).o $(CPPFLAGS)
Alex Lop.
  • 6,810
  • 1
  • 26
  • 45
1

Recipes for a target are executed after any prerequisites, so when you make all make will go through $(PROGS) before running mkdir.

build/release/objs should be a prerequisite of the object files, because they can't be made unless the directory exists. The directory should also be an order-only prerequisite because you don't want to remake every single object file if the dir timestamp changes.

Your pattern rule is broken(see 2) as well, and you're using the wrong flag variables. You can also recycle the built-in implicit rule for compiling.

CXXFLAGS := $(shell pkg-config --cflags --libs gtkmm-3.0)
CPPFLAGS := -Iclasses/
SRCS     := $(wildcard classes/source/*.cc)
OBJS     := $(SRCS:classes/source/%.cc=build/release/objs/%.o)

.PHONY: all
all: $(OBJS)

$(OBJS): build/release/objs/%.o: classes/source/%.cc | build/release/objs
    $(COMPILE.cc) $(OUTPUT_OPTION) $<

build/release/objs: ; mkdir -p $@

Side note: since you're only compiling -libs gtkmm-3.0 probably shouldn't be in the flags, you provide libs when linking.

user657267
  • 20,568
  • 5
  • 58
  • 77