##### BASE MAKEFILE # # This makefile is intended to be a basis on which new makefiles are written. # It is divided into five parts: # * help text generation # * path for subproject locations and thirparty include/library paths # * default values for tools (like CXX) and compiler/linker flags # * description of known libraries that may be used in the build # * functions that define build artifacts # # library handling will be described in detail later. # # we have three functions that defined build artifacts: # * build-executable # * build-static-library # * build-dynamic-library # # they all have the same, basic signature: # (name: string, sources: list, usedLibraries: list) # # $name determines the name given to the output file (for executables) or the library name given # to the output library (for libraries). # # $sources must be a list of source files (allowed types are .cpp and .c), relative to the directory # of the Makefile you are writing (see example below). # # $usedLibraries is a (possibly empty) list of libraries as defined in the KNOWN LIBRARIES section # below. they will be automatically added to compiler and linker invocations as needed. # # EXAMPLE: # # imagine you have a project with a directory structure like this: # - /build # - Makefile # - /source # - /lib # - ... # - /exe # - ... # # you want your Makefile to build a shared library from /source/lib and an executable from # /source/exe. the you could write your Makefile like this: # # include ,../../build/Makefile # # # will generate a libFancy.so in /build, uses libz # $(call build-static-library,\ # Fancy,\ # $(shell find ../source/lib -iname '*.cpp'),\ # z) # # # this registers libFancy as a shared library. see details below (KNOWN LIBRARIES). # $(call define-dep-lib, Fancy, -I ../source/lib/include, -L . -l Fancy) # # # will generate an executable file Run in /build, uses libFancy # $(call build-executable,\ # Run,\ # $(shell find ../source/exe -iname '*.cpp'),\ # Fancy) # # # a dependency must be added between Run and Fancy, to ensure that Fancy is built first. # Run: | libFancy.so override V := $(if $V,,@) all: define HELP_ARGS_GENERIC @echo ' BEEGFS_DEBUG=1 Enables debug information and symbols' @echo ' BEEGFS_DEBUG_OPT=1 Enables internal debug code, but compiled' @echo ' with optimizations' @echo ' BEEGFS_DEBUG_IP=1 Enables low-level debug of network sendto' @echo ' and recvfrom' @echo ' CXX= Specifies a c++ compiler' @echo ' DISTCC=distcc Enables the usage of distcc' @echo ' V=1 Print command lines of tool invocations' @echo ' BEEGFS_COMMON_PATH= Path to the common directory' @echo ' BEEGFS_THIRDPARTY_PATH=' @echo ' Path to the thirdparty directory' @echo ' BEEGFS_USE_PCH=1 Enables use of precompiled headers' endef define HELP_TARGETS_GENERIC @echo ' all (default) build only' @echo ' clean delete compiled files' @echo ' help print this help message' endef help: @echo 'Optional arguments:' $(HELP_ARGS_GENERIC) $(HELP_ARGS_SPECIFIC) @echo @echo 'Targets:' $(HELP_TARGETS_GENERIC) $(HELP_TARGETS_SPECIFIC) root_dir := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))/..) build_dir := $(realpath $(dir $(firstword $(MAKEFILE_LIST)))) BEEGFS_COMMON_PATH ?= $(root_dir)/common BEEGFS_COMMON_PCH_H ?= $(BEEGFS_COMMON_PATH)/source/common/pch.h BEEGFS_THIRDPARTY_PATH ?= $(root_dir)/thirdparty BEEGFS_COMM_DEBUG_PATH ?= $(root_dir)/comm_debug BEEGFS_FSCK_PATH ?= $(root_dir)/fsck BEEGFS_EVENT_LISTENER_PATH ?= $(root_dir)/event_listener BEEGFS_DEVEL_INCLUDE_PATH ?= $(root_dir)/client_devel/include BEEGFS_CLIENT_PATH ?= $(root_dir)/client_module/include BOOST_INC_PATH ?= $(BEEGFS_THIRDPARTY_PATH)/source/boost NU_INC_PATH ?= $(BEEGFS_THIRDPARTY_PATH)/source/nu/include GTEST_INC_PATH ?= $(BEEGFS_THIRDPARTY_PATH)/source/gtest/googletest/include GTEST_LIB_PATH ?= $(BEEGFS_THIRDPARTY_PATH)/build ifneq ($(target_arch),) STRIP := $(target_arch)-strip AR := $(target_arch)-ar CC := $(target_arch)-gcc CXX := $(target_arch)-g++ endif SHELL := /bin/bash STRIP ?= strip CXX ?= g++ AR ?= ar CLANG_TIDY ?= clang-tidy # if -T is supported by ar, use it. thin archives are quicker to create and maintain. ifeq ($(shell ar -TM 2>&1 <<<""),) AR += -T endif CXXFLAGS = \ -std=c++17 -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \ -isystem $(BOOST_INC_PATH) -isystem $(NU_INC_PATH) \ -pthread \ -fno-strict-aliasing -fmessage-length=0 \ -Wall -Wextra -Wunused-variable -Woverloaded-virtual -Wno-unused-parameter -Wuninitialized \ -Wno-missing-field-initializers \ -Winvalid-pch \ -ggdb3 \ $(USER_CXXFLAGS) CXXFLAGS_RELEASE = -O3 -Wuninitialized CXXFLAGS_DEBUG = -O1 -D_FORTIFY_SOURCE=2 \ -DBEEGFS_DEBUG -DDEBUG_READWRITE -DDEBUG_MUTEX_LOCKING -DDEBUG_REFCOUNT \ -DDEBUG_BACKTRACE -DLOG_DEBUG_MESSAGES CXXFLAGS_COVERAGE = --coverage -O1 -fno-inline LDFLAGS = -std=c++1y \ -rdynamic \ -pthread -lrt \ -Wl,-rpath,'$$ORIGIN/../lib' \ $(USER_LDFLAGS) LDFLAGS_COVERAGE = --coverage ifneq ($(BEEGFS_DEBUG),) CXXFLAGS += $(CXXFLAGS_DEBUG) else ifneq ($(BEEGFS_COVERAGE),) CXXFLAGS += $(CXXFLAGS_COVERAGE) LDFLAGS += $(LDFLAGS_COVERAGE) else CXXFLAGS += $(CXXFLAGS_RELEASE) endif CXXFLAGS += -DBEEGFS_VERSION="\"$(BEEGFS_VERSION)\"" ifeq ($(BEEGFS_NVFS),1) CXXFLAGS += -DBEEGFS_NVFS endif ifeq ($(BEEGFS_DEBUG_RDMA),1) CXXFLAGS += -DBEEGFS_DEBUG_RDMA endif ifneq ($(BEEGFS_DEBUG_IP),) CXXFLAGS += -DBEEGFS_DEBUG_IP endif ###### KNOWN LIBRARIES # # this is our local registry of known libraries, both our own and thirdparty libraries. # # currently supports building with our own libraries common and client-devel. # thirdparty packages supported are: # * dl (from system) # # to define new libraries, add appriopriate calls to define-dep-lib in this section. # the signature of define-dep-lib is: # (name: string, CXXFLAGS: string, libraryPath: string[, LDFLAGS: string]) # # $name is used to identify the library within this registry, and is otherwise unused. # # $CXXFLAGS will be added to the set of CXXFLAGS used when compiling files from projects that use # this library (see example at the beginning of this file for reference). # # $libraryPath is the path (absolute or relative) to the library. glob patterns are allowed, and in # fact required if you want to link a dynamic library. # # $LDFLAGS will be added to the set of LDFLAGS used when linking shared libraries or executables # that use this library. if the library you want to link is static, LDFLAGS need not be set - the # library will be added to the list of archives. # # # to define libraries that are taken from the system, use define-dep-lib-system. it will use its # arguments to ask pkgconfig for the correct compiler and linker flags. then signature of # define-dep-lib-system is: # (name: string, pkgconfigID: string) # # $name is used only to identify the library. # # $pkgconfigID is the identifier pkgconfig will be asked about. define resolve-dep-cflags $(if $(filter undefined,$(origin _DEP_LIB_CXX[$(strip $1)])),\ $(error I have no CXXFLAGS for $1!),\ $(_DEP_LIB_CXX[$(strip $1)])) endef define resolve-dep-ldflags $(if $(filter undefined,$(origin _DEP_LIB_LD[$(strip $1)])),\ $(error I have no LDFLAGS for $1!),\ $(_DEP_LIB_LD[$(strip $1)])) endef define resolve-dep-deps $(if $(filter undefined,$(origin _DEP_LIB_DEPS[$(strip $1)])),\ $(error I have no library dependencies for $1!),\ $(_DEP_LIB_DEPS[$(strip $1)])) endef define define-dep-lib $(strip $(eval _DEP_LIB_CXX[$(strip $1)] = $(strip $2)) $(eval _DEP_LIB_LD[$(strip $1)] = $(strip $(or $4, $3))) $(eval _DEP_LIB_DEPS[$(strip $1)] = $(strip $3)) $(eval $(strip $3): )) endef define define-dep-lib-system $(strip $(if $(shell pkg-config --exists $2 || echo fail), $(error Could not find library $2 in system!)) $(call define-dep-lib, $1, $(shell pkg-config --cflags $2), $(shell pkg-config --libs $2))) endef # don't bother to search the system for libraries if we only run `make clean' # this also has the nice side effect that libraries needn't be present in the system # when `make clean' is run - if we override dependency resolution to not do anything. ifeq ($(strip $(MAKECMDGOALS)),clean) resolve-dep-cflags := resolve-dep-ldflags := resolve-dep-deps := else $(call define-dep-lib, common,\ -I $(BEEGFS_COMMON_PATH)/source \ -I $(BEEGFS_CLIENT_PATH), \ -ldl $(BEEGFS_COMMON_PATH)/build/libbeegfs-common.a) $(call define-dep-lib, client-devel,\ -I $(BEEGFS_DEVEL_INCLUDE_PATH),) $(call define-dep-lib, dl, , , -ldl) $(call define-dep-lib, rdma, , -lrdmacm -libverbs) $(call define-dep-lib, gtest,\ -isystem $(GTEST_INC_PATH)/include,\ $(GTEST_LIB_PATH)/libgtest.a) $(call define-dep-lib, cassandra, -I$(BEEGFS_THIRDPARTY_PATH)/source/datastax) $(call define-dep-lib-system, curl, libcurl) $(call define-dep-lib-system, z, zlib) $(call define-dep-lib, blkid, , , -lblkid) $(call define-dep-lib, uuid, , , -luuid) $(call define-dep-lib-system, nl3-route, libnl-route-3.0) endif clean: @$(RM) -rf $(if $V,,-v) $(CLEANUP_FILES) NONDEP_MAKEFILES = $(filter-out %.d,$(MAKEFILE_LIST)) %.autogen.h: @# Generate a proxy-header for precompilation. @true > $@ # truncate file @echo '/* Autogenerated precompiled header (PCH)' >> $@ @echo ' * For each set of compiler options we need to compile ' >> $@ @echo ' * a PCH separately. To realize that, we create ' >> $@ @echo ' * proxy PCHs like this file and compile these. */' >> $@ @echo '#include "$(BEEGFS_COMMON_PCH_H)"' >> $@ %.h.gch: %.h @echo "[CXX] $@" $V$(CXX) $(CXXFLAGS) -c $< -E -MMD -MP -MF$<.d -MT$@ -o/dev/null $V$(DISTCC) $(CXX) $(CXXFLAGS) -o$@ -c $(realpath $<) %.cpp.o: %.cpp $(NONDEP_MAKEFILES) @echo "[CXX] $<" $V$(DISTCC) $(CXX) $(CXXFLAGS) -MMD -MF$<.d -MT$<.o -MP -o $<.o -c $(realpath $<) %.c.o: %.c $(NONDEP_MAKEFILES) @echo "[CXX] $<" $V$(DISTCC) $(CXX) $(CXXFLAGS) -MMD -MF$<.d -MT$<.o -MP -o$<.o -c $(realpath $<) # first partial list: checks we want to exclude on a general basis right now # second list: very minor problems we don't want to be bugged about yet, but want to fix over time # third list: definite errors, fix as soon as possible define -clang_tidy_checks -clang-analyzer-alpha.deadcode.UnreachableCode -fuchsia-default-arguments -fuchsia-overloaded-operator -google-build-using-namespace -google-readability-braces-around-statements -google-readability-casting -google-readability-namespace-comments -hicpp-braces-around-statements -llvm-header-guard -llvm-include-order -llvm-namespace-comment -readability-braces-around-statements -readability-implicit-bool-conversion -android-cloexec-accept -android-cloexec-creat -android-cloexec-dup -android-cloexec-epoll-create -android-cloexec-fopen -android-cloexec-open -boost-use-to-string -bugprone-branch-clone -bugprone-exception-escape -bugprone-narrowing-conversions -bugprone-sizeof-expression -cert-oop54-cpp -clang-analyzer-core.CallAndMessage -clang-analyzer-core.uninitialized.Assign -clang-analyzer-cplusplus.NewDeleteLeaks -clang-analyzer-deadcode.DeadStores -clang-analyzer-optin.cplusplus.UninitializedObject -clang-diagnostic-deprecated-copy -cppcoreguidelines-avoid-c-arrays -cppcoreguidelines-avoid-goto -cppcoreguidelines-avoid-magic-numbers -cppcoreguidelines-init-variables -cppcoreguidelines-interfaces-global-init -cppcoreguidelines-macro-usage -cppcoreguidelines-narrowing-conversions -cppcoreguidelines-non-private-member-variables-in-classes -cppcoreguidelines-pro-bounds-array-to-pointer-decay -cppcoreguidelines-pro-bounds-constant-array-index -cppcoreguidelines-pro-bounds-pointer-arithmetic -cppcoreguidelines-pro-type-const-cast -cppcoreguidelines-pro-type-cstyle-cast -cppcoreguidelines-pro-type-member-init -cppcoreguidelines-pro-type-reinterpret-cast -cppcoreguidelines-pro-type-static-cast-downcast -cppcoreguidelines-pro-type-union-access -cppcoreguidelines-pro-type-vararg -cppcoreguidelines-special-member-functions -fuchsia-default-arguments-calls -fuchsia-default-arguments-declarations -fuchsia-statically-constructed-objects -google-default-arguments -google-explicit-constructor -google-readability-avoid-underscore-in-googletest-name -google-readability-redundant-smartptr-get -google-readability-todo -google-runtime-int -google-runtime-references -hicpp-avoid-c-arrays -hicpp-avoid-goto -hicpp-deprecated-headers -hicpp-explicit-conversions -hicpp-member-init -hicpp-multiway-paths-covered -hicpp-no-array-decay -hicpp-no-assembler -hicpp-no-malloc -hicpp-signed-bitwise -hicpp-special-member-functions -hicpp-uppercase-literal-suffix -hicpp-use-auto -hicpp-use-emplace -hicpp-use-equals-default -hicpp-use-equals-delete -hicpp-use-noexcept -hicpp-use-nullptr -hicpp-vararg -llvm-qualified-auto -misc-misplaced-widening-cast -misc-move-constructor-init -misc-non-private-member-variables-in-classes -misc-redundant-expression -misc-string-compare -misc-unused-parameters -modernize-avoid-bind -modernize-avoid-c-arrays -modernize-loop-convert -modernize-make-shared -modernize-make-unique -modernize-pass-by-value -modernize-raw-string-literal -modernize-replace-random-shuffle -modernize-return-braced-init-list -modernize-use-auto -modernize-use-default -modernize-use-default-member-init -modernize-use-nodiscard -modernize-use-emplace -modernize-use-equals-default -modernize-use-equals-delete -modernize-use-noexcept -modernize-use-nullptr -modernize-use-trailing-return-type -modernize-use-using -performance-faster-string-find -performance-inefficient-string-concatenation -performance-unnecessary-copy-initialization -performance-unnecessary-value-param -readability-const-return-type -readability-convert-member-functions-to-static -readability-else-after-return -readability-implicit-bool-cast -readability-isolate-declaration -readability-magic-numbers -readability-make-member-function-const -readability-named-parameter -readability-non-const-parameter -readability-qualified-auto -readability-redundant-control-flow -readability-redundant-smartptr-get -readability-redundant-string-cstr -readability-string-compare -readability-uppercase-literal-suffix -cert-err58-cpp -cert-err60-cpp -cert-msc30-c -cert-msc50-cpp -clang-analyzer-alpha.cplusplus.VirtualCall -clang-analyzer-core.DivideZero -clang-analyzer-optin.cplusplus.VirtualCall -cppcoreguidelines-no-malloc -cppcoreguidelines-owning-memory -readability-misleading-indentation endef clang_tidy_checks := *,$(shell echo $(strip $(-clang_tidy_checks)) | tr ' ' ',') # make-tidy-rule # # add a rule to invoke clang-tidy for the given source files # arguments: # #1: identifier for library/executable # #2: source files # #3: include paths and defines make-tidy-rule = $(eval $(call -make-tidy-rule,$(strip $1),$2,$3)) define -specific-tidy-rule $1: @echo "[TIDY] $2" $V$(CLANG_TIDY) -checks='$(clang_tidy_checks)' -quiet $2 -- $$(CXXFLAGS) $3 endef define -make-tidy-rule .PHONY: tidy tidy: tidy-$1 .PHONY: tidy-$1 $(foreach file,$2,$(file).tidy-$1) tidy-$1: $(foreach file,$2,$(file).tidy-$1) $(foreach file,$2,$(eval $(call -specific-tidy-rule,$(file).tidy-$1,$(file),$3))) endef define -build-pch-fragment ifneq ($(BEEGFS_USE_PCH),) # .o files depend on precompiled headers. $(addsuffix .o,$2): CXXFLAGS += -include $(1).autogen.h $(addsuffix .o,$2): $(1).autogen.h.gch endif # not behind conditional -- let's delete these always CLEANUP_FILES += $(1).autogen.h $(1).autogen.h.gch CLEANUP_FILES += $(1).autogen.h.d # "dependency" files that get produced when compiling the header endef # build-executable # # define a new executable for the build # arguments: # #1: name of the executable # #2: sources # #3: required libraries # #4: include directories build-executable = $(eval $(call -build-executable-fragment,$(strip $1),$2,$3,$4)) define -build-executable-fragment all: $1 $(call -build-pch-fragment,$1,$2) CLEANUP_FILES += $1 $(addsuffix .o,$2) $(addsuffix .d,$2) $(addsuffix .o .tidy-%,$2): CXXFLAGS += \ $(foreach lib,$3,$(call resolve-dep-cflags,$(lib))) $(addprefix -I, $4) $1: LDFLAGS += \ -Wl,--start-group $(foreach lib,$3,$(call resolve-dep-ldflags,$(lib))) -Wl,--end-group $1: $(addsuffix .o,$2) $(foreach lib,$3,$(call resolve-dep-deps,$(lib))) @echo "[LD] $$@" $$V$$(CXX) -o $$@ $(addsuffix .o,$2) $$(LDFLAGS) -include $(addsuffix .d,$2) $(call make-tidy-rule, $1, $2,\ $(foreach lib,$3,$(call resolve-dep-cflags,$(lib))) $(addprefix -I, $4)) endef # build-test # # build a test runner from specified test files. acts much like build-executable, except that it # includes gtest in the build as a non-library .a # #1: name if the test executable # #2: sources # #3: required libraries (not including gtest) # #4: included directories (not including gtest) define build-test $(call build-executable, $1, $2, $3, $4 -isystem $(GTEST_INC_PATH)) $1: LDFLAGS += $(GTEST_LIB_PATH)/libgtest.a endef # build-static-library # # define a new (static) library for the build # arguments: # #1: name of the library # #2: sources # #3: required libraries # #4: include directories build-static-library = $(eval $(call -build-static-library-fragment,lib$(strip $1).a,$2,$3,$4)) define -build-static-library-fragment all: $1 $(call -build-pch-fragment,$1,$2) CLEANUP_FILES += $1 $(addsuffix .o,$2) $(addsuffix .d,$2) $(addsuffix .o .tidy-%,$2): CXXFLAGS += \ $(foreach lib,$3,$(call resolve-dep-cflags,$(lib))) $(addprefix -I, $4) $(build_dir)/$1: $(addsuffix .o,$2) @echo "[AR] $1" @rm -f $$@ $$V$(AR) -rcs $$@ $$^ $1: $(build_dir)/$1 -include $(addsuffix .d,$2) $(call make-tidy-rule, $1, $2,\ $(foreach lib,$3,$(call resolve-dep-cflags,$(lib))) $(addprefix -I, $4)) endef # build-shared-library # # define a new (shared) library for the build # arguments: # #1: name of the library # #2: sources # #3: required libraries # #4: include directories build-shared-library = $(eval $(call -build-shared-library-fragment,lib$(strip $1).so,$2,$3,$4)) define -build-shared-library-fragment all: $1 $(call -build-pch-fragment,$1,$2) CLEANUP_FILES += $1 $(addsuffix .o,$2) $(addsuffix .d,$2) $(addsuffix .o .tidy-%,$2): CXXFLAGS += \ -fPIC $(foreach lib,$3,$(call resolve-dep-cflags,$(lib))) $(addprefix -I, $4) $1: LDFLAGS += \ -Wl,--start-group $(foreach lib,$3,$(call resolve-dep-ldflags,$(lib))) -Wl,--end-group $(build_dir)/$1: $(addsuffix .o,$2) $(foreach lib,$3,$(call resolve-dep-deps,$(lib))) @echo "[LD] $1" $$V$$(CXX) -shared -o $$@ $(addsuffix .o,$2) \ -Wl,--whole-archive $$(LDFLAGS) -Wl,--no-whole-archive $1: $(build_dir)/$1 -include $(addsuffix .d,$2) $(call make-tidy-rule, $1, $2,\ $(foreach lib,$3,$(call resolve-dep-cflags,$(lib))) $(addprefix -I, $4)) endef