Как получить реальную команду, которую использует функция Distutils compile()?

У меня есть сценарий Distutils setup.py, который использует new_compiler().compile() для компиляции тестовых программ, чтобы убедиться, что в системе доступны определенные функции (например, MPI).

Моя проблема в том, что есть один случай, когда вызов compile() приводит к ошибке компилятора, а компиляция той же небольшой тестовой программы вручную - нет. Ошибка находится внутри стандартного заголовка (mpi.h). Все мои фактические исходные файлы также включают этот заголовок, но они прекрасно компилируются! Эта конкретная проверка была бы очень полезна, но мне нужно выяснить, почему она дает сбой, хотя этого быть не должно.

Итак, мой вопрос: как я могу получить реальную команду, используемую ccompiler.compile()?


person Adam    schedule 28.10.2011    source источник
comment
Если вы не против продираться через множество строк незнакомого python, вы можете взглянуть на исходный код Distutils, который, как я полагаю, полностью состоит из python. Я немного поковырялся, но не смог найти для вас хорошего ответа. На моей машине ccompiler находится здесь: /usr/lib64/python2.7/distutils/ccompiler.py   -  person Adam Wagner    schedule 28.10.2011


Ответы (3)


Комментарий Адама (Вагнера) — правильное место для начала: вам нужно просмотреть исходный код Distutils. Существует много уровней абстракции, поэтому вам нужно отслеживать выполнение через несколько разных файлов, но вот суть:

  • Пакет distutils.ccompiler включает абстрактный класс CCompiler, который обрабатывает вызов фактического компилятора. У него есть методы для различных задач, которые должен выполнить компилятор: preprocess, compile и link. Сам CCompiler не реализует ни один из этих методов, кроме compile, но даже там он делегирует фактический вызов компилятора методу _compile. Поэтому вам нужно проверить подкласс CCompiler, который используется на вашей платформе, и посмотреть на его реализацию _compile.
  • Существует несколько различных подклассов CCompiler, каждый из которых реализован в своем собственном пакете, но «большие два» — это distutils.unixccompiler.UnixCCompiler (который вызывает собственный компилятор в UNIX-подобной системе) и distutils.msvccompiler.MSVCCompiler (который вызывает Visual Studio). Оба они используют функцию distutils.spawn.spawn для фактического запуска внешнего процесса.
  • В исходном коде для distutils.spawn.spawn вы заметите, что каждая команда записывается на уровне INFO перед запуском. Но загвоздка в том, что здесь не используется встроенная система ведения журнала Python; скорее он использует пользовательский регистратор Distutils, реализованный в distutils.log.
  • Если вы теперь посмотрите на исходный код distutils.log, вы увидите, что есть функция set_verbosity, которая устанавливает уровень ведения журнала. К сожалению, он не привязан ни к какой другой инфраструктуре отладки в Distutils (например, к переменной окружения DISTUTILS_DEBUG), поэтому вам нужно будет вручную вызывать

    distutils.log.set_verbosity(1)
    

    где-нибудь в вашем сценарии установки, прежде чем будут запущены какие-либо команды компиляции. Как только вы это сделаете, все команды компиляции (и кто знает, какая еще информация) должны быть выведены на стандартный вывод.

person David Z    schedule 28.10.2011

Это безумно поздно, но я узнал, как это сделать изнутри 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(). Остальные аргументы уникальны для расширения и зависят от аргументов вы указываете при создании класса расширения, например, sources, 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 и добавить эту функциональность, но это слишком много работы и прочего.

person sciencectn    schedule 04.03.2015
comment
Это неправильно. Я просто попробовал это, чтобы посмотреть, что он выдаст, а затем без части sysconfig я запустил компиляцию, но также добавил предварительные аргументы -v, и это совершенно другой набор аргументов. Здесь sysconfig создает другую командную строку, которую бесполезно видеть при попытке сравнить с вызовом компилятора альтернативными сценариями (например, Make, csh, bash и т. д.). - person searchengine27; 22.02.2017
comment
Похоже, это работает только в системах типа Unix. Вы на винде? - person sciencectn; 23.02.2017
comment
Похоже, что MSVCCompiler помещает аргументы компиляции в атрибут с именем compile_options. - person sciencectn; 23.02.2017
comment
Нет, я использую Linux RedHat с драйвером компилятора GCC, и это не работает. Это связано с библиотекой sysconfig, а не с ОС. используйте distutils.log.set_verbosity(1) без sysconfig и посмотрите на свой стандартный вывод, а затем сделайте свой путь. То, как я получаю истинную командную строку, заключается в перенаправлении sys.stdout на мой собственный объект cStringIO.StringIO перед вызовом функции CCompiler.compile, а затем сразу после помещения sys.stdout в истинный стандартный вывод и сброса моего StringIO в истинный стандартный вывод и в другие мои обработчики, которые не являются стандартными. - person searchengine27; 23.02.2017
comment
Я думаю, что мой ответ не ясен: это общие аргументы, характерные для ОС, ДО того, как она обработает расширение (я предполагаю, что вы создаете экземпляры расширения, поскольку мы говорим о distutils). Вы не можете получить полную команду, если исходный файл и объектные файлы неизвестны. Единственный способ сделать это — перехватить аргумент cmd в distutils.spawn.spawn после обработки расширения: bitbucket.org/carljm/python-distutils/src/ - person sciencectn; 24.02.2017
comment
См. обновленный ответ для отрывочного взлома патча обезьяны (который полностью работает) - person sciencectn; 24.02.2017

Кроме того, если вы хотите увидеть флаги компоновщика, вы можете сделать следующее:

import distutils.sysconfig
import distutils.ccompiler
compiler = distutils.ccompiler.new_compiler()
distutils.sysconfig.customize_compiler(compiler)
print compiler.linker_so   # This gives linker information
person Kelly Stevens    schedule 31.10.2016