diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index c334be719ee..2c29b22720b 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -70,6 +70,11 @@ gradlePlugin { id = 'firebase-library' implementationClass = 'com.google.firebase.gradle.plugins.FirebaseLibraryPlugin' } + + firebaseJavaLibraryPlugin { + id = "firebase-java-library" + implementationClass = 'com.google.firebase.gradle.plugins.FirebaseJavaLibraryPlugin' + } } } diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Dokka.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Dokka.java index 19e5030cc62..2f628e5ed69 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Dokka.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/Dokka.java @@ -16,6 +16,7 @@ import com.android.build.gradle.LibraryExtension; import com.google.common.collect.ImmutableMap; +import com.sun.istack.Nullable; import java.io.File; import java.net.MalformedURLException; import java.net.URL; @@ -29,6 +30,7 @@ import org.gradle.api.tasks.Copy; import org.jetbrains.dokka.DokkaConfiguration; import org.jetbrains.dokka.gradle.DokkaAndroidTask; +import org.jetbrains.dokka.gradle.DokkaTask; final class Dokka { /** @@ -47,94 +49,63 @@ final class Dokka { *
  • Remove the "https://firebase.google.com" prefix from all urls */ static void configure( - Project project, LibraryExtension android, FirebaseLibraryExtension firebaseLibrary) { - project.apply(ImmutableMap.of("plugin", "org.jetbrains.dokka-android")); + Project project, + @Nullable LibraryExtension android, + FirebaseLibraryExtension firebaseLibrary) { + + String dokkaPluginName = + android == null ? "org.jetbrains.dokka" : "org.jetbrains.dokka-android"; + project.apply(ImmutableMap.of("plugin", dokkaPluginName)); if (!firebaseLibrary.publishJavadoc) { project.getTasks().register("kotlindoc"); return; } - DokkaAndroidTask dokkaAndroidTask = - project - .getTasks() - .create( - "kotlindocDokka", - DokkaAndroidTask.class, - dokka -> { - dokka.setOutputDirectory(project.getBuildDir() + "/dokka/firebase"); - dokka.setOutputFormat("dac"); - - dokka.setGenerateClassIndexPage(false); - dokka.setGeneratePackageIndexPage(false); - if (!project.getPluginManager().hasPlugin("kotlin-android")) { - dokka.dependsOn("docStubs"); - dokka.setSourceDirs( - Collections.singletonList( - project.file(project.getBuildDir() + "/doc-stubs"))); - } + Class taskClass = + android == null ? DokkaTask.class : DokkaAndroidTask.class; + DokkaTask dokkaTask = configure(project, taskClass); - dokka.setNoAndroidSdkLink(true); + if (dokkaTask instanceof DokkaAndroidTask) { + ((DokkaAndroidTask) dokkaTask).setNoAndroidSdkLink(true); - createLink( - project, - "https://developers.android.com/reference/kotlin/", - "kotlindoc/package-lists/android/package-list") - .map(dokka.getExternalDocumentationLinks()::add); - createLink( - project, - "https://developers.google.com/android/reference/", - "kotlindoc/package-lists/google/package-list") - .map(dokka.getExternalDocumentationLinks()::add); - createLink( - project, - "https://firebase.google.com/docs/reference/kotlin/", - "kotlindoc/package-lists/firebase/package-list") - .map(dokka.getExternalDocumentationLinks()::add); - createLink( - project, - "https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/", - "kotlindoc/package-lists/coroutines/package-list") - .map(dokka.getExternalDocumentationLinks()::add); + android + .getLibraryVariants() + .all( + v -> { + if (v.getName().equals("release")) { + project.afterEvaluate( + p -> { + FileCollection artifactFiles = + v.getRuntimeConfiguration() + .getIncoming() + .artifactView( + view -> { + view.attributes( + attrs -> + attrs.attribute( + Attribute.of("artifactType", String.class), + "jar")); + view.componentFilter( + c -> + !c.getDisplayName() + .startsWith("androidx.annotation:annotation:")); + }) + .getArtifacts() + .getArtifactFiles() + .plus(project.files(android.getBootClasspath())); + dokkaTask.setClasspath(artifactFiles); + }); + } + }); + } - android - .getLibraryVariants() - .all( - v -> { - if (v.getName().equals("release")) { - project.afterEvaluate( - p -> { - FileCollection artifactFiles = - v.getRuntimeConfiguration() - .getIncoming() - .artifactView( - view -> { - view.attributes( - attrs -> - attrs.attribute( - Attribute.of( - "artifactType", String.class), - "jar")); - view.componentFilter( - c -> - !c.getDisplayName() - .startsWith( - "androidx.annotation:annotation:")); - }) - .getArtifacts() - .getArtifactFiles() - .plus(project.files(android.getBootClasspath())); - dokka.setClasspath(artifactFiles); - }); - } - }); - }); project .getTasks() .create( "kotlindoc", Copy.class, copy -> { - copy.dependsOn(dokkaAndroidTask); + copy.dependsOn(dokkaTask); copy.setDestinationDir( project.file(project.getRootProject().getBuildDir() + "/firebase-kotlindoc")); copy.from( @@ -161,6 +132,49 @@ static void configure( }); } + static T configure(Project project, Class taskType) { + return project + .getTasks() + .create( + "kotlindocDokka", + taskType, + dokka -> { + dokka.setOutputDirectory(project.getBuildDir() + "/dokka/firebase"); + dokka.setOutputFormat("dac"); + + dokka.setGenerateClassIndexPage(false); + dokka.setGeneratePackageIndexPage(false); + if (!project.getPluginManager().hasPlugin("kotlin-android")) { + dokka.dependsOn("docStubs"); + dokka.setSourceDirs( + Collections.singletonList(project.file(project.getBuildDir() + "/doc-stubs"))); + } + + dokka.setNoJdkLink(true); + + createLink( + project, + "https://developers.android.com/reference/kotlin/", + "kotlindoc/package-lists/android/package-list") + .map(dokka.getExternalDocumentationLinks()::add); + createLink( + project, + "https://developers.google.com/android/reference/", + "kotlindoc/package-lists/google/package-list") + .map(dokka.getExternalDocumentationLinks()::add); + createLink( + project, + "https://firebase.google.com/docs/reference/kotlin/", + "kotlindoc/package-lists/firebase/package-list") + .map(dokka.getExternalDocumentationLinks()::add); + createLink( + project, + "https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/", + "kotlindoc/package-lists/coroutines/package-list") + .map(dokka.getExternalDocumentationLinks()::add); + }); + } + private static Optional createLink( Project project, String url, String packageListPath) { diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseJavaLibraryPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseJavaLibraryPlugin.java new file mode 100644 index 00000000000..a90c0d2fa90 --- /dev/null +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseJavaLibraryPlugin.java @@ -0,0 +1,200 @@ +// Copyright 2019 Google LLC +// +// 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 +// +// http://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 com.google.firebase.gradle.plugins; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.firebase.gradle.plugins.apiinfo.ApiInformationTask; +import com.google.firebase.gradle.plugins.apiinfo.GenerateApiTxtFileTask; +import com.google.firebase.gradle.plugins.apiinfo.GenerateStubsTask; +import com.google.firebase.gradle.plugins.apiinfo.GetMetalavaJarTask; +import com.google.firebase.gradle.plugins.ci.Coverage; +import java.io.File; +import java.nio.file.Paths; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.attributes.Attribute; +import org.gradle.api.file.FileCollection; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile; + +// TODO(vkryachko): extract functionality common across Firebase{,Java}LibraryPlugin plugins. +public class FirebaseJavaLibraryPlugin implements Plugin { + + @Override + public void apply(Project project) { + project.apply(ImmutableMap.of("plugin", "java-library")); + FirebaseLibraryExtension firebaseLibrary = + project + .getExtensions() + .create("firebaseLibrary", FirebaseLibraryExtension.class, project, LibraryType.JAVA); + + // reduce the likelihood of kotlin module files colliding. + project + .getTasks() + .withType( + KotlinCompile.class, + kotlin -> + kotlin + .getKotlinOptions() + .setFreeCompilerArgs( + ImmutableList.of("-module-name", kotlinModuleName(project)))); + + setupStaticAnalysis(project, firebaseLibrary); + project.afterEvaluate(p -> Dokka.configure(project, null, firebaseLibrary)); + } + + private static void setupStaticAnalysis(Project project, FirebaseLibraryExtension library) { + project.afterEvaluate( + p -> + project + .getConfigurations() + .all( + c -> { + if ("annotationProcessor".equals(c.getName())) { + for (String checkProject : library.staticAnalysis.errorproneCheckProjects) { + project + .getDependencies() + .add("annotationProcessor", project.project(checkProject)); + } + } + if ("lintChecks".equals(c.getName())) { + for (String checkProject : + library.staticAnalysis.androidLintCheckProjects) { + project + .getDependencies() + .add("lintChecks", project.project(checkProject)); + } + } + })); + + setupApiInformationAnalysis(project); + + project.getTasks().register("firebaseLint", task -> task.dependsOn("lint")); + Coverage.apply(library); + } + + private static void setupApiInformationAnalysis(Project project) { + File metalavaOutputJarFile = new File(project.getRootProject().getBuildDir(), "metalava.jar"); + SourceSet mainSourceSet = + project + .getConvention() + .getPlugin(JavaPluginConvention.class) + .getSourceSets() + .getByName("main"); + File outputFile = + project + .getRootProject() + .file( + Paths.get( + project.getRootProject().getBuildDir().getPath(), + "apiinfo", + project.getPath().substring(1).replace(":", "_"))); + File outputApiFile = new File(outputFile.getAbsolutePath() + "_api.txt"); + + project + .getTasks() + .register( + "getMetalavaJar", + GetMetalavaJarTask.class, + task -> { + task.setOutputFile(metalavaOutputJarFile); + }); + File apiTxt = + project.file("api.txt").exists() + ? project.file("api.txt") + : project.file(project.getRootDir() + "/empty-api.txt"); + TaskProvider apiInfo = + project + .getTasks() + .register( + "apiInformation", + ApiInformationTask.class, + task -> { + task.setApiTxt(apiTxt); + task.setMetalavaJarPath(metalavaOutputJarFile.getAbsolutePath()); + task.setSourceSet(mainSourceSet); + task.setOutputFile(outputFile); + task.setBaselineFile(project.file("baseline.txt")); + task.setOutputApiFile(outputApiFile); + if (project.hasProperty("updateBaseline")) { + task.setUpdateBaseline(true); + } else { + task.setUpdateBaseline(false); + } + task.dependsOn("getMetalavaJar"); + }); + + TaskProvider generateApiTxt = + project + .getTasks() + .register( + "generateApiTxtFile", + GenerateApiTxtFileTask.class, + task -> { + task.setApiTxt(project.file("api.txt")); + task.setMetalavaJarPath(metalavaOutputJarFile.getAbsolutePath()); + task.setSourceSet(mainSourceSet); + task.setBaselineFile(project.file("baseline.txt")); + task.setUpdateBaseline(project.hasProperty("updateBaseline")); + task.dependsOn("getMetalavaJar"); + }); + + TaskProvider docStubs = + project + .getTasks() + .register( + "docStubs", + GenerateStubsTask.class, + task -> { + task.setMetalavaJarPath(metalavaOutputJarFile.getAbsolutePath()); + task.setOutputDir(new File(project.getBuildDir(), "doc-stubs")); + task.dependsOn("getMetalavaJar"); + + task.setSourceSet(mainSourceSet); + }); + project.getTasks().getByName("check").dependsOn(docStubs); + + project.afterEvaluate( + p -> { + FileCollection classpath = + project + .getConfigurations() + .getByName("runtimeClasspath") + .getIncoming() + .artifactView( + config -> + config.attributes( + container -> + container.attribute( + Attribute.of("artifactType", String.class), "jar"))) + .getArtifacts() + .getArtifactFiles(); + + apiInfo.configure(t -> t.setClassPath(classpath)); + generateApiTxt.configure(t -> t.setClassPath(classpath)); + docStubs.configure(t -> t.setClassPath(classpath)); + }); + } + + private static String kotlinModuleName(Project project) { + + String fullyQualifiedProjectPath = project.getPath().replaceAll(":", "-"); + + return project.getRootProject().getName() + fullyQualifiedProjectPath; + } +} diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java index fdf6cd66263..9f7149d6565 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java @@ -14,8 +14,10 @@ package com.google.firebase.gradle.plugins; +import com.android.build.gradle.LibraryExtension; import com.google.common.collect.ImmutableSet; import com.google.firebase.gradle.plugins.ci.device.FirebaseTestLabExtension; +import java.io.File; import java.util.Collections; import java.util.HashSet; import java.util.Optional; @@ -27,6 +29,7 @@ import org.gradle.api.Project; import org.gradle.api.UnknownDomainObjectException; import org.gradle.api.internal.provider.DefaultProvider; +import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.provider.Property; import org.gradle.api.publish.maven.MavenPom; @@ -180,6 +183,36 @@ public String getPath() { return project.getPath(); } + public Set getSrcDirs() { + switch (type) { + case ANDROID: + return project + .getExtensions() + .getByType(LibraryExtension.class) + .getSourceSets() + .getByName("main") + .getJava() + .getSrcDirs(); + case JAVA: + return project + .getConvention() + .getPlugin(JavaPluginConvention.class) + .getSourceSets() + .getByName("main") + .getJava() + .getSrcDirs(); + default: + throw new IllegalStateException("Unsupported project type: " + type); + } + } + + public String getRuntimeClasspath() { + if (type.equals(LibraryType.ANDROID)) { + return "releaseRuntimeClasspath"; + } + return "runtimeClasspath"; + } + @Override public String toString() { return String.format( diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/LibraryType.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/LibraryType.java index f29e10ff881..2be7d76b450 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/LibraryType.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/LibraryType.java @@ -27,4 +27,8 @@ public enum LibraryType { public String getFormat() { return format; } + + public String getComponentName() { + return name().toLowerCase(); + } } diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/SdkUtil.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/SdkUtil.java index e8e0906718f..5d8fe665781 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/SdkUtil.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/SdkUtil.java @@ -48,7 +48,7 @@ public static File getSdkDir(Project project) { public static File getAndroidJar(Project project) { LibraryExtension android = project.getExtensions().findByType(LibraryExtension.class); if (android == null) { - throw new GradleException("Project " + project.getPath() + " is not an android library."); + return null; } return new File( getSdkDir(project), diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/ApiInformationTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/ApiInformationTask.java index 6fe7a089ef1..ec20994484d 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/ApiInformationTask.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/ApiInformationTask.java @@ -16,25 +16,22 @@ import com.android.build.gradle.api.AndroidSourceSet; import com.google.firebase.gradle.plugins.SdkUtil; - import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; -import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.FileCollection; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskAction; /** @@ -49,7 +46,7 @@ public abstract class ApiInformationTask extends DefaultTask { @InputFile abstract File getApiTxt(); - abstract AndroidSourceSet getSourceSet(); + abstract Object getSourceSet(); @InputFiles abstract FileCollection getClassPath(); @@ -66,7 +63,7 @@ public abstract class ApiInformationTask extends DefaultTask { @OutputFile abstract File getOutputFile(); - public abstract void setSourceSet(AndroidSourceSet value); + public abstract void setSourceSet(Object value); public abstract void setClassPath(FileCollection value); @@ -82,10 +79,19 @@ public abstract class ApiInformationTask extends DefaultTask { public abstract void setOutputFile(File value); + private Set getSourceDirs() { + if (getSourceSet() instanceof SourceSet) { + return ((SourceSet) getSourceSet()).getJava().getSrcDirs(); + } else if (getSourceSet() instanceof AndroidSourceSet) { + return ((AndroidSourceSet) getSourceSet()).getJava().getSrcDirs(); + } + throw new IllegalStateException("Unsupported sourceSet provided: " + getSourceSet().getClass()); + } + @TaskAction void execute() { String sourcePath = - getSourceSet().getJava().getSrcDirs().stream() + getSourceDirs().stream() .filter(File::exists) .map(File::getAbsolutePath) .collect(Collectors.joining(":")); @@ -97,11 +103,17 @@ void execute() { } String classPath = - Stream.concat( - getClassPath().getFiles().stream(), Stream.of(SdkUtil.getAndroidJar(getProject()))) + getClassPath().getFiles().stream() .map(File::getAbsolutePath) .collect(Collectors.joining(":")); + File androidJar = SdkUtil.getAndroidJar(getProject()); + if (androidJar != null) { + classPath += ":" + androidJar.getAbsolutePath(); + } + + String cp = classPath; + File outputFileDir = getOutputFile().getParentFile(); if (!outputFileDir.exists()) { outputFileDir.mkdirs(); @@ -119,7 +131,7 @@ void execute() { "--source-path", sourcePath, "--classpath", - classPath, + cp, "--api", getOutputApiFile().getAbsolutePath(), "--format=v2")); diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateApiTxtFileTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateApiTxtFileTask.java index 412ff890341..ded7f12956d 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateApiTxtFileTask.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateApiTxtFileTask.java @@ -16,19 +16,18 @@ import com.android.build.gradle.api.AndroidSourceSet; import com.google.firebase.gradle.plugins.SdkUtil; - import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; - import org.gradle.api.DefaultTask; import org.gradle.api.file.FileCollection; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskAction; public abstract class GenerateApiTxtFileTask extends DefaultTask { @@ -39,7 +38,7 @@ public abstract class GenerateApiTxtFileTask extends DefaultTask { @OutputFile abstract File getApiTxt(); - abstract AndroidSourceSet getSourceSet(); + abstract Object getSourceSet(); @InputFiles public abstract FileCollection getClassPath(); @@ -50,7 +49,7 @@ public abstract class GenerateApiTxtFileTask extends DefaultTask { @Input abstract boolean getUpdateBaseline(); - public abstract void setSourceSet(AndroidSourceSet value); + public abstract void setSourceSet(Object value); public abstract void setClassPath(FileCollection value); @@ -62,10 +61,19 @@ public abstract class GenerateApiTxtFileTask extends DefaultTask { public abstract void setApiTxt(File value); + private Set getSourceDirs() { + if (getSourceSet() instanceof SourceSet) { + return ((SourceSet) getSourceSet()).getJava().getSrcDirs(); + } else if (getSourceSet() instanceof AndroidSourceSet) { + return ((AndroidSourceSet) getSourceSet()).getJava().getSrcDirs(); + } + throw new IllegalStateException("Unsupported sourceSet provided: " + getSourceSet().getClass()); + } + @TaskAction void execute() { String sourcePath = - getSourceSet().getJava().getSrcDirs().stream() + getSourceDirs().stream() .filter(File::exists) .map(File::getAbsolutePath) .collect(Collectors.joining(":")); @@ -76,10 +84,14 @@ void execute() { return; } String classPath = - Stream.concat( - getClassPath().getFiles().stream(), Stream.of(SdkUtil.getAndroidJar(getProject()))) + getClassPath().getFiles().stream() .map(File::getAbsolutePath) .collect(Collectors.joining(":")); + + File androidJar = SdkUtil.getAndroidJar(getProject()); + if (androidJar != null) { + classPath += ":" + androidJar.getAbsolutePath(); + } List args = new ArrayList<>( Arrays.asList( @@ -88,7 +100,7 @@ void execute() { "--source-path", sourcePath, "--classpath", - classPath, + classPath, "--api", getApiTxt().getAbsolutePath(), "--format=v2")); diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateStubsTask.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateStubsTask.java index e39a0e882eb..8fff3b252d7 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateStubsTask.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/apiinfo/GenerateStubsTask.java @@ -16,17 +16,16 @@ import com.android.build.gradle.api.AndroidSourceSet; import com.google.firebase.gradle.plugins.SdkUtil; - import java.io.File; import java.util.Arrays; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; - import org.gradle.api.DefaultTask; import org.gradle.api.file.FileCollection; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskAction; public abstract class GenerateStubsTask extends DefaultTask { @@ -35,12 +34,12 @@ public abstract class GenerateStubsTask extends DefaultTask { public abstract void setMetalavaJarPath(String path); - public abstract AndroidSourceSet getSourceSet(); + public abstract Object getSourceSet(); @InputFiles public abstract FileCollection getClassPath(); - public abstract void setSourceSet(AndroidSourceSet sourceSet); + public abstract void setSourceSet(Object sourceSet); public abstract void setClassPath(FileCollection value); @@ -49,20 +48,35 @@ public abstract class GenerateStubsTask extends DefaultTask { public abstract void setOutputDir(File dir); + private Set getSourceDirs() { + if (getSourceSet() instanceof SourceSet) { + return ((SourceSet) getSourceSet()).getJava().getSrcDirs(); + } else if (getSourceSet() instanceof AndroidSourceSet) { + return ((AndroidSourceSet) getSourceSet()).getJava().getSrcDirs(); + } + throw new IllegalStateException("Unsupported sourceSet provided: " + getSourceSet().getClass()); + } + @TaskAction public void run() { String sourcePath = - getSourceSet().getJava().getSrcDirs().stream() + getSourceDirs().stream() .filter(File::exists) .map(File::getAbsolutePath) .collect(Collectors.joining(":")); String classPath = - Stream.concat( - getClassPath().getFiles().stream(), Stream.of(SdkUtil.getAndroidJar(getProject()))) + getClassPath().getFiles().stream() .map(File::getAbsolutePath) .collect(Collectors.joining(":")); + File androidJar = SdkUtil.getAndroidJar(getProject()); + if (androidJar != null) { + classPath += ":" + androidJar.getAbsolutePath(); + } + + String cp = classPath; + getProject() .javaexec( spec -> { @@ -74,7 +88,7 @@ public void run() { "--source-path", sourcePath, "--classpath", - classPath, + cp, "--include-annotations", "--doc-stubs", getOutputDir().getAbsolutePath())); diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/SmokeTestsPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/SmokeTestsPlugin.java index 426cf07813a..5c73a641845 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/SmokeTestsPlugin.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/ci/SmokeTestsPlugin.java @@ -112,7 +112,9 @@ private static Set getChangedProjects(Project p) { private static void getChangedProjectsLoop(Collection projects, Set changed) { for (Project p : projects) { // Skip project if it is not a Firebase library. - if (p.getExtensions().findByType(FirebaseLibraryExtension.class) == null) { + FirebaseLibraryExtension library = + p.getExtensions().findByType(FirebaseLibraryExtension.class); + if (library == null) { continue; } @@ -123,7 +125,7 @@ private static void getChangedProjectsLoop(Collection projects, Set affected = all.stream() .filter(it -> it instanceof ProjectDependency) diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/Publisher.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/Publisher.java index b9531c0ad97..72cd594205e 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/Publisher.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/Publisher.java @@ -90,7 +90,7 @@ private static void validatePomXml(FirebaseLibraryExtension library, Element roo } private static void processDependencies(FirebaseLibraryExtension library, Element rootElement) { - Map deps = getDependencyTypes(library.project); + Map deps = getDependencyTypes(library); NodeList dependencies = rootElement.getElementsByTagName("dependency"); List depsToRemove = new ArrayList<>(); @@ -125,13 +125,14 @@ private static String renderVersion(String baseVersion, Mode mode) { return baseVersion + (Mode.SNAPSHOT.equals(mode) ? "-SNAPSHOT" : ""); } - private static Map getDependencyTypes(Project project) { + private static Map getDependencyTypes(FirebaseLibraryExtension firebaseLibrary) { + Project project = firebaseLibrary.project; Configuration dummyDependencyConfiguration = project.getConfigurations().create("publisherDummyConfig"); Set nonProjectDependencies = project .getConfigurations() - .getByName("releaseRuntimeClasspath") + .getByName(firebaseLibrary.getRuntimeClasspath()) .getAllDependencies() .stream() .filter(dep -> !(dep instanceof ProjectDependency)) @@ -141,7 +142,7 @@ private static Map getDependencyTypes(Project project) { try { return project .getConfigurations() - .getByName("releaseRuntimeClasspath") + .getByName(firebaseLibrary.getRuntimeClasspath()) .getAllDependencies() .stream() .map(dep -> getType(dummyDependencyConfiguration, dep)) diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/PublishingPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/PublishingPlugin.java index 0387f6fb786..d92c78bacd8 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/PublishingPlugin.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/publish/PublishingPlugin.java @@ -14,7 +14,6 @@ package com.google.firebase.gradle.plugins.publish; -import com.android.build.gradle.LibraryExtension; import com.google.common.collect.ImmutableMap; import com.google.firebase.gradle.plugins.FirebaseLibraryExtension; import digital.wup.android_maven_publish.AndroidMavenPublishPlugin; @@ -106,11 +105,11 @@ public void apply(Project project) { Publisher publisher = new Publisher(publishMode, projectsToPublish); project.subprojects( sub -> { - if (!sub.getPlugins().hasPlugin("firebase-library")) { + FirebaseLibraryExtension firebaseLibrary = + sub.getExtensions().findByType(FirebaseLibraryExtension.class); + if (firebaseLibrary == null) { return; } - FirebaseLibraryExtension firebaseLibrary = - sub.getExtensions().getByType(FirebaseLibraryExtension.class); sub.apply(ImmutableMap.of("plugin", AndroidMavenPublishPlugin.class)); PublishingExtension publishing = @@ -132,7 +131,9 @@ public void apply(Project project) { "mavenAar", MavenPublication.class, publication -> { - publication.from(sub.getComponents().findByName("android")); + publication.from( + sub.getComponents() + .findByName(firebaseLibrary.type.getComponentName())); publication.setArtifactId(firebaseLibrary.artifactId.get()); publication.setGroupId(firebaseLibrary.groupId.get()); if (firebaseLibrary.publishSources) { @@ -142,13 +143,7 @@ public void apply(Project project) { "sourceJar", Jar.class, jar -> { - jar.from( - sub.getExtensions() - .getByType(LibraryExtension.class) - .getSourceSets() - .getByName("main") - .getJava() - .getSrcDirs()); + jar.from(firebaseLibrary.getSrcDirs()); jar.getArchiveClassifier().set("sources"); })); } diff --git a/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/PublishingPluginTests.kt b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/PublishingPluginTests.kt index 7492c3fa6eb..6e138919e28 100644 --- a/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/PublishingPluginTests.kt +++ b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/PublishingPluginTests.kt @@ -78,6 +78,49 @@ licenses { scope = "compile"))) } + @Test + fun `Publishing dependent projects one of which is a jar succeeds`() { + val project1 = Project(name = "childProject1", version = "1.0", libraryType = LibraryType.JAVA) + val project2 = Project( + name = "childProject2", + version = "0.9", + projectDependencies = setOf(project1), + customizePom = """ +licenses { + license { + name = 'Hello' + } +} +""") + subprojectsDefined(project1, project2) + val result = publish(Mode.RELEASE, project1, project2) + assertThat(result.task(":firebasePublish")?.outcome).isEqualTo(TaskOutcome.SUCCESS) + + val pomOrNull1 = project1.getPublishedPom("${testProjectDir.root}/build/m2repository") + val pomOrNull2 = project2.getPublishedPom("${testProjectDir.root}/build/m2repository") + assertThat(pomOrNull1).isNotNull() + assertThat(pomOrNull2).isNotNull() + val pom1 = pomOrNull1!! + val pom2 = pomOrNull2!! + + assertThat(pom1.artifact.version).isEqualTo(project1.version) + assertThat(pom2.artifact.version).isEqualTo(project2.version) + assertThat(pom1.license).isEqualTo(License( + "The Apache Software License, Version 2.0", + "http://www.apache.org/licenses/LICENSE-2.0.txt")) + assertThat(pom2.license).isEqualTo(License( + "Hello", + "")) + + assertThat(pom2.dependencies).isEqualTo( + listOf(Artifact( + groupId = project1.group, + artifactId = project1.name, + version = project1.version, + type = Type.JAR, + scope = "compile"))) + } + @Test fun `Publish with unreleased dependency`() { val project1 = Project(name = "childProject1", version = "1.0") diff --git a/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/publishing.kt b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/publishing.kt index 409e2423dbf..285643724ae 100644 --- a/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/publishing.kt +++ b/buildSrc/src/test/kotlin/com/google/firebase/gradle/plugins/publishing.kt @@ -28,12 +28,13 @@ data class Project( val projectDependencies: Set = setOf(), val externalDependencies: Set = setOf(), val releaseWith: Project? = null, - val customizePom: String? = null + val customizePom: String? = null, + val libraryType: LibraryType = LibraryType.ANDROID ) { fun generateBuildFile(): String { return """ plugins { - id 'firebase-library' + id 'firebase-${if (libraryType == LibraryType.JAVA) "java-" else ""}library' } group = '$group' version = '$version' @@ -42,7 +43,7 @@ data class Project( ${if (releaseWith != null) "releaseWith project(':${releaseWith.name}')" else ""} ${if (customizePom != null) "customizePom {$customizePom}" else ""} } - android.compileSdkVersion = 26 + ${if (libraryType == LibraryType.ANDROID) "android.compileSdkVersion = 26" else ""} repositories { google()