How can I activate virtualenv in a Makefile?
I have tried:
venv:
@virtualenv venv
active:
@source venv/bin/activate
And I've also tried:
active:
@. venv/bin/activate
and it doesn't activate virtualenv.
How can I activate virtualenv in a Makefile?
I have tried:
venv:
@virtualenv venv
active:
@source venv/bin/activate
And I've also tried:
active:
@. venv/bin/activate
and it doesn't activate virtualenv.
Here's how to do it:
You can execute a shell command in a Makefile using ();
E.g.
echoTarget:
(echo "I'm an echo")
Just be sure to put a tab character before each line in the shell command. i.e. you will need a tab before (echo "I'm an echo")
Here's what will work for activating virtualenv:
activate:
( \
source path/to/virtualenv/activate; \
pip install -r requirements.txt; \
)
UPD Mar 14 '21
One more variant for pip install
inside virtualenv:
# Makefile
all: install run
install: venv
: # Activate venv and install smthing inside
. venv/bin/activate && pip install -r requirements.txt
: # Other commands here
venv:
: # Create venv if it doesn't exist
: # test -d venv || virtualenv -p python3 --no-site-packages venv
test -d venv || python3 -m venv venv
run:
: # Run your app here, e.g
: # determine if we are in venv,
: # see https://stackoverflow.com/q/1871549
. venv/bin/activate && pip -V
: # Or see @wizurd's answer to exec multiple commands
. venv/bin/activate && (\
python3 -c 'import sys; print(sys.prefix)'; \
pip3 -V \
)
clean:
rm -rf venv
find -iname "*.pyc" -delete
So you can run make
with different 'standard' ways:
make
- target to default all
make venv
- to just create virtualenvmake install
- to make venv and execute other commandsmake run
- to execute your app inside venvmake clean
- to remove venv and python binariesI built a simple way to do that by combining an ACTIVATE_LINUX:=. venv/bin/activate
variable with .ONESHELL:
at the begging of the Makefile:
.ONESHELL:
.PHONY: clean venv tests scripts
ACTIVATE_LINUX:=. venv/bin/activate
setup: venv install scripts
venv:
@echo "Creating python virtual environment in 'venv' folder..."
@python3 -m venv venv
install:
@echo "Installing python packages..."
@$(ACTIVATE_LINUX)
@pip install -r requirements.txt
clean:
@echo "Cleaning previous python virtual environment..."
@rm -rf venv
As pointed out in other answers, Make
recipes run on sh
instead of bash
, so using .
in the ACTIVATE_LINUX
variable instead of @
to activate the virtual environment is the correct syntax.
I combined this strategy with .ONESHELL:
. As shown at this StackOverflow anwser, normally Make
runs every command in a recipe in a different subshell. However, setting .ONESHELL:
will run all the commands in a recipe in the same subshell, allowing you to activate a virtual environment and then run commands inside it. That's exactly what's happening in make install
target and this approach could be applied wherever you need an activated environment to run some python code in your project.
So you can run the following make targets:
make
- target to default setup;make venv
- to just create virtual environment;make install
- to activate venv
and execute the pip install
command; andmake clean
- to remove previous venv
and python binaries.When it is time to execute recipes to update a target, they are executed by invoking a new sub-shell for each line of the recipe. --- GNU Make
Since each line of the recipe executes in a separate sub-shell, we should run the python code in the same line of the recipe.
Simple python script for showing the current source of python environment (filename: whichpy.py
):
import sys
print(sys.prefix)
Running python virtual environment (Make
recipes run on sh
instead of bash
, using .
to activate the virtual environment is the correct syntax):
test:
. pyth3.6/bin/activate && python3.6 whichpy.py
. pyth3.6/bin/activate; python3.6 whichpy.py
Both the above 2 recipes are acceptable and we are free to use backslash/newline to separate one recipe into multiple lines.
Makefiles can't activate an environment directly. This is what worked for me:
activate:
bash -c "venv/bin/activate"
If you get a permission denied error make venv/bin/activate executable:
chmod +x venv/bin/activate
An alternative to activate could be use an environment variable in Makefile. For example:
.PHONY: venv install
VENV=.venv
PYTHON=$(VENV)/bin/python3
venv:
python3 -m venv $(VENV)
install: venv
$(PYTHON) -m pip install -r requirements-dev.txt
isort:
$(PYTHON) -m isort --check-only .
black:
$(PYTHON) -m black --check .
mypy:
$(PYTHON) -m mypy .
flake8:
$(PYTHON) -m flake8 .
bandit:
$(PYTHON) -m bandit -r app
lint: isort black mypy flake8 bandit
test: ## Run tests
$(PYTHON) -m pytest
One way to do it: prepend each command in Makefile
with . venv/bin/activate &&
which must be run under venv
.
That is, use:
target:
. venv/bin/activate && command
instead of
target:
command