How to use virtualenv in makefile

MakefileVirtualenv

Makefile Problem Overview


I want to perform several operations while working on a specified virtualenv.

For example command

make install

would be equivalent to

source path/to/virtualenv/bin/activate
pip install -r requirements.txt

Is it possible?

Makefile Solutions


Solution 1 - Makefile

I like using something that runs only when requirements.txt changes:

This assumes that source files are under project in your project's root directory and that tests are under project/test. (You should change project to match your actually project name.)

venv: venv/touchfile

venv/touchfile: requirements.txt
	test -d venv || virtualenv venv
	. venv/bin/activate; pip install -Ur requirements.txt
	touch venv/touchfile

test: venv
	. venv/bin/activate; nosetests project/test

clean:
	rm -rf venv
	find -iname "*.pyc" -delete
  • Run make to install packages in requirements.txt.
  • Run make test to run your tests (you can update this command if your tests are somewhere else).
  • run make clean to delete all artifacts.

Solution 2 - Makefile

In make you can run a shell as command. In this shell you can do everything you can do in a shell you started from comandline. Example:

install:
    ( \
       source path/to/virtualenv/bin/activate; \
       pip install -r requirements.txt; \
    )

Attention must be paid to the ;and the \.

Everything between the open and close brace will be done in a single instance of a shell.

Solution 3 - Makefile

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 virtualenv and then run commands inside it.

Note that .ONESHELL: applies to the whole Makefile, not just a single recipe. It may change behaviour of existing commands, details of possible errors in the full documentation. This will not let you activate a virtualenv for use outside the Makefile, since the commands are still run inside a subshell.

Reference documentation: https://www.gnu.org/software/make/manual/html_node/One-Shell.html

Example:

.ONESHELL:

.PHONY: install
install:
    source path/to/virtualenv/bin/activate
    pip install -r requirements.txt

Solution 4 - Makefile

I have had luck with this.

install:
    source ./path/to/bin/activate; \
    pip install -r requirements.txt; \

Solution 5 - Makefile

This is an alternate way to run things that you want to run in virtualenv.

BIN=venv/bin/

install:
    $(BIN)pip install -r requirements.txt

run:
    $(BIN)python main.py

PS: This doesn't activate the virtualenv, but gets thing done. Hope you find it clean and useful.

Solution 6 - Makefile

Based on the answers above (thanks @Saurabh and @oneself!) I've written a reusable Makefile that takes care of creating virtual environment and keeping it updated: https://github.com/sio/Makefile.venv

It works by referencing correct executables within virtualenv and does not rely on the "activate" shell script. Here is an example:

test: venv
	$(VENV)/python -m unittest

include Makefile.venv

Differences between Windows and other operating systems are taken into account, Makefile.venv should work fine on any OS that provides Python and make.

Solution 7 - Makefile

I like to set my Makefile up so that it uses a venv directory if one exists, but defaults to using the PATH.

For local development, I like to use a venv, so I run:

# Running this:  # Actually runs this:
make venv        # /usr/bin/python3 -m venv venv
make deps        # .venv/bin/python -m pip install -r requirements.txt
make test        # .venv/bin/python -m tox

If I'm installing into a container though, or into my machine, I might bypass the virtual environment:

# Running this:  # Actually runs this:
make deps        # /usr/bin/python3 -m pip install -r requirements.txt
make test        # /usr/bin/python3 -m tox
make build       # /usr/bin/python3 -m build --wheel
make install     # /usr/bin/python3 -m pip install dist/*.whl

Setup

At the top of your Makefile, define these two variables:

# If `venv/bin/python` exists, it is used. If not, use PATH to find python.
SYSTEM_PYTHON  = $(or $(shell which python3), $(shell which python))
PYTHON         = $(or $(wildcard venv/bin/python), $(SYSTEM_PYTHON))

Which evaluate to:

# If "venv" dir exists:
SYSTEM_PYTHON = /usr/bin/python3
PYTHON        = .venv/bin/python

# If "venv" dir does not exist:
SYSTEM_PYTHON = /usr/bin/python3
PYTHON        = /usr/bin/python3

Note: /usr/bin/python3 could be something else on your system, depending on your PATH.

In your makefile, run executables (including pip) like this:

$(PYTHON) -m tox

You might want to create a target called "venv" that creates the venv directory:

venv:
	rm -rf $(VENV)
	$(SYSTEM_PYTHON) -m venv $(VENV)

And a deps target to install dependencies:

deps:
	$(PYTHON) -m pip install -r requirements.txt

Example

Here's my Makefile:

MAKEFLAGS     = --no-print-directory --no-builtin-rules
.DEFAULT_GOAL = all

# Variables
PACKAGE = mypackage

# If virtualenv exists, use it. If not, use PATH to find
SYSTEM_PYTHON  = $(or $(shell which python3), $(shell which python))
PYTHON         = $(or $(wildcard venv/bin/python), $(SYSTEM_PYTHON))

all: test build

.PHONY: all

## Environment

venv:
	rm -rf venv
	$(SYSTEM_PYTHON) -m venv venv

deps:
	$(PYTHON) -m pip install --upgrade pip -r requirements.txt -r requirements_dev.txt

.PHONY: venv deps

## Lint, test

test:
	$(PYTHON) -m tox

dev/test:
	$(PYTHON) -m tox -e py38

dev/lint:
	$(PYTHON) -m tox -e lint

dev/lintfix:
	$(PYTHON) -m black $(PACKAGE) tests setup.py

.PHONY: test dev/test dev/lint dev/lintfix

## Build, install

build:
	$(PYTHON) -m build --sdist
	$(PYTHON) -m build --wheel

install:
	$(PYTHON) -m pip install dist/$(PACKAGE)-*.whl

.PHONY: build install

## Clean

clean:
	rm -rf .out .pytest_cache .tox *.egg-info dist build

.PHONY: clean

Solution 8 - Makefile

You also could use the environment variable called "VIRTUALENVWRAPPER_SCRIPT". Like this:

install:
    ( \
       source $$VIRTUALENVWRAPPER_SCRIPT; \
       pip install -r requirements.txt; \
    )

Solution 9 - Makefile

You should use this, it's functional for me at moment.

report.ipynb : merged.ipynb
	( bash -c "source ${HOME}/anaconda3/bin/activate py27; which -a python; \
		jupyter nbconvert \
		--to notebook \
		--ExecutePreprocessor.kernel_name=python2 \
		--ExecutePreprocessor.timeout=3000 \
		--execute merged.ipynb \
		--output=$< $<" )

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiondev9View Question on Stackoverflow
Solution 1 - MakefileoneselfView Answer on Stackoverflow
Solution 2 - MakefileKlausView Answer on Stackoverflow
Solution 3 - MakefileHollyView Answer on Stackoverflow
Solution 4 - Makefileuser2693845View Answer on Stackoverflow
Solution 5 - MakefileSaurabh KumarView Answer on Stackoverflow
Solution 6 - MakefileSIOView Answer on Stackoverflow
Solution 7 - MakefilemattalxndrView Answer on Stackoverflow
Solution 8 - MakefilehermancaldaraView Answer on Stackoverflow
Solution 9 - MakefileWilliam TrigosView Answer on Stackoverflow