diff --git a/.bazelrc b/.bazelrc index a9e05a75a..186c2d358 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,3 +1,5 @@ +build --experimental_google_legacy_api + build --java_language_version=11 build --java_runtime_version=remotejdk_11 diff --git a/private/rules/has_maven_deps.bzl b/private/rules/has_maven_deps.bzl index ccdd5add2..07f42e4a1 100644 --- a/private/rules/has_maven_deps.bzl +++ b/private/rules/has_maven_deps.bzl @@ -95,6 +95,7 @@ _gathered = provider( "artifact_infos", "transitive_exports", "dep_infos", + "artifact_coordinates", ], ) @@ -104,7 +105,7 @@ def _extract_from(gathered, maven_info, dep, include_transitive_exports): gathered.all_infos.append(maven_info) gathered.label_to_javainfo.update(maven_info.label_to_javainfo) if java_info: - if maven_info.coordinates: + if maven_info.coordinates and maven_info.coordinates != gathered.artifact_coordinates: gathered.dep_infos.append(dep[JavaInfo]) else: gathered.artifact_infos.append(dep[JavaInfo]) @@ -129,6 +130,7 @@ def _has_maven_deps_impl(target, ctx): transitive_exports = [], dep_infos = [], label_to_javainfo = {target.label: target[JavaInfo]}, + artifact_coordinates = coordinates, ) for attr in _ASPECT_ATTRS: diff --git a/private/rules/maven_project_jar.bzl b/private/rules/maven_project_jar.bzl index 5467dd7b3..8fe1d65e2 100644 --- a/private/rules/maven_project_jar.bzl +++ b/private/rules/maven_project_jar.bzl @@ -1,5 +1,5 @@ load(":has_maven_deps.bzl", "MavenInfo", "calculate_artifact_jars", "calculate_artifact_source_jars", "has_maven_deps") -load(":maven_utils.bzl", "determine_additional_dependencies") +load(":maven_utils.bzl", "determine_additional_dependencies", "unpack_coordinates") DEFAULT_EXCLUDED_WORKSPACES = [ # Note: we choose to drop the dependency entirely because @@ -64,7 +64,12 @@ def _maven_project_jar_impl(ctx): ) # Merge together all the binary jars - bin_jar = ctx.actions.declare_file("%s.jar" % ctx.label.name) + packaging = unpack_coordinates(info.coordinates).type or "jar" + bin_jar = ctx.actions.declare_file("%s.%s" % (ctx.label.name, packaging)) + + if packaging == "aar" and AndroidLibraryAarInfo in target and target[AndroidLibraryAarInfo].aar: + artifact_jars = artifact_jars + [target[AndroidLibraryAarInfo].aar] + _combine_jars( ctx, ctx.executable._merge_jars, diff --git a/private/templates/pom.tpl b/private/templates/pom.tpl index 1028295d1..c9acd719c 100644 --- a/private/templates/pom.tpl +++ b/private/templates/pom.tpl @@ -6,6 +6,7 @@ {groupId} {artifactId} {version} + {type} {dependencies} diff --git a/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/MergeJars.java b/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/MergeJars.java index 8fa7d9a1c..56377ea92 100644 --- a/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/MergeJars.java +++ b/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/MergeJars.java @@ -28,12 +28,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -45,18 +48,27 @@ import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; public class MergeJars { + private enum Packaging { + JAR, AAR; + } + public static void main(String[] args) throws IOException { Path out = null; // Insertion order may matter Set sources = new LinkedHashSet<>(); Set excludes = new HashSet<>(); DuplicateEntryStrategy onDuplicate = LAST_IN_WINS; + Packaging packaging = Packaging.JAR; + PathMatcher aarMatcher = FileSystems.getDefault().getPathMatcher("glob:*.aar"); + // AAR to build from + Path aarSource = null; for (int i = 0; i < args.length; i++) { switch (args[i]) { @@ -75,6 +87,9 @@ public static void main(String[] args) throws IOException { case "--output": out = Paths.get(args[++i]); + if (aarMatcher.matches(out.getFileName())) { + packaging = Packaging.AAR; + } break; case "--sources": @@ -87,6 +102,26 @@ public static void main(String[] args) throws IOException { } } + if (packaging == Packaging.AAR) { + aarSource = sources.stream() + .filter(source -> aarMatcher.matches(source.getFileName())) + .findFirst() // AAR is explicitly only added for top level distribution target, so we _should_ only ever have 1 + .orElseThrow(() -> new IllegalArgumentException("For AAR packaging, we require a prebuilt AAR that already contains the Android resources that we'll add the transitive source closure to.")); + + sources.remove(aarSource); + + // Pull out classes jar and add to source set + Path aarClassesJar = out.getParent().resolve("aar-classes.jar"); + try (ZipFile aar = new ZipFile(aarSource.toFile())) { + ZipEntry classes = aar.getEntry("classes.jar"); + try (InputStream is = aar.getInputStream(classes); + OutputStream fos = Files.newOutputStream(aarClassesJar)) { + ByteStreams.copy(is, fos); + } + } + sources.add(aarClassesJar); + } + Objects.requireNonNull(out, "Output path must be set."); if (sources.isEmpty()) { // Just write an empty jar and leave @@ -144,6 +179,12 @@ public static void main(String[] args) throws IOException { continue; } + // TODO: Why do we need to do this?? Is there a better way? + Pattern rClassMatcher = Pattern.compile("^.*\\/R(\\$.*)?\\.(class|java)"); + if (rClassMatcher.asMatchPredicate().test(entry.getName())) { + continue; + } + if (!entry.isDirectory()) { // Duplicate files, however may not be. We need the hash to determine // whether we should do anything. @@ -171,6 +212,53 @@ public static void main(String[] args) throws IOException { // jar and not useful for consumers. manifest.getMainAttributes().remove(new Attributes.Name("Target-Label")); + switch (packaging) { + case JAR: + writeClassesJar(out, manifest, allServices, sources, fileToSourceJar); + break; + case AAR: + Path classesJar = out.getParent().resolve("classes.jar"); + writeClassesJar(classesJar, manifest, allServices, sources, fileToSourceJar); + writeAar(out, aarSource, classesJar); + } + } + + private static void writeAar(Path out, Path aarSource, Path classesJar) throws IOException { + try (OutputStream os = Files.newOutputStream(out); + JarOutputStream jos = new JarOutputStream(os)) { + ZipEntry je = new StableZipEntry(classesJar.toFile().getName()); + jos.putNextEntry(je); + + try (InputStream is = Files.newInputStream(classesJar)) { + ByteStreams.copy(is, jos); + } + jos.closeEntry(); + + try (ZipFile aar = new ZipFile(aarSource.toFile())) { + Enumeration entries = aar.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + + // transitive class closure is captured in our classes.jar + if ("classes.jar".equals(entry.getName())) { + continue; + } + + jos.putNextEntry(new ZipEntry(entry.getName())); + try (InputStream is = aar.getInputStream(entry)) { + ByteStreams.copy(is, jos); + } + jos.closeEntry(); + } + } + } + } + + private static void writeClassesJar(Path out, + Manifest manifest, + Map> allServices, + Set sources, + Map fileToSourceJar) throws IOException { // Now create the output jar Files.createDirectories(out.getParent()); diff --git a/private/tools/java/com/github/bazelbuild/rules_jvm_external/maven/MavenPublisher.java b/private/tools/java/com/github/bazelbuild/rules_jvm_external/maven/MavenPublisher.java index a9af8b5fe..9ef8e184b 100644 --- a/private/tools/java/com/github/bazelbuild/rules_jvm_external/maven/MavenPublisher.java +++ b/private/tools/java/com/github/bazelbuild/rules_jvm_external/maven/MavenPublisher.java @@ -93,7 +93,6 @@ public static void main(String[] args) try { List> futures = new ArrayList<>(); - futures.add(upload(repo, credentials, coords, ".pom", pom, gpgSign)); if (mainArtifact != null) { String ext = @@ -101,6 +100,8 @@ public static void main(String[] args) futures.add(upload(repo, credentials, coords, "." + ext, mainArtifact, gpgSign)); } + futures.add(upload(repo, credentials, coords, ".pom", pom, gpgSign)); + if (args.length > 3 && !args[3].isEmpty()) { List extraArtifactTuples = Splitter.onPattern(",").splitToList(args[3]); for (String artifactTuple : extraArtifactTuples) {