将命令行参数传递给 pip install

Passing command line arguments to pip install

我目前正在处理一个导入 Fortran 模块的 Python 项目。 setup.py 看起来与

相似
from numpy.distutils.core import Extension
from numpy.distutils.core import setup

ext = Extension(
    name = "fortran_module",
    sources = ["fortran_module.f90"],
    extra_f90_compile_args = ["-some -compile -arguments"]
)

setup(
    ...,
    ...,
    ...,
    ext_modules = [ext],
    ...,
    ...,
    ...
)

我目前正在使用 pip install -e . 安装它,效果很好。 但有时我需要更改 extra_f90_compile_args 并且我想在使用 pip 安装期间将它们作为命令行参数提供,而不是更改 setup.py 文件。例如:

pip install -e --compile_args="-O3 -fbacktrace -fbounds-check -fopenmp" .

是否可以通过 pip 将命令行参数传递到 setup.py 文件中?

与此相关,我还想将不同的设置选项传递给 setup.py。例如

pip install -e --setup=setup1 .

pip install -e --setup=setup2 .

并且根据它们中的 setup1setup2 或 none 是否通过,我想包含不同的 Fortran 源文件并用不同的 extra_f90_compile_args 编译它们.

这可能吗?


编辑: 让我们考虑以下示例:Fortran 模块与 OpenMP 并行化。现在我希望用户决定是否并行编译。可能OpenMP库不可用,用户需要编译串口版。

我的setup.py现在看起来如下

from numpy.distutils.core import Extension
from numpy.distutils.core import setup
import os
from setuptools.command.install import install as _install

extra_f90_compile_args = ""
extra_link_args = ""

class install(_install):
    user_options = _install.user_options + [('build=', None, None)]

    def initialize_options(self):
        _install.initialize_options(self)
        self.build = None

    def finalize_options(self):
        _install.finalize_options(self)

    def run(self):
        global extra_f90_compile_args
        global extra_link_args
        if(self.build == "parallel"):
            extra_f90_compile_args += "-fopenmp"
            extra_link_args += "-lgomp"
            os.makedirs("~/test/")
        _install.run(self)

ext = Extension(
    name="test_module.fortran_module",
    sources=["test_module/fortran_module.f90"],
    extra_f90_compile_args=[extra_f90_compile_args],
    extra_link_args=[extra_link_args]
)

setup(
    name="test_module",
    packages=["test_module"],
    ext_modules=[ext],
    cmdclass={'install': install}
)

如果我用

安装它
pip install . --install-option="--build=parallel"

它正在执行 if 块中的代码。我创建 test/ 目录只是为了检查这个。如果 build 未给出或与 parallel 不同,则不会创建 test/ 目录。

但是,代码不是用 OpenMP 编译的。我认为这是因为 Extension 对象是在调用 setup() 之前创建的,其中参数被评估。我想先评估参数,然后根据参数创建 Extension 对象,然后调用 setup().

我该怎么做?

在深入了解源代码后,我现在可以回答这个问题了。

setup.py 文件:

from numpy.distutils.command.install import install as _install
from numpy.distutils.core import Extension
from numpy.distutils.core import setup

extF = Extension(name="test_module.fortran_module",
                 sources=["test_module/fortran_module.f90"])
compile_args_parallel = "-fopenmp"
link_args_parallel = "-lgomp"

class install(_install):
    user_options = _install.user_options + [('build=', None, None)]

    def initialize_options(self):
        _install.initialize_options(self)
        self.build = None

    def finalize_options(self):
        _install.finalize_options(self)
        if(self.build == "parallel"):
            for ext in self.distribution.ext_modules:
                if(ext.name == "test_module.fortran_module"):
                    ext.extra_f90_compile_args = compile_args_parallel
                    ext.extra_link_args = link_args_parallel

    def run(self):
        _install.run(self)

setup(
    name="test_module",
    packages=["test_module"],
    ext_modules=[extF],
    cmdclass={"install": install}
)

安装这个
pip install .

安装串行版本并

pip install . --install-option="--build=parallel"

安装 Fortran 模块的并行版本。

如果在我看来有不止一种选择,那么同时处理 --install-option--global-option 似乎非常混乱。我想添加 this real life solution

取而代之的是环境变量,并在安装时设置它们。

在setup.py中:

import os
config_var = os.environ.get("MY_PARAM", None)
if config_var:
    do_something()
setup()

然后像这样调用 pip: MY_PARAM="Some_value" pip install my_module