如何在makefile中使用virtualenv

86
我希望在指定的虚拟环境中执行多个操作。
例如命令:
make install

将等同于

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

这是可能的吗?

11个回答

83

我喜欢使用仅在 requirements.txt 变更时运行的东西:

这假定源文件位于项目根目录下的 project 中,并且测试位于 project/test 下。(您应该更改 project 以匹配您实际的项目名称。)

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
  • 运行make安装requirements.txt中的软件包。
  • 运行make test来运行您的测试(如果您的测试在其他地方,则可以更新此命令)。
  • 运行make clean以删除所有产生的文件。

1
这看起来很不错。有人能详细解释一下吗? - conner.xyz
1
你还需要什么额外的信息吗?我可以添加到答案中。 - oneself
1
如果 pip 安装失败,这将删除 venv/bin/activate。最好使用另一个“跟踪文件”。只需更改所有出现的位置(当然不包括源),例如 venv/bin/updated_by_make - Antonio Barreto
这可能是一个愚蠢的问题,但有人能解释一下这里的“touchfile”是什么吗?另外,只输入“make”如何安装“requirements.txt”中的所有包。我已经制作了一个类似的makefile,但只使用“make”会输出“make help”。:( - zean_7
2
@zean_7 只需键入 make 即可执行 makefile 中的第一个目标——在本例中是 venv。由于 venv 依赖于 venv/touchfile,因此只要 requirements.txt(其依赖项)发生更改,就会运行该配方。 venv/touchfile 只是一个空文件,用于跟踪虚拟环境的修改时间。 - Czaporka
显示剩余4条评论

71

在Make中,您可以将shell作为命令运行。在此shell中,您可以执行从命令行启动的shell中可以执行的所有操作。例如:

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

注意必须注意分号;和反斜杠\

大括号之间的所有内容将在单个shell实例中执行。


27
这种情况下不需要使用 () 符号。对于规则主体中的每一行,make命令已经会为其生成一个新的shell实例。你只需要使用续行符来让make命令将这些行读取并在单个shell实例中执行,而不是像默认情况下在多个shell实例中执行。 - Etan Reisner
1
@Yukulélé:请不要将我的答案更改为已经作为解决方案发布的内容。user2693845发布了与您编辑我的答案相同的内容。Etan的评论已经解释了大括号是不必要的。 - Klaus
6
我一开始会看到 "source: command not found" 的提示,所以最后不得不使用同义词。例如更改上述语句的第三行为:. path/to/virtualenv/bin/activate; \ 希望这能帮助其他遇到类似问题的人 :-) - Frito

42

通常情况下,make 命令会在不同的子 shell 中运行每个配方中的命令。但是,设置 .ONESHELL: 将会在同一个子 shell 中运行所有命令,这样您就可以激活一个虚拟环境并在其中运行命令。

请注意,.ONESHELL: 适用于整个 Makefile,而不仅仅是单个配方。它可能会改变现有命令的行为,详细信息可以在完整文档中查看可能的错误。由于命令仍然在子 shell 中运行,这将不能让您在 Makefile 外部使用虚拟环境。

参考文献: https://www.gnu.org/software/make/manual/html_node/One-Shell.html

示例:

.ONESHELL:

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

7
在我看来,这是最佳解决方案。 - El Ruso

21

我用这个很顺利。

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

1
/bin/sh: 1: source: 找不到 - Adil
5
请将以下内容添加到Makefile的第一行:SHELL := /bin/bash。根据您使用的shell进行调整。 - Juha Untinen
1
@Adil,可以使用POSIX点命令代替source,例如. ./path/to/bin/activate - Antonio Barreto

17

这是一种在virtualenv中运行所需内容的替代方法。

BIN=venv/bin/

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

run:
    $(BIN)python main.py

提示:这不会激活虚拟环境,但可以完成任务。希望您觉得它干净有用。


在macOS上工作,不像其他的复制品那样。 - undefined

15
我喜欢设置我的Makefile,如果存在一个.venv目录,它将使用该目录,否则默认使用PATH。
对于本地开发,我喜欢使用虚拟环境,所以我运行:
# Running this:  # Actually runs this:
make venv        # /usr/bin/python3 -m venv .venv
make deps        # .venv/bin/python setup.py develop
make test        # .venv/bin/python -m tox

如果我要安装到容器中或者我的机器上,我可以跳过虚拟环境,直接跳过“make venv”命令。
# Running this:  # Actually runs this:
make deps        # /usr/bin/python3 setup.py develop
make test        # /usr/bin/python3 -m tox

设置

在你的Makefile顶部,定义以下变量:

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

如果存在./.venv,你会得到:
VENV          = .venv
VENV_PYTHON   = .venv/bin/python
SYSTEM_PYTHON = /usr/bin/python3
PYTHON        = .venv/bin/python

如果不是这样的话,你会得到:
VENV          = .venv
VENV_PYTHON   = .venv/bin/python
SYSTEM_PYTHON = /usr/bin/python3
PYTHON        = /usr/bin/python3

注意:在您的系统上,/usr/bin/python3 可能是其他内容,取决于您的 PATH
然后,在需要的地方,像这样运行 Python 代码:
$(PYTHON) -m tox
$(PYTHON) -m pip ...

你可能想要创建一个名为"venv"的目标,用于创建.venv目录。
$(VENV_PYTHON):
    rm -rf $(VENV)
    $(SYSTEM_PYTHON) -m venv $(VENV)

venv: $(VENV_PYTHON)

并且一个deps目标用于安装依赖项:
deps:
    $(PYTHON) setup.py develop
    # or whatever you need:
    #$(PYTHON) -m pip install -r requirements.txt

示例

这是我的 Makefile:

# Variables
VENV           = .venv
VENV_PYTHON    = $(VENV)/bin/python
SYSTEM_PYTHON  = $(or $(shell which python3), $(shell which python))
PYTHON         = $(or $(wildcard $(VENV_PYTHON)), $(SYSTEM_PYTHON))

## Dev/build environment

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

venv: $(VENV_PYTHON)

deps:
    $(PYTHON) -m pip install --upgrade pip
    # Dev dependencies
    $(PYTHON) -m pip install tox pytest
    # Dependencies
    $(PYTHON) setup.py develop

.PHONY: venv deps

## Test

test:
    $(PYTHON) -m tox

.PHONY: test

## Build source distribution, install

sdist:
    $(PYTHON) setup.py sdist

install:
    $(SYSTEM_PYTHON) -m pip install .

.PHONY: build install

什么是 or通配符?你在使用什么作为CLI? - Kuzeko
1
@Kuzeko $(or val1, val2)将返回第一个非空值。$(wildcard path)如果文件存在,则返回路径。我在这个例子中省略了常见的SHELL = /bin/...,因为它可以与/bin/sh/bin/bash一起使用,这是与Makefile一起使用的常用shell。 - mattalxndr
实际的默认路径是 ./venv 还是 ./.venv?命令和注释都涉及到这两个路径,所以我认为有拼写错误... - MestreLion
实际的默认路径是./venv还是./.venv?命令和注释都提到了这两个,所以我认为可能有个拼写错误。 - undefined
VENV_PYTHON = $(VENV_PYTHON)/bin/python 这个赋值是无效的。 - Mikaelblomkvistsson
抱歉,已经修正了... - mattalxndr

7

根据上面的答案(感谢 @Saurabh 和 @oneself!),我编写了一个可重用的 Makefile,它负责创建虚拟环境并将其更新: https://github.com/sio/Makefile.venv

它通过引用 virtualenv 中的正确可执行文件来工作,而不依赖于“activate”shell脚本。以下是一个示例:

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

include Makefile.venv

考虑到Windows与其他操作系统之间的差异,Makefile.venv应该可以在任何提供了Python和make功能的操作系统上正常工作。


1
有点晚了,但这是我的常规设置:
# system python interpreter. used only to create virtual environment
PY = python3
VENV = venv
BIN=$(VENV)/bin

# make it work on windows too
ifeq ($(OS), Windows_NT)
    BIN=$(VENV)/Scripts
    PY=python
endif


all: lint test

$(VENV): requirements.txt requirements-dev.txt setup.py
    $(PY) -m venv $(VENV)
    $(BIN)/pip install --upgrade -r requirements.txt
    $(BIN)/pip install --upgrade -r requirements-dev.txt
    $(BIN)/pip install -e .
    touch $(VENV)

.PHONY: test
test: $(VENV)
    $(BIN)/pytest

.PHONY: lint
lint: $(VENV)
    $(BIN)/flake8

.PHONY: release
release: $(VENV)
    $(BIN)/python setup.py sdist bdist_wheel upload

clean:
    rm -rf $(VENV)
    find . -type f -name *.pyc -delete
    find . -type d -name __pycache__ -delete

我对此进行了更详细的写作, 但基本上的想法是使用系统的Python创建虚拟环境,对于其他目标,只需在命令前加上$(BIN)变量,该变量指向venv内部的binScripts目录。这相当于activate函数。


0

我发现在 $PATH 前面添加并加入 $VIRTUAL_ENV 是最好的方法:

  • 不需要在配方中添加 activate 并限制自己使用 ; 链接
    • 这里和这里都有展示herehere
  • 可以像平常一样简单地使用 python,它会回退到系统 Python
    • 不需要第三方软件包
    • 与 Windows(如果使用 bash)和 POSIX 兼容
# SYSTEM_PYTHON defaults to Python on the local machine
SYSTEM_PYTHON = $(shell which python)

REPO_ROOT = $(shell pwd)
# Specify with REPO_ROOT so recipes can safely change directories
export VIRTUAL_ENV := ${REPO_ROOT}/venv
# bin = POSIX, Scripts = Windows
export PATH := ${VIRTUAL_ENV}/bin:${VIRTUAL_ENV}/Scripts:${PATH}

对于那些对示例用法感兴趣的人:

# SEE: http://redsymbol.net/articles/unofficial-bash-strict-mode/
SHELL=/bin/bash -euo pipefail
.DEFAULT_GOAL := fresh-install

show-python:    ## Show path to python and version.
    @echo -n "python location: "
    @python -c "import sys; print(sys.executable, end='')"
    @echo -n ", version: "
    @python -c "import platform; print(platform.python_version())"

show-venv: show-python
show-venv:  ## Show output of python -m pip list.
    python -m pip list

install: show-python
install:    ## Install all dev dependencies into a local virtual environment.
    python -m pip install -r requirements-dev.txt --progress-bar off

fresh-install:  ## Run a fresh install into a local virtual environment.
    -rm -rf venv
    $(SYSTEM_PYTHON) -m venv venv
    @$(MAKE) install

0

你也可以使用名为“VIRTUALENVWRAPPER_SCRIPT”的环境变量。像这样:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接