All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.gradle.marker.GradleProjectBuilder Maven / Gradle / Ivy

/*
 * Copyright 2022 the original author or authors.
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openrewrite.gradle.marker; import org.gradle.api.Project; import org.gradle.api.artifacts.*; import org.gradle.api.artifacts.repositories.ArtifactRepository; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.initialization.Settings; import org.gradle.api.internal.plugins.PluginManagerInternal; import org.gradle.api.plugins.PluginManager; import org.gradle.invocation.DefaultGradle; import org.gradle.plugin.use.PluginId; import org.gradle.util.GradleVersion; import org.jspecify.annotations.Nullable; import org.openrewrite.maven.tree.GroupArtifact; import org.openrewrite.maven.tree.GroupArtifactVersion; import org.openrewrite.maven.tree.MavenRepository; import org.openrewrite.maven.tree.ResolvedGroupArtifactVersion; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; import static org.openrewrite.Tree.randomId; import static org.openrewrite.gradle.marker.GradleSettingsBuilder.GRADLE_PLUGIN_PORTAL; public final class GradleProjectBuilder { private GradleProjectBuilder() { } public static GradleProject gradleProject(Project project) { Set pluginRepositories = new HashSet<>(); if (GradleVersion.current().compareTo(GradleVersion.version("4.4")) >= 0) { Settings settings = ((DefaultGradle) project.getGradle()).getSettings(); pluginRepositories.addAll(mapRepositories(settings.getPluginManagement().getRepositories())); pluginRepositories.addAll(mapRepositories(settings.getBuildscript().getRepositories())); } List repositories = new ArrayList<>(project.getRepositories()); if (GradleVersion.current().compareTo(GradleVersion.version("6.8")) >= 0) { Settings settings = ((DefaultGradle) project.getGradle()).getSettings(); //noinspection UnstableApiUsage repositories.addAll(settings.getDependencyResolutionManagement().getRepositories()); } pluginRepositories.addAll(mapRepositories(project.getBuildscript().getRepositories())); if (pluginRepositories.isEmpty()) { pluginRepositories.add(GRADLE_PLUGIN_PORTAL); } return new GradleProject(randomId(), project.getGroup().toString(), project.getName(), project.getVersion().toString(), project.getPath(), GradleProjectBuilder.pluginDescriptors(project.getPluginManager()), mapRepositories(repositories), null, GradleProjectBuilder.dependencyConfigurations(project.getConfigurations()), new GradleBuildscript( randomId(), new ArrayList<>(pluginRepositories), GradleProjectBuilder.dependencyConfigurations(project.getBuildscript().getConfigurations()) )); } static List mapRepositories(List repositories) { return repositories.stream() .filter(MavenArtifactRepository.class::isInstance) .map(MavenArtifactRepository.class::cast) .map(repo -> MavenRepository.builder() .id(repo.getName()) .uri(repo.getUrl().toString()) .releases(true) .snapshots(true) .build()) .collect(toList()); } public static List pluginDescriptors(@Nullable PluginManager pluginManager) { if (pluginManager instanceof PluginManagerInternal) { return pluginDescriptors((PluginManagerInternal) pluginManager); } return emptyList(); } public static List pluginDescriptors(PluginManagerInternal pluginManager) { return pluginManager.getPluginContainer().stream() .map(plugin -> new GradlePluginDescriptor( plugin.getClass().getName(), pluginIdForClass(pluginManager, plugin.getClass()))) .collect(toList()); } private static @Nullable String pluginIdForClass(PluginManagerInternal pluginManager, Class pluginClass) { try { Method findPluginIdForClass = PluginManagerInternal.class.getMethod("findPluginIdForClass", Class.class); //noinspection unchecked Optional maybePluginId = (Optional) findPluginIdForClass.invoke(pluginManager, pluginClass); return maybePluginId.map(PluginId::getId).orElse(null); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { // On old versions of gradle that don't have this method, returning null is fine } return null; } private static final Map groupArtifactCache = new ConcurrentHashMap<>(); private static GroupArtifact groupArtifact(org.openrewrite.maven.tree.Dependency dep) { //noinspection ConstantConditions return groupArtifactCache.computeIfAbsent(new GroupArtifact(dep.getGroupId(), dep.getArtifactId()), it -> it); } private static GroupArtifact groupArtifact(ResolvedDependency dep) { return groupArtifactCache.computeIfAbsent(new GroupArtifact(dep.getModuleGroup(), dep.getModuleName()), it -> it); } private static final Map groupArtifactVersionCache = new ConcurrentHashMap<>(); private static GroupArtifactVersion groupArtifactVersion(ResolvedDependency dep) { return groupArtifactVersionCache.computeIfAbsent( new GroupArtifactVersion(dep.getModuleGroup(), dep.getModuleName(), unspecifiedToNull(dep.getModuleVersion())), it -> it); } private static GroupArtifactVersion groupArtifactVersion(Dependency dep) { return groupArtifactVersionCache.computeIfAbsent( new GroupArtifactVersion(dep.getGroup(), dep.getName(), unspecifiedToNull(dep.getVersion())), it -> it); } private static final Map resolvedGroupArtifactVersionCache = new ConcurrentHashMap<>(); private static ResolvedGroupArtifactVersion resolvedGroupArtifactVersion(ResolvedDependency dep) { return resolvedGroupArtifactVersionCache.computeIfAbsent(new ResolvedGroupArtifactVersion( null, dep.getModuleGroup(), dep.getModuleName(), dep.getModuleVersion(), null), it -> it); } /** * Some Gradle dependency functions will have the String "unspecified" to indicate a missing value. * Rewrite's dependency API represents these missing things as "null" */ private static @Nullable String unspecifiedToNull(@Nullable String maybeUnspecified) { if ("unspecified".equals(maybeUnspecified)) { return null; } return maybeUnspecified; } static Map dependencyConfigurations(ConfigurationContainer configurationContainer) { Map results = new HashMap<>(); List configurations = new ArrayList<>(configurationContainer); for (Configuration conf : configurations) { try { List requested = conf.getAllDependencies().stream() .map(dep -> dependency(dep, conf)) .collect(Collectors.toList()); List resolved; Map gaToRequested = requested.stream() .collect(Collectors.toMap(GradleProjectBuilder::groupArtifact, dep -> dep, (a, b) -> a)); String exceptionType = null; String exceptionMessage = null; // Archives and default are redundant with other configurations // Newer versions of gradle display warnings with long stack traces when attempting to resolve them // Some Scala plugin we don't care about creates configurations that, for some unknown reason, are difficult to resolve if (conf.isCanBeResolved() && !"archives".equals(conf.getName()) && !"default".equals(conf.getName()) && !conf.getName().startsWith("incrementalScalaAnalysis")) { ResolvedConfiguration resolvedConf = conf.getResolvedConfiguration(); if (resolvedConf.hasError()) { try { resolvedConf.rethrowFailure(); } catch (ResolveException e) { exceptionType = e.getClass().getName(); exceptionMessage = e.getMessage(); } } Map gaToResolved = resolvedConf.getFirstLevelModuleDependencies().stream() .collect(Collectors.toMap(GradleProjectBuilder::groupArtifact, dep -> dep, (a, b) -> a)); resolved = resolved(gaToRequested, gaToResolved); } else { resolved = emptyList(); } GradleDependencyConfiguration dc = new GradleDependencyConfiguration(conf.getName(), conf.getDescription(), conf.isTransitive(), conf.isCanBeResolved(), conf.isCanBeConsumed(), emptyList(), requested, resolved, exceptionType, exceptionMessage); results.put(conf.getName(), dc); } catch (Exception e) { GradleDependencyConfiguration dc = new GradleDependencyConfiguration(conf.getName(), conf.getDescription(), conf.isTransitive(), conf.isCanBeResolved(), conf.isCanBeConsumed(), emptyList(), emptyList(), emptyList(), e.getClass().getName(), e.getMessage()); results.put(conf.getName(), dc); } } // Record the relationships between dependency configurations for (Configuration conf : configurations) { if (conf.getExtendsFrom().isEmpty()) { continue; } GradleDependencyConfiguration dc = results.get(conf.getName()); if (dc != null) { List extendsFrom = conf.getExtendsFrom().stream() .map(it -> results.get(it.getName())) .collect(Collectors.toList()); dc.unsafeSetExtendsFrom(extendsFrom); } } return results; } private static final Map requestedCache = new ConcurrentHashMap<>(); private static org.openrewrite.maven.tree.Dependency dependency(Dependency dep, Configuration configuration) { GroupArtifactVersion gav = groupArtifactVersion(dep); return requestedCache.computeIfAbsent(gav, it -> org.openrewrite.maven.tree.Dependency.builder() .gav(gav) .type("jar") .scope(configuration.getName()) .exclusions(emptyList()) .build() ); } private static List resolved( Map gaToRequested, Map gaToResolved) { Map resolvedCache = new HashMap<>(); return gaToResolved.entrySet().stream() .map(entry -> { GroupArtifact ga = entry.getKey(); ResolvedDependency resolved = entry.getValue(); // Gradle knows which repository it got a dependency from, but haven't been able to find where that info lives ResolvedGroupArtifactVersion resolvedGav = resolvedGroupArtifactVersion(resolved); org.openrewrite.maven.tree.ResolvedDependency resolvedDependency = resolvedCache.get(resolvedGav); if (resolvedDependency == null) { resolvedDependency = org.openrewrite.maven.tree.ResolvedDependency.builder() .gav(resolvedGav) // There may not be a requested entry if a dependency substitution rule took effect // the DependencyHandler has the substitution mapping buried inside it, but not exposed publicly // Possible improvement to dig that out and use it .requested(gaToRequested.getOrDefault(ga, dependency(resolved))) .dependencies(resolved.getChildren().stream() .map(child -> resolved(child, 1, resolvedCache)) .collect(toList())) .licenses(emptyList()) .depth(0) .build(); resolvedCache.put(resolvedGav, resolvedDependency); } return resolvedDependency; }) .collect(Collectors.toList()); } /** * When there is a resolved dependency that cannot be matched up with a requested dependency, construct a requested * dependency corresponding to the exact version which was resolved. This isn't strictly accurate, but there is no * obvious way to access the resolution of transitive dependencies to figure out what versions are requested during * the resolution process. */ private static org.openrewrite.maven.tree.Dependency dependency(ResolvedDependency dep) { GroupArtifactVersion gav = groupArtifactVersion(dep); return requestedCache.computeIfAbsent(gav, it -> org.openrewrite.maven.tree.Dependency.builder() .gav(gav) .type("jar") .scope(dep.getConfiguration()) .exclusions(emptyList()) .build() ); } private static org.openrewrite.maven.tree.ResolvedDependency resolved( ResolvedDependency dep, int depth, Map resolvedCache ) { ResolvedGroupArtifactVersion resolvedGav = resolvedGroupArtifactVersion(dep); org.openrewrite.maven.tree.ResolvedDependency resolvedDependency = resolvedCache.get(resolvedGav); if (resolvedDependency == null) { List dependencies = new ArrayList<>(); resolvedDependency = org.openrewrite.maven.tree.ResolvedDependency.builder() .gav(resolvedGav) .requested(dependency(dep)) .dependencies(dependencies) .licenses(emptyList()) .depth(depth).build(); //we add a temporal resolved dependency in the cache to avoid stackoverflow with dependencies that have cycles resolvedCache.put(resolvedGav, resolvedDependency); dep.getChildren().forEach(child -> dependencies.add(resolved(child, depth + 1, resolvedCache))); } return resolvedDependency; } @SuppressWarnings("unused") public static void clearCaches() { requestedCache.clear(); groupArtifactCache.clear(); groupArtifactVersionCache.clear(); resolvedGroupArtifactVersionCache.clear(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy