DEV: Makefile changes (add docker support)

This commit is contained in:
Christos Choutouridis 2020-09-28 20:33:23 +03:00
parent 6588e83a68
commit d076b4ae38

View File

@ -1,165 +1,213 @@
# #
# Makefile for utl Unit test # Makefile for utl Unit test
# #
# Copyright (C) 2019-2020 Christos Choutouridis <christos@choutouridis.net> # Copyright (C) 2019-2020 Christos Choutouridis <christos@choutouridis.net>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as # it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 # published by the Free Software Foundation, either version 3
# of the License, or (at your option) any later version. # of the License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details. # GNU Lesser General Public License for more details.
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
# ========== Project settings ========== # ult notes:
# Excecutable's name # ==============
TARGET := utlTest # This makefile provides support for unix-like local builds and
# Source directories list(space seperated). Full or relative path # docker based builds for gcc and clang.
SRC_DIR_LIST := tests gtest # ** msvc is not currently supported.
# Include directories list(space seperated). Full or relative path #
INC_DIR_LIST := ../include gtest # Use cases:
# Exclude files list(space seperated). Filenames only. # 1) build localy using **local rules** in terminal.
# EXC_FILE_LIST := bad.cpp old.cpp # example:
# make -j4 MK_ARG="-std=c++14 -fconcepts" build-gcc
# Build directories #
BUILD_DIR := bin # 2) build localy using **docker based rules** via terminal or script
OBJ_DIR := $(BUILD_DIR)/obj # in order to compile for all compilers/versions/dialects.
DEP_DIR := $(BUILD_DIR)/.dep # example in bash:
# #!/bin/bash
# ========== Compiler settings ========== # for im in gcc:8 gcc:9 gcc:10; do
CLANGXX := clang++ # for dial in -std=c++14 -std=c++17; do
GCCXX := g++ # make IMAGE="$im" MK_ARG="$dial" dock-gcc
CSIZE := size # done
# Compiler flags for debug and release # done
DEB_CFLAGS := -DDEBUG -g3 -Wall -Wextra #
REL_CFLAGS := -Wall -Wextra -O2 # 3) build inside a docker instance using **local rules** in order to
# Pre-defines # build and test using a CD/CI system.
# PRE_DEFS := MYCAB=1729 SUPER_MODE # example in a yml file:
# image: gcc:8
# ========== Linker settings ========== # build:
# Linker flags # stage: build
LDFLAGS := -pthread # - make MK_ARG="-std=c++14" build-gcc
# Map output file # - make MK_ARG="-std=c++14 -fconcepts" build-gcc
MAP_FILE := output.map # - make MK_ARG="-std=c++17" build-gcc
MAP_FLAG := -Xlinker -Map=$(BUILD_DIR)/$(MAP_FILE) # - make MK_ARG="-std=c++17 -fconcepts" build-gcc
# - make MK_ARG="-std=c++2a" build-gcc
# ========== Default settings ========== # ... etc
# compiler and compiler flagfs # test:
CFLAGS := $(DEB_CFLAGS) # stage: test
CXX := $(GCCXX) # - bin/utlTest
# ... etc
#
# =========== Main body and Patterns ===========
# # ============== Project settings ==============
INC := $(foreach dir,$(INC_DIR_LIST),-I$(dir)) # Excecutable's name
DEF := $(foreach def,$(PRE_DEFS),-D$(def)) TARGET := utlTest
EXC := $(foreach fil,$(EXC_FILE_LIST), \ # Source directories list(space seperated).
$(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/$(fil))) \ # Relative path, under current directory only
) SRC_DIR_LIST := tests gtest
# source files, object and dependencies list # Include directories list(space seperated).
# recursive search into current and source directories # Relative path
SRC := $(wildcard *.cpp) INC_DIR_LIST := ../include gtest
SRC += $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/*.cpp)) # Exclude files list(space seperated). Filenames only.
SRC += $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/**/*.cpp)) # EXC_FILE_LIST := bad.cpp old.cpp
SRC := $(abspath $(filter-out $(EXC),$(SRC)))
# Build directories
OBJ := $(foreach file,$(SRC:%.cpp=%.o),$(OBJ_DIR)$(file)) BUILD_DIR := bin
DEP := $(foreach file,$(SRC:%.cpp=%.d),$(DEP_DIR)$(file)) OBJ_DIR := $(BUILD_DIR)/obj
DEP_DIR := $(BUILD_DIR)/.dep
# Make Dependencies pattern. # ============== Compiler settings ==============
# This little trick enables recompilation only when dependencies change CLANGXX := clang++
# and it does so for changes both in source AND header files ;) GCCXX := g++
# CSIZE := size
# It is based on Tom Tromey's method. # Compiler flags for debug and release
# DEB_CFLAGS := -DDEBUG -g3 -Wall -Wextra
# Invoke cpp to create makefile rules with dependencies for each source file REL_CFLAGS := -Wall -Wextra -O2
$(DEP_DIR)%.d: %.cpp # Pre-defines
@mkdir -p $(@D) # PRE_DEFS := MYCAB=1729 SUPER_MODE
@$(CXX) -E $(CFLAGS) $(INC) $(DEF) -MM -MT $(OBJ_DIR)$(<:.cpp=.o) -MF $@ $<
# ============== Linker settings ==============
# objects depent on .cpp AND dependency files, which have an empty recipe # Linker flags
$(OBJ_DIR)%.o: %.cpp $(DEP_DIR)%.d LDFLAGS := -pthread
@mkdir -p $(@D) # Map output file
$(CXX) -c $(CFLAGS) $(INC) $(DEF) -o $@ $< MAP_FILE := output.map
MAP_FLAG := -Xlinker -Map=$(BUILD_DIR)/$(MAP_FILE)
# empty recipe for dependency files. This prevents make errors
$(DEP): # ============== Docker settings ==============
# We need:
# now include all dependencies # 1) Bind the entire project directory(the dir that icludes all the code) as volume.
# After all they are makefile dependency rules ;) # 2) In docker instance change to working directory(where the makefile is).
include $(wildcard $(DEP)) # For utl we use the directories `${PWD%/*}` and `test`.
# We double-$$ for double evaluating.
# main target rule DOCKER_VOL_DIR := "$${PWD%/*}"
$(BUILD_DIR)/$(TARGET): $(OBJ) DOCKER_WRK_DIR := test
@mkdir -p $(@D) DOCKER_RUN := docker run --rm -v $(DOCKER_VOL_DIR):/usr/src/$(TARGET) -w /usr/src/$(TARGET)/$(DOCKER_WRK_DIR)
@echo Linking to target: $(TARGET)
@echo $(CXX) $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET) '$$(OBJ)' # ============== Default settings ==============
@$(CXX) $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET) $(OBJ) # compiler and compiler flags. By default docker is not used.
@echo CFLAGS := $(DEB_CFLAGS)
@echo Print size information CXX := $(GCCXX)
@$(CSIZE) $(@D)/$(TARGET) DOCKER :=
@echo Done
#
.PHONY: clean # =========== Main body and Patterns ===========
clean: #
@echo Cleaning build directories INC := $(foreach dir,$(INC_DIR_LIST),-I$(dir))
@rm -rf $(OBJ_DIR) DEF := $(foreach def,$(PRE_DEFS),-D$(def))
@rm -rf $(DEP_DIR) EXC := $(foreach fil,$(EXC_FILE_LIST), \
@rm -rf $(BUILD_DIR) $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/$(fil))) \
)
# source files, object and dependencies list
# # recursive search into current and source directories
# ============ User Rules ============= SRC := $(wildcard *.cpp)
# SRC += $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/*.cpp))
SRC += $(foreach dir,$(SRC_DIR_LIST),$(wildcard $(dir)/**/*.cpp))
.PHONY: gcc14 #SRC := $(abspath $(filter-out $(EXC),$(SRC)))
gcc14: CFLAGS += -std=c++14 SRC := $(filter-out $(EXC),$(SRC))
gcc14: $(BUILD_DIR)/$(TARGET)
OBJ := $(foreach file,$(SRC:%.cpp=%.o),$(OBJ_DIR)/$(file))
.PHONY: gcc14_conc DEP := $(foreach file,$(SRC:%.cpp=%.d),$(DEP_DIR)/$(file))
gcc14_conc: CFLAGS += -std=c++14 -fconcepts
gcc14_conc: $(BUILD_DIR)/$(TARGET)
# Make Dependencies pattern.
.PHONY: gcc17 # This little trick enables recompilation only when dependencies change
gcc17: CFLAGS += -std=c++17 # and it does so for changes both in source AND header files ;)
gcc17: $(BUILD_DIR)/$(TARGET) #
# It is based on Tom Tromey's method.
.PHONY: gcc17_conc #
gcc17_conc: CFLAGS += -std=c++17 -fconcepts # Invoke cpp to create makefile rules with dependencies for each source file
gcc17_conc: $(BUILD_DIR)/$(TARGET) $(DEP_DIR)/%.d: %.cpp
@mkdir -p $(@D)
.PHONY: gcc2a @$(DOCKER) $(CXX) -E $(CFLAGS) $(INC) $(DEF) -MM -MT $(OBJ_DIR)/$(<:.cpp=.o) -MF $@ $<
gcc2a: CFLAGS += -std=c++2a
gcc2a: $(BUILD_DIR)/$(TARGET) # objects depent on .cpp AND dependency files, which have an empty recipe
$(OBJ_DIR)/%.o: %.cpp $(DEP_DIR)/%.d
.PHONY: clang14 @mkdir -p $(@D)
clang14: CXX := $(CLANGXX) $(DOCKER) $(CXX) -c $(CFLAGS) $(INC) $(DEF) -o $@ $<
clang14: CFLAGS += -std=c++14
clang14: $(BUILD_DIR)/$(TARGET) # empty recipe for dependency files. This prevents make errors
$(DEP):
.PHONY: clang17
clang17: CXX := $(CLANGXX) # now include all dependencies
clang17: CFLAGS += -std=c++17 # After all they are makefile dependency rules ;)
clang17: $(BUILD_DIR)/$(TARGET) include $(wildcard $(DEP))
.PHONY: clang2a # main target rule
clang2a: CXX := $(CLANGXX) $(BUILD_DIR)/$(TARGET): $(OBJ)
clang2a: CFLAGS += -std=c++2a @mkdir -p $(@D)
clang2a: $(BUILD_DIR)/$(TARGET) @echo Linking to target: $(TARGET)
@echo $(DOCKER) $(CXX) $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET) '$$(OBJ)'
.PHONY: debug @$(DOCKER) $(CXX) $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET) $(OBJ)
debug: $(BUILD_DIR)/$(TARGET) @echo
@echo Print size information
.PHONY: release @$(CSIZE) $(@D)/$(TARGET)
release: CFLAGS := $(REL_FLAGS) @echo Done
release: clean $(BUILD_DIR)/$(TARGET)
.PHONY: clean
.PHONY: all clean:
all: clean release @echo Cleaning build directories
@rm -rf $(OBJ_DIR)
@rm -rf $(DEP_DIR)
@rm -rf $(BUILD_DIR)
#
# ================ Local build rules =================
# examples:
# make MK_ARG="-std=c++14 -fconcepts" build-gcc
# make MK_ARG="-std=c++17" build-clang
#
.PHONY: build-gcc
build-gcc: CFLAGS += $(MK_ARG)
build-gcc: $(BUILD_DIR)/$(TARGET)
.PHONY: build-clang
build-clang: CXX := $(CLANGXX)
build-clang: CFLAGS += $(MK_ARG)
build-clang: $(BUILD_DIR)/$(TARGET)
.PHONY: debug
debug: $(BUILD_DIR)/$(TARGET)
.PHONY: release
release: CFLAGS := $(REL_FLAGS)
release: clean $(BUILD_DIR)/$(TARGET)
.PHONY: all
all: clean release
#
# ================ Docker based rules ================
# examples:
# make IMAGE="gcc:8.3" MK_ARG="-std=c++14 -fconcepts" dock-gcc
# make IMAGE="a-clang-image" MK_ARG="-std=c++17" dock-clang
#
.PHONY: dock-gcc
dock-gcc: DOCKER := $(DOCKER_RUN) $(IMAGE)
dock-gcc: CFLAGS += $(MK_ARG)
dock-gcc: $(BUILD_DIR)/$(TARGET)
.PHONY: dock-clang
dock-clang: CXX := $(CLANGXX)
dock-clang: DOCKER := $(DOCKER_RUN) $(IMAGE)
dock-clang: CFLAGS += $(MK_ARG)
dock-clang: $(BUILD_DIR)/$(TARGET)