From a21476ea0239245ea3a33008221b8afd5bf47dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Thu, 1 Feb 2024 17:36:42 +0100 Subject: [PATCH] Support access rules defined from the classpath 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. --- .../compiler/AbstractOsgiCompilerMojo.java | 56 +++++++++++---- .../core/osgitools/DefaultClasspathEntry.java | 3 + .../classpath/ClasspathContainerEntry.java | 8 +++ .../model/classpath/ClasspathParser.java | 70 ++++++++++++++++--- .../model/classpath/ContainerAccessRule.java | 42 +++++++++++ .../model/classpath/JREClasspathEntry.java | 2 +- .../PluginDependenciesClasspathContainer.java | 17 +++++ 7 files changed, 176 insertions(+), 22 deletions(-) create mode 100644 tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ContainerAccessRule.java create mode 100644 tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/PluginDependenciesClasspathContainer.java diff --git a/tycho-compiler-plugin/src/main/java/org/eclipse/tycho/compiler/AbstractOsgiCompilerMojo.java b/tycho-compiler-plugin/src/main/java/org/eclipse/tycho/compiler/AbstractOsgiCompilerMojo.java index 5551f78b79..a169332e78 100644 --- a/tycho-compiler-plugin/src/main/java/org/eclipse/tycho/compiler/AbstractOsgiCompilerMojo.java +++ b/tycho-compiler-plugin/src/main/java/org/eclipse/tycho/compiler/AbstractOsgiCompilerMojo.java @@ -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; @@ -570,10 +573,19 @@ protected File getOutputDirectory() { @Override public List getClasspathElements() throws MojoExecutionException { + Collection classpathEntries = getEclipsePluginProject().getClasspathEntries(); final List classpath = new ArrayList<>(); Set seen = new HashSet<>(); Set includedPathes = new HashSet<>(); boolean useAccessRules = JDT_COMPILER_ID.equals(compilerId); + List 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 classpathLocations = Stream .concat(cpe.getLocations().stream(), @@ -582,7 +594,7 @@ public List 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); @@ -593,7 +605,6 @@ public List getClasspathElements() throws MojoExecutionException { ArtifactRepository repository = session.getLocalRepository(); if (repository != null) { String basedir = repository.getBasedir(); - Collection classpathEntries = getEclipsePluginProject().getClasspathEntries(); for (ProjectClasspathEntry cpe : classpathEntries) { if (cpe instanceof M2ClasspathVariable cpv) { String entry = new File(basedir, cpv.getRepositoryPath()).getAbsolutePath(); @@ -647,17 +658,33 @@ protected BundleProject getBundleProject() throws MojoExecutionException { return (BundleProject) projectType; } - private String toString(Collection rules, boolean useAccessRules) { + private String toString(Collection entryRules, List 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(); @@ -734,9 +761,9 @@ protected CompilerConfiguration getCompilerConfiguration(List compileSou compilerConfiguration.setReleaseVersion(releaseLevel); } configureJavaHome(compilerConfiguration); - configureBootclasspathAccessRules(compilerConfiguration); - configureCompilerLog(compilerConfiguration); Collection classpathEntries = getEclipsePluginProject().getClasspathEntries(); + configureBootclasspathAccessRules(compilerConfiguration, classpathEntries); + configureCompilerLog(compilerConfiguration); for (ProjectClasspathEntry cpe : classpathEntries) { if (cpe instanceof JREClasspathEntry jreClasspathEntry) { if (jreClasspathEntry.isModule()) { @@ -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 classpathEntries) throws MojoExecutionException { List accessRules = new ArrayList<>(); if (requireJREPackageImports != null) { @@ -815,8 +842,11 @@ private void configureBootclasspathAccessRules(CompilerConfiguration compilerCon .addAll(getBundleProject().getBootClasspathExtraAccessRules(DefaultReactorProject.adapt(project))); } if (!accessRules.isEmpty()) { + List 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)); } } diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/DefaultClasspathEntry.java b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/DefaultClasspathEntry.java index 902de4300d..ae0434db2a 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/DefaultClasspathEntry.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/osgitools/DefaultClasspathEntry.java @@ -66,6 +66,9 @@ public String getPattern() { @Override public String toString() { + if (isDiscouraged()) { + return getPattern() + " (discouraged)"; + } return getPattern(); } diff --git a/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ClasspathContainerEntry.java b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ClasspathContainerEntry.java index ac540f89c5..3c106c1939 100644 --- a/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ClasspathContainerEntry.java +++ b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ClasspathContainerEntry.java @@ -12,6 +12,8 @@ *******************************************************************************/ package org.eclipse.tycho.model.classpath; +import java.util.List; + /** * represents a container classpath entry, this could be for example: * @@ -33,4 +35,10 @@ public interface ClasspathContainerEntry extends ProjectClasspathEntry { */ String getContainerPath(); + /** + * + * @return the {@link ContainerAccessRule}s defined for this classpathcontainer + */ + List getAccessRules(); + } diff --git a/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ClasspathParser.java b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ClasspathParser.java index 4a421b19d0..3d6f838cac 100644 --- a/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ClasspathParser.java +++ b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ClasspathParser.java @@ -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; @@ -71,15 +75,18 @@ public static Collection parse(File basedir) throws IOExc new File(file.getParentFile(), output), attributes)); } else if ("con".equals(kind)) { String path = classpathentry.getAttribute("path"); + List 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"); @@ -100,6 +107,33 @@ public static Collection parse(File basedir) throws IOExc } } + private static List parseAccessRules(Element classpathentry) { + NodeList accessrules = classpathentry.getElementsByTagName("accessrule"); + Stream 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 getAttributes(Element parent) { Map map = new HashMap<>(); NodeList attributes = parent.getElementsByTagName("attribute"); @@ -111,10 +145,21 @@ private static Map getAttributes(Element parent) { return map; } + private static final class RequiredPluginsEntry extends JDTContainerClasspathEntry + implements PluginDependenciesClasspathContainer { + + public RequiredPluginsEntry(String path, Map attributes, + List accessRules) { + super(path, attributes, accessRules); + } + + } + private static final class JDTJREClasspathEntry extends JDTContainerClasspathEntry implements JREClasspathEntry { - public JDTJREClasspathEntry(String path, Map attributes) { - super(path, attributes); + public JDTJREClasspathEntry(String path, Map attributes, + List accessRules) { + super(path, attributes, accessRules); } @Override @@ -146,8 +191,9 @@ private static class JDTJUnitContainerClasspathEntry extends JDTContainerClasspa private final String junit; - public JDTJUnitContainerClasspathEntry(String path, String junit, Map attributes) { - super(path, attributes); + public JDTJUnitContainerClasspathEntry(String path, String junit, Map attributes, + List accessRules) { + super(path, attributes, accessRules); this.junit = junit; } @@ -174,10 +220,13 @@ private static class JDTContainerClasspathEntry implements ClasspathContainerEnt protected final String path; protected final Map attributes; + private List accessRules; - public JDTContainerClasspathEntry(String path, Map attributes) { + public JDTContainerClasspathEntry(String path, Map attributes, + List accessRules) { this.path = path; this.attributes = attributes; + this.accessRules = accessRules; } @Override @@ -190,6 +239,11 @@ public String getContainerPath() { return path; } + @Override + public List getAccessRules() { + return accessRules; + } + } private static final class JDTOuput implements OutputClasspathEntry { diff --git a/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ContainerAccessRule.java b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ContainerAccessRule.java new file mode 100644 index 0000000000..d125e587b6 --- /dev/null +++ b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/ContainerAccessRule.java @@ -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(); +} diff --git a/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/JREClasspathEntry.java b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/JREClasspathEntry.java index d61beb7526..80d22477fc 100644 --- a/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/JREClasspathEntry.java +++ b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/JREClasspathEntry.java @@ -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"; diff --git a/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/PluginDependenciesClasspathContainer.java b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/PluginDependenciesClasspathContainer.java new file mode 100644 index 0000000000..90ad80881f --- /dev/null +++ b/tycho-metadata-model/src/main/java/org/eclipse/tycho/model/classpath/PluginDependenciesClasspathContainer.java @@ -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"; +}