44
55include env.mk
66
7+ # Venv path (override with VENV_DIR=/path for devcontainer)
8+ VENV_DIR ?= .venv
9+
10+ # Use venv Python/tools when available, fallback to system
11+ PYTHON := $(shell [ -x $(VENV_DIR ) /bin/python ] && echo $(VENV_DIR ) /bin/python || echo python3)
12+
713# --- Setup ---
814
915# npm install is re-run only when package.json changes
@@ -18,9 +24,12 @@ prepare: node_modules/.package-lock.json ## Install git hooks
1824.PHONY : setup
1925setup : install prepare # # Full dev environment setup
2026
27+ $(VENV_DIR ) /bin/activate :
28+ python3 -m venv $(VENV_DIR )
29+
2130.PHONY : install
22- install : node_modules/.package-lock.json # # Install dev tools (pip + npm)
23- python3 -m pip install -e " .[dev,test]"
31+ install : $( VENV_DIR ) /bin/activate node_modules/.package-lock.json # # Install dev tools (pip + npm)
32+ $( VENV_DIR ) /bin/ pip install -e " .[dev,test]"
2433
2534# --- Linting ---
2635
@@ -37,12 +46,12 @@ lint-fix: ## Auto-fix lint issues
3746# Dynamic per-scenario targets (test-apds9960, test-hts221, etc.)
3847# Uses 'driver' field for driver scenarios, filename stem for board scenarios.
3948# Convention: for board scenarios, the YAML 'name' field must match the filename.
40- SCENARIOS := $(shell python3 -c "import yaml,glob,os; [print(d.get('driver',os.path.basename(f) .replace('.yaml',''))) for f in sorted(glob.glob('tests/scenarios/*.yaml')) for d in [yaml.safe_load(open(f))]]" 2>/dev/null)
41- $(foreach s,$(SCENARIOS ) ,$(eval .PHONY : test-$(s ) )$(eval test-$(s ) : ; python3 -m pytest tests/ -v -k "$(s ) " --port $$(PORT ) -s) )
49+ SCENARIOS := $(shell $( PYTHON ) -c "import yaml,glob,os; [print(d.get('driver',os.path.basename(f) .replace('.yaml',''))) for f in sorted(glob.glob('tests/scenarios/*.yaml')) for d in [yaml.safe_load(open(f))]]" 2>/dev/null)
50+ $(foreach s,$(SCENARIOS ) ,$(eval .PHONY : test-$(s ) )$(eval test-$(s ) : ; $( PYTHON ) -m pytest tests/ -v -k "$(s ) " --port $$(PORT ) -s) )
4251
4352.PHONY : test-mock
4453test-mock : # # Run mock tests (no hardware needed)
45- python3 -m pytest tests/ -v -k mock
54+ $( PYTHON ) -m pytest tests/ -v -k mock
4655
4756.PHONY : test
4857test : test-mock # # Run mock tests (use 'make test-all' for mock + hardware)
@@ -51,23 +60,23 @@ test: test-mock ## Run mock tests (use 'make test-all' for mock + hardware)
5160
5261.PHONY : test-hardware
5362test-hardware : # # Run all hardware tests (needs board on PORT)
54- python3 -m pytest tests/ -v --port $(PORT ) -s -k hardware
63+ $( PYTHON ) -m pytest tests/ -v --port $(PORT ) -s -k hardware
5564
5665.PHONY : test-board
5766test-board : # # Run board tests only (buttons, LEDs, buzzer, screen)
58- python3 -m pytest tests/ -v --port $(PORT ) -s -k " board_ and hardware"
67+ $( PYTHON ) -m pytest tests/ -v --port $(PORT ) -s -k " board_ and hardware"
5968
6069.PHONY : test-sensors
6170test-sensors : # # Run sensor driver hardware tests (I2C devices)
62- python3 -m pytest tests/ -v --port $(PORT ) -s -k " hardware and not board_"
71+ $( PYTHON ) -m pytest tests/ -v --port $(PORT ) -s -k " hardware and not board_"
6372
6473.PHONY : test-all
6574test-all : # # Run all tests (mock + hardware)
66- python3 -m pytest tests/ -v --port $(PORT ) -s
75+ $( PYTHON ) -m pytest tests/ -v --port $(PORT ) -s
6776
6877.PHONY : test-examples
6978test-examples : # # Validate all example files (syntax + imports)
70- python3 -m pytest tests/test_examples.py -v
79+ $( PYTHON ) -m pytest tests/test_examples.py -v
7180
7281# --- CI ---
7382
@@ -179,7 +188,7 @@ bump: ## Create a version tag (PART=patch|minor|major, default: patch)
179188 fi ; \
180189 echo " $$ LAST → $$ NEXT" ; \
181190 VERSION=$$ {NEXT#v}; \
182- python3 -c " import re, pathlib; p=pathlib.Path('pyproject.toml'); p.write_text(re.sub(r'^version = \" .*\" ', 'version = \" $$ VERSION\" ', p.read_text(), count=1, flags=re.MULTILINE))" ; \
191+ $( PYTHON ) -c " import re, pathlib; p=pathlib.Path('pyproject.toml'); p.write_text(re.sub(r'^version = \" .*\" ', 'version = \" $$ VERSION\" ', p.read_text(), count=1, flags=re.MULTILINE))" ; \
183192 git add pyproject.toml; \
184193 git commit -m " chore: Bump version to $$ NEXT." ; \
185194 git tag -a " $$ NEXT" -m " Release $$ NEXT" ; \
@@ -194,10 +203,11 @@ clean: ## Remove build artifacts and caches
194203 find . -type d -name .pytest_cache -exec rm -rf {} + 2> /dev/null || true
195204 find . -type d -name .ruff_cache -exec rm -rf {} + 2> /dev/null || true
196205 find . -type d -name .mypy_cache -exec rm -rf {} + 2> /dev/null || true
206+ find . -type d -name ' *.egg-info' -exec rm -rf {} + 2> /dev/null || true
197207
198208.PHONY : deepclean
199- deepclean : clean # # Remove everything including node_modules and firmware
200- rm -rf node_modules
209+ deepclean : clean # # Remove everything including node_modules, venv and firmware
210+ rm -rf node_modules $( VENV_DIR )
201211 @if [ -d " $( BUILD_DIR) " ]; then rm -rf " $( BUILD_DIR) " ; fi
202212
203213.PHONY : help
0 commit comments