Skip to content

Commit

Permalink
Support access rules defined from the classpath
Browse files Browse the repository at this point in the history
In JDT one can define access rules from a classpath container, these are
currently ignored and lead to warnings (or even errors) or duplicate
configuration.

This now adds support for parsing these rules and use them for the
required plugins and the jre container.
  • Loading branch information
laeubi committed Feb 2, 2024
1 parent fb2b565 commit a444b09
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@
import org.eclipse.tycho.core.osgitools.project.EclipsePluginProject;
import org.eclipse.tycho.core.resolver.shared.PomDependencies;
import org.eclipse.tycho.helper.PluginRealmHelper;
import org.eclipse.tycho.model.classpath.ClasspathContainerEntry;
import org.eclipse.tycho.model.classpath.ContainerAccessRule;
import org.eclipse.tycho.model.classpath.JREClasspathEntry;
import org.eclipse.tycho.model.classpath.M2ClasspathVariable;
import org.eclipse.tycho.model.classpath.PluginDependenciesClasspathContainer;
import org.eclipse.tycho.model.classpath.ProjectClasspathEntry;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
Expand Down Expand Up @@ -570,10 +573,19 @@ protected File getOutputDirectory() {

@Override
public List<String> getClasspathElements() throws MojoExecutionException {
Collection<ProjectClasspathEntry> classpathEntries = getEclipsePluginProject().getClasspathEntries();
final List<String> classpath = new ArrayList<>();
Set<String> seen = new HashSet<>();
Set<String> includedPathes = new HashSet<>();
boolean useAccessRules = JDT_COMPILER_ID.equals(compilerId);
List<ContainerAccessRule> globalRules;
if (useAccessRules) {
globalRules = classpathEntries.stream().filter(PluginDependenciesClasspathContainer.class::isInstance)
.map(PluginDependenciesClasspathContainer.class::cast).map(ClasspathContainerEntry::getAccessRules)
.findFirst().orElse(List.of());
} else {
globalRules = List.of();
}
for (ClasspathEntry cpe : getClasspath()) {
Stream<File> classpathLocations = Stream
.concat(cpe.getLocations().stream(),
Expand All @@ -582,7 +594,7 @@ public List<String> getClasspathElements() throws MojoExecutionException {
.filter(AbstractOsgiCompilerMojo::isValidLocation).distinct();
classpathLocations.forEach(location -> {
String path = location.getAbsolutePath();
String entry = path + toString(cpe.getAccessRules(), useAccessRules);
String entry = path + toString(cpe.getAccessRules(), globalRules, useAccessRules);
if (seen.add(entry)) {
includedPathes.add(path);
classpath.add(entry);
Expand All @@ -593,7 +605,6 @@ public List<String> getClasspathElements() throws MojoExecutionException {
ArtifactRepository repository = session.getLocalRepository();
if (repository != null) {
String basedir = repository.getBasedir();
Collection<ProjectClasspathEntry> classpathEntries = getEclipsePluginProject().getClasspathEntries();
for (ProjectClasspathEntry cpe : classpathEntries) {
if (cpe instanceof M2ClasspathVariable cpv) {
String entry = new File(basedir, cpv.getRepositoryPath()).getAbsolutePath();
Expand Down Expand Up @@ -647,17 +658,33 @@ protected BundleProject getBundleProject() throws MojoExecutionException {
return (BundleProject) projectType;
}

private String toString(Collection<AccessRule> rules, boolean useAccessRules) {
private String toString(Collection<AccessRule> entryRules, List<ContainerAccessRule> containerRules,
boolean useAccessRules) {
if (useAccessRules) {
StringJoiner result = new StringJoiner(RULE_SEPARATOR, "[", "]"); // include all
if (rules != null) {
for (AccessRule rule : rules) {
result.add((rule.isDiscouraged() ? "~" : "+") + rule.getPattern());
if (entryRules != null) {
for (ContainerAccessRule rule : containerRules) {
switch (rule.getKind()) {
case ACCESSIBLE:
result.add("+" + rule.getPattern());
break;
case DISCOURAGED:
result.add("~" + rule.getPattern());
break;
case NON_ACCESSIBLE:
result.add("-" + rule.getPattern());
break;
default:
break;
}
}
if (entryRules != null) {
for (AccessRule rule : entryRules) {
result.add((rule.isDiscouraged() ? "~" : "+") + rule.getPattern());
}
}
result.add(RULE_EXCLUDE_ALL);
} else {
// include everything, not strictly necessary, but lets make this obvious
//result.append("[+**/*]");
return "";
}
return result.toString();
Expand Down Expand Up @@ -734,9 +761,9 @@ protected CompilerConfiguration getCompilerConfiguration(List<String> compileSou
compilerConfiguration.setReleaseVersion(releaseLevel);
}
configureJavaHome(compilerConfiguration);
configureBootclasspathAccessRules(compilerConfiguration);
configureCompilerLog(compilerConfiguration);
Collection<ProjectClasspathEntry> classpathEntries = getEclipsePluginProject().getClasspathEntries();
configureBootclasspathAccessRules(compilerConfiguration, classpathEntries);
configureCompilerLog(compilerConfiguration);
for (ProjectClasspathEntry cpe : classpathEntries) {
if (cpe instanceof JREClasspathEntry jreClasspathEntry) {
if (jreClasspathEntry.isModule()) {
Expand Down Expand Up @@ -789,8 +816,8 @@ private void configureCompilerLog(CompilerConfiguration compilerConfiguration) t
addCompilerCustomArgument(compilerConfiguration, "-log", logPath);
}

private void configureBootclasspathAccessRules(CompilerConfiguration compilerConfiguration)
throws MojoExecutionException {
private void configureBootclasspathAccessRules(CompilerConfiguration compilerConfiguration,
Collection<ProjectClasspathEntry> classpathEntries) throws MojoExecutionException {
List<AccessRule> accessRules = new ArrayList<>();

if (requireJREPackageImports != null) {
Expand All @@ -815,8 +842,11 @@ private void configureBootclasspathAccessRules(CompilerConfiguration compilerCon
.addAll(getBundleProject().getBootClasspathExtraAccessRules(DefaultReactorProject.adapt(project)));
}
if (!accessRules.isEmpty()) {
List<ContainerAccessRule> globalRules = classpathEntries.stream()
.filter(JREClasspathEntry.class::isInstance).map(JREClasspathEntry.class::cast)
.map(ClasspathContainerEntry::getAccessRules).findFirst().orElse(List.of());
addCompilerCustomArgument(compilerConfiguration, "org.osgi.framework.system.packages",
toString(accessRules, true));
toString(accessRules, globalRules, true));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ public String getPattern() {

@Override
public String toString() {
if (isDiscouraged()) {
return getPattern() + " (discouraged)";
}
return getPattern();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
*******************************************************************************/
package org.eclipse.tycho.model.classpath;

import java.util.List;

/**
* represents a container classpath entry, this could be for example:
*
Expand All @@ -33,4 +35,10 @@ public interface ClasspathContainerEntry extends ProjectClasspathEntry {
*/
String getContainerPath();

/**
*
* @return the {@link ContainerAccessRule}s defined for this classpathcontainer
*/
List<ContainerAccessRule> getAccessRules();

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.tycho.model.classpath.ContainerAccessRule.Kind;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

Expand Down Expand Up @@ -71,15 +75,18 @@ public static Collection<ProjectClasspathEntry> parse(File basedir) throws IOExc
new File(file.getParentFile(), output), attributes));
} else if ("con".equals(kind)) {
String path = classpathentry.getAttribute("path");
List<ContainerAccessRule> accessRules = parseAccessRules(classpathentry);
if (path.startsWith(JUnitClasspathContainerEntry.JUNIT_CONTAINER_PATH_PREFIX)) {
String junit = path
.substring(JUnitClasspathContainerEntry.JUNIT_CONTAINER_PATH_PREFIX.length());
list.add(new JDTJUnitContainerClasspathEntry(path, junit, attributes));
list.add(new JDTJUnitContainerClasspathEntry(path, junit, attributes, accessRules));
} else if (path.equals(JREClasspathEntry.JRE_CONTAINER_PATH)
|| path.startsWith(JREClasspathEntry.JRE_CONTAINER_PATH_STANDARDVMTYPE_PREFIX)) {
list.add(new JDTJREClasspathEntry(path, attributes));
list.add(new JDTJREClasspathEntry(path, attributes, accessRules));
} else if (path.equals(PluginDependenciesClasspathContainer.PATH)) {
list.add(new RequiredPluginsEntry(path, attributes, accessRules));
} else {
list.add(new JDTContainerClasspathEntry(path, attributes));
list.add(new JDTContainerClasspathEntry(path, attributes, accessRules));
}
} else if ("lib".equals(kind)) {
String path = classpathentry.getAttribute("path");
Expand All @@ -100,6 +107,33 @@ public static Collection<ProjectClasspathEntry> parse(File basedir) throws IOExc
}
}

private static List<ContainerAccessRule> parseAccessRules(Element classpathentry) {
NodeList accessrules = classpathentry.getElementsByTagName("accessrule");
Stream<Node> stream = IntStream.range(0, accessrules.getLength()).mapToObj(i -> accessrules.item(i));
return stream.map(Element.class::cast).map(elem -> {
Kind kind = Kind.parse(elem.getAttribute("kind"));
String pattern = elem.getAttribute("pattern");
ContainerAccessRule r = new ContainerAccessRule() {

@Override
public Kind getKind() {
return kind;
}

@Override
public String getPattern() {
return pattern;
}

@Override
public String toString() {
return pattern + " [" + kind + "]";
}
};
return r;
}).toList();
}

private static Map<String, String> getAttributes(Element parent) {
Map<String, String> map = new HashMap<>();
NodeList attributes = parent.getElementsByTagName("attribute");
Expand All @@ -111,10 +145,21 @@ private static Map<String, String> getAttributes(Element parent) {
return map;
}

private static final class RequiredPluginsEntry extends JDTContainerClasspathEntry
implements PluginDependenciesClasspathContainer {

public RequiredPluginsEntry(String path, Map<String, String> attributes,
List<ContainerAccessRule> accessRules) {
super(path, attributes, accessRules);
}

}

private static final class JDTJREClasspathEntry extends JDTContainerClasspathEntry implements JREClasspathEntry {

public JDTJREClasspathEntry(String path, Map<String, String> attributes) {
super(path, attributes);
public JDTJREClasspathEntry(String path, Map<String, String> attributes,
List<ContainerAccessRule> accessRules) {
super(path, attributes, accessRules);
}

@Override
Expand Down Expand Up @@ -146,8 +191,9 @@ private static class JDTJUnitContainerClasspathEntry extends JDTContainerClasspa

private final String junit;

public JDTJUnitContainerClasspathEntry(String path, String junit, Map<String, String> attributes) {
super(path, attributes);
public JDTJUnitContainerClasspathEntry(String path, String junit, Map<String, String> attributes,
List<ContainerAccessRule> accessRules) {
super(path, attributes, accessRules);
this.junit = junit;
}

Expand All @@ -174,10 +220,13 @@ private static class JDTContainerClasspathEntry implements ClasspathContainerEnt

protected final String path;
protected final Map<String, String> attributes;
private List<ContainerAccessRule> accessRules;

public JDTContainerClasspathEntry(String path, Map<String, String> attributes) {
public JDTContainerClasspathEntry(String path, Map<String, String> attributes,
List<ContainerAccessRule> accessRules) {
this.path = path;
this.attributes = attributes;
this.accessRules = accessRules;
}

@Override
Expand All @@ -190,6 +239,11 @@ public String getContainerPath() {
return path;
}

@Override
public List<ContainerAccessRule> getAccessRules() {
return accessRules;
}

}

private static final class JDTOuput implements OutputClasspathEntry {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2024 Christoph Läubrich and others.
* 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:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.model.classpath;

import java.util.Objects;

public interface ContainerAccessRule {

public enum Kind {
ACCESSIBLE("accessible"), NON_ACCESSIBLE("nonaccessible"), DISCOURAGED("discouraged"), UNKNOWN("");

private String attribute;

Kind(String attribute) {
this.attribute = attribute;
}

static Kind parse(String value) {
for (Kind kind : values()) {
if (Objects.equals(kind.attribute, value)) {
return kind;
}
}
return Kind.UNKNOWN;
}

}

Kind getKind();

String getPattern();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import java.util.Collection;

public interface JREClasspathEntry extends ProjectClasspathEntry {
public interface JREClasspathEntry extends ClasspathContainerEntry {

static final String JRE_CONTAINER_PATH = "org.eclipse.jdt.launching.JRE_CONTAINER";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2024 Christoph Läubrich and others.
* 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:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.model.classpath;

public interface PluginDependenciesClasspathContainer extends ClasspathContainerEntry {
static final String PATH = "org.eclipse.pde.core.requiredPlugins";
}

0 comments on commit a444b09

Please sign in to comment.