Skip to content

Commit 8d10a61

Browse files
committed
Update Makefile, Update CI
1 parent 1747677 commit 8d10a61

File tree

2 files changed

+129
-70
lines changed

2 files changed

+129
-70
lines changed

.github/workflows/test.yml

+17-18
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,28 @@ jobs:
1111
fail-fast: false
1212
matrix:
1313
os:
14-
- ubuntu-latest
14+
- ubuntu-latest
1515

1616
python:
17-
- "3.7"
18-
- "3.8"
19-
- "3.9"
20-
- "3.10"
21-
- "3.11"
22-
- "3.12"
17+
- "3.9"
18+
- "3.10"
19+
- "3.11"
20+
- "3.12"
21+
- "3.13"
2322

2423
steps:
25-
- uses: actions/checkout@v4
24+
- uses: actions/checkout@v4
2625

27-
- name: Set up Python
28-
uses: actions/setup-python@v5
29-
with:
30-
python-version: ${{ matrix.python }}
26+
- name: Set up Python
27+
uses: actions/setup-python@v5
28+
with:
29+
python-version: ${{ matrix.python }}
3130

32-
- name: Install system dependencies
33-
run: make system-dependencies
31+
- name: Install system dependencies
32+
run: make system-dependencies
3433

35-
- name: Install project
36-
run: make install
34+
- name: Install project
35+
run: make install
3736

38-
- name: Run tests an collect code coverage
39-
run: make coverage
37+
- name: Run tests an collect code coverage
38+
run: make coverage

Makefile

+112-52
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ CLEAN_FS?=
3434
# Default: include.mk
3535
INCLUDE_MAKEFILE?=include.mk
3636

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+
3743
## system.dependencies
3844

3945
# Space separated system package names.
@@ -60,16 +66,31 @@ OPENLDAP_ENV?=PATH=/usr/local/bin:/usr/bin:/bin
6066

6167
## core.mxenv
6268

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`.
6471
# Default: python3
65-
PYTHON_BIN?=python3
72+
PRIMARY_PYTHON?=python3
6673

6774
# 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
7091

7192
# 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.
7394
# Default: true
7495
VENV_ENABLED?=true
7596

@@ -84,7 +105,7 @@ VENV_CREATE?=true
84105
# target folder for the virtual environment. If `VENV_ENABLED` is `true` and
85106
# `VENV_CREATE` is false it is expected to point to an existing virtual
86107
# environment. If `VENV_ENABLED` is `false` it is ignored.
87-
# Default: venv
108+
# Default: .venv
88109
VENV_FOLDER?=venv
89110

90111
# mxdev to install in virtual environment.
@@ -101,6 +122,13 @@ MXMAKE?=mxmake
101122
# Default: mx.ini
102123
PROJECT_CONFIG?=mx.ini
103124

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+
104132
## qa.test
105133

106134
# The command which gets executed. Defaults to the location the
@@ -136,6 +164,8 @@ CHECK_TARGETS?=
136164
TYPECHECK_TARGETS?=
137165
FORMAT_TARGETS?=
138166

167+
export PATH:=$(if $(EXTRA_PATH),$(EXTRA_PATH):,)$(PATH)
168+
139169
# Defensive settings for make: https://tech.davis-hansson.com/p/make/
140170
SHELL:=bash
141171
.ONESHELL:
@@ -152,7 +182,7 @@ MXMAKE_FOLDER?=.mxmake
152182
# Sentinel files
153183
SENTINEL_FOLDER?=$(MXMAKE_FOLDER)/sentinels
154184
SENTINEL?=$(SENTINEL_FOLDER)/about.txt
155-
$(SENTINEL):
185+
$(SENTINEL): $(firstword $(MAKEFILE_LIST))
156186
@mkdir -p $(SENTINEL_FOLDER)
157187
@echo "Sentinels for the Makefile process." > $(SENTINEL)
158188

@@ -215,40 +245,58 @@ CLEAN_TARGETS+=openldap-clean
215245
# mxenv
216246
##############################################################################
217247

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)
222249

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
227257
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)
232262
endif
233263

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
237267
else
238-
MXENV_PATH=
268+
PYTHON_PACKAGE_COMMAND=$(MXENV_PYTHON) -m pip
239269
endif
240270

241271
MXENV_TARGET:=$(SENTINEL_FOLDER)/mxenv.sentinel
242272
$(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 || :
243279
ifeq ("$(VENV_ENABLED)", "true")
244280
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
247288
endif
248289
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)
252300
@touch $(MXENV_TARGET)
253301

254302
.PHONY: mxenv
@@ -265,8 +313,8 @@ ifeq ("$(VENV_CREATE)", "true")
265313
@rm -rf $(VENV_FOLDER)
266314
endif
267315
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)
270318
endif
271319

272320
INSTALL_TARGETS+=mxenv
@@ -282,7 +330,7 @@ SYSTEM_DEPENDENCIES+=python3-dev libldap2-dev libssl-dev libsasl2-dev
282330

283331
PYTHON_LDAP_TARGET:=$(SENTINEL_FOLDER)/python-ldap.sentinel
284332
$(PYTHON_LDAP_TARGET): $(MXENV_TARGET) $(OPENLDAP_TARGET)
285-
@$(MXENV_PATH)pip install \
333+
@$(PYTHON_PACKAGE_COMMAND) install \
286334
--force-reinstall \
287335
python-ldap
288336
@touch $(PYTHON_LDAP_TARGET)
@@ -296,7 +344,7 @@ python-ldap-dirty:
296344

297345
.PHONY: python-ldap-clean
298346
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 || :
300348

301349
INSTALL_TARGETS+=python-ldap
302350
DIRTY_TARGETS+=python-ldap-dirty
@@ -314,13 +362,11 @@ MXMAKE_FILES?=$(MXMAKE_FOLDER)/files
314362

315363
# set environment variables for mxmake
316364
define set_mxfiles_env
317-
@export MXMAKE_MXENV_PATH=$(1)
318-
@export MXMAKE_FILES=$(2)
365+
@export MXMAKE_FILES=$(1)
319366
endef
320367

321368
# unset environment variables for mxmake
322369
define unset_mxfiles_env
323-
@unset MXMAKE_MXENV_PATH
324370
@unset MXMAKE_FILES
325371
endef
326372

@@ -337,9 +383,9 @@ FILES_TARGET:=requirements-mxdev.txt
337383
$(FILES_TARGET): $(PROJECT_CONFIG) $(MXENV_TARGET) $(SOURCES_TARGET) $(LOCAL_PACKAGE_FILES)
338384
@echo "Create project files"
339385
@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)
343389
@test -e $(MXMAKE_FILES)/pip.conf && cp $(MXMAKE_FILES)/pip.conf $(VENV_FOLDER)/pip.conf || :
344390
@touch $(FILES_TARGET)
345391

@@ -368,11 +414,21 @@ ADDITIONAL_SOURCES_TARGETS?=
368414

369415
INSTALLED_PACKAGES=$(MXMAKE_FILES)/installed.txt
370416

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+
371427
PACKAGES_TARGET:=$(INSTALLED_PACKAGES)
372428
$(PACKAGES_TARGET): $(FILES_TARGET) $(ADDITIONAL_SOURCES_TARGETS)
373429
@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)
376432
@touch $(PACKAGES_TARGET)
377433

378434
.PHONY: packages
@@ -385,8 +441,8 @@ packages-dirty:
385441
.PHONY: packages-clean
386442
packages-clean:
387443
@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) \
390446
|| :
391447
@rm -f $(PACKAGES_TARGET)
392448

@@ -401,22 +457,22 @@ CLEAN_TARGETS+=packages-clean
401457
TEST_TARGET:=$(SENTINEL_FOLDER)/test.sentinel
402458
$(TEST_TARGET): $(MXENV_TARGET)
403459
@echo "Install $(TEST_REQUIREMENTS)"
404-
@$(MXENV_PATH)pip install $(TEST_REQUIREMENTS)
460+
@$(PYTHON_PACKAGE_COMMAND) install $(TEST_REQUIREMENTS)
405461
@touch $(TEST_TARGET)
406462

407463
.PHONY: test
408464
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)"
412468

413469
.PHONY: test-dirty
414470
test-dirty:
415471
@rm -f $(TEST_TARGET)
416472

417473
.PHONY: test-clean
418474
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) || :
420476
@rm -rf .pytest_cache
421477

422478
INSTALL_TARGETS+=$(TEST_TARGET)
@@ -430,28 +486,32 @@ DIRTY_TARGETS+=test-dirty
430486
COVERAGE_TARGET:=$(SENTINEL_FOLDER)/coverage.sentinel
431487
$(COVERAGE_TARGET): $(TEST_TARGET)
432488
@echo "Install Coverage"
433-
@$(MXENV_PATH)pip install -U coverage
489+
@$(PYTHON_PACKAGE_COMMAND) install -U coverage
434490
@touch $(COVERAGE_TARGET)
435491

436492
.PHONY: coverage
437493
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)"
441497

442498
.PHONY: coverage-dirty
443499
coverage-dirty:
444500
@rm -f $(COVERAGE_TARGET)
445501

446502
.PHONY: coverage-clean
447503
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 || :
449505
@rm -rf .coverage htmlcov
450506

451507
INSTALL_TARGETS+=$(COVERAGE_TARGET)
452508
DIRTY_TARGETS+=coverage-dirty
453509
CLEAN_TARGETS+=coverage-clean
454510

511+
##############################################################################
512+
# Custom includes
513+
##############################################################################
514+
455515
-include $(INCLUDE_MAKEFILE)
456516

457517
##############################################################################

0 commit comments

Comments
 (0)