Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate a Makefile with source in sub-directories using just one makefile

Tags:

makefile

I have source in a bunch of subdirectories like:

src/widgets/apple.cpp src/widgets/knob.cpp src/tests/blend.cpp src/ui/flash.cpp 

In the root of the project I want to generate a single Makefile using a rule like:

%.o: %.cpp    $(CC) -c $<  build/test.exe: build/widgets/apple.o build/widgets/knob.o build/tests/blend.o src/ui/flash.o    $(LD) build/widgets/apple.o .... build/ui/flash.o -o build/test.exe 

When I try this it does not find a rule for build/widgets/apple.o. Can I change something so that the %.o: %.cpp is used when it needs to make build/widgets/apple.o ?

like image 332
Jeroen Dirks Avatar asked Oct 23 '08 19:10

Jeroen Dirks


People also ask

How do I invoke a makefiles in subdirectories?

# Register all subdirectories in the project's root directory. SUBDIRS := $(wildcard */.) # Recurse `make` into each subdirectory. $(SUBDIRS): FORCE $(MAKE) -C $@ # A target without prerequisites and a recipe, and there is no file named `FORCE`.

What is $@ in makefile?

$@ is the name of the target being generated, and $< the first prerequisite (usually a source file). You can find a list of all these special variables in the GNU Make manual.

Can makefile target be a directory?

Yes, a Makefile can have a directory as target. Your problem could be that the cd doesn't do what you want: it does cd and the git clone is carried out in the original directory (the one you cd ed from, not the one you cd ed to). This is because for every command in the Makefile an extra shell is created.

What is Cflags in makefile?

CFLAGS and CXXFLAGS are either the name of environment variables or of Makefile variables that can be set to specify additional switches to be passed to a compiler in the process of building computer software.


2 Answers

The reason is that your rule

%.o: %.cpp        ... 

expects the .cpp file to reside in the same directory as the .o your building. Since test.exe in your case depends on build/widgets/apple.o (etc), make is expecting apple.cpp to be build/widgets/apple.cpp.

You can use VPATH to resolve this:

VPATH = src/widgets  BUILDDIR = build/widgets  $(BUILDDIR)/%.o: %.cpp       ... 

When attempting to build "build/widgets/apple.o", make will search for apple.cpp in VPATH. Note that the build rule has to use special variables in order to access the actual filename make finds:

$(BUILDDIR)/%.o: %.cpp         $(CC) $< -o $@ 

Where "$<" expands to the path where make located the first dependency.

Also note that this will build all the .o files in build/widgets. If you want to build the binaries in different directories, you can do something like

build/widgets/%.o: %.cpp         ....  build/ui/%.o: %.cpp         ....  build/tests/%.o: %.cpp         .... 

I would recommend that you use "canned command sequences" in order to avoid repeating the actual compiler build rule:

define cc-command $(CC) $(CFLAGS) $< -o $@ endef 

You can then have multiple rules like this:

build1/foo.o build1/bar.o: %.o: %.cpp     $(cc-command)  build2/frotz.o build2/fie.o: %.o: %.cpp     $(cc-command) 
like image 150
JesperE Avatar answered Oct 09 '22 02:10

JesperE


This does the trick:

CC        := g++ LD        := g++  MODULES   := widgets test ui SRC_DIR   := $(addprefix src/,$(MODULES)) BUILD_DIR := $(addprefix build/,$(MODULES))  SRC       := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.cpp)) OBJ       := $(patsubst src/%.cpp,build/%.o,$(SRC)) INCLUDES  := $(addprefix -I,$(SRC_DIR))  vpath %.cpp $(SRC_DIR)  define make-goal $1/%.o: %.cpp     $(CC) $(INCLUDES) -c $$< -o $$@ endef  .PHONY: all checkdirs clean  all: checkdirs build/test.exe  build/test.exe: $(OBJ)     $(LD) $^ -o $@   checkdirs: $(BUILD_DIR)  $(BUILD_DIR):     @mkdir -p $@  clean:     @rm -rf $(BUILD_DIR)  $(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir)))) 

This Makefile assumes you have your include files in the source directories. Also it checks if the build directories exist, and creates them if they do not exist.

The last line is the most important. It creates the implicit rules for each build using the function make-goal, and it is not necessary write them one by one

You can also add automatic dependency generation, using Tromey's way

like image 33
Manzill0 Avatar answered Oct 09 '22 02:10

Manzill0