#python #code-generation #setuptools #bison #flex-lexer
#python #генерация кода #setuptools #зубр #flex-lexer
Вопрос:
У меня есть модуль Python, который построен вокруг собственного расширения, написанного на C. Это расширение включает код, сгенерированный с использованием инструментов GNU Bison и (не GNU) Flex. Это означает, что процесс сборки для моего расширения C включает вызов этих инструментов, а затем включение их выходных данных (исходных файлов C) в исходные файлы расширения.
Чтобы заставить это работать при вызове python setup.py install
, я расширил setuptools.command.build_ext
класс для вызова как Flex, так и Bison, а затем добавил сгенерированный источник в источник расширения перед вызовом метода запуска суперкласса.
Это означает, что мой setup.py выглядит как:
import os
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
c_extension = Extension('_mymod',
include_dirs = ['inc'],
sources = [
os.path.join('src', 'lib.c'),
os.path.join('src', 'etc.c')
])
class MyBuild(build_ext):
def run(self):
parser_dir = os.path.join(self.build_temp, 'parser')
# add the parser directory to include_dirs
self.include_dirs.append(parser_dir)
# add the source files to the sources
self.extensions[0].sources.extend([os.path.join(parser_dir, 'lex.yy.c'), os.path.join(parser_dir, 'parse.tab.c')])
# honor the --dry-run flag
if not self.dry_run:
self.mkpath(parser_dir)
os.system('flex -o ' os.path.join(parser_dir, 'lex.yy.c') ' ' os.path.join('src', 'lex.l'))
os.system('bison -d -o ' os.path.join(parser_dir, 'parse.tab.c') ' ' os.path.join('src', 'parse.y'))
# call the super class method
return build_ext.run(self)
setup (name = 'MyMod',
version = '0.1',
description = 'A module that uses external code generation tools',
author = 'Sean Kauffman',
packages = ['MyMod'],
ext_modules = [c_extension],
cmdclass={'build_ext': MyBuild},
python_requires='>=3',
zip_safe=False)
Однако теперь я пытаюсь упаковать этот модуль для распространения, и у меня возникла проблема. Либо пользователям, которые хотят установить мой пакет, необходимо установить Bison и Flex, либо мне нужно запустить эти инструменты при сборке исходного дистрибутива. Я вижу два возможных решения:
- Я проверяю, что flex и bison находятся на пути выполнения системы. Это сохраняет пользовательский конструктор как есть. Я не нашел документации, которая подразумевала бы, что я могу подтвердить, что системные файлы существуют, такие как bison и flex. Ближе всего использовать
libraries
поле расширения, но, похоже, мне понадобится настоящий хакер, чтобы проверить весь ПУТЬ для исполняемых файлов. Я еще не пробовал это, потому что моим первым выбором был бы вариант 2. - Я переношу генерацию кода, чтобы она происходила при создании исходного дистрибутива. Это означает, что исходный дистрибутив будет содержать выходные файлы из bison и flex, поэтому людям, устанавливающим пакет, не нужны эти инструменты. Это похоже на более чистый вариант. Я попытался расширить
sdist
команду вместоbuild_ext
, как указано выше, но неясно, как я могу добавить сгенерированные файлы в МАНИФЕСТ, чтобы они были включены. Кроме того, я хочу убедиться, что он все еще работает для сборки с использованиемpython setup.py install
, но я не думаю, что эта команда будет запускать sdist перед сборкой.
Любое решение может работать только в Linux и OS X.
Ответ №1:
Обычным решением для распространения кода, требующего (f) lex и bison / yacc, является объединение сгенерированных сканера и анализатора, но будьте готовы сгенерировать их, если они отсутствуют. Вторая часть немного упрощает разработку, а также дает людям возможность использовать свою собственную версию flex / bison, если они считают, что у них есть веская причина для этого. Я полагаю, что этот совет также применим к модулям Python.
(IANAL но я понимаю, что для кода, сгенерированного bison, существует исключение лицензии, что позволяет распространять его даже в проектах, отличных от GPL. Flex не является GPL для начала, и afaik нет ограничений на распространение.)
Для условной сборки сканера и анализатора в исходном дистрибутиве вы можете использовать уже предоставленный вами код, предварительно убедившись, что сгенерированные файлы не существуют. (В идеале вы должны проверить, что сгенерированные файлы не существуют или являются более новыми, чем соответствующий исходный файл. Это зависит от того, что даты файлов не изменяются при их перемещении по архиву. Это будет нормально работать в Linux и OS X, но может быть не полностью переносимым.)
Предполагается, что пакет создается перед выполнением sdist
команды. sdist
обычно следует исключать объектные файлы, встроенные в исходное дерево, поэтому не должно быть необходимости вручную очищать исходный код. Однако, если вы хотите убедиться, что сгенерированные файлы присутствуют при выполнении sdist
, вы можете переопределить его так setup.py
же, как вы переопределяете build_ext
, вызывая bison и flex перед вызовом базовой sdist
команды.
Комментарии:
1. Спасибо за предложение. Лицензии определенно совместимы. Можете ли вы прокомментировать, как можно объединить сгенерированный код, но все равно сгенерировать его, если его нет? Как код для вызова flex / bison должен быть интегрирован в setup.py досье?
2. @seanmk: Я далек от эксперта по дистрибутивам, но я попытался решить эту проблему в редактировании ответа. Извините, что пропустил это в первый раз.
3. Похоже, это делает свое дело: переопределяет как build_ext, так и sdist, и оба генерируют файлы, если они отсутствуют или устарели. Один важный трюк: я обнаружил, что каталог сборки автоматически удаляется setuptools после MANIFEST.in анализируется , поэтому сгенерированный код должен быть отправлен куда-то еще, кроме как в сборку .
4. @seanmk: Ах, это хорошо знать. Я исправлю ответ с помощью этой информации.