Сборка Rust

25 янв 2016 10:38


Rust — это новый системный язык программирования, разрабатываемый в компании Mozilla. Это компилируемый язык, т.е. исходный код программы транслируется в машинный код с помощью компилятора. Как и любое популярное свободное программное обеспечение Rust должен найти свою дорогу в дистрибутивы Linux и другие свободные Unix-системы. Появление его в стабильных дистрибутивах открыло бы возможности для его широкого распространения и использования. Пока этот процесс происходит медленно и связан с некоторыми особенностями сборки.

↓↓↓↓↓↓↓↓

Бутстрап

Сам компилятор rustc также написан на языке Rust, поэтому для сборки самого компилятора требуется операция бутстрапа: когда для сборки требуется некоторый промежуточный вариант компилятора, способный собирать rustc. Этап бутстрапа — это самая интересная часть разработки самокомпилируемого (self-hosted) языка. Возникает известная проблема курицы и яйца: как скомпилировать rustc, если самого компилятора ещё нет. Инсталлятор rust решает это путём загрузки готового статического бинарного файла rustc под текущую операционную систему и архитектуру. Например, при сборке rust 1.6.0 загружается архив со снапшотом от 2015-08-11 (rustc в районе версии 1.2.0).

Сама операция бутстрапа проходит в 3 этапа:

 • На этапе stage0 компилятор rustc собирается с помощью скачанного статического бинарника, готовые скомпилированные библиотеки и исполняемые файлы копируются в каталог следующего этапа stage1.

 • На этапе stage1 запускается свежесобранный компилятор rustc, который повторно собирает сам себя, а полученные файлы копируются в каталог последнего этапа stage2.

 • Проход компиляции на этапе stage2 формирует финальные файлы, которые теоретически уже не должны отличаться от предыдущей стадии.

Для операции бутстрапа используются только определённые снапшоты, список которых ведётся в исходном файле src/snapshots.txt .

Создать свой собственный снапшот для stage0 можно с помощью команды

$ make snap-stage3

Будет сформирован архив с rustc, которым можно попытаться снова собрать компилятор

$ CFG_SRC_DIR=src src/etc/get-snapshot.py x86_64-unknown-linux-gnu rust-stage0-2015-12-04-3d7cd77-linux-x86_64-644b36c46d07069e5a7963102b23eadcb36d4422.tar.bz2
$ make

Но оказалось, что версия 1.5.0 не может использоваться в качестве такого снапшота, поскольку при сборке с его помощью вылезает куча ошибок E0368:

src/libcore/num/f32.rs:215:9: 215:17 error: binary assignment operation `-=` cannot be applied to type `i16` [E0368]
src/libcore/num/f32.rs:215         exponent -= 127 + 23;
                                   ^~~~~~~~
src/libcore/num/f32.rs:215:9: 215:17 help: run `rustc --explain E0368` to see a detailed explanation

Похоже, что сам язык написан по правилам языка, которые уже устарели к текущему релизу. Теперь становится понятно зачем ведётся список снапшотов. С точки зрения сопровождения подобного пакета в любом дистрибутиве Linux — это нарушение политики, касающиеся возможности сборки только из исходников, т.е. без необходимости использования бинарников от Mozilla.

Bundles

Ещё одно серьёзное препятствие для прохождения rust в дистрибутивы Linux — это использование забандленных исходников других программ. Например, llvm, который использутся в rust как бэкенд для компиляции исполняемого кода. Проект rust ведёт свой «временный» клон llvm , который собирается и затем используется в сборке. При попытке использования системного llvm 3.7 я получал такую ошибку:

/usr/src/RPM/BUILD/rust-1.5.0/src/rustllvm/RustWrapper.cpp: In function 'LLVMOpaqueValue* LLVMRustBuildLandingPad(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef, unsigned int, const char*, LLVMValueRef)':
/usr/src/RPM/BUILD/rust-1.5.0/src/rustllvm/RustWrapper.cpp:972:69: error: invalid conversion from 'LLVMValueRef {aka LLVMOpaqueValue*}' to 'unsigned int' [-fpermissive]
     return LLVMBuildLandingPad(Builder, Ty, PersFn, NumClauses, Name);
                                                                     ^
/usr/src/RPM/BUILD/rust-1.5.0/src/rustllvm/RustWrapper.cpp:972:69: error: invalid conversion from 'unsigned int' to 'const char*' [-fpermissive]
/usr/src/RPM/BUILD/rust-1.5.0/src/rustllvm/RustWrapper.cpp:972:69: error: too many arguments to function 'LLVMOpaqueValue* LLVMBuildLandingPad(LLVMBuilderRef, LLVMTypeRef, unsigned int, const char*)'
In file included from /usr/include/llvm/IR/Value.h:17:0,
                 from /usr/include/llvm/IR/User.h:24,
                 from /usr/include/llvm/IR/Instruction.h:22,
                 from /usr/include/llvm/IR/BasicBlock.h:19,
                 from /usr/include/llvm/IR/IRBuilder.h:21,
                 from /usr/src/RPM/BUILD/rust-1.5.0/src/rustllvm/rustllvm.h:11,
                 from /usr/src/RPM/BUILD/rust-1.5.0/src/rustllvm/RustWrapper.cpp:11:/usr/include/llvm-c/Core.h:2677:14: note: declared here LLVMValueRef LLVMBuildLandingPad(LLVMBuilderRef B, LLVMTypeRef Ty,
              ^
make: *** [x86_64-unknown-linux-gnu/rustllvm/RustWrapper.o] Error 1

Помимо llvm, используются также копии compiler-rt и jemalloc .

Большинство дистрибутивов Linux имеют политику, которая запрещает использование забандленных исходников и требует использование разделяемых библиотек.

Ошибки сборки

Кое-что из найденных ошибок сборок. Если при конфигурации сборки указывается --libdir и его значение отличается от /usr/lib (например, /usr/lib64), то сборка останавливается на stage0 при сборке библиотеки liblibc с ошибкой, что не найдена только что собранная libcore:

rustc: x86_64-unknown-linux-gnu/stage0/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc
src/liblibc/src/lib.rs:1:1: 1:1 error: can't find crate for `core`

Связано это с тем, что rustc, загруженный с сайта mozilla собирает библиотеки в stage0/lib/..., а в пути для поиска указан stage0/lib64/.... В качестве обхода проблемы можно указывать в параметрах make потерянный путь:

$ make -j4 RUSTFLAGS_STAGE0="-L x86_64-unknown-linux-gnu/stage0/lib/rustlib/x86_64-unknown-linux-gnu/lib"

rusctc на последующих этапах уже используют правильный путь.

Cargo

Cargo — это менеджер крейтов (модулей на языке rust). Cargo используется для установки и сборки модулей. Это критически важный компонент экосистемы, поскольку уже практически с первых страниц руководства по языку рекомендуется его использование для установки крейтов, а также создания и сборки своих собственных модулей.

Сборка cargo также имеет существенные сложности для упаковки в дистрибутивах:

 • Прежде всего это циклическая зависимость на сам cargo, т.е. для сборки cargo требуется сам cargo, загружаемый с серверов mozilla.

 • Для сборки используется доступ в сеть: получение индексов с crates.io, загрузка исходных пакетов, от которых зависит cargo.

 • Использование забандленных исходников C-библиотек в крейтах.

 • Невозможность передать никаких C-флагов для сборки байндингов C-библиотек.

Проблема бутстрапа в принципе решаема, достаточно один раз собрать cargo и при последующих сборках использовать уже собранную программу. Но необходимость доступа в сеть, даже при наличие загруженных исходников усложняет задачу. На данный момент это можно решить путём прогонки сборки с доступом в сеть и запаковки полученного каталога ~/.cargo, куда загружаются индексы и исходные коды зависимостей. При сборке в сборочной системе путь к каталогу можно указать с помощью переменной окружения CARGO_HOME, в этом случае cargo не делает обращений к сети.

Забандленные исходники C-библиотек нужно удалять, при сборке крейтов используется pkg-config, который способен найти нужные хедеры и библиотеке в системе, но тут слишком много ручной работы.

Проблема с передачей C-флагов особенно актуальна на x86_32, где на собранный бинарник cargo ругается eu-elflint:

either the file containing the function '_ZN4json13_$LT$impl$GT$3fmt20hacb38ece875b7a1dEmnE' or the file containing the function '_ZN3ffi6os_str13_$LT$impl$GT$3fmt20hbe4816b63c3298c7mXeE' is not compiled with -fpic/-fPIC
either the file containing the function '_ZN3fmt3num13_$LT$impl$GT$3fmt20h70931bbc978e2ca6MwVE' or the file containing the function '_fini' is not compiled with -fpic/-fPIC
...

Как следствие cargo также содержит непустую секцию TEXTREL:

$ objdump -p ./cargo  | grep TEXTREL
  TEXTREL              0x00000000

Это означает, что присутствует позиционно зависимый код.

Заключение

Похоже разработчики в курсе проблем упаковки Linux-вендоров, соответствующая тема обсуждается, но далека от оптимального решения. Пока удалось сделать тестовые сборки rust 1.6.0 и rust-cargo 0.8.0 для ALTLinux, чтобы имелась возможность поэкспериментировать с Rust. Будем следить за развитием языка.

Теги: rust ALT

Обновлён: 2016-01-25 10:56


Оставить комментарий