I am trying to understand the solana/example-helloworld by re-writing the rust lib myself. I have done this and from the package.json file, to generate the .so file, following is what is run:
cargo build-bpf --manifest-path=./src/program-rust/Cargo.toml --bpf-out-dir=dist/program
I used above since I already have cargo setup locally and run above against my setup, I faced a few issues, from the edition (was using 2021, had to downgrade to 2018) to having to rename main.rs to lib.rs and others I can't remember.
Below is my actual running command from the terminal in the project root directory where the Cargo.toml file is in:
cargo build-bpf --manifest-path=./Cargo.toml --bpf-out-dir=dist/program
But upon running above, in the dist directory, there is nothing
This is actually a multi-tied question:
.so file are signifies solana files, right?
What do we mean by cargo build-bpf?
Is there any reason, why 2021 edition didn't work for the solana example?
Finally, why does the above command not output my .so file?
My Cargo.toml below:
[package]
name = "jjoek-sc"
version = "0.0.1"
description = "My first rust program for solana (hello john)"
authors = ["JJoek <info#jjoek.com>"]
license = "Apache-2.0"
homepage = "https://jjoek.com/"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
borsh = "0.9.1"
borsh-derive = "0.9.1"
solana-program = "~1.8.14"
Below is my lib.rs
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
msg,
program_error::ProgramError,
};
/// Define the type of state stored in accounts
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
/// number of greetings
pub counter: u32,
}
// Declare and export the program's entrypoint
entrypoint!(process_instruction);
// Program entrypoint's implementation
pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], _instruction_data: &[u8]) -> ProgramResult {
msg!("Hello, John everything is gonna be okay");
msg!("Hello World Rust program entrypoint");
// Iterating accounts is safer than indexing
let accounts_iter = &mut accounts.iter();
// Get the account to say hello to
let account = next_account_info(accounts_iter)?;
// The account must be owned by the program in order to modify its data
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter += 1;
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Greeted {} time(s)!", greeting_account.counter);
Ok(())
}
.so file are signifies solana files, right?
so stand for shared object, also known as a dynamically relocatable library or dylib in Cargo.toml.
What do we mean by cargo build-bpf?
BPF is a virtual machine inside the kernel, so this should instruct cargo to build for that target.
Is there any reason, why 2021 edition didn't work for the solana example?
I don't know, but I suspect it's a simple fix.
Finally, why does the above command not output my .so file?
Could it be that you are missing the lib section in Cargo.toml:
[lib]
crate-type = ["cdylib", "lib"]
Related
If you look at the Tokio docs on docs.rs there's a blue tag indicating that a feature must be activated in order to access this API:
I would like to enable this for my crate as well, how can this be done?
The bad news is: It's a nightly-only feature for now.
The good news is: docs.rs uses nightly by default.
To get this to work all you need is to enable the doc_cfg feature and apply #doc(cfg) to the item being documented
#![feature(doc_cfg)]
#[doc(cfg(feature = "macros"))]
pub fn test() {}
Because this is a nightly-only feature, you probably don't want to enable it all the time. tokio defines the following in its Cargo.toml to only enable this feature on docs.rs:
# docs.rs-specific configuration
[package.metadata.docs.rs]
# document all features
all-features = true
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]
and then they use
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub fn test() {}
In the latest nightly (since v1.57 maybe), you can use feature doc_auto_cfg (merged in PR#90502) and you no longer need to manually mark features for doc, just write cfg as before:
#![feature(doc_auto_cfg)]
#[cfg(feature = "macros")]
pub fn test() {}
To check it locally, run cargo +nightly doc --all-features.
If you want to continue using stable for commands other than cargo doc, you can:
#![cfg_attr(doc, feature(doc_auto_cfg))]
#[cfg(feature = "macros")]
pub fn test() {}
UPDATE: The above method still requires doc-tests to run nightly, and it also requires dependents' doc command to run nightly. A workaround is that we can enable the doc_auto_cfg feature only under nightly.
In Cargo.toml, adding:
[build-dependencies]
rustc_version = "0.4.0"
Create a build.rs file with the following contents:
use rustc_version::{version_meta, Channel};
fn main() {
// Set cfg flags depending on release channel
let channel = match version_meta().unwrap().channel {
Channel::Stable => "CHANNEL_STABLE",
Channel::Beta => "CHANNEL_BETA",
Channel::Nightly => "CHANNEL_NIGHTLY",
Channel::Dev => "CHANNEL_DEV",
};
println!("cargo:rustc-cfg={}", channel)
}
Then we can enable the feature doc_auto_cfg this way:
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
Since docs.rs uses nightly by default, the documentation on there will be shown as you expected.
Do you know how to parse xsd file into Rust structs using https://github.com/lumeohq/xsd-parser-rs?
I do not know how to run the program and how to put files there.
Here's a basic example that parses a file sample.xsd and then prints out the debug representation of the parsed result:
src/main.rs:
use anyhow::{format_err, Result};
use std::fs;
use xsd_parser::parser::parse as xsd_parse;
fn main() -> Result<()> {
let sample = fs::read_to_string("sample.xsd")?;
let parsed = xsd_parse(&sample).map_err(|_| format_err!("failed to parse XSD file"))?;
println!("{:#?}", &parsed);
Ok(())
}
Cargo.toml:
[package]
name = "xsd-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1"
xsd-parser = { git = "https://github.com/lumeohq/xsd-parser-rs", rev = "7f3d433e4f033b55f057e97c0a30de221dbe3ae1" }
sample.xsd:
I used the sample XSD file from here.
From the project folder, you can invoke rustdoc like cargo doc -p xsd-parser --no-deps --open to generate and open the API docs for the xsd-parser crate, which is what allowed me to figure out how to use the crate.
If you look at the Tokio docs on docs.rs there's a blue tag indicating that a feature must be activated in order to access this API:
I would like to enable this for my crate as well, how can this be done?
The bad news is: It's a nightly-only feature for now.
The good news is: docs.rs uses nightly by default.
To get this to work all you need is to enable the doc_cfg feature and apply #doc(cfg) to the item being documented
#![feature(doc_cfg)]
#[doc(cfg(feature = "macros"))]
pub fn test() {}
Because this is a nightly-only feature, you probably don't want to enable it all the time. tokio defines the following in its Cargo.toml to only enable this feature on docs.rs:
# docs.rs-specific configuration
[package.metadata.docs.rs]
# document all features
all-features = true
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]
and then they use
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub fn test() {}
In the latest nightly (since v1.57 maybe), you can use feature doc_auto_cfg (merged in PR#90502) and you no longer need to manually mark features for doc, just write cfg as before:
#![feature(doc_auto_cfg)]
#[cfg(feature = "macros")]
pub fn test() {}
To check it locally, run cargo +nightly doc --all-features.
If you want to continue using stable for commands other than cargo doc, you can:
#![cfg_attr(doc, feature(doc_auto_cfg))]
#[cfg(feature = "macros")]
pub fn test() {}
UPDATE: The above method still requires doc-tests to run nightly, and it also requires dependents' doc command to run nightly. A workaround is that we can enable the doc_auto_cfg feature only under nightly.
In Cargo.toml, adding:
[build-dependencies]
rustc_version = "0.4.0"
Create a build.rs file with the following contents:
use rustc_version::{version_meta, Channel};
fn main() {
// Set cfg flags depending on release channel
let channel = match version_meta().unwrap().channel {
Channel::Stable => "CHANNEL_STABLE",
Channel::Beta => "CHANNEL_BETA",
Channel::Nightly => "CHANNEL_NIGHTLY",
Channel::Dev => "CHANNEL_DEV",
};
println!("cargo:rustc-cfg={}", channel)
}
Then we can enable the feature doc_auto_cfg this way:
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
Since docs.rs uses nightly by default, the documentation on there will be shown as you expected.
If you look at the Tokio docs on docs.rs there's a blue tag indicating that a feature must be activated in order to access this API:
I would like to enable this for my crate as well, how can this be done?
The bad news is: It's a nightly-only feature for now.
The good news is: docs.rs uses nightly by default.
To get this to work all you need is to enable the doc_cfg feature and apply #doc(cfg) to the item being documented
#![feature(doc_cfg)]
#[doc(cfg(feature = "macros"))]
pub fn test() {}
Because this is a nightly-only feature, you probably don't want to enable it all the time. tokio defines the following in its Cargo.toml to only enable this feature on docs.rs:
# docs.rs-specific configuration
[package.metadata.docs.rs]
# document all features
all-features = true
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]
and then they use
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub fn test() {}
In the latest nightly (since v1.57 maybe), you can use feature doc_auto_cfg (merged in PR#90502) and you no longer need to manually mark features for doc, just write cfg as before:
#![feature(doc_auto_cfg)]
#[cfg(feature = "macros")]
pub fn test() {}
To check it locally, run cargo +nightly doc --all-features.
If you want to continue using stable for commands other than cargo doc, you can:
#![cfg_attr(doc, feature(doc_auto_cfg))]
#[cfg(feature = "macros")]
pub fn test() {}
UPDATE: The above method still requires doc-tests to run nightly, and it also requires dependents' doc command to run nightly. A workaround is that we can enable the doc_auto_cfg feature only under nightly.
In Cargo.toml, adding:
[build-dependencies]
rustc_version = "0.4.0"
Create a build.rs file with the following contents:
use rustc_version::{version_meta, Channel};
fn main() {
// Set cfg flags depending on release channel
let channel = match version_meta().unwrap().channel {
Channel::Stable => "CHANNEL_STABLE",
Channel::Beta => "CHANNEL_BETA",
Channel::Nightly => "CHANNEL_NIGHTLY",
Channel::Dev => "CHANNEL_DEV",
};
println!("cargo:rustc-cfg={}", channel)
}
Then we can enable the feature doc_auto_cfg this way:
#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
Since docs.rs uses nightly by default, the documentation on there will be shown as you expected.
The Rust reference states:
A dynamic system library will be produced. This is used when compiling a dynamic library to be loaded from another language. This output type will create *.so files on Linux, *.dylib files on macOS, and *.dll files on Windows.
My WASM is not a *.dylib, *.dll, or *.so... so why must the crate type be set to cdylib? What is really happening under the hood?
lib.rs
#[no_mangle]
pub extern fn add_one(x: u32) -> u32 {
x + 1
}
Cargo.toml
[package]
name = "utils"
version = "0.1.0"
authors = [""]
edition = "2018"
[dependencies]
[lib]
crate-type = ["cdylib"] // Why is this needed
index.html:
<!DOCTYPE html>
<html>
<head>
<script>
fetch("utils.gc.wasm")
.then(response => response.arrayBuffer())
.then(result => WebAssembly.instantiate(result))
.then(wasmModule => {
const result = wasmModule.instance.exports.add_one(2);
const text = document.createTextNode(result);
document.body.appendChild(text);
});
</script>
<head>
<body></body>
<html>
Terminal:
$ cd utils
$ cargo build --target wasm32-unknown-unknown --release
$ wasm-gc target/wasm32-unknown-unknown/release/utils.wasm -o utils.gc.wasm
$ http
It is exactly as the Rust reference states: We are creating a dynamic library to be loaded from another language. So why is the output neither .dll, .so or .dylib? That is because we are not compiling for either Windows, Linux or MacOS. We are compiling for wasm32-unknown-unknown. So the only shortcoming of the reference here is not listing all possible platforms and their dynamic library file endings.
We need a dynamic library, because dynamic libraries can be loaded at runtime (typically by the browser). Rust libraries have to be statically linked to be used.
An extra tidbit of information of what is going on under the hood:
If you invoke the wasm_bindgen macro it will (among other things) expand the signature of a function into a C function.
#[wasm_bindgen]
pub fn my_function()
#[export_name = "my_function"]
pub extern "C" fn __wasm_bindgen_my_function()
Why a C Function? Function calls can differ from language to language. By mangling, order of the arguments on the callstack, etc. . This is called the ABI. C has the distinct advantage of having a defined and stable ABI, which is why it is a popular choice for foreign function interfaces.