beegfs/build/Makefile
2025-08-10 01:34:16 +02:00

599 lines
19 KiB
Makefile

##### 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<string>, usedLibraries: list<string>)
#
# $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=<compiler> 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> Path to the common directory'
@echo ' BEEGFS_THIRDPARTY_PATH=<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