-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathMakefile
140 lines (118 loc) · 7.2 KB
/
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# This makefile only runs tests.
# By default it tests all available compilers with various options, but you can restrict the test matrix by setting the variables defined below.
# A line break.
override define lf :=
$(call)
$(call)
endef
# Make sure the dependencies are installed.
$(if $(wildcard ../macro_sequence_for),,$(error Need `macro_sequence_for` cloned to the parent directory, run:$(lf) git clone https://github.com/HolyBlackCat/macro_sequence_for ../macro_sequence_for$(lf)))
# Optimization modes to test. Override this with a subset of modes if you want to.
OPTIMIZE := O0_sanitized O0 Omax
OPTIM_FLAGS_O0_sanitized := -O0 -g -fsanitize=address -fsanitize=undefined
OPTIM_FLAGS_O0 := -O0
OPTIM_FLAGS_Omax := -O3
$(foreach x,$(OPTIMIZE),$(if $(OPTIM_FLAGS_$x),,$(error Unknown optimization level: $x)))
# Compilers to test. If not specified, we find all versions of GCC and Clang in PATH.
# Note that we only use version-suffixed compilers. The unsufixed compilers should be linked to one of those anyway?
COMPILER := $(shell bash -c 'compgen -c g++-; compgen -c clang++-; compgen -c cl; compgen -c clang-cl' | grep -Po '^((clan)?g\+\+(-[0-9]+)?|cl|clang-cl)(?=.exe)?$$' | sort -hr -t- -k2 | uniq)
$(if $(COMPILER),,$(error Unable to detect compilers, set `COMPILER=??` to a space-separated compiler list))
# C++ standards to test. Override this with a subset of standards if you want to.
# Only MSVC supports `latest`.
STANDARD := latest 20
# C++ standard libraries to test.
STDLIB := libstdc++ libc++ msvc
# Per-compiler flag customization. A list of `compiler=flag`. All matching flags for the current compiler are applied.
CXXFLAGS_PER_COMPILER :=
# Per-compiler+stdlib+optim environment variable customization for the tests executable. A list of `compiler:stdlib:optim:env=value`. Use `%` to match a pattern.
TEST_EXTRA_ENV :=
# Work around a bug: https://github.com/llvm/llvm-project/issues/59432
TEST_EXTRA_ENV += clang++-%:libc++:O0_sanitized:ASAN_OPTIONS=alloc_dealloc_mismatch=0
# Important compiler flags.
CXXFLAGS_DEFAULT := -Iinclude -I../macro_sequence_for/include -g -pedantic-errors -Wall -Wextra -Wdeprecated -Wextra-semi -ftemplate-backtrace-limit=0
CXXFLAGS_DEFAULT_MSVC := -Iinclude -I../macro_sequence_for/include -EHsc -Zc:preprocessor
# Less important compiler flags.
CXXFLAGS :=
SRC := tests.cpp
# Used to create local variables in a safer way. E.g. `$(call var,x := 42)`.
override var = $(eval override $(subst $,$$$$,$1))
.PHONY: tests
tests:
ifneq ($(words $(COMPILER)),1)
@true $(foreach x,$(COMPILER),&& $(MAKE) --no-print-directory COMPILER=$x)
else ifneq ($(words $(STANDARD)),1)
@true $(foreach x,$(STANDARD),&& $(MAKE) --no-print-directory STANDARD=$x)
else ifneq ($(words $(STDLIB)),1)
@true $(foreach x,$(STDLIB),&& $(MAKE) --no-print-directory STDLIB=$x)
else ifneq ($(words $(OPTIMIZE)),1)
@true $(foreach x,$(OPTIMIZE),&& $(MAKE) --no-print-directory OPTIMIZE=$x)
else ifneq ($(and $(filter g++%,$(COMPILER)),$(filter-out libstdc++,$(STDLIB))),)
@true # GCC only supports libstdc++.
else ifneq ($(and $(filter clang++%,$(COMPILER)),$(filter msvc,$(STDLIB))),)
@true # We don't use `clang` with MSVC's standard library. Instead we use `clang-cl`.
else ifneq ($(and $(filter %cl,$(COMPILER)),$(filter-out msvc,$(STDLIB))),)
@true # MSVC only supports its own standard library.
else ifeq ($(if $(filter g++% clang++%,$(COMPILER)),$(shell $(if $(filter g++%,$(COMPILER)),$(COMPILER) -v --help,$(COMPILER) -std=c++0 -xc++ /dev/null) 2>&1 | grep 'c++$(STANDARD)'),x),)
@true # Unsupported standard version for this compiler.
else ifneq ($(and $(filter clang++%,$(COMPILER)),$(filter libc++,$(STDLIB)),$(if $(wildcard /usr/lib/llvm-$(shell $(COMPILER) --version | grep -Po '(?<=version )[0-9]+')/include/c++),,x)),)
@true # Using Clang with libc++, but libc++ is not installed.
else ifneq ($(and $(filter clang-cl,$(COMPILER)),$(filter -fsanitize=address,$(OPTIM_FLAGS_$(OPTIMIZE)))),)
@true # ASAN on clang-cl gives us errors, unsure why. Since ASAN passes on Linux, we skip it.
else
$(call var,_env_overrides := $(strip $(foreach x,$(TEST_EXTRA_ENV),$(if $(and $(filter $(word 1,$(subst :, ,$x)),$(COMPILER)),$(filter $(word 2,$(subst :, ,$x)),$(STDLIB)),$(filter $(word 3,$(subst :, ,$x)),$(OPTIMIZE))),$(word 4,$(subst :, ,$x))))))
@+printf "%-11s C++%-7s %-10s %-14s... " $(COMPILER) $(STANDARD) $(STDLIB) $(OPTIMIZE)
@+$(if $(_env_overrides),echo -n '[with $(_env_overrides)] ')
@$(strip \
$(if $(filter %cl,$(COMPILER)),MSYS2_ARG_CONV_EXCL=/DEBUG)\
$(COMPILER) $(SRC) \
$(CXXFLAGS) \
$(CXXFLAGS_DEFAULT$(if $(filter %cl,$(COMPILER)),_MSVC)) \
$(call var,_optim_flags := $(OPTIM_FLAGS_$(OPTIMIZE)))\
$(if $(filter %cl,$(COMPILER)),\
$(call var,_optim_flags := $(filter-out -fsanitize=undefined -O0,$(_optim_flags)))\
$(call var,_optim_flags := $(patsubst -g,/DEBUG -Zi,$(_optim_flags)))\
$(call var,_optim_flags := $(patsubst -O3,-O2,$(_optim_flags)))\
)\
$(_optim_flags)\
-std$(if $(filter %cl,$(COMPILER)),:,=)c++$(STANDARD) \
$(if $(filter clang++%,$(COMPILER)),-stdlib=$(STDLIB)) \
$(patsubst $(COMPILER)=%,%,$(filter $(COMPILER)=%,$(CXXFLAGS_PER_COMPILER))) \
$(if $(filter %cl,$(COMPILER)),-link $(filter /DEBUG,$(_optim_flags)) -out:,-o)tests \
&& $(_env_overrides) ./tests \
)
endif
.PHONY: commands
commands:
$(eval override cxx := $(firstword $(filter clang++%,$(COMPILER))))
$(if $(cxx),,$(error Unable to guess the compiler))
$(eval override std := $(firstword $(filter-out latest,$(STANDARD))))
$(if $(std),,$(error Unable to guess the C++ standard version))
$(eval override stdlib := $(firstword $(filter-out latest,$(STDLIB))))
$(if $(stdlib),,$(error Unable to guess the C++ standard library))
$(file >compile_commands.json,[{"directory":"$(CURDIR)", "file":"$(abspath $(SRC))", "command":"$(cxx) $(SRC) $(CXXFLAGS) $(CXXFLAGS_DEFAULT) -std=c++$(std) -stdlib=$(stdlib)"}])
@true
# A reminder to bump the version number.
# We store the commit hash (plus the "dirty" flag) and the current version to a file called `$(last_version_file)`.
# If the hash changes but the version doesn't, we emit an error.
# Except that when the "dirty" flag changes from true to false, we silently accept the hash change once.
CHECK_VERSION = 1# Set to 0 to disable the version number check.
ifneq ($(and $(filter 0,$(MAKELEVEL)),$(filter-out 0,$(CHECK_VERSION))),)
last_version_file := .last_version
LAST_VER :=
LAST_COMMIT :=
-include $(last_version_file)
THIS_VER = $(shell printf "%05d" $$(grep -oP '#define RCORO_VERSION\s+\K[0-9]+' include/rcoro.hpp) | sed -E 's/(.*)(.{2})(.{2})/\1.\2.\3/')
$(if $(THIS_VER),,$(error Unable to determine the version number from the header.))
THIS_COMMIT := $(shell git describe --always --abbrev=0 --match "NOT A TAG" --dirty=-dirty)
$(if $(and $(THIS_COMMIT),$(filter 0,$(.SHELLSTATUS))),,$(error Unable to determine the current commit hash.))
$(info Version $(THIS_VER) at commit $(THIS_COMMIT))
$(info )
ifeq ($(LAST_VER),$(THIS_VER))
ifneq ($(LAST_COMMIT),$(THIS_COMMIT))
ifeq ($(and $(filter %-dirty,$(LAST_COMMIT)),$(filter-out %-dirty,$(THIS_COMMIT))),)
$(error Go bump the version number! Or pass `CHECK_VERSION=0` to ignore once. Or `rm .last_version` to ignore for this commit)
endif
endif
endif
$(file >$(last_version_file),LAST_VER := $(THIS_VER)$(lf)LAST_COMMIT := $(THIS_COMMIT))
endif