# # SEAMLESSLY MANAGE PYTHON VIRTUAL ENVIRONMENT WITH A MAKEFILE # # https://github.com/sio/Makefile.venv v2020.08.14 # # # Insert `include Makefile.venv` at the bottom of your Makefile to enable these # rules. # # When writing your Makefile use '$(VENV)/python' to refer to the Python # interpreter within virtual environment and '$(VENV)/executablename' for any # other executable in venv. # # This Makefile provides the following targets: # venv # Use this as a dependency for any target that requires virtual # environment to be created and configured # python, ipython # Use these to launch interactive Python shell within virtual environment # shell, bash, zsh # Launch interactive command line shell. "shell" target launches the # default shell Makefile executes its rules in (usually /bin/sh). # "bash" and "zsh" can be used to refer to the specific desired shell. # show-venv # Show versions of Python and pip, and the path to the virtual environment # clean-venv # Remove virtual environment # $(VENV)/executable_name # Install `executable_name` with pip. Only packages with names matching # the name of the corresponding executable are supported. # Use this as a lightweight mechanism for development dependencies # tracking. E.g. for one-off tools that are not required in every # developer's environment, therefore are not included into # requirements.txt or setup.py. # Note: # Rules using such target or dependency MUST be defined below # `include` directive to make use of correct $(VENV) value. # Example: # codestyle: $(VENV)/pyflakes # $(VENV)/pyflakes . # See `ipython` target below for another example. # # This Makefile can be configured via following variables: # PY # Command name for system Python interpreter. It is used only initialy to # create the virtual environment # Default: python3 # REQUIREMENTS_TXT # Space separated list of paths to requirements.txt files. # Paths are resolved relative to current working directory. # Default: requirements.txt # WORKDIR # Parent directory for the virtual environment. # Default: current working directory. # VENVDIR # Python virtual environment directory. # Default: $(WORKDIR)/.venv # # This Makefile was written for GNU Make and may not work with other make # implementations. # # # Copyright (c) 2019-2020 Vitaly Potyarkin # # Licensed under the Apache License, Version 2.0 # # # # Configuration variables # PY?=python3 WORKDIR?=. VENVDIR?=$(WORKDIR)/.venv REQUIREMENTS_TXT?=$(wildcard requirements.txt) # Multiple paths are supported (space separated) MARKER=.initialized-with-Makefile.venv # # Internal variable resolution # VENV=$(VENVDIR)/bin EXE= # Detect windows ifeq (win32,$(shell $(PY) -c "import __future__, sys; print(sys.platform)")) VENV=$(VENVDIR)/Scripts EXE=.exe endif # # Virtual environment # .PHONY: venv venv: $(VENV)/$(MARKER) .PHONY: clean-venv clean-venv: -$(RM) -r "$(VENVDIR)" .PHONY: show-venv show-venv: venv @$(VENV)/python -c "import sys; print('Python ' + sys.version.replace('\n',''))" @$(VENV)/pip --version @echo venv: $(VENVDIR) .PHONY: debug-venv debug-venv: @$(MAKE) --version $(info PY="$(PY)") $(info REQUIREMENTS_TXT="$(REQUIREMENTS_TXT)") $(info VENVDIR="$(VENVDIR)") $(info VENVDEPENDS="$(VENVDEPENDS)") $(info WORKDIR="$(WORKDIR)") # # Dependencies # ifneq ($(strip $(REQUIREMENTS_TXT)),) VENVDEPENDS+=$(REQUIREMENTS_TXT) endif ifneq ($(wildcard setup.py),) VENVDEPENDS+=setup.py endif ifneq ($(wildcard setup.cfg),) VENVDEPENDS+=setup.cfg endif $(VENV): $(PY) -m venv $(VENVDIR) $(VENV)/python -m pip install --upgrade pip setuptools wheel $(VENV)/$(MARKER): $(VENVDEPENDS) | $(VENV) ifneq ($(strip $(REQUIREMENTS_TXT)),) $(VENV)/pip install $(foreach path,$(REQUIREMENTS_TXT),-r $(path)) endif ifneq ($(wildcard setup.py),) $(VENV)/pip install -e . endif touch $(VENV)/$(MARKER) # # Interactive shells # .PHONY: python python: venv exec $(VENV)/python .PHONY: ipython ipython: $(VENV)/ipython exec $(VENV)/ipython .PHONY: shell shell: venv . $(VENV)/activate && exec $(notdir $(SHELL)) .PHONY: bash zsh bash zsh: venv . $(VENV)/activate && exec $@ # # Commandline tools (wildcard rule, executable name must match package name) # ifneq ($(EXE),) $(VENV)/%: $(VENV)/%$(EXE) ; .PHONY: $(VENV)/% .PRECIOUS: $(VENV)/%$(EXE) endif $(VENV)/%$(EXE): $(VENV)/$(MARKER) $(VENV)/pip install --upgrade $* touch $@