package com.hanhuy.gradle.discovery; import com.android.build.gradle.BaseExtension; import com.android.builder.model.AndroidProject; import com.android.builder.model.PackagingOptions; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.dsl.RepositoryHandler; import org.gradle.api.artifacts.repositories.ArtifactRepository; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.tooling.model.GradleProject; import org.gradle.tooling.provider.model.ToolingModelBuilder; import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry; import javax.inject.Inject; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Set; /** * @author pfnguyen */ public class GradleBuildPlugin implements Plugin<Project> { private final ToolingModelBuilderRegistry registry; @Inject public GradleBuildPlugin(ToolingModelBuilderRegistry registry) { this.registry = registry; } @Override public void apply(Project project) { registry.register(new GradleBuildModelBuilder(registry)); } private static class GradleBuildModelBuilder implements ToolingModelBuilder { private final ToolingModelBuilderRegistry registry; public GradleBuildModelBuilder(ToolingModelBuilderRegistry registry) { this.registry = registry; } @Override public boolean canBuild(String modelName) { return modelName.equals(GradleBuildModel.class.getName()); } @Override public Object buildAll(String modelName, Project project) { return new GradleBuildM(project, registry); } } private static Method findMethod(Object instance, String name) throws NoSuchMethodException { for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) { try { Method method = clazz.getDeclaredMethod(name); if (!method.isAccessible()) { method.setAccessible(true); } return method; } catch (NoSuchMethodException e) { // ignore and search next } } throw new NoSuchMethodException("Method " + name); } public static class GradleBuildM implements Serializable { private final static String GRADLE_PROJECT = GradleProject.class.getName(); private final static String ANDROID_PROJECT = AndroidProject.class.getName(); private final AndroidDiscoveryModel discovery; private final RepositoryListModel repositories; private final Object gradleProject; private final Object androidProject; private final Object packagingOptions; public GradleBuildM(Project project, ToolingModelBuilderRegistry registry) { ToolingModelBuilder b; discovery = new AndroidDiscovery( project.getPlugins().hasPlugin("com.android.application"), project.getPlugins().hasPlugin("com.android.library")); repositories = new RM(project.getRepositories()); b = registry.getBuilder(GRADLE_PROJECT); gradleProject = b.buildAll(GRADLE_PROJECT, project); if (discovery.isApplication() || discovery.isLibrary()) { b = registry.getBuilder(ANDROID_PROJECT); androidProject = b.buildAll(ANDROID_PROJECT, project); packagingOptions = new PO(project.getExtensions().findByName("android")); } else { androidProject = null; packagingOptions = null; } } public RepositoryListModel getRepositories() { return repositories; } public AndroidDiscoveryModel getDiscovery() { return discovery; } public Object getAndroidProject() { return androidProject; } public Object getGradleProject() { return gradleProject; } public Object getPackagingOptions() { return packagingOptions; } } public static class PO implements PackagingOptions, Serializable { private final Set<String> excludes; private final Set<String> firsts; private final Set<String> merges; @SuppressWarnings("unchecked") public PO(Object extension) { Set<String> e = Collections.EMPTY_SET, f = Collections.EMPTY_SET, m = Collections.EMPTY_SET; try { Object po = findMethod(extension, "getPackagingOptions").invoke(extension); e = (Set<String>) findMethod(po, "getExcludes").invoke(po); f = (Set<String>) findMethod(po, "getPickFirsts").invoke(po); m = (Set<String>) findMethod(po, "getMerges").invoke(po); } catch (Exception x) { // noop } excludes = e; firsts = f; merges = m; } @Override public Set<String> getExcludes() { return excludes; } @Override public Set<String> getPickFirsts() { return firsts; } @Override public Set<String> getMerges() { return merges; } } public static class AndroidDiscovery implements Serializable, AndroidDiscoveryModel { public final boolean hasApplicationPlugin; public boolean isLibrary() { return hasLibraryPlugin; } public final boolean hasLibraryPlugin; public AndroidDiscovery(boolean hasApplicationPlugin, boolean hasLibraryPlugin) { this.hasApplicationPlugin = hasApplicationPlugin; this.hasLibraryPlugin = hasLibraryPlugin; } public boolean isApplication() { return hasApplicationPlugin; } } public static class RM implements Serializable, RepositoryListModel { private final Collection<MavenRepositoryModel> resolvers = new ArrayList<MavenRepositoryModel>(); public RM(RepositoryHandler rh) { for (ArtifactRepository r : rh) { if (r instanceof MavenArtifactRepository) { resolvers.add(new MAR((MavenArtifactRepository)r)); } } } public Collection<MavenRepositoryModel> getResolvers() { return resolvers; } } public static class MAR implements MavenRepositoryModel, Serializable { private final URI url; private final Set<URI> artifactUrls; private final String name; public MAR(MavenArtifactRepository mar) { url = mar.getUrl(); artifactUrls = mar.getArtifactUrls(); name = mar.getName(); } public URI getUrl() { return url; } @Override public Set<URI> getArtifactUrls() { return artifactUrls; } @Override public String getName() { return name; } } }