66# : core.mxenv
77# : core.mxfiles
88# : core.packages
9- # : qa.black
109# : qa.isort
1110# : qa.mypy
11+ # : qa.ruff
1212# : qa.test
1313#
1414# SETTINGS (ALL CHANGES MADE BELOW SETTINGS WILL BE LOST)
@@ -54,18 +54,19 @@ PRIMARY_PYTHON?=3.14
5454PYTHON_MIN_VERSION? =3.10
5555
5656# Install packages using the given package installer method.
57- # Supported are `pip` and `uv`. If uv is used, its global availability is
58- # checked. Otherwise, it is installed, either in the virtual environment or
59- # using the `PRIMARY_PYTHON`, dependent on the `VENV_ENABLED` setting. If
60- # `VENV_ENABLED` and uv is selected, uv is used to create the virtual
61- # 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.
6261# Default: pip
6362PYTHON_PACKAGE_INSTALLER? =uv
6463
65- # Flag whether to use a global installed 'uv' or install
66- # it in the virtual environment.
67- # Default: false
68- 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 )
6970
7071# Flag whether to use virtual environment. If `false`, the
7172# interpreter according to `PRIMARY_PYTHON` found in `PATH` is used.
@@ -94,17 +95,17 @@ MXDEV?=mxdev
9495# Default: mxmake
9596MXMAKE? =mxmake
9697
97- # # qa.isort
98+ # # qa.ruff
9899
99- # Source folder to scan for Python files to run isort on.
100+ # Source folder to scan for Python files to run ruff on.
100101# Default: src
101- ISORT_SRC ? =src
102+ RUFF_SRC ? =src
102103
103- # # qa.black
104+ # # qa.isort
104105
105- # Source folder to scan for Python files to run black on.
106+ # Source folder to scan for Python files to run isort on.
106107# Default: src
107- BLACK_SRC ? =src
108+ ISORT_SRC ? =src
108109
109110# # core.mxfiles
110111
@@ -199,30 +200,57 @@ else
199200MXENV_PYTHON =$(PRIMARY_PYTHON )
200201endif
201202
202- # Determine the package installer
203+ # Determine the package installer with non-interactive flags
203204ifeq ("$(PYTHON_PACKAGE_INSTALLER ) ","uv")
204- PYTHON_PACKAGE_COMMAND =uv pip
205+ PYTHON_PACKAGE_COMMAND =uv pip --quiet --no-progress
205206else
206207PYTHON_PACKAGE_COMMAND =$(MXENV_PYTHON ) -m pip
207208endif
208209
210+ # Auto-detect global uv availability (simple existence check)
211+ ifeq ("$(PYTHON_PACKAGE_INSTALLER ) ","uv")
212+ UV_AVAILABLE: =$(shell command -v uv >/dev/null 2>&1 && echo "true" || echo "false")
213+ else
214+ UV_AVAILABLE: =false
215+ endif
216+
217+ # Determine installation strategy
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+
209228MXENV_TARGET: =$(SENTINEL_FOLDER ) /mxenv.sentinel
210229$(MXENV_TARGET ) : $(SENTINEL )
211- ifneq ("$(PYTHON_PACKAGE_INSTALLER )$(MXENV_UV_GLOBAL ) ","uvfalse")
230+ # Validation: Check Python version if not using global uv
231+ ifneq ("$(USE_GLOBAL_UV ) ","true")
212232 @$(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)" \
213233 && echo "Need Python >= $(PYTHON_MIN_VERSION)" && exit 1 || :
214234else
215- @echo "Use Python $(PYTHON_MIN_VERSION) over uv "
235+ @echo "Using global uv for Python $(UV_PYTHON) "
216236endif
237+ # Validation: Check VENV_FOLDER is set if venv enabled
217238 @[[ "$(VENV_ENABLED)" == "true" && "$(VENV_FOLDER)" == "" ]] \
218239 && echo "VENV_FOLDER must be configured if VENV_ENABLED is true" && exit 1 || :
219- @[[ "$(VENV_ENABLED)$(PYTHON_PACKAGE_INSTALLER)" == "falseuv" ]] \
240+ # Validation: Check uv not used with system Python
241+ @[[ "$(VENV_ENABLED)" == "false" && "$(PYTHON_PACKAGE_INSTALLER)" == "uv" ]] \
220242 && 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
221249ifeq ("$(VENV_ENABLED ) ", "true")
222250ifeq ("$(VENV_CREATE ) ", "true")
223- ifeq ("$(PYTHON_PACKAGE_INSTALLER )$( MXENV_UV_GLOBAL ) ","uvtrue ")
224- @echo "Setup Python Virtual Environment using package 'uv' at '$(VENV_FOLDER)'"
225- @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 --quiet --no-progress - p $(UV_PYTHON ) --seed $(VENV_FOLDER)
226254else
227255 @echo "Setup Python Virtual Environment using module 'venv' at '$(VENV_FOLDER)'"
228256 @$(PRIMARY_PYTHON) -m venv $(VENV_FOLDER)
@@ -232,10 +260,14 @@ endif
232260else
233261 @echo "Using system Python interpreter"
234262endif
235- ifeq ("$(PYTHON_PACKAGE_INSTALLER )$(MXENV_UV_GLOBAL ) ","uvfalse")
236- @echo "Install uv"
263+
264+ # Install uv locally if needed
265+ ifeq ("$(USE_LOCAL_UV ) ","true")
266+ @echo "Install uv in virtual environment"
237267 @$(MXENV_PYTHON) -m pip install uv
238268endif
269+
270+ # Install/upgrade core packages
239271 @$(PYTHON_PACKAGE_COMMAND) install -U pip setuptools wheel
240272 @echo "Install/Update MXStack Python packages"
241273 @$(PYTHON_PACKAGE_COMMAND) install -U $(MXDEV) $(MXMAKE)
@@ -263,6 +295,41 @@ INSTALL_TARGETS+=mxenv
263295DIRTY_TARGETS+ =mxenv-dirty
264296CLEAN_TARGETS+ =mxenv-clean
265297
298+ # #############################################################################
299+ # ruff
300+ # #############################################################################
301+
302+ RUFF_TARGET: =$(SENTINEL_FOLDER ) /ruff.sentinel
303+ $(RUFF_TARGET ) : $(MXENV_TARGET )
304+ @echo " Install Ruff"
305+ @$(PYTHON_PACKAGE_COMMAND ) install ruff
306+ @touch $(RUFF_TARGET )
307+
308+ .PHONY : ruff-check
309+ ruff-check : $(RUFF_TARGET )
310+ @echo " Run ruff check"
311+ @ruff check $(RUFF_SRC )
312+
313+ .PHONY : ruff-format
314+ ruff-format : $(RUFF_TARGET )
315+ @echo " Run ruff format"
316+ @ruff format $(RUFF_SRC )
317+
318+ .PHONY : ruff-dirty
319+ ruff-dirty :
320+ @rm -f $(RUFF_TARGET )
321+
322+ .PHONY : ruff-clean
323+ ruff-clean : ruff-dirty
324+ @test -e $(MXENV_PYTHON ) && $(MXENV_PYTHON ) -m pip uninstall -y ruff || :
325+ @rm -rf .ruff_cache
326+
327+ INSTALL_TARGETS+ =$(RUFF_TARGET )
328+ CHECK_TARGETS+ =ruff-check
329+ FORMAT_TARGETS+ =ruff-format
330+ DIRTY_TARGETS+ =ruff-dirty
331+ CLEAN_TARGETS+ =ruff-clean
332+
266333# #############################################################################
267334# isort
268335# #############################################################################
@@ -297,40 +364,6 @@ FORMAT_TARGETS+=isort-format
297364DIRTY_TARGETS+ =isort-dirty
298365CLEAN_TARGETS+ =isort-clean
299366
300- # #############################################################################
301- # black
302- # #############################################################################
303-
304- BLACK_TARGET: =$(SENTINEL_FOLDER ) /black.sentinel
305- $(BLACK_TARGET ) : $(MXENV_TARGET )
306- @echo " Install Black"
307- @$(PYTHON_PACKAGE_COMMAND ) install black
308- @touch $(BLACK_TARGET )
309-
310- .PHONY : black-check
311- black-check : $(BLACK_TARGET )
312- @echo " Run black checks"
313- @black --check $(BLACK_SRC )
314-
315- .PHONY : black-format
316- black-format : $(BLACK_TARGET )
317- @echo " Run black format"
318- @black $(BLACK_SRC )
319-
320- .PHONY : black-dirty
321- black-dirty :
322- @rm -f $(BLACK_TARGET )
323-
324- .PHONY : black-clean
325- black-clean : black-dirty
326- @test -e $(MXENV_PYTHON ) && $(MXENV_PYTHON ) -m pip uninstall -y black || :
327-
328- INSTALL_TARGETS+ =$(BLACK_TARGET )
329- CHECK_TARGETS+ =black-check
330- FORMAT_TARGETS+ =black-format
331- DIRTY_TARGETS+ =black-dirty
332- CLEAN_TARGETS+ =black-clean
333-
334367# #############################################################################
335368# mxfiles
336369# #############################################################################
0 commit comments