Compile-Time Integration of FBP Components via build.rs, flowd.build.toml, and Generated Code
1. Purpose
This document specifies the architecture and implementation requirements for integrating Flow-Based Programming (FBP) components into the runtime at compile time, using:
flowd.build.toml (build configuration file)
build.rs (Cargo build script)
- Generated Rust source (e.g.,
build_generated.rs)
- Static linking via Cargo path dependencies or Git submodules
The goal is to eliminate dynamic plugin systems and ensure:
- Deterministic builds
- Maximum runtime performance
- Compile-time type safety
- No runtime component registration logic duplication
- No manual edits required in multiple source locations when adding/removing components
2. Problem Statement
Currently, components must be referenced in multiple places:
use imports (for component structs)
- Logging setup (
.add_filter_ignore_str(...))
ComponentLibrary::new(vec![ ... ]) metadata registration
graph.start() component instantiation match statement
This creates duplication, fragility, and human error risk.
We require:
- A single source of truth (
flowd.build.toml)
- Automatic code generation during build
- No manual source edits when components change
- Support for per-component log ignore configuration
3. High-Level Design
3.1 Design Principles
-
Components are statically compiled.
-
build.rs reads flowd.build.toml.
-
A generated Rust file (build_generated.rs) contains:
- All necessary
use statements
- Logging filter setup
- Component metadata registration
- Component factory mapping
-
main.rs includes this file using include!().
No string-based source code patching is allowed.
4. Build Configuration File
4.1 File Name
4.2 Structure
Example:
[components]
[[components.entry]]
name = "Repeat"
crate = "repeat_component"
struct = "RepeatComponent"
log_ignore = ["hyper::proto::h1", "tower::buffer"]
[[components.entry]]
name = "Drop"
crate = "drop_component"
struct = "DropComponent"
log_ignore = []
4.3 Fields
| Field |
Type |
Required |
Description |
| name |
string |
yes |
Graph-visible component name |
| crate |
string |
yes |
Rust crate name |
| struct |
string |
yes |
Rust struct implementing the component |
| log_ignore |
array[string] |
no |
List of log prefixes to ignore |
5. build.rs Responsibilities
build.rs must:
- Read
flowd.build.toml
- Parse all component entries
- Generate a file:
$OUT_DIR/build_generated.rs
-
The generated file must contain:
- All required
use statements
- A function to register logging filters
- A function to build the component metadata library
- A factory function mapping component names to constructors
-
Fail compilation if configuration is invalid.
6. Generated File Contract
6.1 Generated File Name
6.2 Must Contain
6.2.1 Imports
use repeat_component::RepeatComponent;
use drop_component::DropComponent;
6.2.2 Logging Setup Function
pub fn register_component_log_filters(logger: &mut Logger) {
logger.add_filter_ignore_str("hyper::proto::h1");
logger.add_filter_ignore_str("tower::buffer");
}
Only include entries defined in config.
6.2.3 Component Library Builder
pub fn build_component_library() -> Arc<RwLock<ComponentLibrary>> {
Arc::new(RwLock::new(ComponentLibrary::new(vec![
RepeatComponent::get_metadata(),
DropComponent::get_metadata(),
])))
}
6.2.4 Component Factory
pub fn instantiate_component(name: &str, args: ComponentArgs) -> Option<Box<dyn FbpComponent>> {
match name {
"Repeat" => Some(Box::new(RepeatComponent::new(args))),
"Drop" => Some(Box::new(DropComponent::new(args))),
_ => None,
}
}
This replaces manual match component_name.as_str() logic.
7. main.rs Integration
main.rs must include:
include!(concat!(env!("OUT_DIR"), "/build_generated.rs"));
And replace manual component logic with:
register_component_log_filters(&mut logger);
let componentlib = build_component_library();
if let Some(component) = instantiate_component(component_name, args) {
component.run();
}
No manual match statements allowed.
8. Why Generated Functions Instead of Inline Code Injection
Alternative Considered:
- Injecting lines into
main.rs after markers
Rejected Because:
- Fragile
- Breaks formatting
- Hard to maintain
- Not Rust-idiomatic
- Violates build determinism
Decision:
Use generated functions in a generated module.
9. Cargo & Component Source Management
Components must be available via:
- Git submodules (preferred)
- Or Cargo path dependencies
Example:
[dependencies]
repeat_component = { path = "components/repeat_component" }
drop_component = { path = "components/drop_component" }
Build script does NOT clone repositories.
10. Error Handling Requirements
The build must fail if:
- A component name is duplicated
- A struct name is missing
- The config is malformed
- A crate cannot be resolved
Use panic!() in build.rs to abort build.
11. Non-Goals
- Runtime dynamic loading
- Partial reload of components
- Plugin ABI systems
- Source patching
12. Resulting Guarantees
After implementation:
-
Adding a component requires:
- Add Git submodule
- Add Cargo dependency
- Add entry in
flowd.build.toml
-
No source code edits required
-
Build is deterministic
-
Component registry and factory are generated
-
Logging suppression is declarative
13. Architectural Rationale
Compile-time integration ensures:
- Zero runtime indirection
- No plugin ABI instability
- Full type safety
- Maximum optimization by Rust compiler
- Static graph of available components
Dynamic systems were rejected to preserve performance and simplicity.
14. Future Extension Points
Optional future enhancements may include:
- Feature flags per component
- Conditional compilation targets
- Component capability validation
- Version pinning metadata
Implementation plan
Resulting Architecture After All Tasks
- Components defined only in:
flowd.build.toml
Cargo.toml
- All runtime wiring auto-generated
- Deterministic, compile-time integrated system
- No plugin system
- No hard-coded registration logic
Compile-Time Integration of FBP Components via
build.rs,flowd.build.toml, and Generated Code1. Purpose
This document specifies the architecture and implementation requirements for integrating Flow-Based Programming (FBP) components into the runtime at compile time, using:
flowd.build.toml(build configuration file)build.rs(Cargo build script)build_generated.rs)The goal is to eliminate dynamic plugin systems and ensure:
2. Problem Statement
Currently, components must be referenced in multiple places:
useimports (for component structs).add_filter_ignore_str(...))ComponentLibrary::new(vec![ ... ])metadata registrationgraph.start()component instantiation match statementThis creates duplication, fragility, and human error risk.
We require:
flowd.build.toml)3. High-Level Design
3.1 Design Principles
Components are statically compiled.
build.rsreadsflowd.build.toml.A generated Rust file (
build_generated.rs) contains:usestatementsmain.rsincludes this file usinginclude!().No string-based source code patching is allowed.
4. Build Configuration File
4.1 File Name
4.2 Structure
Example:
4.3 Fields
5. build.rs Responsibilities
build.rsmust:flowd.build.tomlThe generated file must contain:
usestatementsFail compilation if configuration is invalid.
6. Generated File Contract
6.1 Generated File Name
6.2 Must Contain
6.2.1 Imports
6.2.2 Logging Setup Function
Only include entries defined in config.
6.2.3 Component Library Builder
6.2.4 Component Factory
This replaces manual
match component_name.as_str()logic.7. main.rs Integration
main.rsmust include:And replace manual component logic with:
No manual match statements allowed.
8. Why Generated Functions Instead of Inline Code Injection
Alternative Considered:
main.rsafter markersRejected Because:
Decision:
Use generated functions in a generated module.
9. Cargo & Component Source Management
Components must be available via:
Example:
Build script does NOT clone repositories.
10. Error Handling Requirements
The build must fail if:
Use
panic!()inbuild.rsto abort build.11. Non-Goals
12. Resulting Guarantees
After implementation:
Adding a component requires:
flowd.build.tomlNo source code edits required
Build is deterministic
Component registry and factory are generated
Logging suppression is declarative
13. Architectural Rationale
Compile-time integration ensures:
Dynamic systems were rejected to preserve performance and simplicity.
14. Future Extension Points
Optional future enhancements may include:
Implementation plan
Resulting Architecture After All Tasks
flowd.build.tomlCargo.toml