mod build;
mod codegen;
#[doc(hidden)]
pub mod common;
#[doc(hidden)]
pub mod catalog;
#[doc(hidden)]
pub mod optimizer;
#[doc(hidden)]
pub mod parser;
#[doc(hidden)]
pub mod planner;
#[doc(hidden)]
pub mod profiler;
#[doc(hidden)]
pub mod stratifier;
#[doc(hidden)]
pub mod typechecker;
pub use build::BuildError;
#[doc(hidden)]
pub use codegen::{
const_to_token, data_type_tokens, field_accessor, gen_drain_block, AggSemiringNeeds, CodeGen,
CodeParts, CodegenError, Features,
};
use std::io;
use std::path::{Path, PathBuf};
pub use crate::common::ExecutionMode;
use crate::common::{emit, BoxError, SourceMap};
pub fn compile<P: AsRef<Path>>(program_path: P) -> io::Result<()> {
let out_dir = cargo_out_dir()?;
let mut sm = SourceMap::new();
Builder::default()
.compile_one(program_path.as_ref(), &out_dir, &mut sm)
.map_err(|err| {
let mut buf = Vec::new();
let _ = emit(&err, &sm, &mut buf);
io::Error::other(String::from_utf8_lossy(&buf).into_owned())
})
}
#[derive(Default)]
pub struct Builder {
pub(crate) sip: bool,
pub(crate) string_intern: bool,
pub(crate) mode: ExecutionMode,
pub(crate) profile: bool,
pub(crate) include_dirs: Vec<PathBuf>,
pub(crate) udf_file: Option<PathBuf>,
}
impl Builder {
pub fn sip(mut self, enabled: bool) -> Self {
self.sip = enabled;
self
}
pub fn string_intern(mut self, enabled: bool) -> Self {
self.string_intern = enabled;
self
}
pub fn mode(mut self, mode: ExecutionMode) -> Self {
self.mode = mode;
self
}
pub fn udf_file(mut self, path: impl AsRef<Path>) -> Self {
self.udf_file = Some(path.as_ref().to_path_buf());
self
}
pub fn profile(mut self, enabled: bool) -> Self {
self.profile = enabled;
self
}
pub fn compile<P, I>(mut self, program_paths: &[P], include_dirs: &[I]) -> Result<(), BoxError>
where
P: AsRef<Path>,
I: AsRef<Path>,
{
self.include_dirs = include_dirs
.iter()
.map(|p| p.as_ref().to_path_buf())
.collect();
let out_dir = cargo_out_dir().map_err(BuildError::from)?;
for program_path in program_paths {
let mut sm = SourceMap::new();
self.compile_one(program_path.as_ref(), &out_dir, &mut sm)?;
}
Ok(())
}
fn compile_one(
&self,
program_path: &Path,
out_dir: &Path,
sm: &mut SourceMap,
) -> Result<(), BoxError> {
let stem = program_path
.file_stem()
.and_then(|s| s.to_str())
.ok_or_else(|| {
BuildError::from(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"program path has no usable file stem: {}",
program_path.display()
),
))
})?;
let output = build::Pipeline::build(self, program_path, sm)?;
let source = build::assemble(&output, out_dir).map_err(BuildError::from)?;
self.emit_semiring_modules(&output, out_dir)
.map_err(BuildError::from)?;
std::fs::write(out_dir.join(format!("{stem}.rs")), source).map_err(BuildError::from)?;
self.emit_rerun_if_changed(program_path);
Ok(())
}
fn emit_semiring_modules(&self, output: &build::Pipeline, out_dir: &Path) -> io::Result<()> {
if output.parts.semiring_modules.is_empty() {
return Ok(());
}
let semiring_dir = out_dir.join("semiring");
std::fs::create_dir_all(&semiring_dir)?;
const LIB_ALIASES: &str = "\
use ::flowlog_runtime::serde;
use ::flowlog_runtime::ordered_float;
use ::flowlog_runtime::differential_dataflow;
";
for (rel_path, content) in &output.parts.semiring_modules {
let fname = Path::new(rel_path)
.file_name()
.expect("semiring module path has no file name");
let dst = semiring_dir.join(fname);
if fname == "mod.rs" {
std::fs::write(dst, content)?;
} else {
std::fs::write(dst, format!("{LIB_ALIASES}{content}"))?;
}
}
Ok(())
}
fn emit_rerun_if_changed(&self, program_path: &Path) {
println!("cargo:rerun-if-changed={}", program_path.display());
if let Some(udf) = &self.udf_file {
println!("cargo:rerun-if-changed={}", udf.display());
}
for inc in &self.include_dirs {
println!("cargo:rerun-if-changed={}", inc.display());
}
}
}
fn cargo_out_dir() -> io::Result<PathBuf> {
std::env::var_os("OUT_DIR")
.map(PathBuf::from)
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"OUT_DIR not set — run from a build.rs",
)
})
}