33#
44# DOMAINS:
55# : core.base
6+ # : core.help
67# : core.mxenv
78# : core.mxfiles
89# : core.packages
910# : qa.isort
1011# : qa.ruff
12+ # : qa.test
1113#
1214# SETTINGS (ALL CHANGES MADE BELOW SETTINGS WILL BE LOST)
1315# #############################################################################
@@ -41,26 +43,30 @@ EXTRA_PATH?=
4143
4244# Primary Python interpreter to use. It is used to create the
4345# virtual environment if `VENV_ENABLED` and `VENV_CREATE` are set to `true`.
46+ # If global `uv` is used, this value is passed as `--python VALUE` to the venv creation.
47+ # uv then downloads the Python interpreter if it is not available.
48+ # for more on this feature read the [uv python documentation](https://docs.astral.sh/uv/concepts/python-versions/)
4449# Default: python3
4550PRIMARY_PYTHON? =python3
4651
4752# Minimum required Python version.
48- # Default: 3.9
53+ # Default: 3.10
4954PYTHON_MIN_VERSION? =3.9
5055
5156# Install packages using the given package installer method.
52- # Supported are `pip` and `uv`. If uv is used, its global availability is
53- # checked. Otherwise, it is installed, either in the virtual environment or
54- # using the `PRIMARY_PYTHON`, dependent on the `VENV_ENABLED` setting. If
55- # `VENV_ENABLED` and uv is selected, uv is used to create the virtual
56- # environment.
57+ # Supported are `pip` and `uv`. When `uv` is selected, a global installation
58+ # is auto-detected and used if available. Otherwise, uv is installed in the
59+ # virtual environment or using `PRIMARY_PYTHON`, depending on the
60+ # `VENV_ENABLED` setting.
5761# Default: pip
5862PYTHON_PACKAGE_INSTALLER? =uv
5963
60- # Flag whether to use a global installed 'uv' or install
61- # it in the virtual environment.
62- # Default: false
63- MXENV_UV_GLOBAL? =true
64+ # Python version for UV to install/use when creating virtual
65+ # environments with global UV. Passed to `uv venv -p VALUE`. Supports version
66+ # specs like `3.11`, `3.14`, `cpython@3.14`. Defaults to PRIMARY_PYTHON value
67+ # for backward compatibility.
68+ # Default: $(PRIMARY_PYTHON)
69+ UV_PYTHON? =$(PRIMARY_PYTHON )
6470
6571# Flag whether to use virtual environment. If `false`, the
6672# interpreter according to `PRIMARY_PYTHON` found in `PATH` is used.
@@ -114,6 +120,28 @@ PROJECT_CONFIG?=mx.ini
114120# Default: false
115121PACKAGES_ALLOW_PRERELEASES? =false
116122
123+ # # qa.test
124+
125+ # The command which gets executed. Defaults to the location the
126+ # :ref:`run-tests` template gets rendered to if configured.
127+ # Default: .mxmake/files/run-tests.sh
128+ TEST_COMMAND? =.mxmake/files/run-tests.sh
129+
130+ # Additional Python requirements for running tests to be
131+ # installed (via pip).
132+ # Default: pytest
133+ TEST_REQUIREMENTS? =pytest
134+
135+ # Additional make targets the test target depends on.
136+ # No default value.
137+ TEST_DEPENDENCY_TARGETS? =
138+
139+ # # core.help
140+
141+ # Request to show all targets, descriptions and arguments for a given domain.
142+ # No default value.
143+ HELP_DOMAIN? =
144+
117145# #############################################################################
118146# END SETTINGS - DO NOT EDIT BELOW THIS LINE
119147# #############################################################################
@@ -152,7 +180,7 @@ $(SENTINEL): $(firstword $(MAKEFILE_LIST))
152180# mxenv
153181# #############################################################################
154182
155- export OS: = $( OS )
183+ OS? =
156184
157185# Determine the executable path
158186ifeq ("$(VENV_ENABLED ) ", "true")
@@ -168,26 +196,61 @@ else
168196MXENV_PYTHON =$(PRIMARY_PYTHON )
169197endif
170198
171- # Determine the package installer
199+ # Determine the package installer with non-interactive flags
172200ifeq ("$(PYTHON_PACKAGE_INSTALLER ) ","uv")
173- PYTHON_PACKAGE_COMMAND =uv pip
201+ PYTHON_PACKAGE_COMMAND =uv pip --no-progress
174202else
175203PYTHON_PACKAGE_COMMAND =$(MXENV_PYTHON ) -m pip
176204endif
177205
206+ # Auto-detect global uv availability (simple existence check)
207+ ifeq ("$(PYTHON_PACKAGE_INSTALLER ) ","uv")
208+ UV_AVAILABLE: =$(shell command -v uv >/dev/null 2>&1 && echo "true" || echo "false")
209+ else
210+ UV_AVAILABLE: =false
211+ endif
212+
213+ # Determine installation strategy
214+ # depending on the PYTHON_PACKAGE_INSTALLER and UV_AVAILABLE
215+ # - both vars can be false or
216+ # - one of them can be true,
217+ # - but never boths.
218+ USE_GLOBAL_UV: =$(shell [[ "$(PYTHON_PACKAGE_INSTALLER ) " == "uv" && "$(UV_AVAILABLE ) " == "true" ]] && echo "true" || echo "false")
219+ USE_LOCAL_UV: =$(shell [[ "$(PYTHON_PACKAGE_INSTALLER ) " == "uv" && "$(UV_AVAILABLE ) " == "false" ]] && echo "true" || echo "false")
220+
221+ # Check if global UV is outdated (non-blocking warning)
222+ ifeq ("$(USE_GLOBAL_UV ) ","true")
223+ UV_OUTDATED: =$(shell uv self update --dry-run 2>&1 | grep -q "Would update" && echo "true" || echo "false")
224+ else
225+ UV_OUTDATED: =false
226+ endif
227+
178228MXENV_TARGET: =$(SENTINEL_FOLDER ) /mxenv.sentinel
179229$(MXENV_TARGET ) : $(SENTINEL )
230+ # Validation: Check Python version if not using global uv
231+ ifneq ("$(USE_GLOBAL_UV ) ","true")
180232 @$(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)" \
181233 && echo "Need Python >= $(PYTHON_MIN_VERSION)" && exit 1 || :
234+ else
235+ @echo "Using global uv for Python $(UV_PYTHON)"
236+ endif
237+ # Validation: Check VENV_FOLDER is set if venv enabled
182238 @[[ "$(VENV_ENABLED)" == "true" && "$(VENV_FOLDER)" == "" ]] \
183239 && echo "VENV_FOLDER must be configured if VENV_ENABLED is true" && exit 1 || :
184- @[[ " $( VENV_ENABLED) $( PYTHON_PACKAGE_INSTALLER) " == " falseuv" ]] \
240+ # Validation: Check uv not used with system Python
241+ @[[ "$(VENV_ENABLED)" == "false" && "$(PYTHON_PACKAGE_INSTALLER)" == "uv" ]] \
185242 && echo "Package installer uv does not work with a global Python interpreter." && exit 1 || :
243+ # Warning: Notify if global UV is outdated
244+ ifeq ("$(UV_OUTDATED ) ","true")
245+ @echo "WARNING: A newer version of uv is available. Run 'uv self update' to upgrade."
246+ endif
247+
248+ # Create virtual environment
186249ifeq ("$(VENV_ENABLED ) ", "true")
187250ifeq ("$(VENV_CREATE ) ", "true")
188- ifeq ("$(PYTHON_PACKAGE_INSTALLER )$( MXENV_UV_GLOBAL ) ","uvtrue ")
189- @echo "Setup Python Virtual Environment using package 'uv' at '$(VENV_FOLDER)'"
190- @uv venv -p $(PRIMARY_PYTHON ) --seed $(VENV_FOLDER)
251+ ifeq ("$(USE_GLOBAL_UV ) ","true ")
252+ @echo "Setup Python Virtual Environment using global uv at '$(VENV_FOLDER)'"
253+ @uv venv --allow-existing --no-progress - p $(UV_PYTHON ) --seed $(VENV_FOLDER)
191254else
192255 @echo "Setup Python Virtual Environment using module 'venv' at '$(VENV_FOLDER)'"
193256 @$(PRIMARY_PYTHON) -m venv $(VENV_FOLDER)
@@ -197,10 +260,14 @@ endif
197260else
198261 @echo "Using system Python interpreter"
199262endif
200- ifeq ("$(PYTHON_PACKAGE_INSTALLER )$(MXENV_UV_GLOBAL ) ","uvfalse")
201- @echo "Install uv"
263+
264+ # Install uv locally if needed
265+ ifeq ("$(USE_LOCAL_UV ) ","true")
266+ @echo "Install uv in virtual environment"
202267 @$(MXENV_PYTHON) -m pip install uv
203268endif
269+
270+ # Install/upgrade core packages
204271 @$(PYTHON_PACKAGE_COMMAND) install -U pip setuptools wheel
205272 @echo "Install/Update MXStack Python packages"
206273 @$(PYTHON_PACKAGE_COMMAND) install -U $(MXDEV) $(MXMAKE)
@@ -397,6 +464,43 @@ INSTALL_TARGETS+=packages
397464DIRTY_TARGETS+ =packages-dirty
398465CLEAN_TARGETS+ =packages-clean
399466
467+ # #############################################################################
468+ # test
469+ # #############################################################################
470+
471+ TEST_TARGET: =$(SENTINEL_FOLDER ) /test.sentinel
472+ $(TEST_TARGET ) : $(MXENV_TARGET )
473+ @echo " Install $( TEST_REQUIREMENTS) "
474+ @$(PYTHON_PACKAGE_COMMAND ) install $(TEST_REQUIREMENTS )
475+ @touch $(TEST_TARGET )
476+
477+ .PHONY : test
478+ test : $(FILES_TARGET ) $(SOURCES_TARGET ) $(PACKAGES_TARGET ) $(TEST_TARGET ) $(TEST_DEPENDENCY_TARGETS )
479+ @test -z " $( TEST_COMMAND) " && echo " No test command defined" && exit 1 || :
480+ @echo " Run tests using $( TEST_COMMAND) "
481+ @/usr/bin/env bash -c " $( TEST_COMMAND) "
482+
483+ .PHONY : test-dirty
484+ test-dirty :
485+ @rm -f $(TEST_TARGET )
486+
487+ .PHONY : test-clean
488+ test-clean : test-dirty
489+ @test -e $(MXENV_PYTHON ) && $(MXENV_PYTHON ) -m pip uninstall -y $(TEST_REQUIREMENTS ) || :
490+ @rm -rf .pytest_cache
491+
492+ INSTALL_TARGETS+ =$(TEST_TARGET )
493+ CLEAN_TARGETS+ =test-clean
494+ DIRTY_TARGETS+ =test-dirty
495+
496+ # #############################################################################
497+ # help
498+ # #############################################################################
499+
500+ .PHONY : help
501+ help : $(MXENV_TARGET )
502+ @mxmake help-generator
503+
400504# #############################################################################
401505# Custom includes
402506# #############################################################################
0 commit comments