Skip to content
Merged
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
103 changes: 42 additions & 61 deletions bazel-jdt-bridge/crates/bazel-graph/src/classpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,6 @@ pub struct ComputedClasspath {
pub output_jars: Vec<String>,
}

/// Infer the target kind from a Bazel label based on naming conventions.
pub fn infer_target_kind(label: &str) -> TargetKind {
let rule_name = label.rsplit(':').next().unwrap_or(label);
if rule_name.contains("_test") || rule_name.ends_with("Test") {
TargetKind::JavaTest
} else if rule_name.contains("_binary") || rule_name.ends_with("Binary") || rule_name == "main"
{
TargetKind::JavaBinary
} else if rule_name.contains("_import") || rule_name.ends_with("Import") {
TargetKind::JavaImport
} else {
TargetKind::JavaLibrary
}
}

impl ComputedClasspath {
pub fn compute_for(
graph: &DependencyGraph,
Expand Down Expand Up @@ -124,15 +109,15 @@ impl ComputedClasspath {
}

if labels.len() == 1 {
let kind = infer_target_kind(labels[0]);
let kind = graph.get_target_kind(labels[0]);
return Self::compute_for(graph, labels[0], kind, workspace_root);
}

let mut merged: IndexMap<(ClasspathEntryType, String), ClasspathEntry> = IndexMap::new();
let mut all_output_jars = Vec::new();

for &label in labels {
let kind = infer_target_kind(label);
let kind = graph.get_target_kind(label);
let cp = match Self::compute_for(graph, label, kind, workspace_root) {
Ok(cp) => cp,
Err(e) => {
Expand Down Expand Up @@ -1031,50 +1016,6 @@ mod tests {
}
}

// --- infer_target_kind tests (moved from jni_exports.rs) ---

#[test]
fn test_infer_target_kind_library() {
assert_eq!(infer_target_kind("//foo:utils"), TargetKind::JavaLibrary);
assert_eq!(infer_target_kind("//app:app_lib"), TargetKind::JavaLibrary);
assert_eq!(infer_target_kind("//pkg:Greeter"), TargetKind::JavaLibrary);
}

#[test]
fn test_infer_target_kind_test() {
assert_eq!(infer_target_kind("//foo:my_test"), TargetKind::JavaTest);
assert_eq!(infer_target_kind("//foo:GreeterTest"), TargetKind::JavaTest);
assert_eq!(
infer_target_kind("//foo:integration_test"),
TargetKind::JavaTest
);
}

#[test]
fn test_infer_target_kind_binary() {
assert_eq!(infer_target_kind("//foo:my_binary"), TargetKind::JavaBinary);
assert_eq!(infer_target_kind("//foo:AppBinary"), TargetKind::JavaBinary);
assert_eq!(infer_target_kind("//foo:main"), TargetKind::JavaBinary);
}

#[test]
fn test_infer_target_kind_import() {
assert_eq!(infer_target_kind("//foo:my_import"), TargetKind::JavaImport);
assert_eq!(
infer_target_kind("//foo:MavenImport"),
TargetKind::JavaImport
);
}

#[test]
fn test_infer_target_kind_external() {
assert_eq!(infer_target_kind("@maven//:guava"), TargetKind::JavaLibrary);
assert_eq!(
infer_target_kind("@@rules_jvm_external~maven~maven//:com_google_guava_guava"),
TargetKind::JavaLibrary
);
}

// --- compute_for_targets tests ---

#[test]
Expand Down Expand Up @@ -1605,4 +1546,44 @@ mod tests {
"First valid source should be preserved, not overwritten by later target"
);
}

#[test]
fn test_importer_named_target_gets_transitive_deps() {
let mut graph = DependencyGraph::new();
let results = vec![
make_target(
"//funds/csv:funds_csv_importer",
vec!["@maven//:guava", "//lib:utils"],
vec!["/importer.jar"],
),
make_target("@maven//:guava", vec![], vec!["/guava.jar"]),
make_target("//lib:utils", vec![], vec!["/utils.jar"]),
];
graph.populate_from_aspects(&results, Path::new("/workspace"));

let cp = ComputedClasspath::compute_for_targets(
&graph,
&["//funds/csv:funds_csv_importer"],
None,
)
.unwrap();

let lib_paths: Vec<&str> = cp
.entries
.iter()
.filter(|e| e.entry_type == ClasspathEntryType::Library)
.map(|e| e.path.as_str())
.collect();

assert!(
lib_paths.contains(&"/guava.jar"),
"Expected transitive dep guava.jar for 'importer' target, got: {:?}",
lib_paths
);
assert!(
lib_paths.contains(&"/utils.jar"),
"Expected transitive dep utils.jar for 'importer' target, got: {:?}",
lib_paths
);
}
}
91 changes: 91 additions & 0 deletions bazel-jdt-bridge/crates/bazel-graph/src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::classpath::TargetKind;
use petgraph::graph::{DiGraph, NodeIndex};
use petgraph::visit::EdgeRef;
use petgraph::Direction;
Expand All @@ -22,6 +23,8 @@ pub struct DependencyGraph {
/// Maps apparent external repo labels to canonical bzlmod labels.
/// e.g. `@maven//:guava` → `@@rules_jvm_external~maven~maven//:guava`
pub(crate) label_aliases: HashMap<String, String>,
/// Actual Bazel rule kind from aspect output (e.g., "java_library", "java_import").
target_kinds: HashMap<String, String>,
}

/// Error for graph operations
Expand All @@ -42,6 +45,7 @@ impl DependencyGraph {
target_jars: HashMap::new(),
testonly_targets: HashSet::new(),
label_aliases: HashMap::new(),
target_kinds: HashMap::new(),
}
}

Expand Down Expand Up @@ -150,6 +154,26 @@ impl DependencyGraph {
self.testonly_targets.contains(label)
}

/// Store the actual Bazel rule kind for a target.
pub fn set_target_kind(&mut self, label: &str, kind: &str) {
if !kind.is_empty() {
self.target_kinds
.insert(label.to_string(), kind.to_string());
}
}

/// Look up the stored Bazel rule kind for a target, returning `TargetKind`.
/// Falls back to `Unknown` (routes to `compute_for_library`) when no kind data is stored.
pub fn get_target_kind(&self, label: &str) -> TargetKind {
match self.target_kinds.get(label).map(|s| s.as_str()) {
Some("java_import") => TargetKind::JavaImport,
Some("java_test") => TargetKind::JavaTest,
Some("java_binary") => TargetKind::JavaBinary,
Some("java_library") => TargetKind::JavaLibrary,
_ => TargetKind::Unknown,
}
}

/// Get all target labels
pub fn all_targets(&self) -> Vec<String> {
self.label_to_index.keys().cloned().collect()
Expand All @@ -162,6 +186,7 @@ impl DependencyGraph {
self.target_jars.clear();
self.testonly_targets.clear();
self.label_aliases.clear();
self.target_kinds.clear();
}

/// Populate graph from Bazel aspect output (primary data source).
Expand All @@ -178,6 +203,7 @@ impl DependencyGraph {
for info in results {
let label = &info.label;
self.add_target(label);
self.set_target_kind(label, &info.kind);

if let Some(apparent) = bazel_aspect::canonical_to_apparent_label(label) {
self.label_aliases
Expand Down Expand Up @@ -2704,4 +2730,69 @@ mod tests {
let result = probe_maven_local_cache("com.nonexistent", "artifact", "1.0");
assert_eq!(result, None);
}

#[test]
fn test_get_target_kind_from_aspect_java_library() {
let mut graph = DependencyGraph::new();
let results = vec![make_target(
"//foo:funds_csv_importer",
vec![],
vec!["/foo.jar"],
)];
graph.populate_from_aspects(&results, Path::new("/workspace"));
assert_eq!(
graph.get_target_kind("//foo:funds_csv_importer"),
TargetKind::JavaLibrary,
"Target named 'importer' with kind java_library should be JavaLibrary, not JavaImport"
);
}

#[test]
fn test_get_target_kind_from_aspect_java_import() {
let mut graph = DependencyGraph::new();
let results = vec![make_target_with_compile_jars(
"@maven//:guava",
vec![],
vec!["/guava.jar"],
)];
graph.populate_from_aspects(&results, Path::new("/workspace"));
assert_eq!(
graph.get_target_kind("@maven//:guava"),
TargetKind::JavaImport,
"Target with kind java_import should be JavaImport"
);
}

#[test]
fn test_get_target_kind_no_stored_kind() {
let mut graph = DependencyGraph::new();
graph.add_target("//foo:bar");
assert_eq!(
graph.get_target_kind("//foo:bar"),
TargetKind::Unknown,
"Target with no stored kind should return Unknown"
);
}

#[test]
fn test_get_target_kind_unrecognized_kind() {
let mut graph = DependencyGraph::new();
graph.add_target("//foo:bar");
graph.set_target_kind("//foo:bar", "kt_jvm_library");
assert_eq!(
graph.get_target_kind("//foo:bar"),
TargetKind::Unknown,
"Unrecognized kind string should return Unknown"
);
}

#[test]
fn test_clear_resets_target_kinds() {
let mut graph = DependencyGraph::new();
let results = vec![make_target("//foo:lib", vec![], vec!["/foo.jar"])];
graph.populate_from_aspects(&results, Path::new("/workspace"));
assert_eq!(graph.get_target_kind("//foo:lib"), TargetKind::JavaLibrary);
graph.clear();
assert_eq!(graph.get_target_kind("//foo:lib"), TargetKind::Unknown);
}
}
4 changes: 2 additions & 2 deletions bazel-jdt-bridge/crates/bazel-graph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub mod classpath;
pub mod graph;

pub use classpath::{
infer_target_kind, is_bazel_internal_label, AccessRule, ClasspathEntry, ClasspathEntryType,
ComputedClasspath, JarConflict, TargetKind,
is_bazel_internal_label, AccessRule, ClasspathEntry, ClasspathEntryType, ComputedClasspath,
JarConflict, TargetKind,
};
pub use graph::{normalize_label, DependencyGraph, GraphError, ResolvedJar};
6 changes: 2 additions & 4 deletions bazel-jdt-bridge/crates/bazel-jdt-core/src/jni_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Mutex, OnceLock};

use bazel_graph::infer_target_kind;
use jni::objects::{JClass, JObject, JObjectArray, JString};
use jni::sys::{jint, jlong, jobjectArray, jsize};
use jni::JNIEnv;
Expand Down Expand Up @@ -544,10 +543,9 @@ pub extern "system" fn Java_com_bazel_jdt_BazelBridge_nativeComputeClasspath(
}
}

let target_kind = infer_target_kind(&label);

let graph = state.graph.lock().unwrap_or_else(|e| e.into_inner());
let has_aspect_data = graph.get_target_jars(&label).is_some();
let target_kind = graph.get_target_kind(&label);
drop(graph);

if has_aspect_data {
Expand Down Expand Up @@ -911,7 +909,7 @@ fn run_full_resolution(
bazel_graph::ComputedClasspath::compute_for(
&graph,
target_label,
infer_target_kind(target_label),
graph.get_target_kind(target_label),
Some(state.workspace_root.to_str().unwrap_or("")),
)
.map_err(|e| format!("Graph computation failed: {}", e))
Expand Down
Loading
Loading