【问题标题】:Reliable way to get the "build" directory from within setup.py从 setup.py 中获取“build”目录的可靠方法
【发布时间】:2012-10-05 11:18:00
【问题描述】:

在 setup.py 脚本中,我需要为安装创建一些临时文件。放置它们的自然位置是“build/”目录。

如果通过 pypi 从源代码、easy_install、pip、...进行安装,是否有办法检索其路径?

非常感谢!

【问题讨论】:

  • 导入操作系统; os.path.abspath('.') 不能正常工作?
  • 问题是如果例如通过 pypi 安装,构建目录可能不是当前工作目录的子目录,也不一定是脚本执行位置的子目录。

标签: python numpy setuptools distutils setup.py


【解决方案1】:

默认 distutils 在当前工作目录中创建build/,但可以通过参数--build-base 更改它。似乎 distutils 在执行 setup 时会解析它,并且无法从外部访问解析的参数,但您可以自己剪切它:

import sys
build_base_long = [arg[12:].strip("= ") for arg in sys.argv if arg.startswith("--build-base")]
build_base_short = [arg[2:].strip(" ") for arg in sys.argv if arg.startswith("-b")]
build_base_arg = build_base_long or build_base_short
if build_base_arg:
    build_base = build_base_arg[0]
else:
    build_base = "."

这个天真的解析器版本仍然比optparse 的版本短,对未知标志进行了适当的错误处理。你也可以使用argparse的解析器,它有try_parse方法。

【讨论】:

  • 非常感谢您的广泛回答。不幸的是,它不能解决问题。通过例如安装它“pip install package-name”将在某个临时目录中创建一个构建目录,我也无法从传递的命令行参数中获取该目录。我需要这样的东西:(docs.scipy.org/doc/numpy-1.5.x/reference/generated/…) 不幸的是,这不起作用,因为它抱怨 distutils 没有被启动。但是一旦我启动它,编译就会开始。我需要在此之前创建文件。
  • 如果没有build_base 参数提供给pip 调用,它将在当前工作目录中创建build 目录(build_base 将是.)。
【解决方案2】:

distutils/setuptools 提供了一个抽象的Command 类,用户可以使用该类将自定义命令添加到他们的包的设置过程中。这是与buildinstall 等内置设置命令的子类相同的类。

作为抽象Command 类的子类的每个类都必须实现initialize_optionsfinalize_optionsrun 方法。这些方法名称所指的“选项”是从用户提供的命令行参数派生的类属性(它们也可以具有默认值)。 initialize_options 方法是定义类的选项的地方,finalize_options 方法是分配类的选项值的地方,run 方法是使用类的选项值来执行命令功能的地方。

由于命令行参数可能影响多个命令,一些命令类可能与其他命令类共享选项。例如,所有 distutils/setuptools 构建命令(buildbuild_pybuild_clibbuild_extbuild_scripts)和 install 命令都需要知道构建目录的位置。 build 命令是所有这些命令中的第一个要执行的命令,而不是让这些命令类中的每一个定义和解析相同的命令行参数到相同的选项中,它定义并解析命令行参数和选项,而所有其他类在其finalize_options 方法中从build 命令获取选项值。

例如,build 类在其initialize_options 方法中定义build_basebuild_lib 选项,然后从其finalize_options method 中的命令行参数计算它们的值。 install 类还在其 initialize_options 方法中定义了 build_basebuild_lib 选项,但它在其 finalize_options 方法中获取这些选项 from the build command 的值。

您可以使用相同的模式向build 命令添加自定义子命令,如下所示(install 类似)

import setuptools
from distutils.command.build import build


class BuildSomething(setuptools.Command):

  def initialize_options(self):
    # define the command's options
    self.build_base = None
    self.build_lib = None
  
  def finalize_options(self):
    # get the option values from the build command
    self.set_undefined_options('build',
                               ('build_base', 'build_base'),
                               ('build_lib', 'build_lib'))

  def run(self):
    # do something with the option values
    print(self.build_base)  # defaults to 'build'
    print(self.build_lib)


build_something_command = 'build_something'


class Build(build):

  def has_something(self):
    # update this to check if your build should run
    return True

  sub_commands =  [(build_something_command, has_something)] + build.sub_commands


COMMAND_CLASS = {
  build_something_command: BuildSomething,  # custom command
  'build': Build  # override distutils/setuptools build command
}


setuptools.setup(cmdclass=COMMAND_CLASS)

或者,如果您只是想扩展 distutils/setuptools 类之一,并且它已经具有您需要的选项,您可以将其子类化

import setuptools
from setuptools.command.build_py import build_py


class BuildPy(build_py):

  def initialize_options(self):
    pass
  
  def finalize_options(self):
    pass

  def run(self):
    # do something with the option values
    print(self.build_lib)  # inherited from build_py
    build_py.run(self)  # make sure the regular build_py still runs


COMMAND_CLASS = {
  'build_py': BuildPy  # override distutils/setuptools build_py command
}


setuptools.setup(cmdclass=COMMAND_CLASS)

不幸的是,这些都没有在任何地方得到很好的记录。我从阅读distutilssetuptools 源代码中学到了大部分内容。任一存储库的command 目录中的任何build*.pyinstall*.py 文件都提供信息。抽象的Command 类是defined in distutils

【讨论】:

    【解决方案3】:

    也许是这样的?在我的情况下适用于 python 3.8

    ...
    from distutils.command.build import get_platform
    import sys
    import os
    ...
    def configuration(parent_package='', top_path=None):
    
        config = Configuration('', parent_package, top_path)
        # add xxx library
        config.add_library('xxx',['xxx/src/fil1.F90',
                                'xxx/src/file2.F90',
                                'xxx/src/file3.F90'],
                                language='f90')
    
        # check for the temporary build directory option
        _tempopt = None
        _chkopt = ('-t','--build-temp')
        for _opt in  _chkopt:
            if _opt in sys.argv:
                _i = sys.argv.index(_opt)
                if _i < len(sys.argv)-1:
                    _tempopt = sys.argv[_i+1]
                    break
        # check for the base directory option
        _buildopt = 'build'
        _chkopt = ('-b','--build-base')
        for _opt in  _chkopt:
            if _opt in sys.argv:
                _i = sys.argv.index(_opt)
                if _i < len(sys.argv)-1:
                    _buildopt = sys.argv[_i+1]
                    break    
    
        if _tempopt is None:
            # works with python3 (check distutils/command/build.py)
            platform_specifier = ".%s-%d.%d" % (get_platform(), *sys.version_info[:2])
            _tempopt = '%s%stemp%s'%(_buildopt,os.sep,platform_specifier)
    
        # add yyy module (wraps fortran code in xxx library)
        config.add_extension('fastpost',sources=['yyy/src/fastpost.f90'],
                              f2py_options=['--quiet'],
                              libraries=['xxx'])
    
        # to access the mod files produced from fortran modules comppilaton, add 
        # the temp build directory to the include directories of the configuration
        config.add_include_dirs(_tempopt)
        return config
    
    setup(name="pysimpp",
          version="0.0.1",
          description="xxx",
          author="xxx",
          author_email="xxx@yyy",
          configuration=configuration,)
    
    

    【讨论】:

      猜你喜欢
      • 2022-10-24
      • 2013-02-19
      • 2015-09-13
      • 2018-01-04
      • 1970-01-01
      • 2015-06-10
      • 1970-01-01
      • 2021-12-06
      相关资源
      最近更新 更多