Difference between revisions of "Make"

From HPC Wiki
Jump to navigation Jump to search
Line 46: Line 46:
  
 
The simple example does miss out on many benefits of using make.
 
The simple example does miss out on many benefits of using make.
The following example is a generic makefile for a C/C++ software project making use of some advanced features of make:
+
The following example is a generic makefile for a C/C++ software project making use of some advanced features of make providing the following benefits:
 
* Automatic dependency tracking
 
* Automatic dependency tracking
 
* Multiple tool chain specific build configurations
 
* Multiple tool chain specific build configurations

Revision as of 12:30, 24 October 2017

Make is a build automation tool that automatically builds arbitrary targets (in most cases program executables or libraries) from a list of prerequisites (which can be files or other targets). Make is controlled by reading files called Makefiles which specify how to derive the target. While make is a standard UNIX tool today in most cases people refer to the GNU make https://www.gnu.org/software/make/ implementation which provides a lot of extensions.

The main benefits of using a build automation tool are:

  • Automation :-) of repetitive command execution
  • Consistent build results preventing errors
  • Easier configuration for different tool chains
  • Formulate complex build settings with possibly various targets from the same source tree
  • Automatic matching of prerequisites
  • Speed up compilation by rebuilding only changed source files targets and parallel build support

Any serious software development effort sooner or later has to use a automated build tool. While make is the standard build tool on Linux systems there are various alternatives available, sometimes standalone and sometimes as a frontend to make.

Basic usage

A Makefile consists at its core of rules:

target … : prerequisites …
        recipe

Each rule is made of the target, which is either a real build result as a executable or library or just a label without a build result. After colon is a list of prerequisites, make will search either for a file for every prerequisite or tries to match a rule to build it. Finally the recipe is a command or list of commands to generate the target. A recipe consists of any shell command but may also contain make variables or macros.

A simple Makefile might look like that:

myProg: main.o kbd.o command.o display.o
        cc -o myProg main.o kbd.o command.o display.o

main.o : main.c defs.h
        cc -c main.c
kbd.o : kbd.c defs.h command.h
        cc -c kbd.c
command.o : command.c defs.h command.h
        cc -c command.c
display.o : display.c defs.h buffer.h
        cc -c display.c
clean :
        rm edit main.o kbd.o command.o display.o

By calling make without any arguments it will search for any file called makefile or Makefile and pick the first target in the file (indicated in this example by myProg:). The default target (the first one in the file) is matched first. Next make searches for the prerequisites, in this example a list of object files to link the executable. Next it either tries to find the object file or execute a rule to build it. In our case there is an explicit rule to build every object file. In case the object file was already build or is a primary source file make will check if the prerequisites are newer than the build result and automatically rebuild this file. This works in a recursive fashion: A target depends on prerequisites which themselves depend on prerequisites and so on. Make will automatically figure out which parts need to be executed in which order to get it right.

Above example also contains a target which simply triggers a command execution: The clean target does not build anything but automates cleaning up target and intermediate build results.

Tips and Tricks

The simple example does miss out on many benefits of using make. The following example is a generic makefile for a C/C++ software project making use of some advanced features of make providing the following benefits:

  • Automatic dependency tracking
  • Multiple tool chain specific build configurations
  • Generic build rules
  • Is based on naming conventions
  • Uses dedicated build result directories allowing to build multiple tool chain variants in the same source tree
TAG = ICC

#CONFIGURE BUILD SYSTEM
TARGET	   = myProg-$(TAG)
BUILD_DIR  = ./$(TAG)
SRC_DIR    = ./src
MAKE_DIR   = ./
Q         ?= @

#DO NOT EDIT BELOW
include $(MAKE_DIR)/include_$(TAG).mk
INCLUDES  += -I./src/includes

VPATH     = $(SRC_DIR)
OBJ       = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o,$(wildcard $(SRC_DIR)/*.c))
OBJ      += $(patsubst $(SRC_DIR)/%.cc, $(BUILD_DIR)/%.o,$(wildcard $(SRC_DIR)/*.cc))
OBJ      += $(patsubst $(SRC_DIR)/%.cpp, $(BUILD_DIR)/%.o,$(wildcard $(SRC_DIR)/*.cpp))

CPPFLAGS := $(CPPFLAGS) $(DEFINES) $(INCLUDES) 

${TARGET}: $(BUILD_DIR) $(OBJ)
	@echo "===>  LINKING  $(TARGET)"
	$(Q)${LINKER} ${LFLAGS} -o $(TARGET) $(OBJ) $(LIBS)

$(BUILD_DIR)/%.o:  %.c
	@echo "===>  COMPILE  $@"
	$(Q)$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
	$(Q)$(CC) $(CPPFLAGS) -MT $(@:.d=.o) -MM  $< > $(BUILD_DIR)/$*.d

$(BUILD_DIR)/%.o:  %.cc
	@echo "===>  COMPILE  $@"
	$(Q)$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@
	$(Q)$(CXX) $(CPPFLAGS) -MT $(@:.d=.o) -MM  $< > $(BUILD_DIR)/$*.d

$(BUILD_DIR)/%.o:  %.cpp
	@echo "===>  COMPILE  $@"
	$(Q)$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@
	$(Q)$(CXX) $(CPPFLAGS) -MT $(@:.d=.o) -MM  $< > $(BUILD_DIR)/$*.d

tags:
	@echo "===>  GENERATE  TAGS"
	$(Q)ctags -R


$(BUILD_DIR):
	@mkdir $(BUILD_DIR)

ifeq ($(findstring $(MAKECMDGOALS),clean),)
-include $(OBJ:.o=.d)
endif

.PHONY: clean distclean

clean:
	@echo "===>  CLEAN"
	@rm -rf $(BUILD_DIR)
	@rm -f tags

distclean: clean
	@echo "===> DIST CLEAN"
	@rm -f $(TARGET)
	@rm -f tags

An example tool chain configuration looks like the following (the file is named include_ICC.mk):

CC  = icc
CXX = icpc
LINKER = $(CXX)

CFLAGS   = -O3 -xAVX  -std=c99 
CXXFLAGS = -O3 -xAVX
LFLAGS   =  -vec-report0
DEFINES  = -D_GNU_SOURCE
INCLUDES = 
LIBS     =

Common Pitfalls

Links and more Information

  • GNU make info pages Probably the most complete and exhaustive documentation on make. Includes many examples.