GNU как условно-свободная экосистема
Этот пост скорее попытка выплеснуть свои накопившиеся эмоции от работы с экосистемой GNU, которая глубоко пустила свои корни в Linux и которую я каких-то 5 лет назад ещё боготворил, нежели полноценный технический разбор. Но даже так, всё-таки постараюсь не поскупиться на аргументацию.
И сразу оговорюсь, я не имею ничего против Столлмана (как ни как, я подписывал письмо в его поддержку), только против его излишней консервативности и паранойи, что сдерживает развитие экосистемы СПО, пусть он и его последователи могут думать иначе.
Итак, чем дольше работаешь с GNU-экосистемой, тем сильнее понимаешь, что под лозунгами свободы и открытости она на самом деле выстраивает довольно жесткую зависимость на себя же саму. Формально — да, исходники открыты, лицензии свободные. Бери, используй, модифицируй как тебе угодно (разве что лицензировать будешь на тех же условиях, но я всеми тремя руками за копилефт). Но как только пытаешься сделать что-то не по канонам GNU, выясняется, что всё буквально заточено под то, чтобы не иметь альтернативы.
1. GCC - компилятор без нормального AST
GCC — это, конечно, безусловный флагман среди компиляторов C/C++. Старый, мощный, проверенный временем компилятор, способный генерировать быстрый как понос код (пусть clang и дышит ему в затылок). Но проблема в том, что его архитектура исторически сложилась в стиле «добавляем по мере надобности», без чёткого внутреннего языка представления, удобного для сторонних инструментов.
В то время как Clang изначально проектировался с полноценным, документированным AST и API, позволяющим писать статические анализаторы, IDE-плагины, автокомплитеры (пример — clangd), GCC предоставляет GIMPLE, RTL и прочие внутренние представления, которые мало того что слабо документированы, так ещё и активно меняются между версиями.
И нет, это не баг — это фича. Ричард Столлман в одном из своих писем сделал акцент на том, что сделано это было нарочно, чтобы не давать полный API, так как это могут использовать клятые проприерасты. Как иронично, что по итогу корпорациям это никак не мешало использовать gcc, а ключевым моментом стал вовсе не нестабильный AST, а смена лицензии на GPLv3.
Итого: хочешь написать статический анализатор поверх GCC? Удачи. Именно поэтому современная экосистема тулинга вся сформировалась вокруг LLVM. И помни, пролетарий, это всё сделано во имя твоей свободы!
Ссылки по теме:
- «Why Clang?»: https://clang.llvm.org/why.html
- GCC Internals: https://gcc.gnu.org/onlinedocs/gccint/
- AST representation in GCC: https://icps.u-strasbg.fr/~pop/gcc-ast.html
2. Статическая линковка и чудеса «динамической магии»
Попробуйте собрать статически связанное приложение с glibc — и вы поймёте, что GNU-мир относится к статике как к какой-то ереси. Формально это возможно, но практически — постоянно будете упираться в:
- завязанность на
dlopen()/dlsym()всего подряд; - глючность
-staticв реальных проектах; - невозможность собрать без большого количества хаков.
Те же OpenGL-стек (в реализации libglvnd), X.Org Server, ALSA и многие другие звуковые/графические компоненты полагаются на runtime-загрузку модулей, которая возможна лишь с динамически слинкованной glibc. То есть концептуально статическая сборка не предусмотрена, ибо портабельность — это не по «гнутому».
Именно поэтому люди, кому реально нужна статическая линковка, уходят на musl, uclibc или bionic. Правда это не решает завязанность уже существующих проектов на динамическую линковку, которую так продвигает glibc, увы.
Ссылки по теме:
- Why static linking with glibc is problematic: https://www.akkadia.org/drepper/no_static_linking.html
- musl FAQ: https://musl.libc.org/faq.html
3. Открытый код ≠ понятный
Почему исходники GNU-библиотек и приложений такие тяжёлые для чтения? Потому что стиль разработки «допишем сюда кусок, чтоб добавить фичу» присутствовал десятилетиями не только в gcc. В результате все GNU-программы и библиотеки состоят чуть менее чем полностью из:
- макросов поверх макросов;
- условная компиляция в зависимости от ста миллионов сдохших ещё в 90-ые платформ;
- минимум структурированности и архитектуры;
- прям как в x86, слои совместимости поверх совместимости для совместимости с кодом, написанным в начале нулевых (а он всё равно не скопилируется на современной системе, ибо завязан на другие библиотеки и состоял из UB чуть менее чем полностью).
Пример: попытка разобраться в исходниках coreutils или glibc превращается в археологическую экспедицию . Вроде бы «код открыт», но по факту он не предназначен для чтения простым смертным.
Сравните с LLVM/FreeBSD — там хотя бы виден стиль, архитектура и логика.
Вместо того, чтобы так держаться за совместимость (которая по факту есть только наполовину, спасибо версионированию символов и UB), лучше бы повторили фокус с libc.5->libc.6 из конца девяностых-начала нулевых, сломали бы ABI, но при этом выкинули кучу мусора из библиотеки, одновременно облегчив её и сделав код понятнее. Конечно же, этого никогда не произойдёт. По крайней мере, пока у руля Столлман.
4. Вендорлочность, башизмы и непортируемость
Если в вашем шелл-скрипте есть [[, $'\n', <(...), readarray, ${parameter,,}, то поздравляю, это не шелл-скрипт — это уже баш-скрипт. Вы уже завязаны на bash и можете запускать его только в нём. И все GNU-инструменты любят использовать именно такие расширения.
Формально POSIX-шелл — /bin/sh, но в реальности огромное количество скриптов просто не запустятся на dash, ash или zsh в которых нет башизмов.
То же самое с sed, grep, find — в них постоянно добавляют GNU-специфичные опции, на которые авторы скриптов начинают полагаться, а шишки по итогу приходится собирать пользователям “нестандартных” (по мнению GNU) сетапов. Портируемость умирает.
Ссылки по теме:
- How to make bash scripts work in dash: https://mywiki.wooledge.org/Bashism
- POSIX: https://pubs.opengroup.org/onlinepubs/9699919799/
- What is GNU?: https://lobste.rs/s/jtsabz/what_gnu
5. А что альтернативы?
Когда пытаешься уйти от GNU-зависимости, возникает логичный вопрос: а куда, собственно, уходить? Инструментов вне GNU более чем достаточно, но у всех есть одно общее свойство — почти все они живут под пермиссивными лицензиями. Это удобно корпорациям, это удобно программистам (но только потому что они не слышали про Mozilla Public License), однако далеко не всегда полезно тем самым «пользователям», свободу которых GNU изначально защищала.
LLVM / Clang
Проект LLVM на первый взгляд выглядят образцовой альтернативой GCC. Архитектура понятная, код читаемый, документация систематизированная. Просто приятно пользоваться: хочешь анализатор — подключай clang-tidy, хочешь сделать из своего редактора IDE — бери clangd, хочешь свой оптимизатор — внедри модуль. Но вся эта красота работает на лицензии, которая не требует возвращать улучшения обратно. В результате корпорации вроде Apple или Google спокойно берут LLVM, строят вокруг него свои закрытые инструменты и редко деляться этими улучшениями с сообществом.
Исходник вроде как открыт, но становится чем-то вроде демонстрационной версии: то, что главное и полезное, находится уже в недрах внутренних форков. Та же история с Android NDK, Apple Xcode и экспериментальными Мелкомягкими сборками — всё это LLVM внутри, но открытого там ровно столько, сколько требуется чтобы спихнуть поддержку на сообщество и при этом сохранить вендорлок.
libc
С libc похожая ситуация. Если хочется ясности, минимализма и нормальной статической линковки — на сцене появляется musl. Он простой как палка, довольно чистый по архитектуре, и, в отличие от glibc, не пытается разыгрывать динамическую магию и вечные слои совместимости. При этом, не беря во внимание аллокатор, практически не уступает в скорости.
Но musl — это выбор для тех, кто готов сам контролировать окружение. На десктопе или сервере с произвольным набором старых библиотек и проприентарных драйверов всё начинает ломаться сразу. Экосистема musl в основном живёт там, где всё собирается с нуля с ним: Alpine, Void, DIY-дистрибутивы. Плюс ровно та же проблема пермиссивности, пусть и в отличии от LLVM корпорации ещё не загребли этот проект в свои грязные руки (а может уже!).
coreutils и прочие utils
Самый болезненный момент в любой попытке отойти от GNU — это не компилятор, не bash и даже не libc. Это утилиты. Самые базовые инструменты, которые вызываются рефлекторно: ls, cp, cat, find, sort, diff, tar. То, что является неотъемлимой частью ежедневной работы в терминале. Их никто обычно не обсуждает, потому что кажется, что они есть и такими будут всегда, ибо в линуксе нельзя их просто так взять и заменить, ведь полноценных альтернатив им никогда не существовало.
И вот тут на стену выходит такой проект как uutils, целью которого в конечном итоге создать по настоящему кроссплатформенную и безопасную (с точки зрения памяти) альтернативу GNU coreutils, findutils, diffutils. Я на этот проект возлагаю большие надежды, благо сейчас Canonnical как раз обкатывает их в Ubuntu 25.10.
Увы, лицензия опять всё портит, но я не настолько слепой копилефт-сектант, так что когда они стабилизируются (а я надеюсь это будет скоро, пусть принцип Парето и говорит об обратном), думаю, я без всякой задней мысли окончательно заменю ими GNU-экосистему. Благо NixOS это позволяет сделать легко и просто своей системой оверлеев.
BSD
Если смотреть на BSD-системы, то картина ещё любопытнее. Там код действительно аккуратнее, даже с учётом условной компиляции на каждую железку (особенно в случае NetBSD). FreeBSD, OpenBSD, NetBSD — это попытка делать систему как одно целое, а не набор разрозненных пакетов, собранных по принципу «лишь бы работало». Но именно эта парадоксальная «порядочность» и наталкивает на ту же лицензионную проблему: весь этот труд можно взять, завернуть в закрытый продукт и спокойно продавать. Apple так и сделала, взяв огромный шмат кода для своей macOS/iOS. Sony сделала. Microsoft делала. И ничего обратно они не вернули, потому что лицензия позволяет. Это не нарушение лицензии, но нарушение прав пользователей.
И вот получается интересная дилемма между «код всё ещё наш, но читать его больно» и «код красивый, но завтра может перестать быть общим». С инженерной точки зрения альтернативы GNU есть и даже выглядят привлекательнее: чище код, понятнее структура, удобнее инструменты, меньше исторического хаоса. Но в долгосрочной перспективе это означает, что все ключевые улучшения могут стекать в закрытые форки крупных компаний, а сообщество останется у разбитого корыта как уже произошло с FreeBSD, которая могли бы стать полноценной десктопной платформой, верни корпорации (особенно Sony) свои наработки.
6. Подводя итоги
GNU любит говорить о свободе. Но на практике экосистема GNU устроена так, что тулинг поверх GCC почти невозможен; статическая линковка де-факто запрещена; код сложен настолько, что только «посвящённые» могут его поддерживать и модифицировать; скрипты тянут GNU-расширения, ломая переносимость; альтернативы есть, но они пермиссивные и использовать их неудобно именно из-за пустившей корни архитектуры GNU.
Нет, я не думаю что это какой-то гнутый заговор — это просто результат десятилетий развития без рефлексии, без понимания того, что мир не ограничивается вашей маня-саванной. Но получившаяся в результате инерция работает ничуть не хуже настоящего вендорлока.
Ирония в том, что именно под лозунгом «Свобода — это право менять и изучать код» существует код, который изучать и менять физически и морально больно. Всё как у людей.