Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce build.rs to reduce boilerplate #1253

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
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
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ you for an overview of our simplified source tree:
│ │ └── more_pure_rust_code.rs
```

#### Module exports in `lib.rs`
#### Exporting new functions

To add new functions, you will need to export them in `lib.rs`. `lib.rs` will
import functions defined in Rust modules (see the next section), and export
them to Python using `m.add_wrapped(wrap_pyfunction!(your_new_function))?;`
To add new functions, you will need to export them in the
`export_rustworkx_functions!` statement in the Rust file you are editing.
If the function is not added to that statement, the Rust compiler
will complain about dead-code and Python will not find the function.

#### Adding and changing functions in modules

Expand Down Expand Up @@ -66,7 +67,6 @@ pub fn your_new_function(
> __NOTE:__ If you create a new `your_module.rs`, remember to declare and import it in `lib.rs`:
> ```rust
> mod your_module;
> use your_module::*;
> ```

#### Module directories: when a single file is not enough
Expand Down
93 changes: 93 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::collections::BTreeSet;
use std::env;
use std::fs;
use std::io::Read;
use std::io::Write;
use std::path;

fn main() {
let src_dir_path = env::var("CARGO_MANIFEST_DIR").unwrap();
let src_dir_path = format!("{}/src/", src_dir_path);
let out_dir = fs::read_dir(src_dir_path).expect("could not read src/ directory");

let mut modules = BTreeSet::new();
for entry in out_dir {
let entry = entry.expect("could not read entry");
let path = entry.path();
// Top-level .rs files
if path.is_file() {
let file_name = path.to_str().unwrap();
if file_name.ends_with(".rs") && file_name != "lib.rs" {
modules.insert(file_name.to_string());
}
}
if path.is_dir() {
let sub_dir = fs::read_dir(path).expect("could not read subdirectory");
for entry in sub_dir {
let entry = entry.expect("could not read entry");
let path = entry.path();
if path.is_file() {
let file_name = path.to_str().unwrap();
if file_name.ends_with("mod.rs") {
let module_name = file_name.to_string();
modules.insert(module_name);
}
}
}
}
}

// Create the generated file with the modules
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = path::PathBuf::from(out_dir).join("generated_include_rustworkx_modules.rs");

// Create the file and write the contents to it
let mut f = fs::File::create(dest_path).unwrap();

let mut rustworkx_modules = BTreeSet::new();

for path in modules {
let mut file = fs::File::open(path.clone())
.expect("could not open file to check if it declares a rustworkx module");
let mut content = String::new();
file.read_to_string(&mut content)
.expect("could not read contents of the file");
if content.contains("export_rustworkx_functions!") {
rustworkx_modules.insert(module_name_from_file_name(path.clone()));
}
}

writeln!(f, "fn register_rustworkx_everything(m: &pyo3::Bound<pyo3::types::PyModule>) -> pyo3::prelude::PyResult<()>").expect("could not write function signature");
writeln!(f, "{{").expect("could not write function body");

for module in rustworkx_modules {
writeln!(f, "{}::register_rustworkx_functions(m)?;", module.clone())
.expect("could not write to file");
}
writeln!(f, "Ok(())").expect("could not write function body");
writeln!(f, "}}").expect("could not write function body");
}

fn module_name_from_file_name(filename: String) -> String {
if filename.ends_with("mod.rs") {
let parent = path::Path::new(&filename)
.parent()
.expect("could not get parent directory");
let module_name = parent
.file_name()
.expect("could not get file name")
.to_str()
.expect("could not convert to string");
return module_name.to_string();
}
let module_name = path::Path::new(&filename)
.file_name()
.expect("could not get file name")
.to_str()
.expect("could not convert to string");
return module_name
.to_string()
.strip_suffix(".rs")
.expect("could not strip suffix")
.to_string();
}
4 changes: 3 additions & 1 deletion src/bisimulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ use hashbrown::hash_map::Entry;
use hashbrown::{HashMap, HashSet};

use crate::iterators::{IndexPartitionBlock, RelationalCoarsestPartition};
use crate::{digraph, Directed, StablePyGraph};
use crate::{digraph, export_rustworkx_functions, Directed, StablePyGraph};
use petgraph::graph;
use petgraph::Direction::{Incoming, Outgoing};

export_rustworkx_functions!(digraph_maximum_bisimulation);

type Block = Vec<graph::NodeIndex>;
type Counterimage = HashMap<graph::NodeIndex, Vec<graph::NodeIndex>>;
type NodeToBlockVec = Vec<Rc<RefCell<FineBlock>>>;
Expand Down
4 changes: 3 additions & 1 deletion src/cartesian_product.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// under the License.

use crate::iterators::ProductNodeMap;
use crate::{digraph, graph, StablePyGraph};
use crate::{digraph, export_rustworkx_functions, graph, StablePyGraph};

use hashbrown::HashMap;

Expand All @@ -21,6 +21,8 @@ use petgraph::{algo, EdgeType};
use pyo3::prelude::*;
use pyo3::Python;

export_rustworkx_functions!(graph_cartesian_product, digraph_cartesian_product);

fn cartesian_product<Ty: EdgeType>(
py: Python,
first: &StablePyGraph<Ty>,
Expand Down
14 changes: 14 additions & 0 deletions src/centrality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use std::convert::TryFrom;

use crate::digraph;
use crate::export_rustworkx_functions;
use crate::graph;
use crate::iterators::{CentralityMapping, EdgeCentralityMapping};
use crate::CostFn;
Expand All @@ -29,6 +30,19 @@ use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use rustworkx_core::centrality;

export_rustworkx_functions!(
graph_betweenness_centrality,
digraph_betweenness_centrality,
graph_closeness_centrality,
digraph_closeness_centrality,
graph_edge_betweenness_centrality,
digraph_edge_betweenness_centrality,
graph_eigenvector_centrality,
digraph_eigenvector_centrality,
graph_katz_centrality,
digraph_katz_centrality
);

/// Compute the betweenness centrality of all nodes in a PyGraph.
///
/// Betweenness centrality of a node :math:`v` is the sum of the
Expand Down
11 changes: 10 additions & 1 deletion src/coloring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// under the License.

use crate::GraphNotBipartite;
use crate::{digraph, graph, EdgeIndex, NodeIndex};
use crate::{digraph, export_rustworkx_functions, graph, EdgeIndex, NodeIndex};

use pyo3::prelude::*;
use pyo3::types::PyDict;
Expand All @@ -27,6 +27,15 @@ use rustworkx_core::coloring::{

pub use rustworkx_core::coloring::ColoringStrategy as ColoringStrategyCore;

export_rustworkx_functions!(
graph_greedy_color,
graph_misra_gries_edge_color,
graph_greedy_edge_color,
graph_bipartite_edge_color,
graph_two_color,
digraph_two_color
);

/// Greedy coloring strategies available for `graph_greedy_color`
///
/// .. list-table:: Strategy description
Expand Down
40 changes: 39 additions & 1 deletion src/connectivity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ mod johnson_simple_cycles;
mod subgraphs;

use super::{
digraph, get_edge_iter_with_weights, graph, score, weight_callable, InvalidNode, NullGraph,
digraph, export_rustworkx_functions, get_edge_iter_with_weights, graph, score, weight_callable,
InvalidNode, NullGraph,
};

use hashbrown::{HashMap, HashSet};
Expand Down Expand Up @@ -46,6 +47,43 @@ use rustworkx_core::coloring::two_color;
use rustworkx_core::connectivity;
use rustworkx_core::dag_algo::longest_path;

export_rustworkx_functions!(
cycle_basis,
simple_cycles,
strongly_connected_components,
digraph_find_cycle,
number_connected_components,
connected_components,
node_connected_component,
is_connected,
number_weakly_connected_components,
weakly_connected_components,
is_weakly_connected,
is_semi_connected,
digraph_adjacency_matrix,
graph_adjacency_matrix,
graph_complement,
digraph_complement,
digraph_is_bipartite,
graph_is_bipartite,
graph_isolates,
digraph_isolates,
chain_decomposition,
biconnected_components,
bridges,
articulation_points,
stoer_wagner_min_cut,
graph_all_simple_paths,
digraph_all_simple_paths,
graph_all_pairs_all_simple_paths,
digraph_all_pairs_all_simple_paths,
graph_longest_simple_path,
digraph_longest_simple_path,
graph_core_number,
digraph_core_number,
connected_subgraphs
);

/// Return a list of cycles which form a basis for cycles of a given PyGraph
///
/// A basis for cycles of a graph is a minimal collection of
Expand Down
19 changes: 18 additions & 1 deletion src/dag_algo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ use rustworkx_core::dag_algo::layers as core_layers;
use rustworkx_core::dictmap::InitWithHasher;

use super::iterators::NodeIndices;
use crate::{digraph, DAGHasCycle, InvalidNode, RxPyResult, StablePyGraph};
use crate::{
digraph, export_rustworkx_functions, DAGHasCycle, InvalidNode, RxPyResult, StablePyGraph,
};

use rustworkx_core::dag_algo::collect_bicolor_runs as core_collect_bicolor_runs;
use rustworkx_core::dag_algo::collect_runs as core_collect_runs;
Expand All @@ -37,6 +39,21 @@ use petgraph::visit::NodeIndexable;

use num_traits::{Num, Zero};

export_rustworkx_functions!(
dag_longest_path,
dag_longest_path_length,
dag_weighted_longest_path,
dag_weighted_longest_path_length,
is_directed_acyclic_graph,
transitive_reduction,
topological_sort,
topological_generations,
lexicographical_topological_sort,
collect_runs,
collect_bicolor_runs,
layers
);

/// Calculate the longest path in a directed acyclic graph (DAG).
///
/// This function interfaces with the Python `PyDiGraph` object to compute the longest path
Expand Down
4 changes: 3 additions & 1 deletion src/graphml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ use pyo3::exceptions::PyException;
use pyo3::prelude::*;
use pyo3::PyErr;

use crate::{digraph::PyDiGraph, graph::PyGraph, StablePyGraph};
use crate::{digraph::PyDiGraph, export_rustworkx_functions, graph::PyGraph, StablePyGraph};

export_rustworkx_functions!(read_graphml);

pub enum Error {
Xml(String),
Expand Down
11 changes: 10 additions & 1 deletion src/isomorphism/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,22 @@

mod vf2;

use crate::{digraph, graph};
use crate::{digraph, export_rustworkx_functions, graph};

use std::cmp::Ordering;

use pyo3::prelude::*;
use pyo3::Python;

export_rustworkx_functions!(
graph_is_isomorphic,
digraph_is_isomorphic,
graph_is_subgraph_isomorphic,
digraph_is_subgraph_isomorphic,
digraph_vf2_mapping,
graph_vf2_mapping
);

/// Determine if 2 directed graphs are isomorphic
///
/// This checks if 2 graphs are isomorphic both structurally and also
Expand Down
9 changes: 8 additions & 1 deletion src/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,19 @@ mod node_link_data;
use std::fs::File;
use std::io::BufReader;

use crate::{digraph, graph, JSONDeserializationError, StablePyGraph};
use crate::{digraph, export_rustworkx_functions, graph, JSONDeserializationError, StablePyGraph};
use petgraph::{algo, Directed, Undirected};

use pyo3::prelude::*;
use pyo3::Python;

export_rustworkx_functions!(
digraph_node_link_json,
graph_node_link_json,
from_node_link_json_file,
parse_node_link_json
);

/// Parse a node-link format JSON file to generate a graph
///
/// :param path str: The path to the JSON file to load
Expand Down
17 changes: 16 additions & 1 deletion src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod shell;
mod spiral;
mod spring;

use crate::{digraph, graph};
use crate::{digraph, export_rustworkx_functions, graph};
use spring::Point;

use hashbrown::{HashMap, HashSet};
Expand All @@ -27,6 +27,21 @@ use pyo3::Python;

use crate::iterators::Pos2DMapping;

export_rustworkx_functions!(
graph_random_layout,
digraph_random_layout,
graph_bipartite_layout,
digraph_bipartite_layout,
graph_circular_layout,
digraph_circular_layout,
graph_shell_layout,
digraph_shell_layout,
graph_spiral_layout,
digraph_spiral_layout,
graph_spring_layout,
digraph_spring_layout
);

/// Position nodes using Fruchterman-Reingold force-directed algorithm.
///
/// The algorithm simulates a force-directed representation of the network
Expand Down
Loading