Skip to content

Commit

Permalink
Out of source rust build
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminwinger committed Jun 29, 2023
1 parent 2638b8b commit e7eb7cb
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 98 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ ifeq ($(OS),Windows_NT)
cargo test -- --test-threads=1
else
cd $(ROOT_DIR)/tools/rust_api && \
KUZU_TESTING=1 cargo test -- --test-threads=1
CARGO_BUILD_JOBS=$(NUM_THREADS) KUZU_TESTING=1 cargo test -- --test-threads=1
endif

clean-python-api:
Expand Down
28 changes: 28 additions & 0 deletions examples/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tools/rust_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ time = "0.3"
[build-dependencies]
cxx-build = "1.0"
num_cpus = "1.0"
cmake = "0.1"
[target.'cfg(windows)'.build-dependencies]
which = "4"

Expand Down
211 changes: 114 additions & 97 deletions tools/rust_api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,82 +9,50 @@ fn link_mode() -> &'static str {
}
}

fn build_bundled() -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
// There is a kuzu-src symlink pointing to the root of the repo since Cargo
// only looks at the files within the rust project when packaging crates.
// Using a symlink the library can both be built in-source and from a crate.
let kuzu_root = {
let root = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("kuzu-src");
if root.is_dir() {
root
fn find_openssl_windows() {
// Find openssl library relative to the path of the openssl executable
// Or fall back to OPENSSL_DIR
#[cfg(windows)]
{
let openssl_dir = if let Ok(mut path) = which::which("openssl") {
path.pop();
path.pop();
path
} else if let Ok(path) = env::var("OPENSSL_CONF") {
Path::new(&path)
.parent()
.unwrap()
.parent()
.unwrap()
.to_path_buf()
} else if let Ok(path) = env::var("OPENSSL_DIR") {
Path::new(&path).to_path_buf()
} else {
// If the path is not directory, this is probably an in-source build on windows where the
// symlink is unreadable.
Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("../..")
}
};
// Windows fails to link on windows unless CFLAGS and CXXFLAGS are overridden
// If they are, assume the user knows what they are doing. Otherwise just link against the
// release version of kuzu even in debug mode.
let target = if cfg!(windows) && std::env::var("CXXFLAGS").is_err() {
panic!(
"OPENSSL_DIR must be set if the openssl library cannot be found \
using the path of the openssl executable"
)
};
println!(
"cargo:rustc-link-search=native={}/lib",
openssl_dir.display()
);
}
}

fn get_target() -> String {
if cfg!(windows) && std::env::var("CXXFLAGS").is_err() {
"release".to_string()
} else {
env::var("PROFILE").unwrap()
};
let kuzu_cmake_root = kuzu_root.join(format!("build/{target}"));
let mut command = std::process::Command::new("make");
let threads = env::var("NUM_THREADS")
.map(|x| x.parse::<usize>())
.unwrap_or_else(|_| Ok(num_cpus::get()))
.unwrap();
command
.args(&[&target, &format!("NUM_THREADS={}", threads)])
.current_dir(&kuzu_root);
let make_status = command.status().unwrap_or_else(|_| {
panic!(
"Running make {} on {}/Makefile failed!",
&target,
&kuzu_root.display()
)
});
assert!(make_status.success());

let kuzu_lib_path = kuzu_cmake_root.join("src");

println!("cargo:rustc-link-search=native={}", kuzu_lib_path.display());

let include_paths = vec![
kuzu_root.join("src/include"),
kuzu_root.join("third_party/nlohmann_json"),
kuzu_root.join("third_party/spdlog"),
];
for dir in ["utf8proc", "antlr4_cypher", "antlr4_runtime", "re2"] {
let lib_path = kuzu_cmake_root
.join(format!("third_party/{dir}"))
.canonicalize()
.unwrap_or_else(|_| {
panic!(
"Could not find {}/third_party/{dir}",
kuzu_cmake_root.display()
)
});
println!("cargo:rustc-link-search=native={}", lib_path.display());
}
}

let arrow_install = kuzu_root.join("external/build/arrow/install");
println!(
"cargo:rustc-link-search=native={}",
arrow_install.join("lib").display()
);
println!(
"cargo:rustc-link-search=native={}",
arrow_install.join("lib64").display()
);

fn link_libraries() {
println!("cargo:rustc-link-lib={}=kuzu", link_mode());
if link_mode() == "static" {
if cfg!(windows) {
if target == "debug" {
if get_target() == "debug" {
println!("cargo:rustc-link-lib=dylib=msvcrtd");
} else {
println!("cargo:rustc-link-lib=dylib=msvcrt");
Expand All @@ -101,34 +69,7 @@ fn build_bundled() -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
// Only seems to be necessary when building tests.
if env::var("KUZU_TESTING").is_ok() {
if cfg!(windows) {
// Find openssl library relative to the path of the openssl executable
// Or fall back to OPENSSL_DIR
#[cfg(windows)]
{
let openssl_dir = if let Ok(mut path) = which::which("openssl") {
path.pop();
path.pop();
path
} else if let Ok(path) = env::var("OPENSSL_CONF") {
Path::new(&path)
.parent()
.unwrap()
.parent()
.unwrap()
.to_path_buf()
} else if let Ok(path) = env::var("OPENSSL_DIR") {
Path::new(&path).to_path_buf()
} else {
panic!(
"OPENSSL_DIR must be set if the openssl library cannot be found \
using the path of the openssl executable"
)
};
println!(
"cargo:rustc-link-search=native={}/lib",
openssl_dir.display()
);
}
find_openssl_windows();
println!("cargo:rustc-link-lib=dylib=libssl");
println!("cargo:rustc-link-lib=dylib=libcrypto");
} else {
Expand All @@ -150,7 +91,81 @@ fn build_bundled() -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
println!("cargo:rustc-link-lib=static=antlr4_runtime");
println!("cargo:rustc-link-lib=static=re2");
}
Ok(include_paths)
}

fn build_bundled_cmake() -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
if let Ok(jobs) = std::env::var("NUM_THREADS") {
std::env::set_var("NUM_JOBS", jobs);
}
let kuzu_root = {
let root = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("kuzu-src");
if root.is_symlink() || root.is_dir() {
root
} else {
// If the path is not directory, this is probably an in-source build on windows where the
// symlink is unreadable.
Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("../..")
}
};
let mut arrow_build = cmake::Config::new(kuzu_root.join("external"));
arrow_build
.no_build_target(true)
// Needs separate out directory so they don't clobber each other
.out_dir(Path::new(&env::var("OUT_DIR").unwrap()).join("build-arrow"));

if cfg!(windows) {
arrow_build.generator("Ninja");
arrow_build.cxxflag("/EHsc");
}
let arrow_build_dir = arrow_build.build();

let arrow_install = arrow_build_dir.join("build/arrow/install");
println!(
"cargo:rustc-link-search=native={}",
arrow_install.join("lib").display()
);
println!(
"cargo:rustc-link-search=native={}",
arrow_install.join("lib64").display()
);

let mut build = cmake::Config::new(&kuzu_root);
build
.no_build_target(true)
.define("BUILD_SHELL", "OFF")
.define("BUILD_PYTHON_API", "OFF")
.define("ARROW_INSTALL", &arrow_install);
if cfg!(windows) {
build.generator("Ninja");
build.cxxflag("/EHsc");
}
let build_dir = build.build();

let kuzu_lib_path = build_dir.join("build").join("src");
println!("cargo:rustc-link-search=native={}", kuzu_lib_path.display());

for dir in ["utf8proc", "antlr4_cypher", "antlr4_runtime", "re2"] {
let lib_path = build_dir
.join("build")
.join("third_party")
.join(dir)
.canonicalize()
.unwrap_or_else(|_| {
panic!(
"Could not find {}/build/third_party/{}",
build_dir.display(),
dir
)
});
println!("cargo:rustc-link-search=native={}", lib_path.display());
}

Ok(vec![
kuzu_root.join("src/include"),
kuzu_root.join("third_party/nlohmann_json"),
kuzu_root.join("third_party/spdlog"),
arrow_install.join("include"),
])
}

fn main() {
Expand All @@ -171,11 +186,13 @@ fn main() {
}
include_paths.push(Path::new(&kuzu_include).to_path_buf());
} else {
include_paths.extend(build_bundled().expect("Bundled build failed!"));
include_paths.extend(build_bundled_cmake().expect("Bundled build failed!"));
build.define("KUZU_BUNDLED", None);
}
build.includes(include_paths);

link_libraries();

println!("cargo:rerun-if-env-changed=KUZU_SHARED");

println!("cargo:rerun-if-changed=include/kuzu_rs.h");
Expand Down

0 comments on commit e7eb7cb

Please sign in to comment.