From f2c46a0262e17c3d6f6504a0bc20fcb5ca8a4d49 Mon Sep 17 00:00:00 2001 From: Adam Michael Date: Thu, 27 Oct 2016 17:01:40 +0000 Subject: Rollback of commit fac9be905f0f82f793eb1cc61afab42698769dcf. *** Reason for rollback *** Rollforward with fixes for android_integration_test.sh *** Original change description *** Automated [] rollback of commit 2fcf0e4cfb97af7cc8b8a090c5144c87b759bc13. *** Reason for rollback *** Breaks android_integration_test, see https://github.com/bazelbuild/bazel/issues/1927 *** Original change description *** Generate aar_import and java_import rules for local maven repositories in /extras for sdk in android_sdk_repository. Addresses https://github.com/bazelbuild/bazel/issues/1745. -- MOS_MIGRATED_REVID=137407118 --- .../android/AndroidSdkRepositoryFunction.java | 24 ++ .../bazel/rules/android/SdkMavenRepository.java | 282 +++++++++++++++++++++ .../android/android_sdk_repository_template.txt | 3 + 3 files changed, 309 insertions(+) create mode 100644 src/main/java/com/google/devtools/build/lib/bazel/rules/android/SdkMavenRepository.java (limited to 'src/main/java/com/google/devtools') diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java index 5852a19012..b916bdddf4 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java @@ -13,6 +13,9 @@ // limitations under the License. package com.google.devtools.build.lib.bazel.rules.android; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.packages.AttributeMap; @@ -35,6 +38,7 @@ import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import java.io.IOException; import java.util.Properties; +import javax.annotation.Nullable; /** * Implementation of the {@code android_sdk_repository} rule. @@ -90,6 +94,26 @@ public class AndroidSdkRepositoryFunction extends RepositoryFunction { .replaceAll("%build_tools_directory%", buildToolsDirectory) .replaceAll("%api_level%", apiLevel.toString()); + // All local maven repositories that are shipped in the Android SDK. + // TODO(ajmichael): Create SkyKeys so that if the SDK changes, this function will get rerun. + Iterable localMavenRepositories = ImmutableList.of( + outputDirectory.getRelative("extras/android/m2repository"), + outputDirectory.getRelative("extras/google/m2repository")); + try { + SdkMavenRepository sdkExtrasRepository = + SdkMavenRepository.create(Iterables.filter(localMavenRepositories, new Predicate() { + @Override + public boolean apply(@Nullable Path path) { + return path.isDirectory(); + } + })); + sdkExtrasRepository.writeBuildFiles(outputDirectory); + buildFile = buildFile.replaceAll( + "%exported_files%", sdkExtrasRepository.getExportsFiles(outputDirectory)); + } catch (IOException e) { + throw new RepositoryFunctionException(e, Transience.TRANSIENT); + } + writeBuildFile(outputDirectory, buildFile); return RepositoryDirectoryValue.create(outputDirectory); } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/SdkMavenRepository.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/SdkMavenRepository.java new file mode 100644 index 0000000000..136da18742 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/SdkMavenRepository.java @@ -0,0 +1,282 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// 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.devtools.build.lib.bazel.rules.android; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Ordering; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.Path; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import javax.annotation.Nullable; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * A collection of .pom contents that reference versioned archive files with dependencies. + */ +final class SdkMavenRepository { + private enum PackagingType { + AAR { + @Override + ImmutableList createRule( + String name, String archiveLabel, ImmutableList dependencyLabels) { + ImmutableList.Builder ruleLines = new ImmutableList.Builder<>(); + ruleLines.add("aar_import("); + ruleLines.add(" name = '" + name + "',"); + ruleLines.add(" aar = '" + archiveLabel + "',"); + ruleLines.add(" exports = ["); + for (String dependencyLabel : dependencyLabels) { + ruleLines.add(" '" + dependencyLabel + "',"); + } + ruleLines.add(" ],"); + ruleLines.add(")"); + ruleLines.add(""); + return ruleLines.build(); + } + }, + JAR { + @Override + ImmutableList createRule( + String name, String archiveLabel, ImmutableList dependencyLabels) { + ImmutableList.Builder ruleLines = new ImmutableList.Builder<>(); + ruleLines.add("java_import("); + ruleLines.add(" name = '" + name + "',"); + ruleLines.add(" jars = ['" + archiveLabel + "'],"); + ruleLines.add(" exports = ["); + for (String dependencyLabel : dependencyLabels) { + ruleLines.add(" '" + dependencyLabel + "',"); + } + ruleLines.add(" ],"); + ruleLines.add(")"); + ruleLines.add(""); + return ruleLines.build(); + } + }, + UNKNOWN { + @Override + ImmutableList createRule(String name, String archiveLabel, + ImmutableList dependencyLabels) { + ImmutableList.Builder ruleLines = new ImmutableList.Builder<>(); + ruleLines.add("genrule("); + ruleLines.add(" name = '" + name + "',"); + ruleLines.add(" outs = ['ignored_" + name + "'],"); + ruleLines.add(" cmd = 'echo Bazel does not recognize the Maven packaging type for: \"" + + archiveLabel + "\"; exit 1',"); + ruleLines.add(")"); + ruleLines.add(""); + return ruleLines.build(); + } + }; + + static PackagingType getPackagingType(String name) { + for (PackagingType packagingType : PackagingType.values()) { + if (packagingType.name().equalsIgnoreCase(name)) { + return packagingType; + } + } + return UNKNOWN; + } + + abstract ImmutableList createRule( + String name, String archiveLabel, ImmutableList dependencyLabels); + } + + private final ImmutableSortedSet poms; + private final ImmutableSet allKnownCoordinates; + + private SdkMavenRepository(ImmutableSortedSet poms) { + this.poms = poms; + ImmutableSet.Builder coordinates = new ImmutableSet.Builder<>(); + for (Pom pom : poms) { + if (!PackagingType.getPackagingType(pom.packaging()).equals(PackagingType.UNKNOWN)) { + coordinates.add(pom.mavenCoordinate()); + } + } + allKnownCoordinates = coordinates.build(); + } + + /** + * Parses a set of maven repository directory trees looking for and parsing .pom files. + */ + static SdkMavenRepository create(Iterable mavenRepositories) throws IOException { + Collection pomPaths = new ArrayList<>(); + for (Path mavenRepository : mavenRepositories) { + pomPaths.addAll(FileSystemUtils.traverseTree(mavenRepository, new Predicate() { + @Override + public boolean apply(@Nullable Path path) { + return path.toString().endsWith(".pom"); + } + })); + } + + ImmutableSortedSet.Builder poms = + new ImmutableSortedSet.Builder<>(Ordering.usingToString()); + for (Path pomPath : pomPaths) { + try { + Pom pom = Pom.parse(pomPath); + if (pom != null) { + poms.add(pom); + } + } catch (ParserConfigurationException | SAXException e) { + throw new IOException(e); + } + } + return new SdkMavenRepository(poms.build()); + } + + /** + * Creates BUILD files at {@code @//BUILD} containing aar_import and + * java_import rules with dependencies as {@code exports}. The targets are named + * {@code @//:-}. + */ + void writeBuildFiles(Path outputDirectory) throws IOException { + for (Pom pom : poms) { + Path buildFilePath = outputDirectory.getRelative(pom.mavenCoordinate().groupId() + "/BUILD"); + + if (!buildFilePath.getParentDirectory().exists()) { + buildFilePath.getParentDirectory().createDirectory(); + } + + if (!buildFilePath.exists()) { + FileSystemUtils.writeContentAsLatin1( + buildFilePath, "package(default_visibility = [\"//visibility:public\"])\n\n"); + } + + ImmutableList.Builder dependencyLabels = new ImmutableList.Builder<>(); + for (MavenCoordinate dependencyCoordinate : pom.dependencyCoordinates()) { + // Filter out dependencies that are not present in the Maven repository or have unknown + // packaging types. + if (allKnownCoordinates.contains(dependencyCoordinate)) { + dependencyLabels.add(dependencyCoordinate.targetLabel()); + } + } + + ImmutableList ruleLines = PackagingType.getPackagingType(pom.packaging()) + .createRule( + pom.mavenCoordinate().targetName(), + pom.archiveLabel(outputDirectory), + dependencyLabels.build()); + FileSystemUtils.appendLinesAs(buildFilePath, StandardCharsets.ISO_8859_1, ruleLines); + } + } + + /** + * Creates the contents of the exports_files rule list containing all of the archives specified by + * the pom files in the Maven repositories. + */ + String getExportsFiles(Path outputDirectory) { + StringBuilder exportedFiles = new StringBuilder(); + for (Pom pom : poms) { + exportedFiles.append(String.format( + " '%s',\n", pom.archivePath().relativeTo(outputDirectory).getPathString())); + } + return exportedFiles.toString(); + } + + /** + * The relevant contents of a .pom file needed to populate BUILD files for aars and jars in the + * Android SDK extras maven repositories. + */ + @AutoValue + abstract static class Pom { + private static final String DEFAULT_PACKAGING = "jar"; + + static Pom parse(Path path) throws IOException, ParserConfigurationException, SAXException { + Document pomDocument = + DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(path.getInputStream()); + Node packagingNode = pomDocument.getElementsByTagName("packaging").item(0); + String packaging = packagingNode == null ? DEFAULT_PACKAGING : packagingNode.getTextContent(); + MavenCoordinate coordinate = MavenCoordinate.create( + pomDocument.getElementsByTagName("groupId").item(0).getTextContent(), + pomDocument.getElementsByTagName("artifactId").item(0).getTextContent(), + pomDocument.getElementsByTagName("version").item(0).getTextContent()); + + ImmutableSortedSet.Builder dependencyCoordinates = + new ImmutableSortedSet.Builder<>(Ordering.usingToString()); + NodeList dependencies = pomDocument.getElementsByTagName("dependency"); + for (int i = 0; i < dependencies.getLength(); i++) { + if (dependencies.item(i) instanceof Element) { + Element dependency = (Element) dependencies.item(i); + dependencyCoordinates.add(MavenCoordinate.create( + dependency.getElementsByTagName("groupId").item(0).getTextContent(), + dependency.getElementsByTagName("artifactId").item(0).getTextContent(), + dependency.getElementsByTagName("version").item(0).getTextContent())); + } + } + + return new AutoValue_SdkMavenRepository_Pom( + path, packaging, coordinate, dependencyCoordinates.build()); + } + + abstract Path path(); + + abstract String packaging(); + + abstract MavenCoordinate mavenCoordinate(); + + abstract ImmutableSortedSet dependencyCoordinates(); + + String name() { + String pomFilename = path().getBaseName(); + return pomFilename.substring(0, pomFilename.lastIndexOf(".pom")); + } + + Path archivePath() { + return path().getParentDirectory().getRelative(name() + "." + packaging()); + } + + /** The label for the .aar or .jar file in the repository. */ + String archiveLabel(Path outputDirectory) { + return "//:" + archivePath().relativeTo(outputDirectory); + } + } + + /** + * A 3-tuple of group id, artifact id and version used to identify Maven targets. + */ + @AutoValue + abstract static class MavenCoordinate { + static MavenCoordinate create(String groupId, String artifactId, String version) { + return new AutoValue_SdkMavenRepository_MavenCoordinate(groupId, artifactId, version); + } + + abstract String groupId(); + + abstract String artifactId(); + + abstract String version(); + + /** The target name for the java_import or aar_import for the Maven coordinate. */ + String targetName() { + return artifactId() + "-" + version(); + } + + /** The target label for the java_import or aar_import for the Maven coordinate. */ + String targetLabel() { + return "//" + groupId() + ":" + targetName(); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt index e82b5e4c16..2630685589 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android_sdk_repository_template.txt @@ -8,3 +8,6 @@ create_android_sdk_rules( build_tools_directory = "%build_tools_directory%", api_level = %api_level%, ) + +exports_files([ +%exported_files%]) -- cgit v1.2.3