Explorar o código

Add crates.io metadata for publishing

Dean 利迪恩 hai 1 mes
pai
achega
892eb02ea0

+ 34 - 0
.npmignore

@@ -0,0 +1,34 @@
+# Rust artifacts
+target/
+Cargo.lock
+**/*.rs
+**/Cargo.toml
+.cargo/
+
+# Rust build artifacts
+*.rlib
+*.rmeta
+*.so
+*.dylib
+*.dll
+
+# Source code (keep only compiled artifacts)
+src/
+crates/
+
+# Development files
+.git/
+.github/
+.gitignore
+*.md
+!README.md
+
+# Test and example files
+tests/
+examples/
+benches/
+
+# CI/CD
+.travis.yml
+.gitlab-ci.yml
+azure-pipelines.yml

+ 11 - 6
Cargo.toml

@@ -1,7 +1,14 @@
 [package]
 name = "sbpf"
 version = "0.1.1"
+authors = ["Dean Little <dean@blueshift.gg>", "Claire Fan <claire@blueshift.gg>"]
 edition = "2021"
+description = "A complete toolchain for building and deploying Solana BPF programs"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/blueshift-gg/sbpf"
+readme = "README.md"
+keywords = ["solana", "bpf", "blockchain", "assembler"]
+categories = ["development-tools", "command-line-utilities"]
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [dependencies]
@@ -18,14 +25,13 @@ dirs = "4.0" # For easily getting the home directory
 codespan-reporting = "0.12.0"
 termcolor = "1.4"
 
-sbpf-assembler = { workspace = true }
+sbpf-assembler = { version = "0.1.1", path = "crates/assembler" }
 object = "0.37.3"
-sbpf-disassembler = { workspace = true }
-sbpf-linker = { workspace = true }
-sbpf-common = { workspace = true }
+sbpf-disassembler = { version = "0.1.1", path = "crates/disassembler" }
+sbpf-common = { version = "0.1.1", path = "crates/common" }
 
 [workspace]
-members = ["crates/assembler", "crates/common", "crates/disassembler", "crates/linker"]
+members = ["crates/assembler", "crates/common", "crates/disassembler"]
 exclude = ["examples"]
 
 [workspace.package]
@@ -39,5 +45,4 @@ anyhow = "1.0.86"
 
 sbpf-assembler = { path = "crates/assembler" }
 sbpf-disassembler = { path = "crates/disassembler" }
-sbpf-linker = { path = "crates/linker" }
 sbpf-common = { path = "crates/common" }

+ 8 - 4
crates/assembler/Cargo.toml

@@ -1,8 +1,12 @@
 [package]
-name                   = "sbpf-assembler"
-description            = "SBPF Assembler"
-edition 			   = "2024"
-version.workspace      = true
+name = "sbpf-assembler"
+version.workspace = true
+description = "Assembler for SBPF (Solana BPF) assembly language"
+edition = "2024"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/blueshift-gg/sbpf"
+keywords = ["solana", "bpf", "assembler", "blockchain"]
+categories = ["development-tools", "compilers"]
 
 [lib]
 crate-type = ["cdylib", "lib"]

+ 6 - 2
crates/common/Cargo.toml

@@ -1,8 +1,12 @@
 [package]
 name = "sbpf-common"
-description = "SBPF Common"
-edition = "2024"
 version.workspace = true
+description = "Common types and utilities for SBPF (Solana BPF)"
+edition = "2024"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/blueshift-gg/sbpf"
+keywords = ["solana", "bpf", "blockchain"]
+categories = ["development-tools"]
 
 [lib]
 crate-type = ["cdylib", "lib"]

+ 6 - 2
crates/disassembler/Cargo.toml

@@ -1,8 +1,12 @@
 [package]
 name = "sbpf-disassembler"
-description = "SBPF Disassembler"
-edition = "2024"
 version.workspace = true
+description = "Disassembler for SBPF (Solana BPF) bytecode"
+edition = "2024"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/blueshift-gg/sbpf"
+keywords = ["solana", "bpf", "disassembler", "blockchain"]
+categories = ["development-tools"]
 
 [lib]
 crate-type = ["cdylib", "lib"]

+ 0 - 56
crates/linker/Cargo.toml

@@ -1,56 +0,0 @@
-[package]
-name = "sbpf-linker"
-edition = "2024"
-version.workspace = true
-
-[lib]
-crate-type = ["cdylib", "lib"]
-name = "sbpf_linker"
-
-[dependencies]
-sbpf-assembler = { path = "../assembler" }
-clap = { version = "4.5.13", features = ["derive"] }
-object = "0.37.3"
-anyhow = "1.0"
-bpf-linker = "0.9.15"
-
-aya-rustc-llvm-proxy = { version = "0.9.5", optional = true }
-llvm-sys-19 = { package = "llvm-sys", features = ["disable-alltargets-init"], version = "191.0.0", optional = true }
-llvm-sys-20 = { package = "llvm-sys", features = ["disable-alltargets-init"], version = "201.0.1", optional = true }
-llvm-sys-21 = { package = "llvm-sys", features = ["disable-alltargets-init"], version = "211.0.0-rc1", optional = true }
-
-tracing-appender = "0.2"
-tracing-subscriber = { version = "0.3", features = ["env-filter", "registry"] }
-tracing-tree = "0.4"
-
-tracing = "0.1"
-thiserror.workspace = true
-
-[[bin]]
-name = "sbpf-linker"
-
-[features]
-llvm-19 = ["dep:llvm-sys-19"]
-llvm-20 = ["dep:llvm-sys-20"]
-llvm-21 = ["dep:llvm-sys-21"]
-rust-llvm-19 = [
-    "dep:aya-rustc-llvm-proxy",
-    "llvm-19",
-    "llvm-sys-19/no-llvm-linking",
-]
-rust-llvm-20 = [
-    "dep:aya-rustc-llvm-proxy",
-    "llvm-20",
-    "llvm-sys-20/no-llvm-linking",
-]
-rust-llvm-21 = [
-    "dep:aya-rustc-llvm-proxy",
-    "llvm-21",
-    "llvm-sys-21/no-llvm-linking",
-]
-default = [
-    "llvm-21",
-    "rust-llvm-21",
-    "rustc-build-sysroot",
-]
-rustc-build-sysroot = []

+ 0 - 222
crates/linker/src/bin/sbpf-linker.rs

@@ -1,222 +0,0 @@
-use std::{
-    env, fs,
-    ffi::CString,
-    path::PathBuf,
-    str::FromStr,
-};
-
-#[cfg(any(
-    feature = "rust-llvm-19",
-    feature = "rust-llvm-20",
-    feature = "rust-llvm-21"
-))]
-use aya_rustc_llvm_proxy as _;
-use anyhow::Context;
-use thiserror::Error;
-use bpf_linker::{Cpu, Linker, LinkerOptions, OptLevel, OutputType};
-use clap::{
-    error::ErrorKind,
-    Parser,
-};
-use sbpf_linker::link_program;
-
-#[derive(Debug, Error)]
-enum CliError {
-    #[error("optimization level needs to be between 0-3, s or z (instead was `{0}`)")]
-    InvalidOptimization(String),
-//     #[error("unknown emission type: `{0}` - expected one of: `llvm-bc`, `asm`, `llvm-ir`, `obj`")]
-//     InvalidOutputType(String),
-}
-
-#[derive(Copy, Clone, Debug)]
-struct CliOptLevel(OptLevel);
-
-impl FromStr for CliOptLevel {
-    type Err = CliError;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        Ok(Self(match s {
-            "0" => OptLevel::No,
-            "1" => OptLevel::Less,
-            "2" => OptLevel::Default,
-            "3" => OptLevel::Aggressive,
-            "s" => OptLevel::Size,
-            "z" => OptLevel::SizeMin,
-            _ => return Err(CliError::InvalidOptimization(s.to_string())),
-        }))
-    }
-}
-
-#[derive(Debug, Parser)]
-#[command(version)]
-struct CommandLine {
-    /// Target BPF processor. Can be one of `generic`, `probe`, `v1`, `v2`, `v3`
-    #[clap(long, default_value = "generic")]
-    cpu: Cpu,
-    
-    /// Write output to <output>
-    #[clap(short, long)]
-    output: PathBuf,
-
-    /// Emit BTF information
-    #[clap(long)]
-    btf: bool,
-
-    /// Permit automatic insertion of __bpf_trap calls.
-    /// See: https://github.com/llvm/llvm-project/commit/ab391beb11f733b526b86f9df23734a34657d876
-    #[clap(long)]
-    allow_bpf_trap: bool,
-
-    /// Add a directory to the library search path
-    #[clap(short = 'L', number_of_values = 1)]
-    libs: Vec<PathBuf>,
-
-    /// Optimization level. 0-3, s, or z
-    #[clap(short = 'O', default_value = "2")]
-    optimize: Vec<CliOptLevel>,
-
-    /// Export the symbols specified in the file `path`. The symbols must be separated by new lines
-    #[clap(long, value_name = "path")]
-    export_symbols: Option<PathBuf>,
-
-    /// Try hard to unroll loops. Useful when targeting kernels that don't support loops
-    #[clap(long)]
-    unroll_loops: bool,
-
-    /// Ignore `noinline`/`#[inline(never)]`. Useful when targeting kernels that don't support function calls
-    #[clap(long)]
-    ignore_inline_never: bool,
-
-    /// Dump the final IR module to the given `path` before generating the code
-    #[clap(long, value_name = "path")]
-    dump_module: Option<PathBuf>,
-
-    /// Extra command line arguments to pass to LLVM
-    #[clap(long, value_name = "args", use_value_delimiter = true, action = clap::ArgAction::Append)]
-    llvm_args: Vec<CString>,
-
-    /// Disable passing --bpf-expand-memcpy-in-order to LLVM.
-    #[clap(long)]
-    disable_expand_memcpy_in_order: bool,
-
-    /// Disable exporting memcpy, memmove, memset, memcmp and bcmp. Exporting
-    /// those is commonly needed when LLVM does not manage to expand memory
-    /// intrinsics to a sequence of loads and stores.
-    #[clap(long)]
-    disable_memory_builtins: bool,
-
-    /// Input files. Can be object files or static libraries
-    #[clap(required = true)]
-    inputs: Vec<PathBuf>,
-
-    /// Comma separated list of symbols to export. See also `--export-symbols`
-    #[clap(long, value_name = "symbols", use_value_delimiter = true, action = clap::ArgAction::Append)]
-    export: Vec<String>,
-
-    /// Whether to treat LLVM errors as fatal.
-    #[clap(long, action = clap::ArgAction::Set, default_value_t = true)]
-    fatal_errors: bool,
-
-    // The options below are for wasm-ld compatibility
-    #[clap(long = "debug", hide = true)]
-    _debug: bool,
-}
-
-fn main() -> anyhow::Result<()> {
-    let args = env::args().map(|arg| {
-        if arg == "-flavor" {
-            "--flavor".to_string()
-        } else {
-            arg
-        }
-    });
-
-    let CommandLine {
-        cpu,
-        output,
-        btf,
-        allow_bpf_trap,
-        libs,
-        optimize,
-        export_symbols,
-        unroll_loops,
-        ignore_inline_never,
-        dump_module,
-        llvm_args,
-        disable_expand_memcpy_in_order,
-        disable_memory_builtins,
-        inputs,
-        export,
-        fatal_errors,
-        _debug,
-    } = match Parser::try_parse_from(args) {
-        Ok(command_line) => command_line,
-        Err(err) => match err.kind() {
-            ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => {
-                print!("{err}");
-                return Ok(());
-            }
-            _ => return Err(err.into()),
-        },
-    };
-
-    let export_symbols = export_symbols.map(fs::read_to_string).transpose()?;
-
-    // TODO: the data is owned by this call frame; we could make this zero-alloc.
-    let export_symbols = export_symbols
-        .as_deref()
-        .into_iter()
-        .flat_map(str::lines)
-        .map(str::to_owned)
-        .chain(export)
-        .map(Into::into)
-        .collect();
-    
-    let optimize = match *optimize.as_slice() {
-        [] => unreachable!("emit has a default value"),
-        [.., CliOptLevel(optimize)] => optimize,
-    };
-    
-    let mut linker = Linker::new(LinkerOptions {
-        target: Some("bpf".to_string()),
-        cpu,
-        cpu_features: "".to_string(),
-        inputs,
-        output: output.clone(),
-        output_type: OutputType::Object,
-        libs,
-        optimize,
-        export_symbols,
-        unroll_loops,
-        ignore_inline_never,
-        dump_module,
-        llvm_args: llvm_args.into_iter().map(|cstring| cstring.into_string().unwrap_or_default()).collect(),
-        disable_expand_memcpy_in_order,
-        disable_memory_builtins,
-        btf,
-        allow_bpf_trap,
-    });
-
-    linker.link()?;
-
-    if fatal_errors && linker.has_errors() {
-        return Err(anyhow::anyhow!(
-            "LLVM issued diagnostic with error severity"
-        ));
-    }
-
-    let program = std::fs::read(&output).context("Failed to read bytecode")?;
-    let bytecode = link_program(&program)
-        .map_err(|e| anyhow::anyhow!("Link error: {}", e))?;
-    let src_name = std::path::Path::new(&output)
-        .file_stem()
-        .and_then(|s| s.to_str())
-        .unwrap_or("main");
-    let output_path = std::path::Path::new(&output)
-        .parent()
-        .unwrap_or_else(|| std::path::Path::new("."))
-        .join(format!("{}.so", src_name));
-    std::fs::write(output_path, bytecode)?;
-
-    Ok(())
-}

+ 0 - 100
crates/linker/src/byteparser.rs

@@ -1,100 +0,0 @@
-use sbpf_assembler::ast::AST;
-use sbpf_assembler::astnode::{ASTNode, ROData};
-use sbpf_assembler::instruction::Instruction;
-use sbpf_assembler::lexer::{ImmediateValue, Token};
-use sbpf_assembler::opcode::Opcode;
-use sbpf_assembler::parser::ParseResult;
-
-use object::{File, Object, ObjectSection, ObjectSymbol};
-use object::RelocationTarget::Symbol;
-
-use anyhow::Result;
-use std::collections::HashMap;
-
-pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, String> {
-    let mut ast = AST::new();
-
-    let obj = File::parse(bytes).map_err(|e| e.to_string())?;
-
-    // only handle symbols in the .rodata section for now
-    let ro_section = obj.section_by_name(".rodata").unwrap();
-    let mut rodata_table = HashMap::new();
-    let mut rodata_offset = 0;
-    for symbol in obj.symbols() {
-        if symbol.section_index() == Some(ro_section.index()) && symbol.size() > 0 {
-            let mut bytes = Vec::new();
-            for i in 0..symbol.size() {
-                bytes.push(ImmediateValue::Int(
-                    ro_section.data().unwrap()[(symbol.address() + i) as usize] as i64));
-            }
-            ast.rodata_nodes.push(ASTNode::ROData {
-                rodata: ROData {
-                    name: symbol.name().unwrap().to_string(),
-                    args: vec![Token::Directive(String::from("byte"), 0..1) //
-                            , Token::VectorLiteral(bytes.clone(), 0..1)],
-                    span: 0..1,
-                },
-                offset: rodata_offset,
-            });
-            rodata_table.insert(symbol.address(), symbol.name().unwrap().to_string());
-            rodata_offset += symbol.size();
-        }
-    }
-    ast.set_rodata_size(rodata_offset);
-
-    for section in obj.sections() {
-        if section.name() == Ok(".text") {
-            // parse text section and build instruction nodes
-            // lddw takes 16 bytes, other instructions take 8 bytes
-            let mut offset = 0;
-            while offset < section.data().unwrap().len() {
-                let node_len = match Opcode::from_u8(section.data().unwrap()[offset]) {
-                    Some(Opcode::Lddw) => 16,
-                    _ => 8,
-                };
-                let node = &section.data().unwrap()[offset..offset + node_len];
-                ast.nodes.push(ASTNode::Instruction {
-                    instruction: Instruction::from_bytes(node).unwrap(),
-                    offset: offset as u64,
-                });
-                offset += node_len;
-            }
-            
-            // handle relocations
-            for rel in section.relocations() {
-                // only handle relocations for symbols in the .rodata section for now
-                let symbol = match rel.1.target() {
-                    Symbol(sym) => Some(obj.symbol_by_index(sym).unwrap()), 
-                    _ => None
-                };
-                
-                if symbol.unwrap().section_index() == Some(ro_section.index()) {
-                    // addend is not explicit in the relocation entry, but implicitly encoded
-                    // as the immediate value of the instruction
-                    let addend = //
-                        match ast.get_instruction_at_offset(rel.0 as u64).unwrap()
-                            .operands.last().unwrap().clone() {
-                                Token::ImmediateValue(ImmediateValue::Int(val), _) => val,
-                                _ => 0 
-                        };
-
-                    // Replace the immediate value with the rodata label
-                    let ro_label = rodata_table.get(&(addend as u64)).unwrap();
-                    let ro_label_name = ro_label.clone();
-                    let node: &mut Instruction = ast.get_instruction_at_offset(rel.0 as u64).unwrap();
-                    let last_idx = node.operands.len() - 1;
-                    node.operands[last_idx] = Token::Identifier(ro_label_name, 0..1);
-                }
-            }
-            ast.set_text_size(section.size());
-        }
-    }
-
-    let parse_result = ast.build_program();
-    if let Ok(parse_result) = parse_result {
-        Ok(parse_result)
-    } else {
-        // TODO: handle errors
-        Err("Failed to build program".to_string())
-    }
-}

+ 0 - 19
crates/linker/src/lib.rs

@@ -1,19 +0,0 @@
-pub mod byteparser;
-
-use byteparser::parse_bytecode;
-
-use sbpf_assembler::Program;
-
-use anyhow::Result;
-
-pub fn link_program(source: &Vec<u8>) -> Result<Vec<u8>, String> {
-    let parse_result = match parse_bytecode(source) {
-        Ok(program) => program,
-        Err(errors) => {
-            return Err(errors);
-        }
-    };
-    let program = Program::from_parse_result(parse_result);
-    let bytecode = program.emit_bytecode();
-    Ok(bytecode)
-}

+ 0 - 18
src/commands/link.rs

@@ -1,18 +0,0 @@
-use anyhow::{Result, Context};
-use sbpf_linker::link_program;
-
-pub fn link(source: &str) -> Result<()> {
-    let program = std::fs::read(source).context("Failed to read bytecode")?;
-    let bytecode = link_program(&program)
-        .map_err(|e| anyhow::anyhow!("Link error: {}", e))?;
-    let src_name = std::path::Path::new(source)
-        .file_stem()
-        .and_then(|s| s.to_str())
-        .unwrap_or("main");
-    let output_path = std::path::Path::new(source)
-        .parent()
-        .unwrap_or_else(|| std::path::Path::new("."))
-        .join(format!("{}.so", src_name));
-    std::fs::write(output_path, bytecode)?;
-    Ok(())
-}

+ 0 - 3
src/commands/mod.rs

@@ -4,9 +4,6 @@ pub use init::*;
 pub mod build;
 pub use build::*;
 
-pub mod link;
-pub use link::*;
-
 pub mod deploy;
 pub use deploy::*;
 

+ 1 - 4
src/main.rs

@@ -1,7 +1,7 @@
 pub mod commands;
 use anyhow::Error;
 use clap::{Args, Parser, Subcommand};
-use commands::{build, clean, deploy, disassemble, link, init, test};
+use commands::{build, clean, deploy, disassemble, init, test};
 
 #[derive(Parser)]
 #[command(version, about, long_about = None)]
@@ -17,8 +17,6 @@ enum Commands {
     Init(InitArgs),
     #[command(about = "Compile into a Solana program executable")]
     Build,
-    #[command(about = "Link object file into a Solana program executable")]
-    Link(LinkArgs),
     #[command(about = "Build and deploy the program")]
     Deploy(DeployArgs),
     #[command(about = "Test deployed program")]
@@ -66,7 +64,6 @@ fn main() -> Result<(), Error> {
     match &cli.command {
         Commands::Init(args) => init(args.name.clone(), args.ts_tests),
         Commands::Build => build(),
-        Commands::Link(args) => link(&args.source.clone().unwrap_or("src".to_string())),
         Commands::Deploy(args) => deploy(args.name.clone(), args.url.clone()),
         Commands::Test => test(),
         // use arg to specify if use light build