Skip to content

Add classpath_depth to allow controlling transitive dependency depth #503

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

Merged
merged 12 commits into from
Jul 19, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@
*/
package com.salesforce.bazel.eclipse.core.classpath;

import static java.util.Objects.requireNonNull;

import java.io.Serializable;
import java.util.Arrays;

import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathContainer;
Expand All @@ -49,14 +52,16 @@
*/
public class BazelClasspathContainer implements IClasspathContainer, Serializable {

private static final long serialVersionUID = 390898179243551621L;
private static final long serialVersionUID = 390898179243551622L;

private final IPath path;
private final IClasspathEntry[] classpath;
private final IClasspathEntry[] transitiveClasspath;

public BazelClasspathContainer(IPath path, IClasspathEntry[] classpath) {
public BazelClasspathContainer(IPath path, IClasspathEntry[] classpath, IClasspathEntry[] transitiveClasspath) {
this.path = path;
this.classpath = classpath;
this.classpath = requireNonNull(classpath);
this.transitiveClasspath = requireNonNull(transitiveClasspath);
}

@Override
Expand All @@ -69,6 +74,15 @@ public String getDescription() {
return "Bazel Dependencies";
}

public IClasspathEntry[] getFullClasspath() {
if (transitiveClasspath.length == 0) {
return classpath;
}
var fullClasspath = Arrays.copyOf(classpath, classpath.length + transitiveClasspath.length);
System.arraycopy(transitiveClasspath, 0, fullClasspath, classpath.length, transitiveClasspath.length);
return fullClasspath;
}

@Override
public int getKind() {
return IClasspathContainer.K_APPLICATION;
Expand All @@ -78,4 +92,13 @@ public int getKind() {
public IPath getPath() {
return path;
}

/**
* A array of transitive classpath entries.
*
* @return
*/
public IClasspathEntry[] getTransitiveEntries() {
return transitiveClasspath;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private void populateWithSavedContainer(IJavaProject project, Collection<IRuntim
var bazelContainer = getClasspathManager().getSavedContainer(project.getProject());
if (bazelContainer != null) {
var workspaceRoot = project.getResource().getWorkspace().getRoot();
var entries = bazelContainer.getClasspathEntries();
var entries = bazelContainer.getFullClasspath();
for (IClasspathEntry e : entries) {
switch (e.getEntryKind()) {
case IClasspathEntry.CPE_PROJECT: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaCore;

Expand Down Expand Up @@ -103,8 +102,13 @@ static final class LibraryEntryReplace implements Serializable {
}

IClasspathEntry getEntry() {
return JavaCore.newLibraryEntry(path, sourceAttachmentPath, sourceAttachmentRootPath, //
accessRules, extraAttributes, exported);
return JavaCore.newLibraryEntry(
path,
sourceAttachmentPath,
sourceAttachmentRootPath,
accessRules,
extraAttributes,
exported);
}
}

Expand Down Expand Up @@ -150,12 +154,16 @@ static final class ProjectEntryReplace implements Serializable {
}

IClasspathEntry getEntry() {
return JavaCore.newProjectEntry(path, accessRules, //
combineAccessRules, extraAttributes, exported);
return JavaCore.newProjectEntry(
path,
accessRules,
combineAccessRules,
extraAttributes,
exported);
}
}

public IClasspathContainer readContainer(InputStream input) throws IOException, ClassNotFoundException {
public BazelClasspathContainer readContainer(InputStream input) throws IOException, ClassNotFoundException {
ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(input)) {
{
enableResolveObject(true);
Expand All @@ -181,10 +189,10 @@ protected Object resolveObject(Object o) throws IOException {
return super.resolveObject(o);
}
};
return (IClasspathContainer) is.readObject();
return (BazelClasspathContainer) is.readObject();
}

public void writeContainer(IClasspathContainer container, OutputStream output) throws IOException {
public void writeContainer(BazelClasspathContainer container, OutputStream output) throws IOException {
ObjectOutputStream os = new ObjectOutputStream(new BufferedOutputStream(output)) {
{
enableReplaceObject(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ String getJavadocLocation(IClasspathEntry entry) {
* @return a saved classpath container for the specified project (may be <code>null</code>)
* @throws CoreException
*/
public IClasspathContainer getSavedContainer(IProject project) throws CoreException {
public BazelClasspathContainer getSavedContainer(IProject project) throws CoreException {
var containerStateFile = getContainerStateFile(project);
if (!containerStateFile.exists()) {
return null;
Expand Down Expand Up @@ -239,8 +239,8 @@ TargetProvisioningStrategy getTargetProvisioningStrategy(BazelWorkspace bazelWor
* @param monitor
* @throws CoreException
*/
public void patchClasspathContainer(BazelProject bazelProject, List<ClasspathEntry> classpath,
IProgressMonitor progress) throws CoreException {
public void patchClasspathContainer(BazelProject bazelProject, ClasspathHolder classpath, IProgressMonitor progress)
throws CoreException {
var monitor = SubMonitor.convert(progress);
try {
monitor.beginTask("Patchig classpath: " + bazelProject.getName(), 2);
Expand Down Expand Up @@ -300,8 +300,10 @@ public void persistAttachedSourcesAndJavadoc(IJavaProject project, IClasspathCon
bazelProject.getBazelWorkspace(),
DEFAULT_CLASSPATH,
monitor.split(1, SUPPRESS_ALL_LABELS));
entries =
configureClasspathWithSourceAttachments(classpaths.get(bazelProject), null /* no props */, monitor);
entries = configureClasspathWithSourceAttachments(
classpaths.get(bazelProject).compile(),
null /* no props */,
monitor);
for (IClasspathEntry entry : entries) {
if (IClasspathEntry.CPE_LIBRARY == entry.getEntryKind()) {
var path = entry.getPath().toPortableString();
Expand Down Expand Up @@ -338,16 +340,22 @@ public void persistAttachedSourcesAndJavadoc(IJavaProject project, IClasspathCon
}
}

void saveAndSetContainer(IJavaProject javaProject, Collection<ClasspathEntry> classpath, IProgressMonitor monitor)
void saveAndSetContainer(IJavaProject javaProject, ClasspathHolder classpath, IProgressMonitor monitor)
throws CoreException, JavaModelException {
var containerEntry = getBazelContainerEntry(javaProject);
var path = containerEntry != null ? containerEntry.getPath()
: new Path(BazelCoreSharedContstants.CLASSPATH_CONTAINER_ID);

var sourceAttachmentProperties = getSourceAttachmentProperties(javaProject.getProject());
var transativeClasspath = classpath.transitive().stream().map(ClasspathEntry::build).collect(toList());

var container = new BazelClasspathContainer(
path,
configureClasspathWithSourceAttachments(classpath, sourceAttachmentProperties, monitor.slice(1)));
configureClasspathWithSourceAttachments(
classpath.compile(),
sourceAttachmentProperties,
monitor.slice(1)),
transativeClasspath.toArray(new IClasspathEntry[transativeClasspath.size()]));

JavaCore.setClasspathContainer(
container.getPath(),
Expand All @@ -359,7 +367,7 @@ void saveAndSetContainer(IJavaProject javaProject, Collection<ClasspathEntry> cl
saveContainerState(javaProject.getProject(), container);
}

private void saveContainerState(IProject project, IClasspathContainer container) throws CoreException {
private void saveContainerState(IProject project, BazelClasspathContainer container) throws CoreException {
var containerStateFile = getContainerStateFile(project);
try (var is = new FileOutputStream(containerStateFile)) {
new BazelClasspathContainerSaveHelper().writeContainer(container, is);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*-
* Copyright (c) 2024 Salesforce and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Salesforce - adapted from M2E, JDT or other Eclipse project
*/
package com.salesforce.bazel.eclipse.core.classpath;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

import org.eclipse.core.runtime.IPath;

import com.salesforce.bazel.eclipse.core.model.discovery.classpath.ClasspathEntry;

/**
* Represents a targets classpath as two parts, compile being the classpath entries that are loaded into the project
* model, and transtative as classpaths that are part of the targets runtime, but are not to be loaded in the project
* model.
*/
public record ClasspathHolder(Collection<ClasspathEntry> compile, Collection<ClasspathEntry> transitive) {

public static class ClasspathHolderBuilder {
// Preserve classpath order. Add leaf level dependencies first and work the way up. This
// prevents conflicts when a JAR repackages it's dependencies. In such a case we prefer to
// resolve symbols from the original JAR rather than the repackaged version.
// Using accessOrdered LinkedHashMap because jars that are present in `workspaceBuilder.jdeps`
// and in `workspaceBuilder.directDeps`, we want to treat it as a directDep
private final Map<IPath, ClasspathEntry> compileEntries =
new LinkedHashMap<>(/* initialCapacity= */ 32, /* loadFactor= */ 0.75f, /* accessOrder= */ true);
private final Map<IPath, ClasspathEntry> transitiveEntries =
new LinkedHashMap<>(/* initialCapacity= */ 32, /* loadFactor= */ 0.75f, /* accessOrder= */ true);
private boolean hasUnloadedEntries = false;

private final boolean partialClasspathEnabled;

public ClasspathHolderBuilder(boolean partialClasspathEnabled) {
this.partialClasspathEnabled = partialClasspathEnabled;
}

public ClasspathHolder build() {
return new ClasspathHolder(
compileEntries.values(),
hasUnloadedEntries ? transitiveEntries.values() : Collections.emptyList());
}

public void put(IPath path, ClasspathEntry entry) {
compileEntries.put(path, entry);
}

public void putIfAbsent(IPath path, ClasspathEntry entry) {
compileEntries.putIfAbsent(path, entry);
}

public void putUnloadedIfAbsent(IPath path, ClasspathEntry entry) {
if (!compileEntries.containsKey(path)) {
transitiveEntries.putIfAbsent(path, entry);
hasUnloadedEntries = true;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
Expand All @@ -21,6 +22,7 @@
import org.eclipse.core.runtime.jobs.Job;

import com.google.idea.blaze.base.model.primitives.Label;
import com.salesforce.bazel.eclipse.core.classpath.ClasspathHolder;
import com.salesforce.bazel.eclipse.core.classpath.InitializeOrRefreshClasspathJob;
import com.salesforce.bazel.eclipse.core.model.BazelProject;
import com.salesforce.bazel.eclipse.core.model.BazelTarget;
Expand Down Expand Up @@ -147,7 +149,11 @@ public IStatus runInWorkspace(final IProgressMonitor monitor) throws CoreExcepti
}

if (modified) {
classpathManager.patchClasspathContainer(bazelProject, classpath, monitor);
var transitive = Arrays.stream(container.getTransitiveEntries())
.map(ClasspathEntry::fromExisting)
.collect(toList());
classpathManager
.patchClasspathContainer(bazelProject, new ClasspathHolder(classpath, transitive), monitor);
}
return Status.OK_STATUS;
}
Expand Down
Loading
Loading