Python 如何获取Distutils compile()函数使用的实际命令?

Python 如何获取Distutils compile()函数使用的实际命令?,python,distutils,Python,Distutils,我有一个Distutils setup.py脚本,它使用new_compiler().compile()编译测试程序,以确保某些功能(如MPI)在系统上可用 我的问题是,有一种情况下,compile()调用会导致编译器错误,但手动编译相同的小测试程序不会。错误在标准标头(mpi.h)中。我所有的实际源文件也包括这个头文件,但它们编译得很好!这个特殊的检查非常有用,但是我需要弄清楚为什么它不应该失败 因此,我的问题是,如何获得ccompiler.compile()使用的实际命令?Adam(Wagn

我有一个Distutils setup.py脚本,它使用new_compiler().compile()编译测试程序,以确保某些功能(如MPI)在系统上可用

我的问题是,有一种情况下,compile()调用会导致编译器错误,但手动编译相同的小测试程序不会。错误在标准标头(mpi.h)中。我所有的实际源文件也包括这个头文件,但它们编译得很好!这个特殊的检查非常有用,但是我需要弄清楚为什么它不应该失败

因此,我的问题是,如何获得ccompiler.compile()使用的实际命令?

Adam(Wagner)的注释是正确的起点:您必须搜索Distutils源代码。有很多抽象级别,因此您必须通过几个不同的文件跟踪执行情况,但要点如下:

  • distutils.ccompiler
    包含一个抽象类
    ccompiler
    ,该类处理调用实际编译器的操作。它具有编译器需要执行的各种任务的方法:
    预处理
    编译
    ,以及
    链接
    CCompiler
    本身不实现除
    compile
    之外的任何这些方法,但即使在那里,它也将实际的编译器调用委托给方法
    \u compile
    。因此,您需要检查平台上使用的
    CCompiler
    的子类,并查看其
    \u compile
    的实现
  • CCompiler
    有几个不同的子类,每个子类都在自己的包中实现,但“两大类”是
    distutils.unixcompiler.unixcompiler
    (在类UNIX系统上调用本机编译器)和
    distutils.msvcciler.msvcciler
    (调用Visual Studio)。两者都使用函数
    distutils.spawn.spawn
    来实际运行外部进程
  • distutils.spawn.spawn
    的源代码中,您会注意到每个命令在运行之前都记录在
    INFO
    级别。但问题是,它没有使用Python的内置日志系统;相反,它使用在
    Distutils.log
    中实现的Distutils自定义记录器
  • 如果您现在查看
    distutils.log
    的源代码,您将看到有一个函数
    set\u verbosity
    ,用于设置日志记录级别。不幸的是,它没有绑定到Distutils中的任何其他调试基础结构(例如
    Distutils\u DEBUG
    环境变量),因此您需要手动调用

    distutils.log.set_verbosity(1)
    
    在运行任何编译命令之前,在安装脚本中的某个位置。一旦这样做,所有编译命令(以及谁知道还有什么其他信息)都应该打印到标准输出


这太晚了,但我发现了如何在python中实现这一点(不检查日志),以防有人怀疑:

import distutils.sysconfig
import distutils.ccompiler
compiler = distutils.ccompiler.new_compiler()
distutils.sysconfig.customize_compiler(compiler)
print compiler.compiler_so   # This attribute is what you want
“compiler_so”属性是distutils在编译某些内容时要使用的每个参数的列表。它在实际开始编译时添加文件名和
-c
(对于对象文件)

编辑:我只在macOS和Linux上测试过这个,它似乎在Windows上不起作用

EDIT2:我应该补充一点,它不是完整的命令,只是distutils处理任何Extension()实例之前的参数。其余的参数对于扩展是唯一的,并且取决于您在创建扩展类时给出的参数,如源代码、include_dirs、define_macros

如果您想要distutils运行的完整原始命令,我知道的唯一方法(无需解析日志)是在所有处理之后的最后一分钟和之前获取命令字符串。以下是一种令人难以置信的骇客方式:

# Put this at the very top of all your imports 
import distutils.spawn
old_spawn = distutils.spawn.spawn
def my_spawn(*args, **kwargs):
    print " ".join(args[0]) # <-- this is your command right here 
    old_spawn(*args, **kwargs)
distutils.spawn.spawn = my_spawn
#将其放在所有导入的最顶端
导入distutils.spawn
old_spawn=distutils.spawn.spawn
def my_spawn(*args,**kwargs):

打印“.join(args[0])#此外,如果希望查看链接器标志,可以执行以下操作:

import distutils.sysconfig
import distutils.ccompiler
compiler = distutils.ccompiler.new_compiler()
distutils.sysconfig.customize_compiler(compiler)
print compiler.linker_so   # This gives linker information

如果您不介意浏览许多不熟悉的python代码行,那么可以看看源代码Distutils,我相信它们都是python代码。我翻了一下,但没能为你找到一个好的答案。在我的机器上,ccompiler在这里:/usr/lib64/python2.7/distutils/ccompiler.py这是不正确的。我只是想看看它会吐出什么,然后在没有sysconfig部分的情况下,我运行了一个编译,但还添加了一个pre-args
-v
,这是一组完全不同的参数。这里的sysconfig创建了一个不同的命令行,在尝试与编译器的备用脚本(即Make、csh、bash等)调用进行比较时,该命令行没有帮助。看起来这只适用于Unix类型的系统。你在Windows上吗?看起来MSVCCompiler将编译参数放在一个名为“compile_options”的属性中。不,我在Linux RedHat上使用GCC编译器驱动程序,但这不起作用。这与
sysconfig
库有关,而与操作系统无关。使用
distutils.log.set\u verbosity(1)
而不使用
sysconfig
并查看您的标准输出,然后按自己的方式执行。获取真实命令行的方法是,在调用
CCompiler.compile
函数之前,将
sys.stdout
重定向到我自己的
cStringIO.StringIO
对象,然后在将
sys.stdout
放入真实的stdout之后,并将我的
StringIO
转储到真正的stdout和其他非stdout的处理程序。我认为我的答案不清楚:这些是操作系统在处理扩展之前特定的常规参数(我假设您正在创建扩展实例,因为我们正在讨论distutils)。源文件和目标文件未知时,无法获取完整命令。唯一的方法是拦截参数