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 и продолжайте работу, а затем планируйте обновление, когда следующий релиз подтянет фикс из апстрим‑патча.