- Свежий GNU ARM Embedded Toolchain
- System Workbench for STM32 (опционально)
- Свежий Eclipse CDT
- GNU ARM Eclipse Plugin
- Rust
- RustDT. Для комфортной разработки также рекомендуется установить Racer, Rainicorn и rustfmt.
Идея состоит в том, чтобы с скомпилировать написанную на Rust программу в библиотеку, которую можно будет слинковать с помощью тулчейна для ARM.
В итоге мы сможем даже вполне комфортно дебажить смешанный код на Rust и С.
1. Генерация проекта на C
Воспользуемся для этого утилитой STM32CubeMX. Для демо-проекта нам понадобится:
- SYS = Serial Wire (если у устройство подключено по SWD) либо JTAG
- USART2 в конфигурации Asynchronous
- Несколько пинов на одном порту в режиме GPIO_Output (назовем их LED_R, LED_G, LED_B)
![image](https://habrastorage.org/getpro/habr/post_images/729/eda/bf6/729edabf636fcdd62689fce1310d137a.png)
Проверим настройки тактирования. Тут при желании можем указать тактирование от внешнего кварца и его частоту.
![image](https://habrastorage.org/getpro/habr/post_images/b50/bd3/85a/b50bd385ada4f3ae8e08aa82f6959b94.png)
Сгенерируем проект. Назовем его “HwApi”, т.к. этот слой кода у нас будет представлять собой абстракцию над железом, который мы будем использовать при написании кода на Rust. В качестве IDE выбираем SW4STM32.
![image](https://habrastorage.org/getpro/habr/post_images/725/f21/98a/725f2198af0a5978015b085e9e99275f.png)
Если Workbench установлен, то можем открыть сгенерированный проект и проверить, что он успешно компилируется.
![image](https://habrastorage.org/getpro/habr/post_images/a6c/bc2/149/a6cbc21490ea5ebd14b6f1b0491cba02.png)
2. Создаем проект для свежей версии Eclipse
Хоть System Workbench и основан на Eclipse, нам придется создать новый проект в свежей мажорной версии Eclipse (Neon), т.к. RustDT несовместим с той версией Eclipse.
Также нам понадобится шаблон проекта, который устанавливается вместе с GNU ARM Eclipse Plugin.
![image](https://habrastorage.org/getpro/habr/post_images/226/eb8/e26/226eb8e26d1095f2a78d87f4113cd2da.png)
Для того, чтобы успешно слинковать либу, сгенерированную rust компилятором, нам понадобится заранее установленная свежая версия GNU ARM Embedded Toolchain.
![image](https://habrastorage.org/getpro/habr/post_images/2a7/1d7/c4d/2a71d7c4d4c2fffacd67fb2b76566649.png)
Начинаем процесс переноса проекта из System Workbench в Eclipse CDT. В интернете можно найти скрипты, которые этот процесс автоматизируют, но я буду это делать вручную, т.к. собираюсь переиспользовать HwApiLib в других проектах, изменяя только написанную на Rust часть кода.
Копируем следующие папки/файлы в новый проект:
- Drivers
- Inc
- Src
- startup
- STM32F103C8Tx_FLASH.ld
Если Workbench установлен, то разворачиваем два окна настроек проектов (из старого и нового Eclipse) так, чтобы было удобно копировать значения из одного окна в другое. Окна немного отличаются, поэтому при копировании ориентируемся на флаги, которые указаны в скобках.
Если Workbench не установлен, можно просто скопировать настройки со скриншотов, приложенных ниже.
Копируем Defined Symbols:
![image](https://habrastorage.org/getpro/habr/post_images/4ad/c39/092/4adc39092e438882a39057bc2dc9715d.png)
Пути к папкам, содержащие *.h файлы:
![image](https://habrastorage.org/getpro/habr/post_images/46c/d76/b50/46cd76b501a805271a1bab32ae0f407e.png)
На вкладке “Optimization” можно включить оптимизацию Optimize size(-Os).
Далее указываем, что нам нужны все предупреждения компилятора:
![image](https://habrastorage.org/getpro/habr/post_images/56a/b2d/e21/56ab2de21706a31015d738fa7564e4d5.png)
Указываем путь к скрипту линкера + отмечаем чекбокс для удаления из результата линковки неиспользуемых в коде секций:
![image](https://habrastorage.org/getpro/habr/post_images/af0/189/0ab/af01890aba95439cbf0ed3d1d71c2d5f.png)
На следующей вкладке важно отметить чекбокс “Use newlib-nano” и вручную указать флаг
-specs=nosys.specs
:![image](https://habrastorage.org/getpro/habr/post_images/cd9/0c9/94b/cd90c994bdf81cfeb5335741c92ddb1d.png)
Указываем пути к папкам с файлами для компиляции:
![image](https://habrastorage.org/getpro/habr/post_images/e31/303/b47/e31303b478754208a9d52a873d60ace2.png)
Нажимаем Ок. После чего меняем расширение startup файла на заглавную .S, чтобы файл успешно подхватился компилятором. Проверяем, что проект компилируется.
![image](https://habrastorage.org/getpro/habr/post_images/f8f/43d/649/f8f43d64951bce512f1c9ce3e072282b.png)
Теперь нужно настроить дебаггер (Run — Debug Configurations — GDB OpenOCD Debugging). Создаем файл для OpenOCD с описанием железа, в котором будет запускаться программа (в моем случае файл называется STM32F103C8x_SWD.cfg):
source [find interface/stlink-v2.cfg]
set WORKAREASIZE 0x5000
transport select "hla_swd"
set CHIPNAME STM32F103C8Tx
source [find target/stm32f1x.cfg]
# use hardware reset, connect under reset
reset_config none
Если у вас используется другой микроконтроллер или другой способ подключения к нему, то корректный файл для OpenOCD можно сгенерировать в Workbench (с помощью Debugging options — Ac6).
В Config options указываем флаг -f и путь к созданному в предыдущем шаге файлу.
![image](https://habrastorage.org/getpro/habr/post_images/834/db0/25a/834db025a064bdd65ebb7c08f7ac8abb.png)
Жмем Debug. Проверяем, что дебаггер успешно залил код в микроконтроллер и началась отладка.
![image](https://habrastorage.org/getpro/habr/post_images/39e/87b/cc9/39e87bcc9abd157a0d54bdb567cb79c6.png)
Пришло время создавать Rust проект. Т.к. нам понадобятся инструкции компилятора, которые не поддерживаются в stable версии, нам нужно будет переключиться нам nightly версию компилятора, запустив в cmd следующие команды:
rustup update
rustup default nightly
Далее нужно получить текущую версию компилятора:
rustc -v --version
![image](https://habrastorage.org/getpro/habr/post_images/92f/951/a80/92f951a806fda5964a85b6ab9a0f8150.png)
Затем склонировать себе исходники rust и переключится на коммит, который использовался для сборки этого компилятора (указан в commit-hash).
git clone git@github.com:rust-lang/rust.git
cd rust
git checkout cab4bff3de1a61472f3c2e7752ef54b87344d1c9
Следующим шагом скомпилируем необходимые нам библиотеки под ARM.
mkdir libs-arm
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g src/libcore/lib.rs --out-dir libs-arm --emit obj,link
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g src/liballoc/lib.rs --out-dir libs-arm -L libs-arm --emit obj,link
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g src/libstd_unicode/lib.rs --out-dir libs-arm -L libs-arm --emit obj,link
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g src/libcollections/lib.rs --out-dir libs-arm -L libs-arm --emit obj,link
В будущем, при каждом обновлении компилятора (rustup update) нужно будет переключаться на актуальную версию исходников и перекомпилировать библиотеки для ARM, иначе потеряется возможность дебажить код на rust.
Наконец-то можно приступить к создают Rust-проекта в eclipse.
![image](https://habrastorage.org/getpro/habr/post_images/bdc/1a3/3e4/bdc1a33e4ed1c6277a53710a18ca784b.png)
![image](https://habrastorage.org/getpro/habr/post_images/1c8/dca/aac/1c8dcaaac46251623fc661d8b1a4d505.png)
Eclipse просит указать путь к компилятору, исходникам и утилитам для работы с rust-кодом.
![image](https://habrastorage.org/getpro/habr/post_images/fc9/f8a/b25/fc9f8ab2589ee4e0fbd67eabc97860df.png)
Обычно эти компоненты можно найти в C:\Users\%username%\.cargo. Rust src — путь к папке src в исходниках, которые мы скачали ранее.
Теперь основной код:
lib.rs
#![feature(macro_reexport)]
#![feature(unboxed_closures)]
#![feature(lang_items, asm)]
#![no_std]
#![feature(alloc, collections)]
#![allow(dead_code)]
#![allow(non_snake_case)]
extern crate alloc;
pub mod runtime_support;
pub mod api;
#[macro_reexport(vec, format)]
pub extern crate collections;
use api::*;
#[no_mangle]
pub extern fn demo_main_loop() -> ! {
let usart2 = Stm32Usart::new(Stm32UsartDevice::Usart2);
loop {
let u2_byte = usart2.try_read_byte();
match u2_byte {
Some(v) => {
let c = v as char;
match c {
'r' => { toggle_led(Stm32Led::Red); }
'g' => { toggle_led(Stm32Led::Green); }
'b' => { toggle_led(Stm32Led::Blue); }
_ => { usart2.print("cmd not found"); }
}
}
_ => {}
}
delay(1);
}
}
api.rs — прослойка для интеграции между собой Rust и C кода
use collections::Vec;
extern {
fn stm32_delay(millis: u32);
fn usart2_send_string(str: *const u8, len: u16);
fn usart2_send_byte(byte: u8);
fn usart2_try_get_byte() -> i16;
fn stm32_toggle_led(led: u8);
fn stm32_enable_led(led: u8);
fn stm32_disable_led(led: u8);
}
pub fn delay(millis: u32) {
unsafe {
stm32_delay(millis);
}
}
#[derive(Copy, Clone)]
pub enum Stm32UsartDevice {
Usart2
}
#[derive(Copy, Clone)]
pub struct Stm32Usart {
device: Stm32UsartDevice
}
impl Stm32Usart {
pub fn new(device: Stm32UsartDevice) -> Stm32Usart {
Stm32Usart {
device: device
}
}
pub fn print(&self, str: &str) {
let bytes = str.bytes().collect::<Vec<u8>>();
self.print_bytes(bytes.as_slice());
}
pub fn print_bytes(&self, bytes: &[u8]) {
unsafe {
match self.device {
Stm32UsartDevice::Usart2 => usart2_send_string(bytes.as_ptr(), bytes.len() as u16)
}
}
}
pub fn println(&self, str: &str) {
self.print(str);
self.print("\r\n");
}
pub fn send_byte(&self, byte: u8) {
unsafe {
match self.device {
Stm32UsartDevice::Usart2 => usart2_send_byte(byte)
}
}
}
pub fn try_read_byte(&self) -> Option<u8> {
unsafe {
let r = usart2_try_get_byte();
if r == -1 { return None; }
return Some(r as u8);
}
}
}
pub enum Stm32Led {
Red,
Green,
Blue,
Orange
}
impl Stm32Led {
fn to_api(&self) -> u8 {
match *self {
Stm32Led::Green => 2,
Stm32Led::Blue => 3,
Stm32Led::Red => 1,
Stm32Led::Orange => 0
}
}
}
pub fn toggle_led(led: Stm32Led) {
unsafe {
stm32_toggle_led(led.to_api());
}
}
pub fn enable_led(led: Stm32Led) {
unsafe {
stm32_enable_led(led.to_api());
}
}
pub fn disable_led(led: Stm32Led) {
unsafe {
stm32_disable_led(led.to_api());
}
}
runtime_support.rs — для поддержки низкоуровневых функций Rust
extern crate core;
/// Call the debugger and halts execution.
#[no_mangle]
pub extern "C" fn abort() -> ! {
loop {}
}
#[cfg(not(test))]
#[inline(always)]
/// NOP instruction
pub fn nop() {
unsafe {
asm!("nop" :::: "volatile");
}
}
#[cfg(test)]
/// NOP instruction (mock)
pub fn nop() {}
#[cfg(not(test))]
#[inline(always)]
/// WFI instruction
pub fn wfi() {
unsafe {
asm!("wfi" :::: "volatile");
}
}
#[cfg(test)]
/// WFI instruction (mock)
pub fn wfi() {}
#[lang = "panic_fmt"]
fn panic_fmt(_: core::fmt::Arguments, _: &(&'static str, usize)) -> ! {
loop {}
}
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
// Memory allocator support, via C's stdlib
#[repr(u8)]
#[allow(non_camel_case_types)]
pub enum c_void {
__variant1,
__variant2,
}
extern "C" {
pub fn malloc(size: u32) -> *mut c_void;
pub fn realloc(p: *mut c_void, size: u32) -> *mut c_void;
pub fn free(p: *mut c_void);
}
#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
malloc(size as u32) as *mut u8
}
#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
free(ptr as *mut c_void);
}
#[no_mangle]
#[allow(unused_variables)]
pub unsafe extern "C" fn __rust_reallocate(ptr: *mut u8,
old_size: usize,
size: usize,
align: usize)
-> *mut u8 {
realloc(ptr as *mut c_void, size as u32) as *mut u8
}
thumbv7m-none-eabi.json
{
"arch": "arm",
"cpu": "cortex-m3",
"data-layout": "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:64-v128:64:128-a:0:32-n32-S64",
"disable-redzone": true,
"executables": true,
"llvm-target": "thumbv7m-none-eabi",
"morestack": false,
"os": "none",
"relocation-model": "static",
"target-endian": "little",
"target-pointer-width": "32"
}
Копируем в папку Rust проекта папку libs-arm содержащую скомпилированные для работы под ARM компоненты из стандартной библиотеки Rust.
Изменяем Debug target, так чтобы он запускал компиляцию с нужными нам параметрами
rustc -C opt-level=2 -Z no-landing-pads --target thumbv7m-none-eabi -g --crate-type lib -L libs-arm src/lib.rs --emit obj,link
![image](https://habrastorage.org/getpro/habr/post_images/b8a/d21/a4b/b8ad21a4b52b46632c525ad5447d97ee.png)
![image](https://habrastorage.org/getpro/habr/post_images/098/0f4/752/0980f47524beeef25524a6b4ba81d801.png)
Компилируем Rust-проект. В результате в папке проекта появится файл lib.o.
Теперь в С-проекте создаем файлы api.h/api.c, в которых объявляем и реализуем функции, которые используются в api.rs.
api.h
#ifndef SERIAL_DEMO_API_H_
#define SERIAL_DEMO_API_H_
#include "stm32f1xx_hal.h"
void stm32_delay(uint32_t milli);
void usart2_send_string(uint8_t* str, uint16_t len);
void usart2_send_byte(uint8_t byte);
int16_t usart2_try_get_byte(void);
void stm32_toggle_led(uint8_t led);
void stm32_enable_led(uint8_t led);
void stm32_disable_led(uint8_t led);
#endif
api.c
#include "api.h"
#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_uart.h"
#include "main.h"
void stm32_delay(uint32_t milli) {
HAL_Delay(milli);
}
extern UART_HandleTypeDef huart2;
void usart2_send_string(uint8_t* str, uint16_t len) {
HAL_UART_Transmit(&huart2, str, len, 1000);
}
void usart2_send_byte(uint8_t byte) {
while (!(USART2->SR & UART_FLAG_TXE));
USART2->DR = (byte & 0xFF);
}
int16_t usart2_try_get_byte(void) {
volatile unsigned int vsr;
vsr = USART2->SR;
if (vsr & UART_FLAG_RXNE) {
USART2->SR &= ~(UART_FLAG_RXNE);
return (USART2->DR & 0x1FF);
}
return -1;
}
uint16_t stm32_led_to_pin(uint8_t led);
void stm32_toggle_led(uint8_t led) {
HAL_GPIO_TogglePin(LED_R_GPIO_Port, stm32_led_to_pin(led));
}
void stm32_enable_led(uint8_t led) {
HAL_GPIO_WritePin(LED_R_GPIO_Port, stm32_led_to_pin(led), GPIO_PIN_SET);
}
void stm32_disable_led(uint8_t led) {
HAL_GPIO_WritePin(LED_R_GPIO_Port, stm32_led_to_pin(led), GPIO_PIN_RESET);
}
uint16_t stm32_led_to_pin(uint8_t led) {
switch (led) {
case 1:
return LED_R_Pin;
case 2:
return LED_G_Pin;
case 3:
return LED_B_Pin;
default:
return LED_B_Pin;
}
}
Добавляем вызов demo_main_loop() внутри функции main.
main.c
...
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
demo_main_loop();
}
/* USER CODE END 3 */
...
Осталось всё слинковать. Для этого открываем свойства проекта на C и укажем линковщику где взять недостающие obj файлы.
![image](https://habrastorage.org/getpro/habr/post_images/823/ec6/726/823ec67268d95ed4033d75358cbdcc37.png)
Компилируем. Бинарник сильно прибавил в весе, но все еще умещается в STM32F103C8.
![image](https://habrastorage.org/getpro/habr/post_images/1ee/287/70e/1ee28770e1c9f49cca0c081817a93a3f.png)
Запускаем Debug и видим, что Eclipse без проблем переходит из C-кода в Rust.
![image](https://habrastorage.org/getpro/habr/post_images/20c/b42/e84/20cb42e844e117ece5911f014d7a1b27.png)
В завершении статьи хочу выразить благодарность авторам следующих постов, без них я бы не осилил этот процесс:
www.hashmismatch.net/pragmatic-bare-metal-rust
spin.atomicobject.com/2015/02/20/rust-language-c-embedded
github.com/japaric/rust-cross
Статью писал с надеждой на то, что это послужит дополнительным шагом в появлении комьюнити разработчиков использующих Rust для программирования под микроконтроллеры, т.к. это действительно удобный и современный язык, несмотря на то, что у него довольно высокий порог вхождения.
Комментарии (24)
red75prim
23.03.2017 10:44+6match u2_byte { Some(v) => { let c = v as char; match c { 'r' => ... 'g' => ... 'b' => ...
От преобразования
u8
вchar
в этом коде можно избавиться, если использовать байтовые литералыb'r'
,b'g'
и т.д. Например,
match u2_byte { Some(v) => { match v { b'r' => ... b'g' => ... b'b' => ...
или
match u2_byte { Some(b'r') => ... Some(b'g') => ... Some(b'b') => ...
snuk182
23.03.2017 13:59+4Кто-то использует RustDT + Eclipse кроме меня, вау!
Для непривыкшего к файлово-конфиговой возне с vim/emacs, вроде меня, Eclipse на удивление хорошо и стабильно работает. Есть приколы с созданием exe-проекта, но в остальном, включая дебаг, вполне себе рулит.chabapok
29.03.2017 13:28а у вас дебаг в RustDT нормально работает? Я пробовал RustDT и сразу наткнулся на 2 бага:
- Если что-то поменять в программе и пытаться перекомпилить, то бывает выводится сообщение "Blocking waiting for file lock on build directory" — и компиляция не начинается
- если у нас есть:
fn func(v:&i32) { println!("v={}", v); }
мы поставили туда точку останова и стали на нее, то v в окошке переменных отображается как указатель — показывает адрес, а не значение по этому адресу. Посмотреть значение невозможно. Ну и плюс, переменная v дважды там показывается в списке. И, при выделении одной из строк — обе начинают перемигиваться.
snuk182
29.03.2017 15:21+2Что работает:
— Пошаговая отладка, в любом потоке
— Отображение содержимого переменных. Для enum приходится напрячь воображение, но в принципе читаемо
— Отладка ffi (если сишная библиотека собрана с дебагом)
Что не работает
— Автоматический выход из процедуры (нужно нажать F7 в конце процедуры, тогда выходит нормально)
— Отображение сложно вложенных структур и векторов (отображается только первый элемент)
— Отладка макросов (топчется несколько тактов и перескакивает)
Перекомпиляция и перезапуск дебага происходят без проблем.
Windows + Rust Nightly GNU.chabapok
29.03.2017 16:15ясно. Я под убунтой. Не знаю, что не так, но RustDT произвел крайне плохое впечатление: глючно до такой степени, что не подходит.
potan
24.03.2017 21:16+1А исполняемые файлы на Rust на много жирнее получаются, по сравнению с C?
Удается ли воспользоваться какими-нибудь готовыми библиотеками, просто указав зависимость в Cargo.toml?snuk182
25.03.2017 22:05+1В основном да, но можно заморочиться — https://habrahabr.ru/post/305246/
chabapok
29.03.2017 14:03-3У меня это не сработало: https://habrahabr.ru/post/305246/#comment_10142540
Видимо, когда-то так можно было, но теперь это сломали.
В общем, сколько бы не говорили, что язык уже довольно рслый — но реально это штука пока еще сырая, хотя и выглядит перспективно.DarkEld3r
29.03.2017 14:16+2Так ведь компилятор говорит чего ему не хватает. И в мануале в примере это есть.
chabapok
29.03.2017 16:32Вы напрасно минусуете. Пример из мануала предполагает, что если его закопипастить и попытаться собрать — должно собраться.
В данном случае это не так. В мануале написано, что нужны эти функции, эти функции используют #[feature] — а это фича только ночных сборок.
Сейчас я «застрял» на попытке собрать с #[no_std] в стабильном варианте. Согласно документации, либу оно должно собирать и в стабильном варианте тоже.
Если оно не может так делать в стабильном варианте — для продакшена не подходит.ozkriff
29.03.2017 16:40+2Пример из мануала предполагает, что если его закопипастить и попытаться собрать — должно собраться. В данном случае это не так.
Глава из секции "Nightly Rust", вполне логично что нужен ночной канал для использования того что там описано.
Сейчас я «застрял» на попытке собрать с #[no_std] в стабильном варианте.
Да, на стабильном канале этого сделать нельзя, возможность пока только для ночников. Когда-нибудь стабилизируют.
Если оно не может так делать в стабильном варианте — для продакшена не подходит.
Это может быть, хотя "продакшен" "продакшену" рознь — https://www.rust-lang.org/en-US/friends.html — многие из этих компаний пользуются именно ночными сборками.
С rustup вполне удобно зафиксировать какой-то ночник по дате и только когда сам захочешь — обновиться на более свежий ночник.
chabapok
29.03.2017 17:16+1вот тут: https://doc.rust-lang.org/book/using-rust-without-the-standard-library.html
Пишут Note, в котором заявляют, что stable должно уметь собирать либы без std. И это глава 5, а ночные сборки — это глава 6. Или я что-то неверно понял.
Так что должно. Но не собирает.ozkriff
29.03.2017 17:28+1Или я что-то неверно понял.
Верно. Это я пропустил момент когда обсуждение перешло с этой ссылки на эту.
В общем, библиотеку без std можно собрать на стабильной ржавчине уже давно, исполняемый файл — нет.
Стабилизация последнего не особо приоритетна, как я понимаю, потому что не так-то проста и на практике обычно не так уж и важна (по сравнению со сборкой библиотек).
chabapok
29.03.2017 17:59Как? Как собрать библиотеку на стабильной ржавчине?
Пример из документации вываливает ошибки на все те же panic_fmt и eh_personality.ozkriff
29.03.2017 19:53+2$ cat src/lib.rs #![no_std] pub fn plus_one(x: i32) -> i32 { x + 1 } $ cat Cargo.toml [package] name = "nostr" version = "0.1.0" [lib] # Если заменить rlib на staticlib или еще что - все печально :( crate-type = ["rlib"] $ c b Compiling nostr v0.1.0 (file:///home/ozkriff/zoc/nostr) Finished dev [unoptimized + debuginfo] target(s) in 0.10 secs $ ls -lh target/debug/libnostr.rlib -rw-rw-r-- 2 ozkriff ozkriff 6,5K мар 29 19:45 target/debug/libnostr.rlib
Черт, и правда.
rlib
собрать можно, но толку от rlib без std никакого нет, потому что кроме как с ржавчиной такую библиотеку не используешь.
А вот
staticlib
илиcdylib
, которые как раз притворились бы сишными библиотеками, хотятpanic_fmt
иeh_personality
:-( .chabapok
29.03.2017 20:46Ответили в итоге тут:
http://stackoverflow.com/questions/43097676/how-to-build-a-standard-linux-so-library-with-stable-rust-without-using-std
Получается, если хочешь создать либу, которую надо линковать с сишным кодом — ты ограничен только ночными сборками. И, по всей видимости, все нормальные люди этим моментом особо не заморачиваются. Так же, документация документирует ночные сборки, и дифференциации по фичам не делает.ozkriff
29.03.2017 21:07+1Ответили в итоге тут:
вроде, примерно то же что и я написал.
Получается, если хочешь создать либу, которую надо линковать с сишным кодом — ты ограничен только ночными сборками.
Если речь именно о "без std" то выходит так, да.
Так же, документация документирует ночные сборки, и дифференциации по фичам не делает.
Звучит как слишком мощная экстраполяция, так-то весь нестабильный функционал описывается в доках отдельно или с пометками. Просто конкретно этот момент с nostd такой хитрожопый вышел.
Зачем nostd тогда вообще стабилизировали я теперь не очень понимаю.
chabapok
30.03.2017 00:43+1Если речь именно о «без std» то выходит так, да
Я тут путем эксперимента выяснил — оптимизатор выбрасывает std и core, если их не используешь де-факто (тип либ — cdylib). В том числе, при сборке в стабильной версии. Никакого #[no_std] писать вообще ненадо. При линковке такого .so ругается: undefined reference to `rust_begin_unwind', но это всего 1 ссылка, ее наверняка можно объявить в сишном коде, тем более что она вызываться не будет. Так что, на этот вопрос я пока просто забил.
Мне пока надо понять концептуально — готов ли руст для продакшена. Выглядит для меня он весьма привлекательно, как замена си. Но вот использовать нестабильное апи — как-то стремно.ozkriff
30.03.2017 08:27+1Я тут путем эксперимента выяснил — оптимизатор выбрасывает std и core, если их не используешь де-факто (тип либ — cdylib).
Конечно, без этой оптимизации статическое связывание теряло бы всякий смысл.
Только надо учитывать, что какой-нибудь простой
println
косвенно использует значительную часть стандартной библиотеки.
При линковке такого .so ругается: undefined reference to `rust_begin_unwind', но это всего 1 ссылка, ее наверняка можно объявить в сишном коде, тем более что она вызываться не будет.
Можно в Cargo.toml прописать
panic="abort"
, по идее это всю раскрутку (unwind) должно отключить: http://doc.crates.io/manifest.html#the-profile-sectionsgrossws
30.03.2017 10:37+1Но в std и прочих частях останутся landing pads, в клиентском коде их, естественно не будет. Если хочется избавиться ещё и от них, то можно использовать xargo
build_your_web
25.03.2017 22:13+3Если писать код, не используя фреймворки, то по объему получается примерно также, как если программировать на С.
Например, если в примере из статьи убрать вызов usart2.print("..."), который использует collections::Vec, то вызов Rust-кода делает результат компиляции всего на сотню байт больше.
А вот если подключить векторы, то это увеличит размер бинарника на целых 14кБ. Если взять пример посложнее, который использует стандартную библиотеку «на полную», то там будет 50кБ+.
Но на мой взгляд, гораздо дешевле купить чип в котором будет больше памяти и при этом иметь возможность комфортно писать код. В том же STM32F103C8T6 64кБ flash памяти, чего обычно вполне хватает.
Использовать другие библиотеки не пробовал.
grossws
Попробуйте xargo, чтобы не перекомпилировать нужное руками.
grossws
Стараниями Jorge Aparicio (автора xargo, cross, svd2rust и кучи других полезных вещей, а также одного из самых активных движителей развития rust в embedded) теперь должно быть можно спокойно жить без ручного создания этого файла (тем более он имел свойство ломаться при изменениях версий llvm), т. к. оно есть в комплекте с компилятором.
Ещё рекомендую посмотреть на его же проект utest поддерживающий тестирование в qemu и под аппаратным отладчиком с использованием semihosting.
build_your_web
Проверил, действительно, теперь проект можно компилировать без этого файла. Спасибо!