-
Notifications
You must be signed in to change notification settings - Fork 22
/
Makefile.package
476 lines (426 loc) · 17.3 KB
/
Makefile.package
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
# vim: set ft=make noet:
#
# This Makefile is written to be a generic entrypoint to building packages
# with Conan. It is used for every Conan package in this project through
# the `include` directive. You can override defaults here with the
# `override` keyword. For example:
#
# override SOURCE_DIR := .
# override BUILD_IN_SOURCE := true
# override CLEAN_SOURCE_DIR := false
# override PACKAGE_CHANNEL := cloe/develop
# include ../../Makefile.package
#
# As the Makefile is written for Conan packages, it expects a conanfile.py
# to reside in the directory and reads package information from it.
#
# You may also use it for your own Cloe plugins or other Conan packages.
# In this case though, you should remove variables that make explicit
# reference to files found in the Cloe repository, such as PROJECT_VERSION.
# You should also adjust the PACKAGE_CHANNEL variable.
#
PROJECT_ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
PROJECT_VERSION := $(shell [ -r $(PROJECT_ROOT)/VERSION ] && cat $(PROJECT_ROOT)/VERSION || echo unknown)
include $(PROJECT_ROOT)/Makefile.help
# Command for cloe-launch.
#
# This definition could be `cloe-launch`, but then you wouldn't get the
# version of cloe-launch that is from this repository, but one that is
# available globally.
CLOE_LAUNCH := PYTHONPATH="$(PROJECT_ROOT)/cli" python3 -m cloe_launch
SHELL := /bin/bash
DATE := $(shell date +"%Y%m%d")
TIMESTAMP := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
# Check if we are in a Git repository and try to describe the current
# state if we are. This is used for setting the $(PACKAGE_VERSION) if
# it is not set in conanfile or by $(PROJECT_VERSION).
HAS_GIT := $(shell [ -e $(PROJECT_ROOT)/.git ] && echo "true")
ifeq ($(PROJECT_VERSION),unknown)
ifeq ($(HAS_GIT),true)
GIT_COMMIT_HASH := $(shell git log -1 --format=%h)
GIT_COMMIT_DIRTY := $(shell git diff --quiet || echo "-dirty")
GIT_DESCRIBE := $(shell git describe --dirty="-dirty" | sed -r "s/^v(.*)/\1/")
else
GIT_DESCRIBE := "unknown"
endif
PROJECT_VERSION := $(GIT_DESCRIBE)
endif
# Default in-source directory.
#
# Change this to something like `src` if the source is not stored in the
# same repository as the conanfile and this Makefile, and put that directory
# in the .gitignore file.
SOURCE_DIR := .
# Don't change these unless you absolutely have to.
SOURCE_CONANFILE := conanfile.py
SOURCE_CMAKELISTS:= $(SOURCE_DIR)/CMakeLists.txt
LOCKFILE_SOURCE :=
LOCKFILE_OPTION :=
# When running `make clean`, delete the configured $(SOURCE_DIR)?
# Anything other than `true` is taken to be false.
CLEAN_SOURCE_DIR := false
# Whether this package can be built in-source, i.e. not in the Conan
# cache, but in a local build folder. The various targets for in-source
# builds will be defined if this is `true`.
BUILD_IN_SOURCE := true
# Default in-source build directory.
BUILD_DIR := build
# Don't change these unless you know what you are doing.
BUILD_CONANINFO := $(BUILD_DIR)/conanbuildinfo.txt
BUILD_LOCKFILE := $(BUILD_DIR)/conan.lock
# Default Conan build policy to use when installing Conan dependencies
# as well as creating packages. See Conan documentation for possible values.
BUILD_POLICY := outdated
# Package name and version are normally derived from the Conanfile and files
# in the repository.
#
# WARN: If you define package version in the Conanfile via function,
# then that value will probably conflict with what is set in this Makefile
# and Conan will probably complain.
#
# You probably want to override PACKAGE_CHANNEL, which is actually a USER/CHANNEL
# combination. See your project for guidance on naming.
PACKAGE_NAME := $(shell sed -rn 's/.*name\s*=\s*"([^"]+)"$$/\1/p' $(SOURCE_CONANFILE))
PACKAGE_VERSION := $(or \
$(shell sed -rn 's/\s+version\s*=\s*"([^"]+)"$$/\1/p' $(SOURCE_CONANFILE)), \
$(PROJECT_VERSION), \
unknown \
)
PACKAGE_CHANNEL := cloe/develop
PACKAGE_FQN := $(PACKAGE_NAME)/$(PACKAGE_VERSION)@$(PACKAGE_CHANNEL)
# Which files to consider as smoketest profiles when running targets
# `smoketest-deps` and `smoketest`. This can be useful to override from the
# command line if you just want to test one configuration.
TEST_CONANFILES := tests/conanfile_*.py
TEST_DIR := tests
# It is an order of magnitude faster to parse ~/.conan/editable_packages.json
# ourselves than to get conan to do it for us. This should only take us 20ms,
# while conan needs 250ms. Also, this will tell us if the package is editable,
# or the path to the current conanfile.py is marked as editable. This helps
# inform the user when they make a package editable, but then check out another
# state of the tree with a different version.
SOURCE_CONANFILE_REALPATH := $(realpath $(SOURCE_CONANFILE))
define JQ_PACKAGE_EDITABLE
if has(\"$(PACKAGE_FQN)\") then
if .[\"$(PACKAGE_FQN)\"].path == \"$(SOURCE_CONANFILE_REALPATH)\" then
\"editable\"
else
\"editable-elsewhere\"
end
else
if ([.[].path] | any(. == \"$(SOURCE_CONANFILE_REALPATH)\")) then
\"editable-other-name\"
else
\"not-editable\"
end
end
endef
PACKAGE_EDITABLE := $(shell [ -e ~/.conan/editable_packages.json ] \
&& jq -r "$(JQ_PACKAGE_EDITABLE)" ~/.conan/editable_packages.json \
|| echo "not-editable")
# Normally, you should set this in your profile, but if you just want to build
# the package in debug mode once, you can do it this way, although it will
# only apply to local builds.
#
# This can be one of: None, Debug, Release, RelWithDebInfo, MinSizeRel
BUILD_TYPE :=
ifneq "$(BUILD_TYPE)" ""
BUILD_TYPE_OPTION := -s $(PACKAGE_NAME):build_type=$(BUILD_TYPE)
else
BUILD_TYPE_OPTION :=
endif
# These options can be set to influence package and configure.
CONAN_OPTIONS :=
ifneq "$(LOCKFILE_SOURCE)" ""
LOCKFILE_OPTION := --lockfile="$(BUILD_LOCKFILE)"
.PHONY: $(BUILD_LOCKFILE)
# Create lockfile from LOCKFILE_SOURCE.
#
$(BUILD_LOCKFILE): $(LOCKFILE_SOURCE) export
mkdir -p "$(BUILD_DIR)"
conan lock create --lockfile-out "$(BUILD_LOCKFILE)" $(BUILD_TYPE_OPTION) $(CONAN_OPTIONS) --build -- "$(LOCKFILE_SOURCE)" >/dev/null
else
# Lockfile will be created automatically by conan install command.
$(BUILD_LOCKFILE):
endif
# When using a --lockfile option, we cannot use profile, settings, options, env
# or conf 'host' Conan options.
ifneq "$(LOCKFILE_OPTION)" ""
ALL_OPTIONS := $(LOCKFILE_OPTION) $(CONAN_OPTIONS)
else
ALL_OPTIONS := $(BUILD_TYPE_OPTION) $(CONAN_OPTIONS)
endif
# INFORMATIONAL TARGETS -------------------------------------------------------
.DEFAULT_GOAL := help
.SILENT: help
.PHONY: help
help:: parse-info
ifneq ($(DISABLE_HELP_PREAMBLE), true)
help::
$(call print_help_usage)
echo
echo "The following targets define common operations with this package in"
echo "editable (local in-source) and uneditable (in the Conan cache) modes."
echo
$(call print_help_section, "Available targets")
$(call print_help_target, help, "show this help")
endif
help::
$(call print_help_target, status, "show status of package")
$(call print_help_target, info, "show detailed package info")
$(call print_help_target, smoketest-deps, "build smoketest dependencies for package")
$(call print_help_target, smoketest, "run smoketests for package (requires built packages)")
echo
$(call print_help_target, export, "export recipe and sources", "[conan-cache]")
$(call print_help_target, download, "download or create package", "[conan-cache]")
$(call print_help_target, package, "create package with build policy", "[conan-cache]")
$(call print_help_target, list, "list installed package files", "[conan-cache]")
$(call print_help_target, purge, "remove package from cache", "[conan-cache]")
echo
ifeq ($(BUILD_IN_SOURCE), true)
$(call print_help_target, editable, "instruct Conan to use in-source build")
$(call print_help_target, uneditable, "instruct Conan to use local cache")
echo
$(call print_help_target, all, "build the package", "[in-source]")
$(call print_help_target, configure, "install dependencies and configure package", "[in-source]")
$(call print_help_target, test, "run CMake tests if they are available", "[in-source]")
$(call print_help_target, export-pkg, "export build artifacts to Conan cache", "[in-source]")
ifeq ($(CLEAN_SOURCE_DIR), true)
$(call print_help_target, clean, "remove source and build directories", "[in-source]")
else
$(call print_help_target, clean, "remove build directory", "[in-source]")
endif
echo
endif
$(call print_help_subsection, "Configuration")
$(call print_help_define_align, LOCKFILE_SOURCE, "$(LOCKFILE_SOURCE)")
ifeq ($(BUILD_IN_SOURCE), true)
$(call print_help_define_align, SOURCE_DIR, "$(SOURCE_DIR)")
endif
$(call print_help_define_align, BUILD_DIR, "$(BUILD_DIR)")
$(call print_help_define_align, BUILD_POLICY, "$(BUILD_POLICY)")
$(call print_help_define_align, BUILD_TYPE, "$(BUILD_TYPE)")
$(call print_help_define_align, CONAN_OPTIONS, "$(CONAN_OPTIONS)")
echo
$(call print_help_subsection, "Package information")
$(call print_help_define_align, PACKAGE_NAME, "$(PACKAGE_NAME)")
$(call print_help_define_align, PACKAGE_VERSION, "$(PACKAGE_VERSION)")
$(call print_help_define_align, PACKAGE_CHANNEL, "$(PACKAGE_CHANNEL)")
$(call print_help_define_align, PACKAGE_FQN, "$(PACKAGE_FQN)")
$(call print_help_define_align, PACKAGE_EDITABLE,"$(PACKAGE_EDITABLE)")
$(call print_help_define_align, PACKAGE_ID, "$(PACKAGE_ID)")
$(call print_help_define_align, PACKAGE_DIR, "$(PACKAGE_DIR)")
$(call print_help_define_align, PACKAGE_DATE, "$(PACKAGE_DATE)")
$(call print_help_define_align, GIT_COMMIT_HASH, "$(GIT_COMMIT_HASH)")
echo
.PHONY: parse-info
.SILENT: parse-info
parse-info: $(BUILD_LOCKFILE)
# Fetch package information from Conan.
#
# This command takes long, so we won't run it by default. Instead, if any
# target needs one of these variables, they should depend on this target
# to ensure that these variables are set.
#
$(eval PACKAGE_INFO := $(shell CONAN_REQUEST_TIMEOUT=0.1 conan info $(ALL_OPTIONS) "$(PACKAGE_FQN)" --package-filter "$(PACKAGE_FQN)" --paths 2>/dev/null | sed -r 's/$$/\\n/'))
$(eval PACKAGE_ID := $(shell echo -e "$(PACKAGE_INFO)" | sed -rn 's/^ *ID: *(.*)$$/\1/p'))
$(eval PACKAGE_DIR := $(shell echo -e "$(PACKAGE_INFO)" | sed -rn 's/^ *package_folder: *(.*)$$/\1/p'))
$(eval PACKAGE_DATE := $(shell echo -e "$(PACKAGE_INFO)" | sed -rn 's/^ *Creation date: *(.*)$$/\1/p'))
.PHONY: status
.SILENT: status
status: parse-info
# Show the *approximate* status of each package in the cloe workspace.
#
# This lets you know whether a package is in editable mode or not,
# and will also let you know if any of the files in the package
# directory has been modified more recently than the package in the
# Conan cache.
#
if [ "$(PACKAGE_EDITABLE)" != "not-editable" ] ; then \
echo "$(PACKAGE_EDITABLE) : $(PACKAGE_FQN)"; \
else \
if [ -n "$(PACKAGE_DATE)" ] && [ -z "$$(find -type f -newermt "$(PACKAGE_DATE)")" ]; then \
echo "ok : $(PACKAGE_FQN)"; \
else \
echo "outdated : $(PACKAGE_FQN)"; \
fi \
fi
.PHONY: info-name
.SILENT: info-name
info-name:
echo $(PACKAGE_NAME)
.PHONY: info-version
.SILENT: info-version
info-version:
echo $(PACKAGE_VERSION)
.PHONY: info-channel
.SILENT: info-channel
info-channel:
echo $(PACKAGE_CHANNEL)
.PHONY: info-fqn
.SILENT: info-fqn
info-fqn:
echo $(PACKAGE_FQN)
.PHONY: info
.SILENT: info
info: parse-info
if [ -z "$(PACKAGE_INFO)" ]; then \
echo "Errors occurred, no output available."; \
else \
echo $(PACKAGE_INFO); \
fi
.PHONY: smoketest
smoketest:
# Ensure that you have built all smoketest dependencies!
@for conanfile in $(TEST_CONANFILES); do \
test -f "$${conanfile}" || continue; \
printf "Running BATS tests with conanfile: $${conanfile}\n\n"; \
SHELL=/bin/bash $(CLOE_LAUNCH) shell "$${conanfile}" $(CONAN_OPTIONS) -- \
-c "source $(PROJECT_ROOT)/tests/setup_testname.bash; bats $(TEST_DIR)"; \
done
.PHONY: smoketest-deps
smoketest-deps:
# Ensure that you have exported all relevant packages!
@for conanfile in $(TEST_CONANFILES); do \
test -f "$${conanfile}" || continue; \
echo "Building dependencies for conanfile: $${conanfile}"; \
$(CLOE_LAUNCH) prepare "$${conanfile}" $(CONAN_OPTIONS) || exit 1; \
done
# CONFIGURATION TARGETS -------------------------------------------------------
.PHONY: editable
editable:
# Conan will now resolve references to the in-source build.
#
# In editable mode, Conan will use the in-source build for all references
# to this package. You should use [in-source] targets while the package is
# editable. It is not possible to create a Conan package while the package
# is in editable mode.
#
# Run `make uneditable` to leave this mode.
#
conan editable add $(SOURCE_CONANFILE) $(PACKAGE_FQN)
.PHONY: uneditable
uneditable:
# Conan will now resolve references to the package in the cache.
#
# In uneditable mode, Conan will use the package within the Conan cache
# (normally located in ~/.conan/data). This is the default behavior.
#
conan editable remove $(PACKAGE_FQN)
# CONAN TARGETS ---------------------------------------------------------------
.PHONY: lockfile
lockfile: $(BUILD_LOCKFILE)
.PHONY: export
export:
ifneq (,$(filter $(PACKAGE_EDITABLE),not-editable editable-other-name))
# Export sources to Conan cache.
#
# This does not build this package but provides the sources and the
# build recipe to Conan for on-demand building.
#
conan export $(SOURCE_CONANFILE) $(PACKAGE_FQN)
else
# Export sources to Conan cache: skipped (package is $(PACKAGE_EDITABLE))
endif
.PHONY: download
download:
# Try to download the package to Conan cache.
#
# Only if the package is not available in the remote, will the package be built:
# The first command tries to create the package without building anything; if
# there is an error, then we build the package using the default build policy.
# Thus, errors arising from the first command can be safely ignored.
# Note that this cannot be called if the package is currently in editable mode.
#
# See: https://docs.conan.io/en/latest/mastering/policies.html
#
conan create $(SOURCE_CONANFILE) $(PACKAGE_FQN) --build=never $(ALL_OPTIONS) || \
conan create $(SOURCE_CONANFILE) $(PACKAGE_FQN) --build=$(BUILD_POLICY) --build=$(PACKAGE_NAME) $(ALL_OPTIONS)
.PHONY: package
package: $(BUILD_LOCKFILE)
# Build the package in Conan cache unconditionally.
#
# Conan will retrieve and build all dependencies based on the build policy.
# Note that this cannot be called if the package is currently in editable mode.
#
# See: https://docs.conan.io/en/latest/mastering/policies.html
#
conan create $(SOURCE_CONANFILE) $(PACKAGE_FQN) \
--build=$(BUILD_POLICY) --build=$(PACKAGE_NAME) $(ALL_OPTIONS)
.PHONY: purge
purge:
# Remove all instances of this package in the Conan cache.
#
# Normally, building a package only replaces the instance in the Cache that
# has the same ID. Purging all instances is useful for forcing a rebuild
# of all instances of this package henceforth.
#
-conan remove -f $(PACKAGE_FQN)
.PHONY: list
list: parse-info
# List all files in the Conan cache package directory.
#
@tree $(PACKAGE_DIR)
# IN-SOURCE TARGETS -----------------------------------------------------------
.PHONY: clean
clean:
# Clean the build directory and Python cache files.
#
rm -rf "$(BUILD_DIR)" __pycache__ CMakeUserPresets.json compile_commands.json graph_info.json
ifeq ($(CLEAN_SOURCE_DIR), true)
[ "$(SOURCE_DIR)" != "." ] && rm -rf "$(SOURCE_DIR)"
endif
ifeq ($(BUILD_IN_SOURCE), false)
.PHONY: all configure test export-pkg
all configure test export-pkg $(SOURCE_DIR) $(SOURCE_CMAKELISTS) $(BUILD_CONANINFO):
@echo "Error: [in-source] targets are not supported for this package."
@echo "Note: please use [conan-cache] targets, such as 'package'."
exit 1
else
.PHONY: all
all: $(BUILD_CONANINFO) | $(SOURCE_DIR)
# Build the package in-source.
#
conan build $(SOURCE_CONANFILE) --source-folder="$(SOURCE_DIR)" --install-folder="$(BUILD_DIR)"
.PHONY: configure
configure: $(BUILD_CONANINFO)
ln -rsf "$$(dirname $$(dirname $$(jq -r '.include[0]' CMakeUserPresets.json)))/compile_commands.json"
.PHONY: test
test:
# Run tests available to CMake ctest.
#
# If no tests are available, this will simply return true.
#
@if [ -f "$(BUILD_DIR)"/CTestTestfile.cmake ]; then \
cd "$(BUILD_DIR)" && ctest; \
else \
true; \
fi
.PHONY: export-pkg
export-pkg:
# Export in-source build artifacts to Conan cache.
#
# This requires the in-source build to be complete and uses the package
# recipe in the conanfile. This is useful when you want to make the
# binaries available to Conan but not the source.
#
# Note that this does not require the package to be editable.
#
conan export-pkg $(SOURCE_CONANFILE) $(PACKAGE_FQN) --build-folder="$(BUILD_DIR)"
$(SOURCE_DIR):
# Copy source to an external source directory.
#
# This usually isn't necessary, and should not be called when
# SOURCE_DIR is identical to the current directory.
#
[ "$(shell readlink -f "$(SOURCE_DIR)")" != "$(shell readlink -f .)" ]
conan source $(SOURCE_CONANFILE) --source-folder="$(SOURCE_DIR)"
$(SOURCE_CMAKELISTS): | $(SOURCE_DIR)
$(BUILD_CONANINFO): $(SOURCE_CONANFILE) $(BUILD_LOCKFILE) $(SOURCE_CMAKELISTS)
# Install package dependencies and configure in-source build.
#
mkdir -p "$(BUILD_DIR)"
conan install $(SOURCE_CONANFILE) $(PACKAGE_FQN) --install-folder="$(BUILD_DIR)" --build=$(BUILD_POLICY) $(ALL_OPTIONS)
conan build --configure $(SOURCE_CONANFILE) --source-folder="$(SOURCE_DIR)" --install-folder="$(BUILD_DIR)"
touch $(BUILD_CONANINFO)
endif