This is an automated email from the ASF dual-hosted git repository. sdedic pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push: new 1715954033 Allow to find precise output folder for each language. (#4287) 1715954033 is described below commit 1715954033e443c043f2e9845d449f4fbf70d883 Author: Svatopluk Dedic <svatopluk.de...@oracle.com> AuthorDate: Mon Jul 11 10:56:40 2022 +0200 Allow to find precise output folder for each language. (#4287) Allow to find precise output folder for each language. --- .../gradle/tooling/NbProjectInfoBuilder.java | 96 +++++++++++++++++++--- .../modules/gradle/cache/ProjectInfoDiskCache.java | 2 +- java/gradle.java/apichanges.xml | 13 +++ java/gradle.java/manifest.mf | 2 +- .../gradle/java/api/GradleJavaProjectBuilder.java | 5 ++ .../gradle/java/api/GradleJavaSourceSet.java | 26 ++++++ .../gradle/java/queries/GradleSourceForBinary.java | 52 ++++++++++-- 7 files changed, 176 insertions(+), 20 deletions(-) diff --git a/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/NbProjectInfoBuilder.java b/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/NbProjectInfoBuilder.java index 346fe53226..ff2e93c938 100644 --- a/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/NbProjectInfoBuilder.java +++ b/extide/gradle/netbeans-gradle-tooling/src/main/java/org/netbeans/modules/gradle/tooling/NbProjectInfoBuilder.java @@ -22,12 +22,14 @@ package org.netbeans.modules.gradle.tooling; import groovy.lang.MissingPropertyException; import java.io.File; import java.io.Serializable; +import java.nio.file.Path; import java.util.ArrayList; import static java.util.Arrays.asList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -58,6 +60,7 @@ import org.gradle.api.attributes.Attribute; import org.gradle.api.attributes.AttributeContainer; import org.gradle.api.distribution.DistributionContainer; import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; import org.gradle.api.initialization.IncludedBuild; import org.gradle.api.plugins.JavaPlatformPlugin; import org.gradle.api.specs.Specs; @@ -267,6 +270,29 @@ class NbProjectInfoBuilder { .forEach(e -> nbprops.put(e.getKey().substring(NB_PREFIX.length()), String.valueOf(e.getValue()))); model.getInfo().put("nbprops", nbprops); } + + private Path longestPrefixPath(List<Path> files) { + if (files.size() < 2) { + return null; + } + Path first = files.get(0); + Path result = null; + Path root = first.getRoot(); + int count = first.getNameCount(); + for (int i = 1; i <= count; i++) { + Path match = root != null ? root.resolve(first.subpath(0, i)) : first.subpath(0, i); + + for (int pi = 1; pi < files.size(); pi++) { + Path p = files.get(pi); + if (!p.startsWith(match)) { + return result; + } + } + result = match; + } + // if all paths (more than one) are the same, something is strange. + return null; + } private void detectSources(NbProjectInfoModel model) { long time = System.currentTimeMillis(); @@ -277,15 +303,36 @@ class NbProjectInfoBuilder { boolean hasKotlin = project.getPlugins().hasPlugin("org.jetbrains.kotlin.android") || project.getPlugins().hasPlugin("org.jetbrains.kotlin.js") || project.getPlugins().hasPlugin("org.jetbrains.kotlin.jvm"); - + Map<String, Boolean> available = new HashMap<>(); + available.put("java", hasJava); + available.put("groovy", hasGroovy); + available.put("kotlin", hasKotlin); + if (hasJava) { SourceSetContainer sourceSets = (SourceSetContainer) getProperty(project, "sourceSets"); if (sourceSets != null) { model.getInfo().put("sourcesets", storeSet(sourceSets.getNames())); for(SourceSet sourceSet: sourceSets) { String propBase = "sourceset_" + sourceSet.getName() + "_"; + + Set<File> outDirs = new LinkedHashSet<>(); + sinceGradle("4.0", () -> { + // classesDirs is just an iterable + for (File dir: (ConfigurableFileCollection) getProperty(sourceSet, "output", "classesDirs")) { + outDirs.add(dir); + } + }); + beforeGradle("4.0", () -> { + outDirs.add((File)getProperty(sourceSet, "output", "classesDir")); + }); + + List<Path> outPaths = outDirs.stream().map(File::toPath).collect(Collectors.toList()); + // find the longest common prefix: + Path base = longestPrefixPath(outPaths); + for(String lang: new String[] {"JAVA", "GROOVY", "SCALA", "KOTLIN"}) { - Task compileTask = project.getTasks().findByName(sourceSet.getCompileTaskName(lang.toLowerCase())); + String langId = lang.toLowerCase(); + Task compileTask = project.getTasks().findByName(sourceSet.getCompileTaskName(langId)); if (compileTask != null) { model.getInfo().put( propBase + lang + "_source_compatibility", @@ -307,6 +354,39 @@ class NbProjectInfoBuilder { } model.getInfo().put(propBase + lang + "_compiler_args", new ArrayList<>(compilerArgs)); } + if (Boolean.TRUE.equals(available.get(langId))) { + model.getInfo().put(propBase + lang, storeSet(getProperty(sourceSet, langId, "srcDirs"))); + DirectoryProperty dirProp = (DirectoryProperty)getProperty(sourceSet, langId, "classesDirectory"); + if (dirProp != null) { + File outDir; + + if (dirProp.isPresent()) { + outDir = dirProp.get().getAsFile(); + } else { + // kotlin plugin uses some weird late binding, so it has the output item, but it cannot be resolved to a + // concrete file path at this time. Let's make an approximation from + Path candidate = null; + if (base != null) { + Path prefix = base.resolve(langId); + // assume the language has just one output dir in the source set: + for (int i = 0; i < outPaths.size(); i++) { + Path p = outPaths.get(i); + if (p.startsWith(prefix)) { + if (candidate != null) { + candidate = null; + break; + } else { + candidate = p; + } + } + } + } + outDir = candidate != null ? candidate.toFile() : new File(""); + } + + model.getInfo().put(propBase + lang + "_output_classes", outDir); + } + } } model.getInfo().put(propBase + "JAVA", storeSet(getProperty(sourceSet, "java", "srcDirs"))); @@ -320,17 +400,7 @@ class NbProjectInfoBuilder { if (hasKotlin) { model.getInfo().put(propBase + "KOTLIN", storeSet(getProperty(getProperty(sourceSet, "kotlin"), "srcDirs"))); } - sinceGradle("4.0", () -> { - Set<File> dirs = new LinkedHashSet<>(); - // classesDirs is just an iterable - for (File dir: (ConfigurableFileCollection) getProperty(sourceSet, "output", "classesDirs")) { - dirs.add(dir); - } - model.getInfo().put(propBase + "output_classes", dirs); - }); - beforeGradle("4.0", () -> { - model.getInfo().put(propBase + "output_classes", Collections.singleton(getProperty(sourceSet, "output", "classesDir"))); - }); + model.getInfo().put(propBase + "output_classes", outDirs); model.getInfo().put(propBase + "output_resources", sourceSet.getOutput().getResourcesDir()); sinceGradle("5.2", () -> { model.getInfo().put(propBase + "GENERATED", storeSet(getProperty(sourceSet, "output", "generatedSourcesDirs", "files"))); diff --git a/extide/gradle/src/org/netbeans/modules/gradle/cache/ProjectInfoDiskCache.java b/extide/gradle/src/org/netbeans/modules/gradle/cache/ProjectInfoDiskCache.java index 127130c78d..b28c2902dc 100644 --- a/extide/gradle/src/org/netbeans/modules/gradle/cache/ProjectInfoDiskCache.java +++ b/extide/gradle/src/org/netbeans/modules/gradle/cache/ProjectInfoDiskCache.java @@ -45,7 +45,7 @@ import org.netbeans.modules.gradle.spi.GradleFiles; public final class ProjectInfoDiskCache extends AbstractDiskCache<GradleFiles, QualifiedProjectInfo> { // Increase this number if new info is gathered from the projects. - private static final int COMPATIBLE_CACHE_VERSION = 22; + private static final int COMPATIBLE_CACHE_VERSION = 23; private static final String INFO_CACHE_FILE_NAME = "project-info.ser"; //NOI18N private static final Map<GradleFiles, ProjectInfoDiskCache> DISK_CACHES = Collections.synchronizedMap(new WeakHashMap<>()); diff --git a/java/gradle.java/apichanges.xml b/java/gradle.java/apichanges.xml index f77b1ba1bd..dfd5b57876 100644 --- a/java/gradle.java/apichanges.xml +++ b/java/gradle.java/apichanges.xml @@ -83,6 +83,19 @@ is the proper place. <!-- ACTUAL CHANGES BEGIN HERE: --> <changes> + <change id="sourceset-lang-output"> + <api name="gradle.java.api"/> + <summary>Support for per-language output directories</summary> + <version major="1" minor="19"/> + <date day="28" month="6" year="2022"/> + <author login="sdedic"/> + <compatibility semantic="compatible" addition="yes" deprecation="no"/> + <description> + Each language plugin typically generates output to a specific directory. The mapping + allows more precise output-to-source mapping. + </description> + <class package="org.netbeans.modules.gradle.java.api" name="GradleJavaSourceSet"/> + </change> <change id="nested-class-locations"> <api name="gradle.java.api"/> <summary>Location can represent nested classes</summary> diff --git a/java/gradle.java/manifest.mf b/java/gradle.java/manifest.mf index 6a3ea04484..e378d4e486 100644 --- a/java/gradle.java/manifest.mf +++ b/java/gradle.java/manifest.mf @@ -3,4 +3,4 @@ AutoUpdate-Show-In-Client: false OpenIDE-Module: org.netbeans.modules.gradle.java OpenIDE-Module-Layer: org/netbeans/modules/gradle/java/layer.xml OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/gradle/java/Bundle.properties -OpenIDE-Module-Specification-Version: 1.18 +OpenIDE-Module-Specification-Version: 1.19 diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/GradleJavaProjectBuilder.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/GradleJavaProjectBuilder.java index cc0136ac25..652b2eca17 100644 --- a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/GradleJavaProjectBuilder.java +++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/GradleJavaProjectBuilder.java @@ -99,6 +99,11 @@ final class GradleJavaProjectBuilder implements ProjectInfoExtractor.Result { if (compArgs != null) { compilerArgs.put(lang, Collections.unmodifiableList(compArgs)); } + // if detected, note the output dirs for individual language(s). + File f = (File)info.get("sourceset_" + name + "_" + lang.name() + "_output_classes"); + if (f != null) { + sourceSet.outputs.put(lang, f); + } } sourceSet.sourcesCompatibility = Collections.unmodifiableMap(sourceComp); sourceSet.targetCompatibility = Collections.unmodifiableMap(targetComp); diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/GradleJavaSourceSet.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/GradleJavaSourceSet.java index 061348190d..fad03f4e87 100644 --- a/java/gradle.java/src/org/netbeans/modules/gradle/java/api/GradleJavaSourceSet.java +++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/api/GradleJavaSourceSet.java @@ -78,6 +78,7 @@ public final class GradleJavaSourceSet implements Serializable { private static final String DEFAULT_SOURCE_COMPATIBILITY = "1.5"; //NOI18N Map<SourceType, Set<File>> sources = new EnumMap<>(SourceType.class); + Map<SourceType, File> outputs = new EnumMap<>(SourceType.class); String name; String runtimeConfigurationName; String compileConfigurationName; @@ -365,6 +366,31 @@ public final class GradleJavaSourceSet implements Serializable { public Set<File> getOutputClassDirs() { return outputClassDirs != null ? outputClassDirs : Collections.<File>emptySet(); } + + /** + * Represents an unknown value. This is different from a value that is not present, + * i.e. an output directory for a language that is not used in the project. + * @since 1.19 + */ + public static final File UNKNOWN = new File(""); + + /** + * Returns output directories for the given source type in the sourceset. Returns + * null, if the source type has no output directories. Returns UNKNOWN, if the + * output location is not known. + * + * @param srcType language type + * @return location or {@code null}. + * @since 1.19 + */ + public File getOutputClassDir(SourceType srcType) { + File f = outputs.get(srcType); + if (UNKNOWN.equals(f)) { + // make the value canonical, so == can be used. + return UNKNOWN; + } + return f; + } /** * Return the directory of resources output. diff --git a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleSourceForBinary.java b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleSourceForBinary.java index e27921c790..962f079351 100644 --- a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleSourceForBinary.java +++ b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleSourceForBinary.java @@ -32,6 +32,7 @@ import java.io.File; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; +import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -53,13 +54,49 @@ import org.openide.util.WeakListeners; * @author Laszlo Kishalmi */ public class GradleSourceForBinary implements SourceForBinaryQueryImplementation2 { + + private static Map<SourceType, String> sourceMimeTypes = new EnumMap<>(SourceType.class); + + static { + sourceMimeTypes.put(JAVA, "text/x-java"); + sourceMimeTypes.put(GROOVY, "text/x-groovy"); + sourceMimeTypes.put(SCALA, "text/x-scala"); + sourceMimeTypes.put(KOTLIN, "text/x-kotlin"); + } private final Project project; private final Map<URL, Res> cache = new HashMap<>(); - + public GradleSourceForBinary(Project project) { this.project = project; } + + private static final EnumSet<SourceType> ALL_LANGUAGES; + + static { + EnumSet<SourceType> s = EnumSet.allOf(SourceType.class); + s.remove(SourceType.RESOURCES); + s.remove(SourceType.GENERATED); + + ALL_LANGUAGES = s; + } + + private Result languageSourceForOutput(GradleJavaSourceSet ss, File outputDir) { + + for (SourceType st : ALL_LANGUAGES) { + File f = ss.getOutputClassDir(st); + if (outputDir.equals(f)) { + // special case for java here; Java replaces its classpath entries by corresponding cache entries with + // indexed sources (.sig files) so that it tracks not compiled source changes. Other languages do not generate + // the expected files. + // The proper solution will be to change java implementation to use output folder as a fallback if nothing + // is found in the cache; then this special case may be removed. + boolean canPreferSource = st == SourceType.JAVA; + return new Res(project, ss.getName(), EnumSet.of(RESOURCES), canPreferSource); + } + } + return new Res(project, ss.getName(), ALL_LANGUAGES, false); + } @Override public Result findSourceRoots2(URL binaryRoot) { @@ -76,8 +113,7 @@ public class GradleSourceForBinary implements SourceForBinaryQueryImplementation File outputDir = ss.getCompilerArgs(JAVA).contains("--module-source-path") ? //NOI18N root.getParentFile() : root; if (ss.getOutputClassDirs().contains(outputDir)) { - ret = new Res(project, ss.getName(), EnumSet.of(JAVA, GROOVY, SCALA, KOTLIN, GENERATED)); - break; + return languageSourceForOutput(ss, outputDir); } if ((ret == null) && root.equals(ss.getOutputResources())) { ret = new Res(project, ss.getName(), EnumSet.of(RESOURCES)); @@ -122,11 +158,17 @@ public class GradleSourceForBinary implements SourceForBinaryQueryImplementation private final Set<SourceType> sourceTypes; private final PropertyChangeListener listener; private final ChangeSupport support = new ChangeSupport(this); - + private final boolean preferSources; + public Res(Project project, String sourceSet, Set<SourceType> sourceTypes) { + this(project, sourceSet, sourceTypes, true); + } + + public Res(Project project, String sourceSet, Set<SourceType> sourceTypes, boolean preferSources) { this.project = project; this.sourceSet = sourceSet; this.sourceTypes = sourceTypes; + this.preferSources = preferSources; listener = (PropertyChangeEvent evt) -> { if (NbGradleProject.PROP_PROJECT_INFO.equals(evt.getPropertyName())) { support.fireChange(); @@ -137,7 +179,7 @@ public class GradleSourceForBinary implements SourceForBinaryQueryImplementation @Override public boolean preferSources() { - return true; + return preferSources; } @Override --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists