Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ jobs:
- name: Run Rust tests
run: make test
working-directory: rust

- name: Vendor C sources for packaging
run: make vendor
working-directory: rust

- name: Test cargo package
run: cargo package --allow-dirty
working-directory: rust
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ java/.java_compiled

# Rust Build Artifacts
rust/target/
rust/vendor/
rust/Cargo.lock

# NX Monorepo
Expand Down
8 changes: 8 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ license = "MIT"
repository = "https://github.com/marcoroth/herb"
keywords = ["erb", "html", "html+erb", "ruby", "rails"]
categories = ["parser-implementations", "development-tools"]
include = [
"src/**/*",
"vendor/**/*.c",
"vendor/**/*.h",
"build.rs",
"Cargo.toml",
"README.md"
]

[lib]
name = "herb"
Expand Down
15 changes: 14 additions & 1 deletion rust/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ BIN_NAME = herb-rust
BIN_PATH = $(BUILD_DIR)/debug/$(BIN_NAME)
RELEASE_BIN_PATH = $(BUILD_DIR)/release/$(BIN_NAME)

.PHONY: all build release clean test cli help templates format
.PHONY: all build release clean test cli help templates format vendor

all: templates build

Expand Down Expand Up @@ -44,6 +44,18 @@ clean:
cargo clean
@echo "Cleaned Rust build artifacts"

vendor:
@echo "Vendoring C sources into vendor/libherb..."
rm -rf vendor/
mkdir -p vendor/libherb/
mkdir -p vendor/prism/
cp -r ../src vendor/libherb/
@echo "Vendoring prism library..."
PRISM_PATH=$$(bundle show prism) && \
cp -r "$$PRISM_PATH/src" vendor/prism/ && \
cp -r "$$PRISM_PATH/include" vendor/prism/
@echo "Vendored C sources and prism successfully"

help:
@echo "Herb Rust Build System"
@echo ""
Expand All @@ -55,6 +67,7 @@ help:
@echo " cli - Show CLI usage"
@echo " test - Run Rust tests"
@echo " format - Format Rust code with rustfmt"
@echo " vendor - Vendor C sources into vendor/"
@echo " clean - Remove build artifacts"
@echo " help - Show this help"
@echo ""
Expand Down
13 changes: 12 additions & 1 deletion rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ make all # Generate templates and build

## Usage

### CLI
### CLI (within the Herb repo)

```bash
./bin/herb-rust version
Expand Down Expand Up @@ -55,6 +55,17 @@ fn main() {
cargo test
```

## Publishing

Before publishing to crates.io, vendor the C sources:

```bash
make vendor # Vendor C sources from ../src and prism
cargo publish --allow-dirty # Publish to crates.io
```

The `vendor/` directory is gitignored to avoid committing duplicate files. The `make vendor` task copies C sources from the parent directory into `vendor/libherb` and `vendor/prism` so the published crate is self-contained.

## Cleaning

```bash
Expand Down
76 changes: 55 additions & 21 deletions rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,51 @@ use std::process::Command;

fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let root_dir = manifest_dir.parent().unwrap();
let src_dir = root_dir.join("src");
let prism_path = get_prism_path(root_dir);
let prism_include = prism_path.join("include");
let prism_build = prism_path.join("build");
let vendor_src_dir = manifest_dir.join("vendor/libherb/src");
let vendor_include_dir = manifest_dir.join("vendor/libherb/src/include");

println!("cargo:rustc-link-search=native={}", prism_build.display());
println!("cargo:rustc-link-lib=static=prism");
let (src_dir, include_dir, root_dir) = if vendor_src_dir.exists() {
(vendor_src_dir, vendor_include_dir, manifest_dir.clone())
} else {
let root = manifest_dir.parent().unwrap();
(
root.join("src"),
root.join("src/include"),
root.to_path_buf(),
)
};

let vendor_prism_src = manifest_dir.join("vendor/prism/src");
let vendor_prism_include = manifest_dir.join("vendor/prism/include");

let prism_include = if vendor_prism_src.exists() {
let mut prism_sources = Vec::new();
for path in glob::glob(vendor_prism_src.join("**/*.c").to_str().unwrap())
.unwrap()
.flatten()
{
prism_sources.push(path);
}

let mut prism_build = cc::Build::new();
prism_build
.flag("-std=c99")
.flag("-fPIC")
.opt_level(2)
.include(&vendor_prism_include)
.files(&prism_sources)
.warnings(false);

prism_build.compile("prism");

vendor_prism_include
} else {
let prism_path = get_prism_path(&root_dir);
let prism_build = prism_path.join("build");
println!("cargo:rustc-link-search=native={}", prism_build.display());
println!("cargo:rustc-link-lib=static=prism");
prism_path.join("include")
};

let mut c_sources = Vec::new();

Expand All @@ -32,22 +69,22 @@ fn main() {
.flag("-Wno-unused-parameter")
.flag("-fPIC")
.opt_level(2)
.include(src_dir.join("include"))
.include(&include_dir)
.include(&prism_include)
.files(&c_sources);

build.compile("herb");

let bindings = bindgen::Builder::default()
.header(src_dir.join("include/analyze.h").to_str().unwrap())
.header(src_dir.join("include/herb.h").to_str().unwrap())
.header(src_dir.join("include/ast_nodes.h").to_str().unwrap())
.header(src_dir.join("include/errors.h").to_str().unwrap())
.header(src_dir.join("include/element_source.h").to_str().unwrap())
.header(src_dir.join("include/token_struct.h").to_str().unwrap())
.header(src_dir.join("include/util/hb_string.h").to_str().unwrap())
.header(src_dir.join("include/util/hb_array.h").to_str().unwrap())
.clang_arg(format!("-I{}", src_dir.join("include").display()))
.header(include_dir.join("analyze.h").to_str().unwrap())
.header(include_dir.join("herb.h").to_str().unwrap())
.header(include_dir.join("ast_nodes.h").to_str().unwrap())
.header(include_dir.join("errors.h").to_str().unwrap())
.header(include_dir.join("element_source.h").to_str().unwrap())
.header(include_dir.join("token_struct.h").to_str().unwrap())
.header(include_dir.join("util/hb_string.h").to_str().unwrap())
.header(include_dir.join("util/hb_array.h").to_str().unwrap())
.clang_arg(format!("-I{}", include_dir.display()))
.clang_arg(format!("-I{}", prism_include.display()))
.allowlist_function("herb_.*")
.allowlist_function("hb_array_.*")
Expand Down Expand Up @@ -86,10 +123,7 @@ fn main() {
println!("cargo:rerun-if-changed={}", source.display());
}

println!(
"cargo:rerun-if-changed={}",
src_dir.join("include").display()
);
println!("cargo:rerun-if-changed={}", include_dir.display());
println!("cargo:rerun-if-changed=build.rs");
}

Expand Down
Loading