-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathMakefile.common.header
256 lines (216 loc) · 9.91 KB
/
Makefile.common.header
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# -*- Makefile -*-
# This is a part of the mrbuild project: https://github.com/dkogan/mrbuild
#
# Released under an MIT-style license. Modify and distribute as you like:
#
# Copyright 2016-2019 California Institute of Technology
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# osx
COND_DARWIN := $(filter Darwin,$(shell uname -s))
SO := $(if $(COND_DARWIN),dylib,so)
# Hacks to get spaces and newlines in places where Make would normally parse
# them out
SPACE :=
SPACE := $(SPACE) $(SPACE)
define NEWLINE
endef
dirs_to_dotdot = $(subst $(SPACE),/,$(patsubst %,..,$(subst /, ,$1)))
get_parentdir_relative_to_childdir = /$(call dirs_to_dotdot,$(patsubst $1%,%,$2))
RPATH_SUFFIX = $(call get_parentdir_relative_to_childdir,$(abspath .),$(dir $(abspath $1)))
ifeq ($(COND_DARWIN),)
RPATH = '$$ORIGIN'$(RPATH_SUFFIX)
else
RPATH = @executable_path$(RPATH_SUFFIX)
endif
RPATH_OPTION = -Wl,-rpath,$(call RPATH,$@)
# This stuff defines variables (PY_EXT_SUFFIX) that could be used by the user
# Makefile at parsing time. So this must be included BEFORE the rest of the user
# Makefile
PYTHON_VERSION_FOR_EXTENSIONS ?= 3 # 2 or 3
# Flags for python extension modules. See
# http://notes.secretsauce.net/notes/2017/11/14_python-extension-modules-without-setuptools-or-distutils.html
#
# I build the python extension module without any setuptools or anything.
# Instead I ask python about the build flags it likes, and build the DSO
# normally using those flags.
#
# There's some sillyness in Make I need to work around. First, I produce a
# python script to query the various build flags, but replacing all whitespace
# with __whitespace__. The string I get when running this script will then have
# a number of whitespace-separated tokens, each setting ONE variable
#
# I set up a number of variables:
#
# These come from Python queries. I ask Python about XXX and store the result
# into PY_XXX
#
# PY_CC
# PY_CFLAGS
# PY_CCSHARED
# PY_INCLUDEPY
# PY_BLDSHARED
# PY_LDFLAGS
# PY_EXT_SUFFIX
# PY_MULTIARCH
#
# These process the above into a single set of CFLAGS:
#
# PY_MRBUILD_CFLAGS
#
# These process the above into a single set of LDFLAGS:
#
# PY_MRBUILD_LDFLAGS
#
# These process the above into a DSO-building linker command
#
# PY_MRBUILD_LINKER
#
# When the user Makefile evaluates ANY of these variables I query python, and
# memoize the results. So the python is invoked at MOST one time. Any Makefiles
# that don't touch the PY_... variables will not end up invoking the python
# thing at all
#
# Variables to ask Python about
_PYVARS_LIST := CC CFLAGS CCSHARED INCLUDEPY BLDSHARED BLDLIBRARY LDFLAGS EXT_SUFFIX MULTIARCH
ifneq ($(DEB_HOST_MULTIARCH),)
# We have a debian multiarch. We're almost certainly cross-building with
# Debian. Try to get the variables directly, using any-architecture python
define _PYVARS_GET_CONFIG :=
import ast
import sys
prefixes = ("_sysconfigdata_", "_sysconfigdata_m_linux")
sysconfig_paths = [d + "/" + prefix + "_$(DEB_HOST_MULTIARCH).py" for d in sys.path for prefix in prefixes]
for filename in sysconfig_paths:
try:
f = open(filename)
except:
continue
s = f.read()
s = re.sub("^#.*", "", s, flags=re.M)
s = re.sub("^[0-9a-zA-Z_]+ *= *", "", s, flags=re.M)
conf = ast.literal_eval(s)
break
else:
raise Exception("Could not find sysconfig file in any sys.path directories. Tried: {}".format(sysconfig_paths))
endef
else
_PYVARS_GET_CONFIG := conf = sysconfig.get_config_vars()
endif
# Python script to query those variables
define _PYVARS_SCRIPT
from __future__ import print_function
import sysconfig
import re
# get "conf" dict
$(_PYVARS_GET_CONFIG)
for v in ($(foreach v,$(_PYVARS_LIST),"$v",)):
if v in conf:
print(re.sub("[\t ]+", "__whitespace__", "_PY_{}:={}".format(v, conf[v])))
endef
# I eval this to actually invoke the Python and to ingest its results. I only
# eval this ONLY when necessary.
define query_python_extension_building_flags
_PYVARS = $(shell python$(PYTHON_VERSION_FOR_EXTENSIONS) -c '$(_PYVARS_SCRIPT)')
# I then $(eval) these tokens one at a time, restoring the whitespace
$(foreach setvarcmd,$(_PYVARS),$(eval $(subst __whitespace__, ,$(setvarcmd))))
# Add the numpy include path. See https://bugs.debian.org/1067398
_PY_INCLUDEPY += $(shell $(or $(PKG_CONFIG),pkg-config) --cflags-only-I numpy 2>/dev/null)
# pull out flags from CC, throw out the compiler itself, since I know better
_FLAGS_FROM_PYCC = $(wordlist 2,$(words $(_PY_CC)),$(_PY_CC))
_PY_MRBUILD_CFLAGS = $(filter-out -O%,$(_FLAGS_FROM_PYCC) $(_PY_CFLAGS) $(_PY_CCSHARED) -I$(_PY_INCLUDEPY))
# In the python api I have to cast a PyCFunctionWithKeywords to a PyCFunction,
# and the compiler complains. But that's how Python does it! So I tell the
# compiler to chill
_PY_MRBUILD_CFLAGS += -Wno-cast-function-type
# similarly, with gcc-14 I get this without turning off this warning:
# /usr/include/python3.12/object.h:142:9: error: initialization of 'long int' from 'void *' makes integer from pointer without a cast [-Wint-conversion]
_PY_MRBUILD_CFLAGS += -Wno-int-conversion
# This is how we link the extension module DSOs. Note that we do NOT link with
# -lpython, or anything of the sort (from $(_PY_BLDLIBRARY)). This isn't
# strictly necessary because this will only be linked from the python
# executable, so the library will already be loaded. And in some situations
# (static linking), passing $(_PY_BLDLIBRARY) by itself doesn't actually work:
# https://github.com/dkogan/mrcal/issues/22
_PY_MRBUILD_LDFLAGS = $(RPATH_OPTION) $(_PY_LDFLAGS) -L$(abspath .)
_PY_MRBUILD_LINKER = $(_PY_BLDSHARED)
endef
# List of variables a user Makefile could touch. These are all PY_...
_PYVARS_API := $(foreach v,$(_PYVARS_LIST),PY_$v) PY_MRBUILD_CFLAGS PY_MRBUILD_LDFLAGS PY_MRBUILD_LINKER
# The first time the user touches these variables, ask Python. Each subsequent
# time, use the previously-returned value. So we query Python at most once. If a
# project isn't using the Python extension modules, we will not query Python at
# all
#
# I handle all the Python API variables identically, except for PY_EXT_SUFFIX.
# If Python gives me a suffix, I use it (this is available in python3; it has
# ABI, architecture details). Otherwise, I try the multiarch suffix, or if even
# THAT isn't available, just do .so. I need to handle it specially to make the
# self-referential logic work with the memoization logic
define _PY_DEFINE_API_VAR
$1 = $$(or $$(_$1),$$(eval $$(value query_python_extension_building_flags))$$(_$1))
endef
define _PY_DEFINE_API_VAR_EXTSUFFIX
$1 = $$(or $$(_$1),$$(eval $$(value query_python_extension_building_flags))$$(or $$(_$1),$$(if $$(PY_MULTIARCH),.$$(PY_MULTIARCH)).$$(SO)))
endef
$(foreach v,$(filter-out PY_EXT_SUFFIX,$(_PYVARS_API)),$(eval $(call _PY_DEFINE_API_VAR,$v)))
$(eval $(call _PY_DEFINE_API_VAR_EXTSUFFIX,PY_EXT_SUFFIX))
# Useful to pull in a local build of some library. For testing. Sets the
# compiler and linker (runtime and build-time) flags. Invoke like this:
# $(eval $(call add_local_library_path,/home/user/library))
define add_local_library_path
CFLAGS += -I$1
CXXFLAGS += -I$1
LDFLAGS += -L$1
LDFLAGS += -Wl,-rpath,$1
endef
#### VERSION_FROM_PROJECT variable: parse git tags or, if those aren't
#### available, the debian/changelog. To use this, add to your Makefile:
####
#### VERSION := $(VERSION_FROM_PROJECT)
####
#### Otherwise, we'll use $(ABI_VERSION).$(TAIL_VERSION). VERSION_FROM_PROJECT
#### is useful in mature-ish projects that are actually being distributed to
#### others
# "git describe --tags" says things like "v2.0-17-gcef328f". This converts it to "v2.0"
_VERSION_STRIP_POST_TAG_ANNOTATIONS := s/-\d+-g[0-9a-f]+$$//
# I might have a tag such as "debian/2.0-1". This converts it to "2.0-1"
_VERSION_STRIP_LEADING_PATH := s/^.*\///
# I might have a tag such as "v2.0". This converts it to "2.0"
_VERSION_STRIP_LEADING_V := s/^v//g
# Custom version from git (or from debian/changelog if no git repo available)
# If user says "VERSION_USE_LATEST_TAG=1 make" I'll use the latest tag for the
# version, without annotations about the subsequent commits
PAREN0 := (
PAREN1 := )
_VERSION = $(shell \
if test -e debian/changelog; then \
< debian/changelog perl -ane 'if($$F[1] =~ /^\$(PAREN0)(.+)\$(PAREN1)$$/) { print $$1 } else { print "VERSION_UNKNOWN"}; exit 0'; \
elif test -d .git && git describe --tags >/dev/null 2>/dev/null; then \
git describe --tags | \
perl -pe '$(if $(VERSION_USE_LATEST_TAG),$(_VERSION_STRIP_POST_TAG_ANNOTATIONS)); \
$(_VERSION_STRIP_LEADING_PATH); \
$(_VERSION_STRIP_LEADING_V);'; \
else \
echo "VERSION_UNKNOWN"; \
fi \
)
# Memoize. $(VERSION) will evaluate the result the first time, and use the
# cached result during subsequent calls
VERSION_FROM_PROJECT = $(if $(_VERSION_EXPANDED),,$(eval _VERSION_EXPANDED:=$$(_VERSION)))$(_VERSION_EXPANDED)