1
0
mirror of https://github.com/TREX-CoE/trexio.git synced 2025-04-16 13:49:19 +02:00

Removing need to compile C library

This commit is contained in:
Anthony Scemama 2024-12-29 22:27:31 +01:00
parent df0e3a8e1c
commit 52eae38230
3 changed files with 154 additions and 129 deletions

View File

@ -8,19 +8,17 @@ description = "TREXIO is an open-source file format and library developed for th
repository = "https://github.com/trex-coe/trexio"
keywords = ["quantum", "chemistry"]
readme = "README.md"
links = "trexio"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
pkg-config = "0.3.31"
bindgen = "0.65.1"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.11", features = ["blocking", "rustls-tls"] }
tar = "0.4"
flate2 = "1.0"
[lib]
doctest = false
[dependencies]
hdf5 = "0.8.1"

View File

@ -1,7 +1,3 @@
extern crate reqwest;
extern crate tar;
extern crate flate2;
const WRAPPER_H: &str = "wrapper.h";
const GENERATED_RS: &str = "generated.rs";
@ -11,60 +7,52 @@ use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use serde_json::Value;
use flate2::read::GzDecoder;
use tar::Archive;
use pkg_config::Config;
/// The header path will be searched in the following order:
///
/// 1. pkg-config
/// 2. Environment variable TREXIO_INCLUDE_DIR
/// 3. Common system paths
///
/// If the header is found, the JSON configuration will be extracted and
/// written to trex.json in the output directory
fn find_header_path() -> Option<PathBuf> {
// First try pkg-config
if let Ok(lib) = Config::new().probe("trexio") {
// pkg-config returns include paths, we need to append trexio.h
for include_path in lib.include_paths {
let header_path = include_path.join("trexio.h");
if header_path.exists() {
return Some(header_path);
}
}
}
fn download_trexio() -> PathBuf {
let version = env::var("CARGO_PKG_VERSION").unwrap();
println!("Version : {}", version);
// Try environment variable next
if let Ok(dir) = env::var("TREXIO_INCLUDE_DIR") {
let path = PathBuf::from(dir).join("trexio.h");
if path.exists() {
return Some(path);
}
}
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let trexio_url = format!("https://github.com/TREX-CoE/trexio/releases/download/v{version}/trexio-{version}.tar.gz");
// Finally check common system paths
let possible_paths = vec![
"/usr/include/trexio.h",
"/usr/local/include/trexio.h",
"/opt/local/include/trexio.h",
];
// Download the .tar.gz archive
let tar_gz = out_path.join("trexio.tar.gz");
let trexio_dir= out_path.join("trexio_dir");
let mut resp = reqwest::blocking::get(trexio_url).expect("Failed to download the archive");
let mut out = File::create(tar_gz.clone()).expect("Failed to create archive file");
std::io::copy(&mut resp, &mut out).expect("Failed to copy content");
for path in possible_paths {
let p = PathBuf::from(path);
if p.exists() {
return Some(p);
}
}
// Unpack the .tar.gz archive
let tar_gz = File::open(tar_gz).unwrap();
let tar = GzDecoder::new(tar_gz);
let mut archive = Archive::new(tar);
archive.unpack(trexio_dir.clone()).expect("Failed to unpack");
// Assume that the archive extracts to a directory named 'trexio-0.1.0'
trexio_dir.join(format!("trexio-{}", version))
}
fn install_trexio(trexio_dir: &PathBuf) -> PathBuf {
println!("{}", trexio_dir.display());
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let install_path = out_path.join("trexio_install");
// Run configure script
let configure_status = std::process::Command::new("./configure")
.arg(format!("--prefix={}",install_path.display()))
.arg("--without-fortran")
.current_dir(&trexio_dir)
.status()
.unwrap();
assert!(configure_status.success());
// Run make
let make_status = std::process::Command::new("make")
.arg("install")
.current_dir(&trexio_dir)
.status()
.unwrap();
assert!(make_status.success());
install_path
None
}
@ -617,12 +605,14 @@ pub fn write_{group_l}_{element_l}(&self, offset: usize, data: &[{typ}]) -> Resu
}}
let size_max: i64 = data.len().try_into().expect("try_into failed in write_{group}_{element} (size_max)");
let buffer_size = size_max;
let size_max_val: i64 = size_max / ({size}+1);
let size_max_idx: i64 = {size} * size_max_val;
let buffer_size = size_max_val;
let idx_ptr = idx.as_ptr() as *const i32;
let val_ptr = val.as_ptr() as *const f64;
let offset: i64 = offset.try_into().expect("try_into failed in write_{group}_{element} (offset)");
let rc = unsafe {{ c::trexio_write_safe_{group}_{element}(self.ptr,
offset, buffer_size, idx_ptr, size_max, val_ptr, size_max) }};
offset, buffer_size, idx_ptr, size_max_idx, val_ptr, size_max_val) }};
rc_return((), rc)
}}"#));
},
@ -637,6 +627,48 @@ pub fn write_{group_l}_{element_l}(&self, offset: usize, data: &[{typ}]) -> Resu
fn extract_json(trexio_h: &PathBuf) -> io::Result<()> {
// Read the header file
let file = File::open(&trexio_h)?;
let reader = BufReader::new(file);
// Extract JSON configuration
let mut in_json = false;
let mut json_content = String::new();
for line in reader.lines() {
let line = line?;
if line.contains("/* JSON configuration") {
in_json = true;
continue;
}
if in_json {
if line.contains("*/") {
break;
}
json_content.push_str(&line);
json_content.push('\n');
}
}
// Write JSON to output file
let out_dir = env::var("OUT_DIR").unwrap();
let json_path = PathBuf::from(out_dir).join("trex.json");
let mut output_file = File::create(json_path)?;
output_file.write_all(json_content.as_bytes())?;
// Tell cargo to rerun if the header file changes
println!("cargo:rerun-if-changed={}", trexio_h.display());
// Rerun if relevant env vars change
println!("cargo:rerun-if-env-changed=TREXIO_INCLUDE_DIR");
println!("cargo:rerun-if-env-changed=PKG_CONFIG_PATH");
Ok(())
}
@ -678,24 +710,18 @@ impl File {
fn main() {
let source_path = download_trexio();
println!("source path: {}", source_path.display());
fn main() -> Result<(), Box<dyn std::error::Error>> {
let trexio_h = find_header_path()
.ok_or("Could not find trexio.h - please ensure trexio is installed and findable via pkg-config, TREXIO_INCLUDE_DIR, or in system paths")?;
let install_path = install_trexio(&source_path);
println!("install path: {}", install_path.display());
// Print some helpful information during build
println!("cargo:warning=Found trexio.h at: {}", trexio_h.display());
// Tell cargo to look for shared libraries in the specified directory
println!("cargo:rustc-link-search={}/lib", install_path.display());
let out_path = PathBuf::from(env::var("OUT_DIR")?);
// Tell cargo to tell rustc to link the system trexio shared library.
println!("cargo:rustc-link-lib=trexio");
make_interface(&trexio_h)?;
extract_json(&trexio_h)?;
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let trexio_h = install_path.join("include").join("trexio.h");
println!("trexio.h: {}", trexio_h.display());
make_interface(&trexio_h).unwrap();
// The bindgen::Builder is the main entry point
// to bindgen, and lets you build up options for
// the resulting bindings.
@ -722,8 +748,9 @@ fn main() {
.write_to_file(&bindings_path)
.expect("Couldn't write bindings!");
let json_path = source_path.join("trex.json");
let json_path = out_path.join("trex.json");
println!("json path: {}", json_path.display());
make_functions(&json_path).unwrap();
Ok(())
}

View File

@ -1,7 +1,7 @@
use trexio::back_end::BackEnd;
use trexio::bitfield::Bitfield;
fn write(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
fn write(file_name: &str, back_end: BackEnd) {
// Prepare data to be written
let nucleus_num = 12;
@ -34,32 +34,32 @@ fn write(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
let sym_str = "B3U with some comments";
println!("Write {}", file_name);
assert!(!trexio::File::inquire(file_name)?);
assert!(!trexio::File::inquire(file_name).unwrap());
let trex_file = trexio::File::open(file_name, 'w', back_end)?;
let trex_file = trexio::File::open(file_name, 'w', back_end).unwrap();
assert!(!trex_file.has_nucleus()?);
assert!(!trex_file.has_nucleus_num()?);
assert!(!trex_file.has_nucleus_charge()?);
assert!(!trex_file.has_ao_2e_int()?);
assert!(!trex_file.has_ao_2e_int_eri()?);
assert!(!trex_file.has_determinant_list()?);
assert!(!trex_file.has_nucleus().unwrap());
assert!(!trex_file.has_nucleus_num().unwrap());
assert!(!trex_file.has_nucleus_charge().unwrap());
assert!(!trex_file.has_ao_2e_int().unwrap());
assert!(!trex_file.has_ao_2e_int_eri().unwrap());
assert!(!trex_file.has_determinant_list().unwrap());
trex_file.write_nucleus_num(nucleus_num)?;
trex_file.write_nucleus_charge(&charge)?;
trex_file.write_nucleus_point_group(sym_str)?;
trex_file.write_nucleus_coord(&flat_coord)?;
trex_file.write_nucleus_label(&label)?;
trex_file.write_basis_shell_num(basis_shell_num)?;
trex_file.write_basis_nucleus_index(&basis_nucleus_index)?;
trex_file.write_state_id(state_id)?;
trex_file.write_nucleus_num(nucleus_num).unwrap();
trex_file.write_nucleus_charge(&charge).unwrap();
trex_file.write_nucleus_point_group(sym_str).unwrap();
trex_file.write_nucleus_coord(&flat_coord).unwrap();
trex_file.write_nucleus_label(&label).unwrap();
trex_file.write_basis_shell_num(basis_shell_num).unwrap();
trex_file.write_basis_nucleus_index(&basis_nucleus_index).unwrap();
trex_file.write_state_id(state_id).unwrap();
if !trex_file.has_ao_num()? {
trex_file.write_ao_num(ao_num)?;
if !trex_file.has_ao_num().unwrap() {
trex_file.write_ao_num(ao_num).unwrap();
}
if !trex_file.has_mo_num()? {
trex_file.write_mo_num(mo_num)?;
if !trex_file.has_mo_num().unwrap() {
trex_file.write_mo_num(mo_num).unwrap();
}
let mut energy = Vec::with_capacity(mo_num);
@ -67,13 +67,13 @@ fn write(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
let e: f64 = i as f64 - 100.0f64;
energy.push(e);
}
trex_file.write_mo_energy(&energy)?;
trex_file.write_mo_energy(&energy).unwrap();
let mut spin = vec![0; mo_num];
for i in mo_num / 2..mo_num {
spin[i] = 1;
}
trex_file.write_mo_spin(&spin)?;
trex_file.write_mo_spin(&spin).unwrap();
// Integrals
let nmax = 100;
@ -82,7 +82,7 @@ fn write(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
let n_buffers = 5;
let bufsize = nmax / n_buffers;
for i in 0..100 {
for i in 0..nmax {
// Quadruplet of indices + value
let data = (4 * i, 4 * i + 1, 4 * i + 2, 4 * i + 3, 3.14 + (i as f64));
ao_2e_int_eri.push(data);
@ -90,7 +90,7 @@ fn write(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
let mut offset = 0;
for _ in 0..n_buffers {
trex_file.write_ao_2e_int_eri(offset, &ao_2e_int_eri[offset..offset + bufsize])?;
trex_file.write_ao_2e_int_eri(offset, &ao_2e_int_eri[offset..offset + bufsize]).unwrap();
offset += bufsize;
}
@ -109,39 +109,39 @@ fn write(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
let bufsize = 50 / n_buffers;
let mut offset = 0;
for _ in 0..n_buffers {
trex_file.write_determinant_list(offset, &det_list[offset..offset + bufsize])?;
trex_file.write_determinant_list(offset, &det_list[offset..offset + bufsize]).unwrap();
offset += bufsize;
}
trex_file.close()
trex_file.close().unwrap()
}
fn read(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
fn read(file_name: &str, back_end: BackEnd) {
println!("Read {}", file_name);
assert!(trexio::File::inquire(file_name)?);
assert!(trexio::File::inquire(file_name).unwrap());
let trex_file = trexio::File::open(file_name, 'r', back_end)?;
let trex_file = trexio::File::open(file_name, 'r', back_end).unwrap();
assert!(trex_file.has_nucleus()?);
assert!(trex_file.has_nucleus_num()?);
assert!(trex_file.has_nucleus_charge()?);
assert!(trex_file.has_ao_2e_int()?);
assert!(trex_file.has_ao_2e_int_eri()?);
assert!(trex_file.has_determinant_list()?);
assert!(trex_file.has_nucleus().unwrap());
assert!(trex_file.has_nucleus_num().unwrap());
assert!(trex_file.has_nucleus_charge().unwrap());
assert!(trex_file.has_ao_2e_int().unwrap());
assert!(trex_file.has_ao_2e_int_eri().unwrap());
assert!(trex_file.has_determinant_list().unwrap());
let nucleus_num = trex_file.read_nucleus_num()?;
let nucleus_num = trex_file.read_nucleus_num().unwrap();
assert_eq!(nucleus_num, 12);
let sym_str = trex_file.read_nucleus_point_group(64)?;
let sym_str = trex_file.read_nucleus_point_group(64).unwrap();
assert_eq!(sym_str, "B3U with some comments");
let charge = trex_file.read_nucleus_charge()?;
let charge = trex_file.read_nucleus_charge().unwrap();
assert_eq!(
charge,
vec![6., 6., 6., 6., 6., 6., 1., 1., 1., 1., 1., 1.0f64]
);
let coord = trex_file.read_nucleus_coord()?;
let coord = trex_file.read_nucleus_coord().unwrap();
assert_eq!(
coord,
vec![
@ -184,26 +184,26 @@ fn read(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
]
);
let label = trex_file.read_nucleus_label(6)?;
let label = trex_file.read_nucleus_label(6).unwrap();
assert_eq!(
label,
vec!["C", "Na", "C", "C 66", "C", "C", "H 99", "Ru", "H", "H", "H", "H"]
);
let basis_shell_num = trex_file.read_basis_shell_num()?;
let basis_shell_num = trex_file.read_basis_shell_num().unwrap();
assert_eq!(basis_shell_num, 24);
let basis_nucleus_index = trex_file.read_basis_nucleus_index()?;
let basis_nucleus_index = trex_file.read_basis_nucleus_index().unwrap();
let ref_val: Vec<usize> = (0..24).collect();
assert_eq!(basis_nucleus_index, ref_val);
let state_id = trex_file.read_state_id()?;
let state_id = trex_file.read_state_id().unwrap();
assert_eq!(state_id, 2);
let ao_num = trex_file.read_ao_num()?;
let ao_num = trex_file.read_ao_num().unwrap();
assert_eq!(ao_num, 1000);
let mo_num = trex_file.read_mo_num()?;
let mo_num = trex_file.read_mo_num().unwrap();
assert_eq!(mo_num, 150);
let mut energy_ref = Vec::with_capacity(mo_num);
@ -211,14 +211,14 @@ fn read(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
let e: f64 = i as f64 - 100.0f64;
energy_ref.push(e);
}
let energy = trex_file.read_mo_energy()?;
let energy = trex_file.read_mo_energy().unwrap();
assert_eq!(energy, energy_ref);
let mut spin_ref = vec![0; mo_num];
for i in mo_num / 2..mo_num {
spin_ref[i] = 1;
}
let spin = trex_file.read_mo_spin()?;
let spin = trex_file.read_mo_spin().unwrap();
assert_eq!(spin, spin_ref);
// Integrals
@ -237,14 +237,14 @@ fn read(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
let mut offset = 0;
let mut ao_2e_int_eri = Vec::<(usize, usize, usize, usize, f64)>::with_capacity(nmax);
for _ in 0..n_buffers {
let buffer = trex_file.read_ao_2e_int_eri(offset, bufsize)?;
let buffer = trex_file.read_ao_2e_int_eri(offset, bufsize).unwrap();
offset += buffer.len();
ao_2e_int_eri.extend(buffer);
}
assert_eq!(ao_2e_int_eri_ref, ao_2e_int_eri);
// Determinants
let det_num = trex_file.read_determinant_num()?;
let det_num = trex_file.read_determinant_num().unwrap();
assert_eq!(det_num, 50);
let mut det_list_ref = Vec::with_capacity(det_num);
@ -261,13 +261,13 @@ fn read(file_name: &str, back_end: BackEnd) -> Result<(), trexio::ExitCode> {
let mut offset = 0;
let mut det_list: Vec<Bitfield> = Vec::with_capacity(det_num);
for _ in 0..n_buffers {
let buffer = trex_file.read_determinant_list(offset, bufsize)?;
let buffer = trex_file.read_determinant_list(offset, bufsize).unwrap();
offset += buffer.len();
det_list.extend(buffer);
}
assert_eq!(det_list_ref, det_list);
trex_file.close()
trex_file.close().unwrap()
}
#[test]
@ -279,14 +279,14 @@ use std::fs;
#[test]
pub fn text_backend() {
let _ = write("tmp/test_write.dir", trexio::BackEnd::Text).unwrap();
let _ = read("tmp/test_write.dir", trexio::BackEnd::Text).unwrap();
let _ = write("tmp/test_write.dir", trexio::BackEnd::Text);
let _ = read("tmp/test_write.dir", trexio::BackEnd::Text);
fs::remove_dir_all("tmp/test_write.dir").unwrap()
}
#[test]
pub fn hdf5_backend() {
let _ = write("tmp/test_write.hdf5", trexio::BackEnd::Hdf5).unwrap();
let _ = read("tmp/test_write.hdf5", trexio::BackEnd::Hdf5).unwrap();
let _ = write("tmp/test_write.hdf5", trexio::BackEnd::Hdf5);
let _ = read("tmp/test_write.hdf5", trexio::BackEnd::Hdf5);
fs::remove_file("tmp/test_write.hdf5").unwrap()
}