Ржавчина и Wasm бок о бок |

На моей работе мы рассматриваем возможность использования Веб-сборка (далее сокращенно WASM), потому что он позволяет нам кросс-компилировать практически любой язык для использования в Интернете. WebAssembly — это «двоичный формат инструкций для виртуальной машины на основе стека». По сути, это означает, что это двоичный язык, предназначенный для запуска в любом месте, но, вообще говоря, прямо сейчас он используется в веб-браузерах в качестве замены модулей Javascript. Один из языков, которые мы рассматриваем в качестве исходного языка, это Ржавчинановый язык, разработанный для безопасности типов, производительности и параллелизма.

Таким образом, я читал язык программирования Rust и, что более важно, как его скомпилировать Rust в WebAssembly. Изучив пример модуля, приведенный в книге RustWasm, я решил конвертировать одну из моих личных библиотек npm из JS -> Rust. Первым шагом в этом начинании было преобразование существующих тестов, которые у меня были в mocha/chai, в собственный формат тестов Rust, чтобы, добавляя код, я мог убедиться, что он работает так, как я ожидал. Однако возможность запуска этих тестов стала проблемой, когда я перешел на цель WASM.

Проблема

Одним из больших недостатков компиляции Rust в WASM является то, что нет хорошего способа отлаживать код в браузере и связывать его с исходным кодом Rust. В книге Rust-WASM это отмечено в раздел по отладке:

К сожалению, история отладки для WebAssembly все еще незрелая. В большинстве систем Unix DWARF используется для кодирования информации, необходимой отладчику для обеспечения проверки работающей программы на уровне исходного кода. Существует альтернативный формат, который кодирует аналогичную информацию в Windows. В настоящее время для WebAssembly нет эквивалента.

Вместо этого они рекомендуют использовать тестирование (в частности, автоматизированное тестирование) для выявления регрессий. до они делают это в сборке. Это отличная идея, и я, безусловно, поддерживаю ее, учитывая, что я изначально писал тесты до реализации моей библиотеки. Однако есть один большой недостаток в написании тестов на Rust при компиляции в WASM, как подробно описано в следующий подраздел книги Rust-WASM:

Обратите внимание, что для запуска #[test]s без ошибок компилятора и компоновщика, вам нужно будет закомментировать биты crate-type = «cdylib» в wasm-game-of-life/Cargo.toml.

Чего ждать? Чтобы запустить мои тесты Rust, мне нужно внести изменения в мой исходный репозиторий только для того, чтобы заставить его построить? Это немного не для меня, потому что я хочу работать в системе непрерывной интеграции, где я могу собрать пакет Rust, запустить тесты, собрать двоичный файл WASM, а затем выполнить развертывание в NPM, все автоматически.

Другими словами, то, что я хочу сделать, выглядит примерно так:

# These commands compile (and subsequently test) the native Rust code
$ cargo build
$ cargo test

# This command builds the wasm module
$ cargo build --target=wasm32-unknown-unknown

Ключевым здесь, однако, является то, что я хочу быть в состоянии сделать это без внесение любых изменений в исходный файл или файл сборки.

Возможные решения

Я потратил довольно много времени, пытаясь определить, что я мог делай здесь. Одна мысль, которая пришла на ум, заключалась в том, что у меня мог бы быть сценарий предварительной сборки, который выполняет это комментирование из #[wasm_bindgen] атрибуты вручную. Как это будет работать, так это то, что перед сборкой скрипт будет копировать все в локальном каталоге в подкаталог. Затем он переключил бы crate-type в subdirectory/Cargo.toml к libвместо cdylib и закомментируйте любые экземпляры #[wasm_bindgen]. Излишне говорить, что я не хотел идти по этому пути — он казался невероятно хрупким и подверженным ошибкам.

Затем мой коллега предложил мне отделить нативный код Rust от аспекта кода WASM и заставить библиотеку WASM использовать код из нативной библиотеки, подобно тому, как геоигрушка справляется с этим. Это отличное решение, но когда я устанавливал свою библиотеку, я всю жизнь не мог понять, как импортировать перечисление или структуру в часть библиотеки WASM.

По сути, у geotoy есть настройка, в которой она использует библиотеку WASM для предоставления набора функции которые служат API для библиотеки. Затем эти функции используют структуры данных в src/lib.rs. Однако структуры данных сами себя не выставляются. С cratchitто, что я хочу разоблачить, это Account и AccountsChart структуры данных, а также Currency и AccountType перечисления напрямуювместо набора функций шлюза, использующих эти структуры данных. я подозреваю, что там, вероятно, является способ заставить его выставлять структуры, но я не смог понять это.

Окончательное решение

Решение, к которому я в конце концов пришел, не разделяет библиотеки как таковые. Вместо этого он делает #[wasm_bindgen] атрибуты зависят от того, строите ли вы для цели WASM.

Первое, что вам нужно сделать, это убедиться, что вы используете оба cdylib и rlib (или lib) в вашей Cargo.toml:

[lib]
crate-type = ["cdylib", "rlib"]

Далее (это может быть конкретно для меня) мне пришлось добавить #![feature(custom_attribute)] вверху атрибутов ящика (т.е. вверху src/lib.rs:

#![feature(custom_attribute)]

extern crate cfg_if;
extern crate json;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;
use cfg_if::cfg_if;
use std::collections::HashMap;

...

Далее, везде, где вы ранее использовали #[wasm_bindgen]сделайте его условным для целевой архитектуры:

/// Use this instead of #[wasm_bindgen]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]

Наконец, и это опять же может быть чем-то специфичным для моего варианта использования, вам нужно вытащить типы из подмодулей в корневой крейт. src/lib.rs. Другими словами, в моем src/lib.rsу меня было следующее:

pub mod accounts;
pub mod currency;

И у меня было два файла, src/currency.rs и src/accounts.rsкоторые определяют типы, связанные со счетами и валютами соответственно. wasm_bindgen мне это не понравилось, поэтому я переместил этот код в src/lib.rs и убраны ссылки на модули в тестах.

То, что я сделал для проверки, не использовалось wasm-pack изначально. Вместо этого я побежал cargo +nightly build --target=wasm32-unknown-unknown чтобы убедиться, что ошибок нет и что он создал файл .wasm в соответствующем target каталог:

$ cargo +nightly build --target=wasm32-unknown-unknown
...
$ ls -al target/wasm32-unknown-unknown/debug
total 9520
drwxr-xr-x@ 13 scottj staff 416 Oct 26 10:15 .
drwxr-xr-x 3 scottj staff 96 Oct 26 10:14 ..
-rw-r--r-- 1 scottj staff 0 Oct 26 10:14 .cargo-lock
drwxr-xr-x 8 scottj staff 256 Oct 26 10:14 .fingerprint
drwxr-xr-x 3 scottj staff 96 Oct 26 10:14 build
-rw-r--r-- 1 scottj staff 122 Oct 26 10:15 cratchit.d
-rwxr-xr-x 2 scottj staff 3549051 Oct 26 10:15 cratchit.wasm
drwxr-xr-x 13 scottj staff 416 Oct 26 10:15 deps
drwxr-xr-x 2 scottj staff 64 Oct 26 10:14 examples
drwxr-xr-x 3 scottj staff 96 Oct 26 10:15 incremental
-rw-r--r-- 1 scottj staff 125 Oct 26 10:15 libcratchit.d
-rw-r--r-- 2 scottj staff 1311774 Oct 26 10:15 libcratchit.rlib
drwxr-xr-x 2 scottj staff 64 Oct 26 10:14 native

И, конечно же, я убедился, что тесты прошли без каких-либо изменений:

$ cargo test
running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/test_accounts-54475044840749bd

running 6 tests
test account_type_from_integer ... ok
test account_creation ... ok
test account_type_from_string ... ok
test adding_top_level_accounts_to_accounts_chart ... ok
test getting_all_account_ids_in_a_chart ... ok
test creating_accounts_chart_from_json ... ok

test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/test_currency-d2d59a6d3436c103

running 1 test
test currency_translation_from_string ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests cratchit

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Теперь, когда вы хотите создать свой модуль WASM, вы можете запустить:

wasm-pack build

И он создаст для вас упакованную библиотеку WASM в pkg.

Бонус: запустите тесты на Travis и разверните WASM в NPM.

Это была еще одна область, в которой мне пришлось немного разобраться. Проблема, с которой я столкнулся при использовании Travis-CI, заключалась в том, что он не устанавливает wasm-pack по умолчанию. Таким образом, вам необходимо установить его вручную в сценарии. Однако, если у вас есть cache: cargo включен, он будет зависать, если wasm-pack ранее был установлен. Таким образом, я добавил следующее в свой .travis.yml файл для условной установки wasm-pack:

before_script: |
  if hash wasm-pack 2>/dev/null; then
      echo "Wasm-pack already is installed"
  else
      curl  -sSf | sh
  fi

Теперь вы можете добавить следующее в свой script раздел, посвященный сборке как модуля WASM, так и собственного модуля Rust:

script:
- cargo clean
- cargo build
- cargo test
- wasm-pack build --target=nodejs

Обратите внимание на раздел, который вызывает wasm-pack build имеет дополнительный аргумент: --target=nodejs. Если вы хотите протестировать локально, используя npm linkвам понадобится этот аргумент, так как он упаковывает модуль WASM с main параметр в package.json.

И, наконец, добавим логику развертывания:

before_deploy:
- cd pkg
deploy:
  provider: npm
  email: <your email address>
  on:
    tags: true
  skip_cleanup: true
  api_key:
    secure: <YOUR_API_KEY>

Обратите внимание, что эта логика развертывания будет развертываться только в помеченных выпусках, поэтому вы можете изменить ее, если хотите изменить поведение.

Последнее, что нужно понять: в настоящее время для этого требуется ночной ржавчина. Итак, вам нужно убедиться, что вы используете nightly Rust локально, и вам нужно убедиться, что, если вы строите на travis, вы разрешаете stable и beta Ржавчина до отказа:

rust:
- stable
- beta
- nightly
matrix:
  allow_failures:
    - rust: stable
    - rust: beta

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *