@@ -34,6 +34,12 @@ CLEAN_FS?=
34
34
# Default: include.mk
35
35
INCLUDE_MAKEFILE? =include.mk
36
36
37
+ # Optional additional directories to be added to PATH in format
38
+ # `/path/to/dir/:/path/to/other/dir`. Gets inserted first, thus gets searched
39
+ # first.
40
+ # No default value.
41
+ EXTRA_PATH? =
42
+
37
43
# # system.dependencies
38
44
39
45
# Space separated system package names.
@@ -60,16 +66,31 @@ OPENLDAP_ENV?=PATH=/usr/local/bin:/usr/bin:/bin
60
66
61
67
# # core.mxenv
62
68
63
- # Python interpreter to use.
69
+ # Primary Python interpreter to use. It is used to create the
70
+ # virtual environment if `VENV_ENABLED` and `VENV_CREATE` are set to `true`.
64
71
# Default: python3
65
- PYTHON_BIN ? =python3
72
+ PRIMARY_PYTHON ? =python3
66
73
67
74
# Minimum required Python version.
68
- # Default: 3.7
69
- PYTHON_MIN_VERSION? =3.7
75
+ # Default: 3.9
76
+ PYTHON_MIN_VERSION? =3.9
77
+
78
+ # Install packages using the given package installer method.
79
+ # Supported are `pip` and `uv`. If uv is used, its global availability is
80
+ # checked. Otherwise, it is installed, either in the virtual environment or
81
+ # using the `PRIMARY_PYTHON`, dependent on the `VENV_ENABLED` setting. If
82
+ # `VENV_ENABLED` and uv is selected, uv is used to create the virtual
83
+ # environment.
84
+ # Default: pip
85
+ PYTHON_PACKAGE_INSTALLER? =uv
86
+
87
+ # Flag whether to use a global installed 'uv' or install
88
+ # it in the virtual environment.
89
+ # Default: false
90
+ MXENV_UV_GLOBAL? =false
70
91
71
92
# Flag whether to use virtual environment. If `false`, the
72
- # interpreter according to `PYTHON_BIN ` found in `PATH` is used.
93
+ # interpreter according to `PRIMARY_PYTHON ` found in `PATH` is used.
73
94
# Default: true
74
95
VENV_ENABLED? =true
75
96
@@ -84,7 +105,7 @@ VENV_CREATE?=true
84
105
# target folder for the virtual environment. If `VENV_ENABLED` is `true` and
85
106
# `VENV_CREATE` is false it is expected to point to an existing virtual
86
107
# environment. If `VENV_ENABLED` is `false` it is ignored.
87
- # Default: venv
108
+ # Default: . venv
88
109
VENV_FOLDER? =venv
89
110
90
111
# mxdev to install in virtual environment.
@@ -101,6 +122,13 @@ MXMAKE?=mxmake
101
122
# Default: mx.ini
102
123
PROJECT_CONFIG? =mx.ini
103
124
125
+ # # core.packages
126
+
127
+ # Allow prerelease and development versions.
128
+ # By default, the package installer only finds stable versions.
129
+ # Default: false
130
+ PACKAGES_ALLOW_PRERELEASES? =false
131
+
104
132
# # qa.test
105
133
106
134
# The command which gets executed. Defaults to the location the
@@ -136,6 +164,8 @@ CHECK_TARGETS?=
136
164
TYPECHECK_TARGETS? =
137
165
FORMAT_TARGETS? =
138
166
167
+ export PATH: =$(if $(EXTRA_PATH ) ,$(EXTRA_PATH ) :,)$(PATH )
168
+
139
169
# Defensive settings for make: https://tech.davis-hansson.com/p/make/
140
170
SHELL: =bash
141
171
.ONESHELL :
@@ -152,7 +182,7 @@ MXMAKE_FOLDER?=.mxmake
152
182
# Sentinel files
153
183
SENTINEL_FOLDER? =$(MXMAKE_FOLDER ) /sentinels
154
184
SENTINEL? =$(SENTINEL_FOLDER ) /about.txt
155
- $(SENTINEL ) :
185
+ $(SENTINEL ) : $( firstword $( MAKEFILE_LIST ) )
156
186
@mkdir -p $(SENTINEL_FOLDER )
157
187
@echo " Sentinels for the Makefile process." > $(SENTINEL )
158
188
@@ -215,40 +245,58 @@ CLEAN_TARGETS+=openldap-clean
215
245
# mxenv
216
246
# #############################################################################
217
247
218
- # Check if given Python is installed
219
- ifeq (,$(shell which $(PYTHON_BIN ) ) )
220
- $(error "PYTHON=$(PYTHON_BIN) not found in $(PATH)")
221
- endif
248
+ export OS: =$(OS )
222
249
223
- # Check if given Python version is ok
224
- PYTHON_VERSION_OK =$(shell $(PYTHON_BIN ) -c "import sys; print((int(sys.version_info[0]) , int(sys.version_info[1])) >= tuple(map(int, '$(PYTHON_MIN_VERSION ) '.split('.'))))")
225
- ifeq ($(PYTHON_VERSION_OK ) ,0)
226
- $(error "Need Python >= $(PYTHON_MIN_VERSION)")
250
+ # Determine the executable path
251
+ ifeq ("$(VENV_ENABLED ) ", "true")
252
+ export VIRTUAL_ENV =$(abspath $(VENV_FOLDER ) )
253
+ ifeq ("$(OS ) ", "Windows_NT")
254
+ VENV_EXECUTABLE_FOLDER =$(VIRTUAL_ENV ) /Scripts
255
+ else
256
+ VENV_EXECUTABLE_FOLDER =$(VIRTUAL_ENV ) /bin
227
257
endif
228
-
229
- # Check if venv folder is configured if venv is enabled
230
- ifeq ( $( shell [[ " $( VENV_ENABLED ) " == "true" && " $( VENV_FOLDER ) " == "" ]] && echo "true") ,"true")
231
- $(error "VENV_FOLDER must be configured if VENV_ENABLED is true" )
258
+ export PATH: = $( VENV_EXECUTABLE_FOLDER ) : $( PATH )
259
+ MXENV_PYTHON =python
260
+ else
261
+ MXENV_PYTHON = $( PRIMARY_PYTHON )
232
262
endif
233
263
234
- # determine the executable path
235
- ifeq ("$(VENV_ENABLED ) ", "true ")
236
- MXENV_PATH = $( VENV_FOLDER ) /bin/
264
+ # Determine the package installer
265
+ ifeq ("$(PYTHON_PACKAGE_INSTALLER ) ","uv ")
266
+ PYTHON_PACKAGE_COMMAND =uv pip
237
267
else
238
- MXENV_PATH =
268
+ PYTHON_PACKAGE_COMMAND = $( MXENV_PYTHON ) -m pip
239
269
endif
240
270
241
271
MXENV_TARGET: =$(SENTINEL_FOLDER ) /mxenv.sentinel
242
272
$(MXENV_TARGET ) : $(SENTINEL )
273
+ @$(PRIMARY_PYTHON ) -c " import sys; vi = sys.version_info; sys.exit(1 if (int(vi[0]), int(vi[1])) >= tuple(map(int, '$( PYTHON_MIN_VERSION) '.split('.'))) else 0)" \
274
+ && echo " Need Python >= $( PYTHON_MIN_VERSION) " && exit 1 || :
275
+ @[[ " $( VENV_ENABLED) " == " true" && " $( VENV_FOLDER) " == " " ]] \
276
+ && echo " VENV_FOLDER must be configured if VENV_ENABLED is true" && exit 1 || :
277
+ @[[ " $( VENV_ENABLED) $( PYTHON_PACKAGE_INSTALLER) " == " falseuv" ]] \
278
+ && echo " Package installer uv does not work with a global Python interpreter." && exit 1 || :
243
279
ifeq ("$(VENV_ENABLED ) ", "true")
244
280
ifeq ("$(VENV_CREATE ) ", "true")
245
- @echo "Setup Python Virtual Environment under '$(VENV_FOLDER)'"
246
- @$(PYTHON_BIN) -m venv $(VENV_FOLDER)
281
+ ifeq ("$(PYTHON_PACKAGE_INSTALLER )$(MXENV_UV_GLOBAL ) ","uvtrue")
282
+ @echo "Setup Python Virtual Environment using package 'uv' at '$(VENV_FOLDER)'"
283
+ @uv venv -p $(PRIMARY_PYTHON) --seed $(VENV_FOLDER)
284
+ else
285
+ @echo "Setup Python Virtual Environment using module 'venv' at '$(VENV_FOLDER)'"
286
+ @$(PRIMARY_PYTHON) -m venv $(VENV_FOLDER)
287
+ @$(MXENV_PYTHON) -m ensurepip -U
247
288
endif
248
289
endif
249
- @$(MXENV_PATH)pip install -U pip setuptools wheel
250
- @$(MXENV_PATH)pip install -U $(MXDEV)
251
- @$(MXENV_PATH)pip install -U $(MXMAKE)
290
+ else
291
+ @echo "Using system Python interpreter"
292
+ endif
293
+ ifeq ("$(PYTHON_PACKAGE_INSTALLER )$(MXENV_UV_GLOBAL ) ","uvfalse")
294
+ @echo "Install uv"
295
+ @$(MXENV_PYTHON) -m pip install uv
296
+ endif
297
+ @$(PYTHON_PACKAGE_COMMAND) install -U pip setuptools wheel
298
+ @echo "Install/Update MXStack Python packages"
299
+ @$(PYTHON_PACKAGE_COMMAND) install -U $(MXDEV) $(MXMAKE)
252
300
@touch $(MXENV_TARGET)
253
301
254
302
.PHONY : mxenv
@@ -265,8 +313,8 @@ ifeq ("$(VENV_CREATE)", "true")
265
313
@rm -rf $(VENV_FOLDER)
266
314
endif
267
315
else
268
- @$(MXENV_PATH)pip uninstall -y $(MXDEV)
269
- @$(MXENV_PATH)pip uninstall -y $(MXMAKE)
316
+ @$(PYTHON_PACKAGE_COMMAND) uninstall -y $(MXDEV)
317
+ @$(PYTHON_PACKAGE_COMMAND) uninstall -y $(MXMAKE)
270
318
endif
271
319
272
320
INSTALL_TARGETS+ =mxenv
@@ -282,7 +330,7 @@ SYSTEM_DEPENDENCIES+=python3-dev libldap2-dev libssl-dev libsasl2-dev
282
330
283
331
PYTHON_LDAP_TARGET: =$(SENTINEL_FOLDER ) /python-ldap.sentinel
284
332
$(PYTHON_LDAP_TARGET ) : $(MXENV_TARGET ) $(OPENLDAP_TARGET )
285
- @$(MXENV_PATH ) pip install \
333
+ @$(PYTHON_PACKAGE_COMMAND ) install \
286
334
--force-reinstall \
287
335
python-ldap
288
336
@touch $(PYTHON_LDAP_TARGET )
@@ -296,7 +344,7 @@ python-ldap-dirty:
296
344
297
345
.PHONY : python-ldap-clean
298
346
python-ldap-clean : python-ldap-dirty
299
- @test -e $(MXENV_PATH ) pip && $(MXENV_PATH ) pip uninstall -y python-ldap || :
347
+ @test -e $(MXENV_PYTHON ) && $(MXENV_PYTHON ) -m pip uninstall -y python-ldap || :
300
348
301
349
INSTALL_TARGETS+ =python-ldap
302
350
DIRTY_TARGETS+ =python-ldap-dirty
@@ -314,13 +362,11 @@ MXMAKE_FILES?=$(MXMAKE_FOLDER)/files
314
362
315
363
# set environment variables for mxmake
316
364
define set_mxfiles_env
317
- @export MXMAKE_MXENV_PATH=$(1 )
318
- @export MXMAKE_FILES=$(2 )
365
+ @export MXMAKE_FILES=$(1 )
319
366
endef
320
367
321
368
# unset environment variables for mxmake
322
369
define unset_mxfiles_env
323
- @unset MXMAKE_MXENV_PATH
324
370
@unset MXMAKE_FILES
325
371
endef
326
372
@@ -337,9 +383,9 @@ FILES_TARGET:=requirements-mxdev.txt
337
383
$(FILES_TARGET ) : $(PROJECT_CONFIG ) $(MXENV_TARGET ) $(SOURCES_TARGET ) $(LOCAL_PACKAGE_FILES )
338
384
@echo " Create project files"
339
385
@mkdir -p $(MXMAKE_FILES )
340
- $(call set_mxfiles_env,$(MXENV_PATH ) , $( MXMAKE_FILES ) )
341
- @$( MXENV_PATH ) mxdev -n -c $(PROJECT_CONFIG )
342
- $(call unset_mxfiles_env, $( MXENV_PATH ) , $( MXMAKE_FILES ) )
386
+ $(call set_mxfiles_env,$(MXMAKE_FILES ) )
387
+ @mxdev -n -c $(PROJECT_CONFIG )
388
+ $(call unset_mxfiles_env)
343
389
@test -e $(MXMAKE_FILES ) /pip.conf && cp $(MXMAKE_FILES ) /pip.conf $(VENV_FOLDER ) /pip.conf || :
344
390
@touch $(FILES_TARGET )
345
391
@@ -368,11 +414,21 @@ ADDITIONAL_SOURCES_TARGETS?=
368
414
369
415
INSTALLED_PACKAGES =$(MXMAKE_FILES ) /installed.txt
370
416
417
+ ifeq ("$(PACKAGES_ALLOW_PRERELEASES ) ","true")
418
+ ifeq ("$(PYTHON_PACKAGE_INSTALLER ) ","uv")
419
+ PACKAGES_PRERELEASES=--prerelease =allow
420
+ else
421
+ PACKAGES_PRERELEASES =--pre
422
+ endif
423
+ else
424
+ PACKAGES_PRERELEASES =
425
+ endif
426
+
371
427
PACKAGES_TARGET: =$(INSTALLED_PACKAGES )
372
428
$(PACKAGES_TARGET ) : $(FILES_TARGET ) $(ADDITIONAL_SOURCES_TARGETS )
373
429
@echo " Install python packages"
374
- @$(MXENV_PATH ) pip install -r $(FILES_TARGET )
375
- @$(MXENV_PATH ) pip freeze > $(INSTALLED_PACKAGES )
430
+ @$(PYTHON_PACKAGE_COMMAND ) install $( PACKAGES_PRERELEASES ) -r $(FILES_TARGET )
431
+ @$(PYTHON_PACKAGE_COMMAND ) freeze > $(INSTALLED_PACKAGES )
376
432
@touch $(PACKAGES_TARGET )
377
433
378
434
.PHONY : packages
@@ -385,8 +441,8 @@ packages-dirty:
385
441
.PHONY : packages-clean
386
442
packages-clean :
387
443
@test -e $(FILES_TARGET ) \
388
- && test -e $(MXENV_PATH ) pip \
389
- && $(MXENV_PATH ) pip uninstall -y -r $(FILES_TARGET ) \
444
+ && test -e $(MXENV_PYTHON ) \
445
+ && $(MXENV_PYTHON ) -m pip uninstall -y -r $(FILES_TARGET ) \
390
446
|| :
391
447
@rm -f $(PACKAGES_TARGET )
392
448
@@ -401,22 +457,22 @@ CLEAN_TARGETS+=packages-clean
401
457
TEST_TARGET: =$(SENTINEL_FOLDER ) /test.sentinel
402
458
$(TEST_TARGET ) : $(MXENV_TARGET )
403
459
@echo " Install $( TEST_REQUIREMENTS) "
404
- @$(MXENV_PATH ) pip install $(TEST_REQUIREMENTS )
460
+ @$(PYTHON_PACKAGE_COMMAND ) install $(TEST_REQUIREMENTS )
405
461
@touch $(TEST_TARGET )
406
462
407
463
.PHONY : test
408
464
test : $(FILES_TARGET ) $(SOURCES_TARGET ) $(PACKAGES_TARGET ) $(TEST_TARGET ) $(TEST_DEPENDENCY_TARGETS )
409
- @echo " Run tests "
410
- @test -z " $( TEST_COMMAND ) " && echo " No test command defined "
411
- @test -z " $( TEST_COMMAND ) " || bash -c " $( TEST_COMMAND) "
465
+ @test -z " $( TEST_COMMAND ) " && echo " No test command defined " && exit 1 || :
466
+ @echo " Run tests using $( TEST_COMMAND ) "
467
+ @/usr/bin/env bash -c " $( TEST_COMMAND) "
412
468
413
469
.PHONY : test-dirty
414
470
test-dirty :
415
471
@rm -f $(TEST_TARGET )
416
472
417
473
.PHONY : test-clean
418
474
test-clean : test-dirty
419
- @test -e $(MXENV_PATH ) pip && $(MXENV_PATH ) pip uninstall -y $(TEST_REQUIREMENTS ) || :
475
+ @test -e $(MXENV_PYTHON ) && $(MXENV_PYTHON ) -m pip uninstall -y $(TEST_REQUIREMENTS ) || :
420
476
@rm -rf .pytest_cache
421
477
422
478
INSTALL_TARGETS+ =$(TEST_TARGET )
@@ -430,28 +486,32 @@ DIRTY_TARGETS+=test-dirty
430
486
COVERAGE_TARGET: =$(SENTINEL_FOLDER ) /coverage.sentinel
431
487
$(COVERAGE_TARGET ) : $(TEST_TARGET )
432
488
@echo " Install Coverage"
433
- @$(MXENV_PATH ) pip install -U coverage
489
+ @$(PYTHON_PACKAGE_COMMAND ) install -U coverage
434
490
@touch $(COVERAGE_TARGET )
435
491
436
492
.PHONY : coverage
437
493
coverage : $(FILES_TARGET ) $(SOURCES_TARGET ) $(PACKAGES_TARGET ) $(COVERAGE_TARGET )
438
- @echo " Run coverage"
439
- @test -z " $( COVERAGE_COMMAND ) " && echo " No coverage command defined "
440
- @test -z " $( COVERAGE_COMMAND ) " || bash -c " $( COVERAGE_COMMAND) "
494
+ @test -z " $( COVERAGE_COMMAND ) " && echo " No coverage command defined " && exit 1 || :
495
+ @echo " Run coverage using $( COVERAGE_COMMAND ) "
496
+ @/usr/bin/env bash -c " $( COVERAGE_COMMAND) "
441
497
442
498
.PHONY : coverage-dirty
443
499
coverage-dirty :
444
500
@rm -f $(COVERAGE_TARGET )
445
501
446
502
.PHONY : coverage-clean
447
503
coverage-clean : coverage-dirty
448
- @test -e $(MXENV_PATH ) pip && $(MXENV_PATH ) pip uninstall -y coverage || :
504
+ @test -e $(MXENV_PYTHON ) && $(MXENV_PYTHON ) -m pip uninstall -y coverage || :
449
505
@rm -rf .coverage htmlcov
450
506
451
507
INSTALL_TARGETS+ =$(COVERAGE_TARGET )
452
508
DIRTY_TARGETS+ =coverage-dirty
453
509
CLEAN_TARGETS+ =coverage-clean
454
510
511
+ # #############################################################################
512
+ # Custom includes
513
+ # #############################################################################
514
+
455
515
-include $(INCLUDE_MAKEFILE )
456
516
457
517
# #############################################################################
0 commit comments