Skip to content
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -1262,7 +1262,28 @@ private IPath findCommonParentPackagePrefix(Collection<IPath> detectedJavaPackag
}
}

return null;
// none of the detected packages is a prefix of all others (sibling packages case)
// compute the actual longest common prefix path
// e.g. for [com/foo/bar/dto, com/foo/bar/merge] this yields com/foo/bar
IPath commonPrefix = null;
for (IPath path : detectedJavaPackagesForSourceDirectory) {
if (commonPrefix == null) {
commonPrefix = path;
} else {
var minLen = Math.min(commonPrefix.segmentCount(), path.segmentCount());
var commonLen = 0;
for (var i = 0; i < minLen; i++) {
if (commonPrefix.segment(i).equals(path.segment(i))) {
commonLen = i + 1;
} else {
break;
}
}
commonPrefix = commonPrefix.uptoSegment(commonLen);
}
}

return (commonPrefix == null) || commonPrefix.isEmpty() ? null : commonPrefix;
Comment on lines +1265 to +1286

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longest common prefix computation: Previously, when detected Java packages were siblings (e.g., com/foo/bar/dto and com/foo/bar/merge) rather than nested, the method returned null, causing source root detection to fail. The fix computes the longest common path prefix (e.g., com/foo/bar) to correctly infer the source root.

Why: Non-standard Bazel structures may have sources spread across sibling packages. The old logic only handled parent-child relationships, not siblings, causing Eclipse to fail at identifying source directories.

}

private IProject findProjectForLocation(IPath location) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,8 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
var libraryArtifact = new LibraryArtifact(artifact, classJar, srcJars);
var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null;
library = new BlazeJarLibrary(libraryArtifact, targetKey);
// Register back to avoid repeated fallback for the same jar across multiple targets
aspectsInfo.registerFallbackLibrary(artifact.getRelativePath(), library);
Comment on lines +361 to +362

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discovering a library through fallback logic (filesystem scan), it's registered back via registerFallbackLibrary() so subsequent lookups for the same jar hit the cache instead of repeating the expensive fallback.

Why: Multiple targets may reference the same jar in their jdeps. Without caching, each occurrence triggers a fresh fallback scan, causing redundant computation.

}
var entry = resolveLibrary(library);
if (entry != null) {
Expand All@@ -375,9 +377,14 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
classpathBuilder.addCompileEntry(entry);
} else {
entry.getAccessRules().add(new AccessRule(PATTERN_EVERYTHING, IAccessRule.K_ACCESSIBLE));
// Export EXPLICIT jdeps entries so downstream projects can resolve types
// referenced in this project's public API. This addresses ECJ vs javac differences:
// ECJ aggressively resolves all types in the API chain, while javac may not
// record them in jdeps of downstream targets.
entry.setExported(true);
Comment on lines +378 to +382

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: Eclipse uses ECJ (Eclipse Compiler for Java), which differs from javac in type resolution behavior. ECJ aggressively resolves all types in the public API chain — if project A's public method returns a type from project B, ECJ requires B on the classpath when compiling project C (which depends on A). However, javac may not record B as a dependency in C's jdeps. By marking explicit jdeps entries as exported, downstream projects can transitively see these dependencies, resolving ECJ compilation errors.

classpathBuilder.addCompileEntry(entry);
}
} else if (LOG.isDebugEnabled()) {
} else {
LOG.warn("Unable to resolve compile jar: {}", jdepsDependency);
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -261,6 +261,18 @@ private void addLibrary(BlazeJarLibrary library) {
var classJar = libraryArtifact.getClassJar();
if (classJar != null) {
libraryByJdepsRootRelativePath.put(classJar.getRelativePath(), library);

// When there is no interfaceJar (e.g., libraries discovered from runtime classpath),
// also index under potential ijar/hjar paths so that jdeps lookups can find them.
// jdeps files typically reference ijar/hjar paths, not class jar paths.
if (interfaceJar == null) {
var classPath = classJar.getRelativePath();
if (classPath.endsWith(".jar")) {
var base = classPath.substring(0, classPath.length() - ".jar".length());
libraryByJdepsRootRelativePath.putIfAbsent(base + "-ijar.jar", library);
libraryByJdepsRootRelativePath.putIfAbsent(base + "-hjar.jar", library);
}
}
Comment on lines +264 to +275

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a library that has only a classJar (no interfaceJar), the system now also indexes it under -ijar.jar and -hjar.jar path variants.

Why: Bazel's jdeps files typically reference ijar/hjar paths, not raw class jar paths. Libraries discovered from runtime classpath often lack interface jars, causing jdeps lookups to miss. Pre-indexing these variants ensures direct hits without fallback.

}
}

Expand All@@ -276,6 +288,14 @@ public BlazeJarLibrary getLibraryByJdepsRootRelativePath(String relativePath) {
return libraryByJdepsRootRelativePath.get(relativePath);
}

/**
* Registers a fallback library discovered during jdeps resolution so that subsequent lookups for the same path
* don't need to repeat the fallback logic.
*/
public void registerFallbackLibrary(String relativePath, BlazeJarLibrary library) {
libraryByJdepsRootRelativePath.putIfAbsent(relativePath, library);
}

/**
* Checks if a JAR file comes from an external Bazel repository.
* <p>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix: compute longest common prefix for sibling packages in empty source roots by runchen0919 · Pull Request #3 · runchen0919/bazel-eclipse · GitHub
Skip to content
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -1262,7 +1262,28 @@ private IPath findCommonParentPackagePrefix(Collection<IPath> detectedJavaPackag
}
}

return null;
// none of the detected packages is a prefix of all others (sibling packages case)
// compute the actual longest common prefix path
// e.g. for [com/foo/bar/dto, com/foo/bar/merge] this yields com/foo/bar
IPath commonPrefix = null;
for (IPath path : detectedJavaPackagesForSourceDirectory) {
if (commonPrefix == null) {
commonPrefix = path;
} else {
var minLen = Math.min(commonPrefix.segmentCount(), path.segmentCount());
var commonLen = 0;
for (var i = 0; i < minLen; i++) {
if (commonPrefix.segment(i).equals(path.segment(i))) {
commonLen = i + 1;
} else {
break;
}
}
commonPrefix = commonPrefix.uptoSegment(commonLen);
}
}

return (commonPrefix == null) || commonPrefix.isEmpty() ? null : commonPrefix;
Comment on lines +1265 to +1286

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longest common prefix computation: Previously, when detected Java packages were siblings (e.g., com/foo/bar/dto and com/foo/bar/merge) rather than nested, the method returned null, causing source root detection to fail. The fix computes the longest common path prefix (e.g., com/foo/bar) to correctly infer the source root.

Why: Non-standard Bazel structures may have sources spread across sibling packages. The old logic only handled parent-child relationships, not siblings, causing Eclipse to fail at identifying source directories.

}

private IProject findProjectForLocation(IPath location) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,8 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
var libraryArtifact = new LibraryArtifact(artifact, classJar, srcJars);
var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null;
library = new BlazeJarLibrary(libraryArtifact, targetKey);
// Register back to avoid repeated fallback for the same jar across multiple targets
aspectsInfo.registerFallbackLibrary(artifact.getRelativePath(), library);
Comment on lines +361 to +362

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discovering a library through fallback logic (filesystem scan), it's registered back via registerFallbackLibrary() so subsequent lookups for the same jar hit the cache instead of repeating the expensive fallback.

Why: Multiple targets may reference the same jar in their jdeps. Without caching, each occurrence triggers a fresh fallback scan, causing redundant computation.

}
var entry = resolveLibrary(library);
if (entry != null) {
Expand All@@ -375,9 +377,14 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
classpathBuilder.addCompileEntry(entry);
} else {
entry.getAccessRules().add(new AccessRule(PATTERN_EVERYTHING, IAccessRule.K_ACCESSIBLE));
// Export EXPLICIT jdeps entries so downstream projects can resolve types
// referenced in this project's public API. This addresses ECJ vs javac differences:
// ECJ aggressively resolves all types in the API chain, while javac may not
// record them in jdeps of downstream targets.
entry.setExported(true);
Comment on lines +378 to +382

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: Eclipse uses ECJ (Eclipse Compiler for Java), which differs from javac in type resolution behavior. ECJ aggressively resolves all types in the public API chain — if project A's public method returns a type from project B, ECJ requires B on the classpath when compiling project C (which depends on A). However, javac may not record B as a dependency in C's jdeps. By marking explicit jdeps entries as exported, downstream projects can transitively see these dependencies, resolving ECJ compilation errors.

classpathBuilder.addCompileEntry(entry);
}
} else if (LOG.isDebugEnabled()) {
} else {
LOG.warn("Unable to resolve compile jar: {}", jdepsDependency);
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -261,6 +261,18 @@ private void addLibrary(BlazeJarLibrary library) {
var classJar = libraryArtifact.getClassJar();
if (classJar != null) {
libraryByJdepsRootRelativePath.put(classJar.getRelativePath(), library);

// When there is no interfaceJar (e.g., libraries discovered from runtime classpath),
// also index under potential ijar/hjar paths so that jdeps lookups can find them.
// jdeps files typically reference ijar/hjar paths, not class jar paths.
if (interfaceJar == null) {
var classPath = classJar.getRelativePath();
if (classPath.endsWith(".jar")) {
var base = classPath.substring(0, classPath.length() - ".jar".length());
libraryByJdepsRootRelativePath.putIfAbsent(base + "-ijar.jar", library);
libraryByJdepsRootRelativePath.putIfAbsent(base + "-hjar.jar", library);
}
}
Comment on lines +264 to +275

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a library that has only a classJar (no interfaceJar), the system now also indexes it under -ijar.jar and -hjar.jar path variants.

Why: Bazel's jdeps files typically reference ijar/hjar paths, not raw class jar paths. Libraries discovered from runtime classpath often lack interface jars, causing jdeps lookups to miss. Pre-indexing these variants ensures direct hits without fallback.

}
}

Expand All@@ -276,6 +288,14 @@ public BlazeJarLibrary getLibraryByJdepsRootRelativePath(String relativePath) {
return libraryByJdepsRootRelativePath.get(relativePath);
}

/**
* Registers a fallback library discovered during jdeps resolution so that subsequent lookups for the same path
* don't need to repeat the fallback logic.
*/
public void registerFallbackLibrary(String relativePath, BlazeJarLibrary library) {
libraryByJdepsRootRelativePath.putIfAbsent(relativePath, library);
}

/**
* Checks if a JAR file comes from an external Bazel repository.
* <p>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: compute longest common prefix for sibling packages in empty source roots by runchen0919 · Pull Request #3 · runchen0919/bazel-eclipse · GitHub
Skip to content
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -1262,7 +1262,28 @@ private IPath findCommonParentPackagePrefix(Collection<IPath> detectedJavaPackag
}
}

return null;
// none of the detected packages is a prefix of all others (sibling packages case)
// compute the actual longest common prefix path
// e.g. for [com/foo/bar/dto, com/foo/bar/merge] this yields com/foo/bar
IPath commonPrefix = null;
for (IPath path : detectedJavaPackagesForSourceDirectory) {
if (commonPrefix == null) {
commonPrefix = path;
} else {
var minLen = Math.min(commonPrefix.segmentCount(), path.segmentCount());
var commonLen = 0;
for (var i = 0; i < minLen; i++) {
if (commonPrefix.segment(i).equals(path.segment(i))) {
commonLen = i + 1;
} else {
break;
}
}
commonPrefix = commonPrefix.uptoSegment(commonLen);
}
}

return (commonPrefix == null) || commonPrefix.isEmpty() ? null : commonPrefix;
Comment on lines +1265 to +1286

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longest common prefix computation: Previously, when detected Java packages were siblings (e.g., com/foo/bar/dto and com/foo/bar/merge) rather than nested, the method returned null, causing source root detection to fail. The fix computes the longest common path prefix (e.g., com/foo/bar) to correctly infer the source root.

Why: Non-standard Bazel structures may have sources spread across sibling packages. The old logic only handled parent-child relationships, not siblings, causing Eclipse to fail at identifying source directories.

}

private IProject findProjectForLocation(IPath location) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,8 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
var libraryArtifact = new LibraryArtifact(artifact, classJar, srcJars);
var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null;
library = new BlazeJarLibrary(libraryArtifact, targetKey);
// Register back to avoid repeated fallback for the same jar across multiple targets
aspectsInfo.registerFallbackLibrary(artifact.getRelativePath(), library);
Comment on lines +361 to +362

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discovering a library through fallback logic (filesystem scan), it's registered back via registerFallbackLibrary() so subsequent lookups for the same jar hit the cache instead of repeating the expensive fallback.

Why: Multiple targets may reference the same jar in their jdeps. Without caching, each occurrence triggers a fresh fallback scan, causing redundant computation.

}
var entry = resolveLibrary(library);
if (entry != null) {
Expand All@@ -375,9 +377,14 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
classpathBuilder.addCompileEntry(entry);
} else {
entry.getAccessRules().add(new AccessRule(PATTERN_EVERYTHING, IAccessRule.K_ACCESSIBLE));
// Export EXPLICIT jdeps entries so downstream projects can resolve types
// referenced in this project's public API. This addresses ECJ vs javac differences:
// ECJ aggressively resolves all types in the API chain, while javac may not
// record them in jdeps of downstream targets.
entry.setExported(true);
Comment on lines +378 to +382

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: Eclipse uses ECJ (Eclipse Compiler for Java), which differs from javac in type resolution behavior. ECJ aggressively resolves all types in the public API chain — if project A's public method returns a type from project B, ECJ requires B on the classpath when compiling project C (which depends on A). However, javac may not record B as a dependency in C's jdeps. By marking explicit jdeps entries as exported, downstream projects can transitively see these dependencies, resolving ECJ compilation errors.

classpathBuilder.addCompileEntry(entry);
}
} else if (LOG.isDebugEnabled()) {
} else {
LOG.warn("Unable to resolve compile jar: {}", jdepsDependency);
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -261,6 +261,18 @@ private void addLibrary(BlazeJarLibrary library) {
var classJar = libraryArtifact.getClassJar();
if (classJar != null) {
libraryByJdepsRootRelativePath.put(classJar.getRelativePath(), library);

// When there is no interfaceJar (e.g., libraries discovered from runtime classpath),
// also index under potential ijar/hjar paths so that jdeps lookups can find them.
// jdeps files typically reference ijar/hjar paths, not class jar paths.
if (interfaceJar == null) {
var classPath = classJar.getRelativePath();
if (classPath.endsWith(".jar")) {
var base = classPath.substring(0, classPath.length() - ".jar".length());
libraryByJdepsRootRelativePath.putIfAbsent(base + "-ijar.jar", library);
libraryByJdepsRootRelativePath.putIfAbsent(base + "-hjar.jar", library);
}
}
Comment on lines +264 to +275

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a library that has only a classJar (no interfaceJar), the system now also indexes it under -ijar.jar and -hjar.jar path variants.

Why: Bazel's jdeps files typically reference ijar/hjar paths, not raw class jar paths. Libraries discovered from runtime classpath often lack interface jars, causing jdeps lookups to miss. Pre-indexing these variants ensures direct hits without fallback.

}
}

Expand All@@ -276,6 +288,14 @@ public BlazeJarLibrary getLibraryByJdepsRootRelativePath(String relativePath) {
return libraryByJdepsRootRelativePath.get(relativePath);
}

/**
* Registers a fallback library discovered during jdeps resolution so that subsequent lookups for the same path
* don't need to repeat the fallback logic.
*/
public void registerFallbackLibrary(String relativePath, BlazeJarLibrary library) {
libraryByJdepsRootRelativePath.putIfAbsent(relativePath, library);
}

/**
* Checks if a JAR file comes from an external Bazel repository.
* <p>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: compute longest common prefix for sibling packages in empty source roots by runchen0919 · Pull Request #3 · runchen0919/bazel-eclipse · GitHub
Skip to content
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -1262,7 +1262,28 @@ private IPath findCommonParentPackagePrefix(Collection<IPath> detectedJavaPackag
}
}

return null;
// none of the detected packages is a prefix of all others (sibling packages case)
// compute the actual longest common prefix path
// e.g. for [com/foo/bar/dto, com/foo/bar/merge] this yields com/foo/bar
IPath commonPrefix = null;
for (IPath path : detectedJavaPackagesForSourceDirectory) {
if (commonPrefix == null) {
commonPrefix = path;
} else {
var minLen = Math.min(commonPrefix.segmentCount(), path.segmentCount());
var commonLen = 0;
for (var i = 0; i < minLen; i++) {
if (commonPrefix.segment(i).equals(path.segment(i))) {
commonLen = i + 1;
} else {
break;
}
}
commonPrefix = commonPrefix.uptoSegment(commonLen);
}
}

return (commonPrefix == null) || commonPrefix.isEmpty() ? null : commonPrefix;
Comment on lines +1265 to +1286

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longest common prefix computation: Previously, when detected Java packages were siblings (e.g., com/foo/bar/dto and com/foo/bar/merge) rather than nested, the method returned null, causing source root detection to fail. The fix computes the longest common path prefix (e.g., com/foo/bar) to correctly infer the source root.

Why: Non-standard Bazel structures may have sources spread across sibling packages. The old logic only handled parent-child relationships, not siblings, causing Eclipse to fail at identifying source directories.

}

private IProject findProjectForLocation(IPath location) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,8 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
var libraryArtifact = new LibraryArtifact(artifact, classJar, srcJars);
var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null;
library = new BlazeJarLibrary(libraryArtifact, targetKey);
// Register back to avoid repeated fallback for the same jar across multiple targets
aspectsInfo.registerFallbackLibrary(artifact.getRelativePath(), library);
Comment on lines +361 to +362

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discovering a library through fallback logic (filesystem scan), it's registered back via registerFallbackLibrary() so subsequent lookups for the same jar hit the cache instead of repeating the expensive fallback.

Why: Multiple targets may reference the same jar in their jdeps. Without caching, each occurrence triggers a fresh fallback scan, causing redundant computation.

}
var entry = resolveLibrary(library);
if (entry != null) {
Expand All@@ -375,9 +377,14 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
classpathBuilder.addCompileEntry(entry);
} else {
entry.getAccessRules().add(new AccessRule(PATTERN_EVERYTHING, IAccessRule.K_ACCESSIBLE));
// Export EXPLICIT jdeps entries so downstream projects can resolve types
// referenced in this project's public API. This addresses ECJ vs javac differences:
// ECJ aggressively resolves all types in the API chain, while javac may not
// record them in jdeps of downstream targets.
entry.setExported(true);
Comment on lines +378 to +382

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: Eclipse uses ECJ (Eclipse Compiler for Java), which differs from javac in type resolution behavior. ECJ aggressively resolves all types in the public API chain — if project A's public method returns a type from project B, ECJ requires B on the classpath when compiling project C (which depends on A). However, javac may not record B as a dependency in C's jdeps. By marking explicit jdeps entries as exported, downstream projects can transitively see these dependencies, resolving ECJ compilation errors.

classpathBuilder.addCompileEntry(entry);
}
} else if (LOG.isDebugEnabled()) {
} else {
LOG.warn("Unable to resolve compile jar: {}", jdepsDependency);
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -261,6 +261,18 @@ private void addLibrary(BlazeJarLibrary library) {
var classJar = libraryArtifact.getClassJar();
if (classJar != null) {
libraryByJdepsRootRelativePath.put(classJar.getRelativePath(), library);

// When there is no interfaceJar (e.g., libraries discovered from runtime classpath),
// also index under potential ijar/hjar paths so that jdeps lookups can find them.
// jdeps files typically reference ijar/hjar paths, not class jar paths.
if (interfaceJar == null) {
var classPath = classJar.getRelativePath();
if (classPath.endsWith(".jar")) {
var base = classPath.substring(0, classPath.length() - ".jar".length());
libraryByJdepsRootRelativePath.putIfAbsent(base + "-ijar.jar", library);
libraryByJdepsRootRelativePath.putIfAbsent(base + "-hjar.jar", library);
}
}
Comment on lines +264 to +275

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a library that has only a classJar (no interfaceJar), the system now also indexes it under -ijar.jar and -hjar.jar path variants.

Why: Bazel's jdeps files typically reference ijar/hjar paths, not raw class jar paths. Libraries discovered from runtime classpath often lack interface jars, causing jdeps lookups to miss. Pre-indexing these variants ensures direct hits without fallback.

}
}

Expand All@@ -276,6 +288,14 @@ public BlazeJarLibrary getLibraryByJdepsRootRelativePath(String relativePath) {
return libraryByJdepsRootRelativePath.get(relativePath);
}

/**
* Registers a fallback library discovered during jdeps resolution so that subsequent lookups for the same path
* don't need to repeat the fallback logic.
*/
public void registerFallbackLibrary(String relativePath, BlazeJarLibrary library) {
libraryByJdepsRootRelativePath.putIfAbsent(relativePath, library);
}

/**
* Checks if a JAR file comes from an external Bazel repository.
* <p>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix: compute longest common prefix for sibling packages in empty source roots by runchen0919 · Pull Request #3 · runchen0919/bazel-eclipse · GitHub
Skip to content
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -1262,7 +1262,28 @@ private IPath findCommonParentPackagePrefix(Collection<IPath> detectedJavaPackag
}
}

return null;
// none of the detected packages is a prefix of all others (sibling packages case)
// compute the actual longest common prefix path
// e.g. for [com/foo/bar/dto, com/foo/bar/merge] this yields com/foo/bar
IPath commonPrefix = null;
for (IPath path : detectedJavaPackagesForSourceDirectory) {
if (commonPrefix == null) {
commonPrefix = path;
} else {
var minLen = Math.min(commonPrefix.segmentCount(), path.segmentCount());
var commonLen = 0;
for (var i = 0; i < minLen; i++) {
if (commonPrefix.segment(i).equals(path.segment(i))) {
commonLen = i + 1;
} else {
break;
}
}
commonPrefix = commonPrefix.uptoSegment(commonLen);
}
}

return (commonPrefix == null) || commonPrefix.isEmpty() ? null : commonPrefix;
Comment on lines +1265 to +1286

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longest common prefix computation: Previously, when detected Java packages were siblings (e.g., com/foo/bar/dto and com/foo/bar/merge) rather than nested, the method returned null, causing source root detection to fail. The fix computes the longest common path prefix (e.g., com/foo/bar) to correctly infer the source root.

Why: Non-standard Bazel structures may have sources spread across sibling packages. The old logic only handled parent-child relationships, not siblings, causing Eclipse to fail at identifying source directories.

}

private IProject findProjectForLocation(IPath location) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,8 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
var libraryArtifact = new LibraryArtifact(artifact, classJar, srcJars);
var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null;
library = new BlazeJarLibrary(libraryArtifact, targetKey);
// Register back to avoid repeated fallback for the same jar across multiple targets
aspectsInfo.registerFallbackLibrary(artifact.getRelativePath(), library);
Comment on lines +361 to +362

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discovering a library through fallback logic (filesystem scan), it's registered back via registerFallbackLibrary() so subsequent lookups for the same jar hit the cache instead of repeating the expensive fallback.

Why: Multiple targets may reference the same jar in their jdeps. Without caching, each occurrence triggers a fresh fallback scan, causing redundant computation.

}
var entry = resolveLibrary(library);
if (entry != null) {
Expand All@@ -375,9 +377,14 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
classpathBuilder.addCompileEntry(entry);
} else {
entry.getAccessRules().add(new AccessRule(PATTERN_EVERYTHING, IAccessRule.K_ACCESSIBLE));
// Export EXPLICIT jdeps entries so downstream projects can resolve types
// referenced in this project's public API. This addresses ECJ vs javac differences:
// ECJ aggressively resolves all types in the API chain, while javac may not
// record them in jdeps of downstream targets.
entry.setExported(true);
Comment on lines +378 to +382

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: Eclipse uses ECJ (Eclipse Compiler for Java), which differs from javac in type resolution behavior. ECJ aggressively resolves all types in the public API chain — if project A's public method returns a type from project B, ECJ requires B on the classpath when compiling project C (which depends on A). However, javac may not record B as a dependency in C's jdeps. By marking explicit jdeps entries as exported, downstream projects can transitively see these dependencies, resolving ECJ compilation errors.

classpathBuilder.addCompileEntry(entry);
}
} else if (LOG.isDebugEnabled()) {
} else {
LOG.warn("Unable to resolve compile jar: {}", jdepsDependency);
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -261,6 +261,18 @@ private void addLibrary(BlazeJarLibrary library) {
var classJar = libraryArtifact.getClassJar();
if (classJar != null) {
libraryByJdepsRootRelativePath.put(classJar.getRelativePath(), library);

// When there is no interfaceJar (e.g., libraries discovered from runtime classpath),
// also index under potential ijar/hjar paths so that jdeps lookups can find them.
// jdeps files typically reference ijar/hjar paths, not class jar paths.
if (interfaceJar == null) {
var classPath = classJar.getRelativePath();
if (classPath.endsWith(".jar")) {
var base = classPath.substring(0, classPath.length() - ".jar".length());
libraryByJdepsRootRelativePath.putIfAbsent(base + "-ijar.jar", library);
libraryByJdepsRootRelativePath.putIfAbsent(base + "-hjar.jar", library);
}
}
Comment on lines +264 to +275

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a library that has only a classJar (no interfaceJar), the system now also indexes it under -ijar.jar and -hjar.jar path variants.

Why: Bazel's jdeps files typically reference ijar/hjar paths, not raw class jar paths. Libraries discovered from runtime classpath often lack interface jars, causing jdeps lookups to miss. Pre-indexing these variants ensures direct hits without fallback.

}
}

Expand All@@ -276,6 +288,14 @@ public BlazeJarLibrary getLibraryByJdepsRootRelativePath(String relativePath) {
return libraryByJdepsRootRelativePath.get(relativePath);
}

/**
* Registers a fallback library discovered during jdeps resolution so that subsequent lookups for the same path
* don't need to repeat the fallback logic.
*/
public void registerFallbackLibrary(String relativePath, BlazeJarLibrary library) {
libraryByJdepsRootRelativePath.putIfAbsent(relativePath, library);
}

/**
* Checks if a JAR file comes from an external Bazel repository.
* <p>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: compute longest common prefix for sibling packages in empty source roots by runchen0919 · Pull Request #3 · runchen0919/bazel-eclipse · GitHub
Skip to content
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -1262,7 +1262,28 @@ private IPath findCommonParentPackagePrefix(Collection<IPath> detectedJavaPackag
}
}

return null;
// none of the detected packages is a prefix of all others (sibling packages case)
// compute the actual longest common prefix path
// e.g. for [com/foo/bar/dto, com/foo/bar/merge] this yields com/foo/bar
IPath commonPrefix = null;
for (IPath path : detectedJavaPackagesForSourceDirectory) {
if (commonPrefix == null) {
commonPrefix = path;
} else {
var minLen = Math.min(commonPrefix.segmentCount(), path.segmentCount());
var commonLen = 0;
for (var i = 0; i < minLen; i++) {
if (commonPrefix.segment(i).equals(path.segment(i))) {
commonLen = i + 1;
} else {
break;
}
}
commonPrefix = commonPrefix.uptoSegment(commonLen);
}
}

return (commonPrefix == null) || commonPrefix.isEmpty() ? null : commonPrefix;
Comment on lines +1265 to +1286

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longest common prefix computation: Previously, when detected Java packages were siblings (e.g., com/foo/bar/dto and com/foo/bar/merge) rather than nested, the method returned null, causing source root detection to fail. The fix computes the longest common path prefix (e.g., com/foo/bar) to correctly infer the source root.

Why: Non-standard Bazel structures may have sources spread across sibling packages. The old logic only handled parent-child relationships, not siblings, causing Eclipse to fail at identifying source directories.

}

private IProject findProjectForLocation(IPath location) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,8 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
var libraryArtifact = new LibraryArtifact(artifact, classJar, srcJars);
var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null;
library = new BlazeJarLibrary(libraryArtifact, targetKey);
// Register back to avoid repeated fallback for the same jar across multiple targets
aspectsInfo.registerFallbackLibrary(artifact.getRelativePath(), library);
Comment on lines +361 to +362

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discovering a library through fallback logic (filesystem scan), it's registered back via registerFallbackLibrary() so subsequent lookups for the same jar hit the cache instead of repeating the expensive fallback.

Why: Multiple targets may reference the same jar in their jdeps. Without caching, each occurrence triggers a fresh fallback scan, causing redundant computation.

}
var entry = resolveLibrary(library);
if (entry != null) {
Expand All@@ -375,9 +377,14 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
classpathBuilder.addCompileEntry(entry);
} else {
entry.getAccessRules().add(new AccessRule(PATTERN_EVERYTHING, IAccessRule.K_ACCESSIBLE));
// Export EXPLICIT jdeps entries so downstream projects can resolve types
// referenced in this project's public API. This addresses ECJ vs javac differences:
// ECJ aggressively resolves all types in the API chain, while javac may not
// record them in jdeps of downstream targets.
entry.setExported(true);
Comment on lines +378 to +382

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: Eclipse uses ECJ (Eclipse Compiler for Java), which differs from javac in type resolution behavior. ECJ aggressively resolves all types in the public API chain — if project A's public method returns a type from project B, ECJ requires B on the classpath when compiling project C (which depends on A). However, javac may not record B as a dependency in C's jdeps. By marking explicit jdeps entries as exported, downstream projects can transitively see these dependencies, resolving ECJ compilation errors.

classpathBuilder.addCompileEntry(entry);
}
} else if (LOG.isDebugEnabled()) {
} else {
LOG.warn("Unable to resolve compile jar: {}", jdepsDependency);
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -261,6 +261,18 @@ private void addLibrary(BlazeJarLibrary library) {
var classJar = libraryArtifact.getClassJar();
if (classJar != null) {
libraryByJdepsRootRelativePath.put(classJar.getRelativePath(), library);

// When there is no interfaceJar (e.g., libraries discovered from runtime classpath),
// also index under potential ijar/hjar paths so that jdeps lookups can find them.
// jdeps files typically reference ijar/hjar paths, not class jar paths.
if (interfaceJar == null) {
var classPath = classJar.getRelativePath();
if (classPath.endsWith(".jar")) {
var base = classPath.substring(0, classPath.length() - ".jar".length());
libraryByJdepsRootRelativePath.putIfAbsent(base + "-ijar.jar", library);
libraryByJdepsRootRelativePath.putIfAbsent(base + "-hjar.jar", library);
}
}
Comment on lines +264 to +275

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a library that has only a classJar (no interfaceJar), the system now also indexes it under -ijar.jar and -hjar.jar path variants.

Why: Bazel's jdeps files typically reference ijar/hjar paths, not raw class jar paths. Libraries discovered from runtime classpath often lack interface jars, causing jdeps lookups to miss. Pre-indexing these variants ensures direct hits without fallback.

}
}

Expand All@@ -276,6 +288,14 @@ public BlazeJarLibrary getLibraryByJdepsRootRelativePath(String relativePath) {
return libraryByJdepsRootRelativePath.get(relativePath);
}

/**
* Registers a fallback library discovered during jdeps resolution so that subsequent lookups for the same path
* don't need to repeat the fallback logic.
*/
public void registerFallbackLibrary(String relativePath, BlazeJarLibrary library) {
libraryByJdepsRootRelativePath.putIfAbsent(relativePath, library);
}

/**
* Checks if a JAR file comes from an external Bazel repository.
* <p>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: compute longest common prefix for sibling packages in empty source roots by runchen0919 · Pull Request #3 · runchen0919/bazel-eclipse · GitHub
Skip to content
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -1262,7 +1262,28 @@ private IPath findCommonParentPackagePrefix(Collection<IPath> detectedJavaPackag
}
}

return null;
// none of the detected packages is a prefix of all others (sibling packages case)
// compute the actual longest common prefix path
// e.g. for [com/foo/bar/dto, com/foo/bar/merge] this yields com/foo/bar
IPath commonPrefix = null;
for (IPath path : detectedJavaPackagesForSourceDirectory) {
if (commonPrefix == null) {
commonPrefix = path;
} else {
var minLen = Math.min(commonPrefix.segmentCount(), path.segmentCount());
var commonLen = 0;
for (var i = 0; i < minLen; i++) {
if (commonPrefix.segment(i).equals(path.segment(i))) {
commonLen = i + 1;
} else {
break;
}
}
commonPrefix = commonPrefix.uptoSegment(commonLen);
}
}

return (commonPrefix == null) || commonPrefix.isEmpty() ? null : commonPrefix;
Comment on lines +1265 to +1286

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longest common prefix computation: Previously, when detected Java packages were siblings (e.g., com/foo/bar/dto and com/foo/bar/merge) rather than nested, the method returned null, causing source root detection to fail. The fix computes the longest common path prefix (e.g., com/foo/bar) to correctly infer the source root.

Why: Non-standard Bazel structures may have sources spread across sibling packages. The old logic only handled parent-child relationships, not siblings, causing Eclipse to fail at identifying source directories.

}

private IProject findProjectForLocation(IPath location) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,8 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
var libraryArtifact = new LibraryArtifact(artifact, classJar, srcJars);
var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null;
library = new BlazeJarLibrary(libraryArtifact, targetKey);
// Register back to avoid repeated fallback for the same jar across multiple targets
aspectsInfo.registerFallbackLibrary(artifact.getRelativePath(), library);
Comment on lines +361 to +362

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discovering a library through fallback logic (filesystem scan), it's registered back via registerFallbackLibrary() so subsequent lookups for the same jar hit the cache instead of repeating the expensive fallback.

Why: Multiple targets may reference the same jar in their jdeps. Without caching, each occurrence triggers a fresh fallback scan, causing redundant computation.

}
var entry = resolveLibrary(library);
if (entry != null) {
Expand All@@ -375,9 +377,14 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
classpathBuilder.addCompileEntry(entry);
} else {
entry.getAccessRules().add(new AccessRule(PATTERN_EVERYTHING, IAccessRule.K_ACCESSIBLE));
// Export EXPLICIT jdeps entries so downstream projects can resolve types
// referenced in this project's public API. This addresses ECJ vs javac differences:
// ECJ aggressively resolves all types in the API chain, while javac may not
// record them in jdeps of downstream targets.
entry.setExported(true);
Comment on lines +378 to +382

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: Eclipse uses ECJ (Eclipse Compiler for Java), which differs from javac in type resolution behavior. ECJ aggressively resolves all types in the public API chain — if project A's public method returns a type from project B, ECJ requires B on the classpath when compiling project C (which depends on A). However, javac may not record B as a dependency in C's jdeps. By marking explicit jdeps entries as exported, downstream projects can transitively see these dependencies, resolving ECJ compilation errors.

classpathBuilder.addCompileEntry(entry);
}
} else if (LOG.isDebugEnabled()) {
} else {
LOG.warn("Unable to resolve compile jar: {}", jdepsDependency);
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -261,6 +261,18 @@ private void addLibrary(BlazeJarLibrary library) {
var classJar = libraryArtifact.getClassJar();
if (classJar != null) {
libraryByJdepsRootRelativePath.put(classJar.getRelativePath(), library);

// When there is no interfaceJar (e.g., libraries discovered from runtime classpath),
// also index under potential ijar/hjar paths so that jdeps lookups can find them.
// jdeps files typically reference ijar/hjar paths, not class jar paths.
if (interfaceJar == null) {
var classPath = classJar.getRelativePath();
if (classPath.endsWith(".jar")) {
var base = classPath.substring(0, classPath.length() - ".jar".length());
libraryByJdepsRootRelativePath.putIfAbsent(base + "-ijar.jar", library);
libraryByJdepsRootRelativePath.putIfAbsent(base + "-hjar.jar", library);
}
}
Comment on lines +264 to +275

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a library that has only a classJar (no interfaceJar), the system now also indexes it under -ijar.jar and -hjar.jar path variants.

Why: Bazel's jdeps files typically reference ijar/hjar paths, not raw class jar paths. Libraries discovered from runtime classpath often lack interface jars, causing jdeps lookups to miss. Pre-indexing these variants ensures direct hits without fallback.

}
}

Expand All@@ -276,6 +288,14 @@ public BlazeJarLibrary getLibraryByJdepsRootRelativePath(String relativePath) {
return libraryByJdepsRootRelativePath.get(relativePath);
}

/**
* Registers a fallback library discovered during jdeps resolution so that subsequent lookups for the same path
* don't need to repeat the fallback logic.
*/
public void registerFallbackLibrary(String relativePath, BlazeJarLibrary library) {
libraryByJdepsRootRelativePath.putIfAbsent(relativePath, library);
}

/**
* Checks if a JAR file comes from an external Bazel repository.
* <p>
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix: compute longest common prefix for sibling packages in empty source roots by runchen0919 · Pull Request #3 · runchen0919/bazel-eclipse · GitHub
Skip to content
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -1262,7 +1262,28 @@ private IPath findCommonParentPackagePrefix(Collection<IPath> detectedJavaPackag
}
}

return null;
// none of the detected packages is a prefix of all others (sibling packages case)
// compute the actual longest common prefix path
// e.g. for [com/foo/bar/dto, com/foo/bar/merge] this yields com/foo/bar
IPath commonPrefix = null;
for (IPath path : detectedJavaPackagesForSourceDirectory) {
if (commonPrefix == null) {
commonPrefix = path;
} else {
var minLen = Math.min(commonPrefix.segmentCount(), path.segmentCount());
var commonLen = 0;
for (var i = 0; i < minLen; i++) {
if (commonPrefix.segment(i).equals(path.segment(i))) {
commonLen = i + 1;
} else {
break;
}
}
commonPrefix = commonPrefix.uptoSegment(commonLen);
}
}

return (commonPrefix == null) || commonPrefix.isEmpty() ? null : commonPrefix;
Comment on lines +1265 to +1286

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Longest common prefix computation: Previously, when detected Java packages were siblings (e.g., com/foo/bar/dto and com/foo/bar/merge) rather than nested, the method returned null, causing source root detection to fail. The fix computes the longest common path prefix (e.g., com/foo/bar) to correctly infer the source root.

Why: Non-standard Bazel structures may have sources spread across sibling packages. The old logic only handled parent-child relationships, not siblings, causing Eclipse to fail at identifying source directories.

}

private IProject findProjectForLocation(IPath location) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,8 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
var libraryArtifact = new LibraryArtifact(artifact, classJar, srcJars);
var targetKey = targetLabel != null ? TargetKey.forPlainTarget(targetLabel) : null;
library = new BlazeJarLibrary(libraryArtifact, targetKey);
// Register back to avoid repeated fallback for the same jar across multiple targets
aspectsInfo.registerFallbackLibrary(artifact.getRelativePath(), library);
Comment on lines +361 to +362

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discovering a library through fallback logic (filesystem scan), it's registered back via registerFallbackLibrary() so subsequent lookups for the same jar hit the cache instead of repeating the expensive fallback.

Why: Multiple targets may reference the same jar in their jdeps. Without caching, each occurrence triggers a fresh fallback scan, causing redundant computation.

}
var entry = resolveLibrary(library);
if (entry != null) {
Expand All@@ -375,9 +377,14 @@ public CompileAndRuntimeClasspath compute() throws CoreException {
classpathBuilder.addCompileEntry(entry);
} else {
entry.getAccessRules().add(new AccessRule(PATTERN_EVERYTHING, IAccessRule.K_ACCESSIBLE));
// Export EXPLICIT jdeps entries so downstream projects can resolve types
// referenced in this project's public API. This addresses ECJ vs javac differences:
// ECJ aggressively resolves all types in the API chain, while javac may not
// record them in jdeps of downstream targets.
entry.setExported(true);
Comment on lines +378 to +382

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: Eclipse uses ECJ (Eclipse Compiler for Java), which differs from javac in type resolution behavior. ECJ aggressively resolves all types in the public API chain — if project A's public method returns a type from project B, ECJ requires B on the classpath when compiling project C (which depends on A). However, javac may not record B as a dependency in C's jdeps. By marking explicit jdeps entries as exported, downstream projects can transitively see these dependencies, resolving ECJ compilation errors.

classpathBuilder.addCompileEntry(entry);
}
} else if (LOG.isDebugEnabled()) {
} else {
LOG.warn("Unable to resolve compile jar: {}", jdepsDependency);
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -261,6 +261,18 @@ private void addLibrary(BlazeJarLibrary library) {
var classJar = libraryArtifact.getClassJar();
if (classJar != null) {
libraryByJdepsRootRelativePath.put(classJar.getRelativePath(), library);

// When there is no interfaceJar (e.g., libraries discovered from runtime classpath),
// also index under potential ijar/hjar paths so that jdeps lookups can find them.
// jdeps files typically reference ijar/hjar paths, not class jar paths.
if (interfaceJar == null) {
var classPath = classJar.getRelativePath();
if (classPath.endsWith(".jar")) {
var base = classPath.substring(0, classPath.length() - ".jar".length());
libraryByJdepsRootRelativePath.putIfAbsent(base + "-ijar.jar", library);
libraryByJdepsRootRelativePath.putIfAbsent(base + "-hjar.jar", library);
}
}
Comment on lines +264 to +275

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When registering a library that has only a classJar (no interfaceJar), the system now also indexes it under -ijar.jar and -hjar.jar path variants.

Why: Bazel's jdeps files typically reference ijar/hjar paths, not raw class jar paths. Libraries discovered from runtime classpath often lack interface jars, causing jdeps lookups to miss. Pre-indexing these variants ensures direct hits without fallback.

}
}

Expand All@@ -276,6 +288,14 @@ public BlazeJarLibrary getLibraryByJdepsRootRelativePath(String relativePath) {
return libraryByJdepsRootRelativePath.get(relativePath);
}

/**
* Registers a fallback library discovered during jdeps resolution so that subsequent lookups for the same path
* don't need to repeat the fallback logic.
*/
public void registerFallbackLibrary(String relativePath, BlazeJarLibrary library) {
libraryByJdepsRootRelativePath.putIfAbsent(relativePath, library);
}

/**
* Checks if a JAR file comes from an external Bazel repository.
* <p>
Expand Down
Loading