Skip to content

Commit

Permalink
Resolve classpath entries against project base directory #3818
Browse files Browse the repository at this point in the history
When using a linked .classpath file, the path entries within that file
are resolved against the directory containing the .classpath file
instead of the project base directory into which the .classpath file is
linked.

With this change, the paths within a .classpath file are resolved
against a passed EclipseProject. An existing regression test for linked
.classpath files is enhanced to not contain all linked resources in the
same folder (thus hiding the bug of using the folder containing the
linked .classpath file for resolving classpath entry paths).

Fixes #3818
  • Loading branch information
HeikoKlare authored and laeubi committed May 5, 2024
1 parent 66d631c commit 7490426
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 28 deletions.
Expand Up @@ -18,6 +18,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import org.codehaus.plexus.component.annotations.Component;
Expand All @@ -30,6 +31,7 @@
import org.eclipse.tycho.model.classpath.ClasspathParser;
import org.eclipse.tycho.model.classpath.JUnitBundle;
import org.eclipse.tycho.model.classpath.ProjectClasspathEntry;
import org.eclipse.tycho.model.project.EclipseProject;

@Component(role = ClasspathReader.class)
public class ClasspathReader implements Disposable {
Expand All @@ -48,14 +50,18 @@ public void dispose() {
}

public Collection<ProjectClasspathEntry> parse(File basedir) throws IOException {

Path resolvedClasspath = projectManager.getEclipseProject(basedir)
.map(project -> project.getFile(ClasspathParser.CLASSPATH_FILENAME))
Optional<EclipseProject> eclipseProject = projectManager.getEclipseProject(basedir);
Path resolvedClasspath = eclipseProject.map(project -> project.getFile(ClasspathParser.CLASSPATH_FILENAME))
.orElse(basedir.toPath().resolve(ClasspathParser.CLASSPATH_FILENAME));

return cache.computeIfAbsent(resolvedClasspath.normalize().toString(), f -> {
File resolvedClasspathFile = resolvedClasspath.toFile();
try {
return ClasspathParser.parse(resolvedClasspath.toFile());
if (eclipseProject.isPresent()) {
return ClasspathParser.parse(resolvedClasspathFile, eclipseProject.get());
} else {
return ClasspathParser.parse(resolvedClasspathFile);
}
} catch (IOException e) {
logger.warn("Can't read classpath from " + basedir);
return Collections.emptyList();
Expand Down
Expand Up @@ -32,23 +32,18 @@
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<linkedResources>
<!-- A linked file relative to the location of the project -->
<!-- A linked .classpath file relative to the location of the project's parent -->
<link>
<name>.classpath</name>
<type>1</type>
<locationURI>PROJECT_LOC/linkedresources/.classpath</locationURI>
<locationURI>PARENT-1-PROJECT_LOC/junit5-with-linked-resources/testresources/linkedclasspath/.classpath</locationURI>
</link>
<!-- A linked folder relative to the location of the project's parent and defined in a variable -->
<!-- A linked file relative to the location of the project -->
<link>
<name>src_test</name>
<type>2</type>
<locationURI>VARIABLE_SELFREF_VIA_PARENT/linkedresources/src_test/</locationURI>
<locationURI>PROJECT_LOC/testresources/linkedtestsources/src_test/</locationURI>
</link>
</linkedResources>
<variableList>
<variable>
<name>VARIABLE_SELFREF_VIA_PARENT</name>
<value>$%7BPARENT-1-PROJECT_LOC%7D/junit5-with-linked-files</value>
</variable>
</variableList>
</projectDescription>

Expand Up @@ -42,6 +42,7 @@ public void testJUnit5ContainerWithLinkedResources() throws Exception {
Verifier verifier = getVerifier("compiler.junitcontainer/junit5-with-linked-resources", false, true);
verifier.executeGoal("test");
verifier.verifyErrorFreeLog();
verifier.verifyTextInLog("Compiling 2 source files");
verifier.verifyTextInLog("-- in bundle.test.AdderTest");
verifier.verifyTextInLog("-- in bundle.test.SubtractorTest");
verifier.verifyTextInLog("Tests run: 5, Failures: 0, Errors: 0, Skipped: 0");
Expand Down
Expand Up @@ -21,6 +21,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;

Expand All @@ -29,6 +30,7 @@
import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.tycho.model.classpath.ContainerAccessRule.Kind;
import org.eclipse.tycho.model.project.EclipseProject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
Expand All @@ -38,7 +40,19 @@
public class ClasspathParser {
public static final String CLASSPATH_FILENAME = ".classpath";

public static Collection<ProjectClasspathEntry> parse(File file) throws IOException {
public static Collection<ProjectClasspathEntry> parse(File classpathFile, EclipseProject project)
throws IOException {
Function<String, File> pathInProjectResolver = path -> project.getFile(path).normalize().toFile();
return parse(classpathFile, pathInProjectResolver);
}

public static Collection<ProjectClasspathEntry> parse(File classpathFile) throws IOException {
Function<String, File> pathInProjectResolver = path -> new File(classpathFile.getParent(), path);
return parse(classpathFile, pathInProjectResolver);
}

private static Collection<ProjectClasspathEntry> parse(File file, Function<String, File> pathInProjectResolver)
throws IOException {
if (!file.isFile()) {
return Collections.emptyList();
}
Expand All @@ -50,14 +64,13 @@ public static Collection<ProjectClasspathEntry> parse(File file) throws IOExcept
NodeList classpathentries = doc.getDocumentElement().getElementsByTagName("classpathentry");
int length = classpathentries.getLength();
List<ProjectClasspathEntry> list = new ArrayList<>();
String defaultOutput = "bin";
File defaultOutput = pathInProjectResolver.apply("bin");
for (int i = 0; i < length; i++) {
Element classpathentry = (Element) classpathentries.item(i);
String kind = classpathentry.getAttribute("kind");
if ("output".equals(kind)) {
defaultOutput = classpathentry.getAttribute("path");
list.add(
new JDTOuput(new File(file.getParentFile(), defaultOutput), getAttributes(classpathentry)));
defaultOutput = pathInProjectResolver.apply(classpathentry.getAttribute("path"));
list.add(new JDTOuput(defaultOutput, getAttributes(classpathentry)));
}
}
for (int i = 0; i < length; i++) {
Expand All @@ -66,13 +79,11 @@ public static Collection<ProjectClasspathEntry> parse(File file) throws IOExcept

String kind = classpathentry.getAttribute("kind");
if ("src".equals(kind)) {
String path = classpathentry.getAttribute("path");
String output = classpathentry.getAttribute("output");
if (output.isBlank()) {
output = defaultOutput;
}
list.add(new JDTSourceFolder(new File(file.getParentFile(), path),
new File(file.getParentFile(), output), attributes));
File path = pathInProjectResolver.apply(classpathentry.getAttribute("path"));
String outputAttribute = classpathentry.getAttribute("output");
File output = !outputAttribute.isBlank() ? pathInProjectResolver.apply(outputAttribute)
: defaultOutput;
list.add(new JDTSourceFolder(path, output, attributes));
} else if ("con".equals(kind)) {
String path = classpathentry.getAttribute("path");
List<ContainerAccessRule> accessRules = parseAccessRules(classpathentry);
Expand All @@ -89,8 +100,8 @@ public static Collection<ProjectClasspathEntry> parse(File file) throws IOExcept
list.add(new JDTContainerClasspathEntry(path, attributes, accessRules));
}
} else if ("lib".equals(kind)) {
String path = classpathentry.getAttribute("path");
list.add(new JDTLibraryClasspathEntry(new File(file.getParentFile(), path), attributes));
File path = pathInProjectResolver.apply(classpathentry.getAttribute("path"));
list.add(new JDTLibraryClasspathEntry(path, attributes));
} else if ("var".equals(kind)) {
String path = classpathentry.getAttribute("path");
if (path.startsWith(M2ClasspathVariable.M2_REPO_VARIABLE_PREFIX)) {
Expand Down

0 comments on commit 7490426

Please sign in to comment.