2025, Nov 13 06:01
Как исправить ошибку линковки multiple definitions в Cython 3.1.2
Сборка модулей Cython может падать с multiple definitions из-за бага в Cython 3.1.2. Разбираем причину, симптомы и решение: фикс static и откат на 3.1.1.
Ошибка компоновщика с C-файлами, сгенерированными Cython: что изменилось и как это исправить
Сборка нативного бинарника из нескольких модулей, прошедших через Cython, может внезапно сломаться с неожиданной ошибкой компоновки на одной машине, тогда как на другой всё идёт гладко. Конкретный пример: на Ubuntu 22.04 с Python 3.10 и Cython 0.29.37 всё работает, а на Kubuntu 20.04 с Python 3.8 и Cython 3.1.2 — падает. Сбой проявляется как жалоба компоновщика на множественные определения.
multiple definitions of '__pyx_CommonTypesMetaclass_get_module'Если вы пересобрали C‑файлы новым тулчейном и затем скомпилировали их вместе с GCC, сборка остановится на этапе линковки. Первопричина кроется в C‑коде, который выдаёт конкретная версия Cython.
Минимальное воспроизведение симптома
Рассмотрим два C‑файла, полученных из независимых Python‑модулей. В каждом из них одноимённый вспомогательный символ определён как глобальная (не static) функция. При линковке их в единый бинарник или библиотеку компоновщик находит дубликаты глобальных символов и падает с ошибкой.
alpha.c
int aux_get_module(void) {
return 0;
}
beta.c
int aux_get_module(void) {
return 0;
}
Совместная компиляция и линковка приводят к классической ошибке множественного определения.
gcc -c alpha.c -o alpha.o
gcc -c beta.c -o beta.o
gcc alpha.o beta.o -o app
multiple definitions of '__pyx_CommonTypesMetaclass_get_module'Что происходит на самом деле
Это баг в Cython 3.1.2: вспомогательная функция, которая должна иметь внутреннее связывание, была сгенерирована без ключевого слова static. Когда разные C‑файлы, созданные Cython, определяют один и тот же не‑static хелпер, их совместная линковка приводит к конфликту. Расхождение между машинами объясняется разными версиями Cython: на Ubuntu 22.04 использовался Cython 0.29.37, а на Kubuntu 20.04 — Cython 3.1.2, и именно в последнем появилась регрессия.
Проблема зафиксирована здесь: bug report, а соответствующее исправление уже влито. Имя символа, встречающееся в ошибке (__pyx_CommonTypesMetaclass_get_module), — конкретный пример такого хелпера, сгенерированного без static.
Что можно сделать прямо сейчас
Пока следующий релиз Cython не включил фикс, зафиксируйте Cython на версии 3.1.1. Перегенерируйте C‑исходники этой версией и пересоберите проект. Так вы обойдёте регрессию, появившуюся в 3.1.2.
Как должен выглядеть корректный код
По задумке, вспомогательные функции в каждом сгенерированном C‑файле должны иметь внутреннее связывание, чтобы не конфликтовать между единицами трансляции. С ключевым словом static оба файла компилируются и линкуются без проблем, потому что каждое определение остаётся приватным для своего объектного файла.
alpha.c
static int aux_get_module(void) {
return 0;
}
beta.c
static int aux_get_module(void) {
return 0;
}
Почему это важно
Такие сбои проявляются, когда вы объединяете несколько C‑файлов, сгенерированных Cython, в одну библиотеку или исполняемый файл. При сборке подобных монолитных целей малейшие изменения в том, как формируются вспомогательные символы, могут решить исход: чистая сборка или сорванный релиз. Понимание, что ошибка привязана к версии Cython, а не к вашему коду, экономит время и избавляет от ложных поисков в GCC, Python или операционной системе.
Выводы
Если ранее рабочая многофайловая сборка на Cython начала падать с сообщением вроде “multiple definitions of '__pyx_CommonTypesMetaclass_get_module'” и вы используете Cython 3.1.2, скорее всего, это известная ошибка. Перегенерируйте C‑код с Cython 3.1.1 и продолжайте работу, а затем планируйте обновление, когда следующий релиз подтянет фикс из апстрим‑патча.