aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Kristina Chodorow <kchodorow@google.com>2015-06-11 16:46:41 +0000
committerGravatar Philipp Wollermann <philwo@google.com>2015-06-12 11:45:36 +0000
commit86746cefb3996f3014d88a281d719b469761bf98 (patch)
tree80408fdd03bdef0841a5476c6ad65afb361c9ff1
parent4371064760b8dae8243c603d7dc2d16193008043 (diff)
Fix permissions for zip files
Now [new_]http_archive can be used for executables as well as "default permission" (644) files. This also gets rid of the Apache Commons Compress dependency entirely, which is nice. Fixing this also exposed some bugs in how archives were being decompressed (the same archive was being decompressed multiple times), which I also fixed by making the decompressors SkyFunctions. Fixes #238. -- MOS_MIGRATED_REVID=95747810
-rwxr-xr-xscripts/bootstrap/compile.sh2
-rw-r--r--src/java_tools/singlejar/BUILD6
-rw-r--r--src/java_tools/singlejar/java/com/google/devtools/build/zip/ZipFileEntry.java5
-rw-r--r--src/main/java/BUILD2
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/DecompressorFactory.java233
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/DecompressorValue.java162
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/HttpArchiveFunction.java35
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadFunction.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadValue.java9
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/JarFunction.java91
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java22
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/NewHttpArchiveFunction.java15
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/ZipFunction.java106
-rw-r--r--src/test/shell/bazel/BUILD1
-rwxr-xr-xsrc/test/shell/bazel/external_integration_test.sh16
-rw-r--r--third_party/BUILD5
18 files changed, 423 insertions, 300 deletions
diff --git a/scripts/bootstrap/compile.sh b/scripts/bootstrap/compile.sh
index 25729a410a..1bb3a62e0a 100755
--- a/scripts/bootstrap/compile.sh
+++ b/scripts/bootstrap/compile.sh
@@ -18,7 +18,7 @@
PROTO_FILES=$(ls src/main/protobuf/*.proto)
LIBRARY_JARS=$(find third_party -name '*.jar' | tr "\n" " ")
-DIRS=$(echo src/{main/java,tools/xcode-common/java/com/google/devtools/build/xcode/{common,util}} ${OUTPUT_DIR}/src)
+DIRS=$(echo src/{java_tools/singlejar/java/com/google/devtools/build/zip,main/java,tools/xcode-common/java/com/google/devtools/build/xcode/{common,util}} ${OUTPUT_DIR}/src)
BLAZE_CC_FILES=(
src/main/cpp/blaze_startup_options.cc
diff --git a/src/java_tools/singlejar/BUILD b/src/java_tools/singlejar/BUILD
index 2f9391db40..8d3685c7d2 100644
--- a/src/java_tools/singlejar/BUILD
+++ b/src/java_tools/singlejar/BUILD
@@ -1,5 +1,11 @@
package(default_visibility = ["//src:__subpackages__"])
+filegroup(
+ name = "srcs",
+ srcs = glob(["**"]),
+ visibility = ["//src/test/shell/bazel:__pkg__"],
+)
+
java_library(
name = "libSingleJar",
srcs = glob(["java/**/singlejar/**/*.java"]),
diff --git a/src/java_tools/singlejar/java/com/google/devtools/build/zip/ZipFileEntry.java b/src/java_tools/singlejar/java/com/google/devtools/build/zip/ZipFileEntry.java
index e8687f1c74..f4e08ab73c 100644
--- a/src/java_tools/singlejar/java/com/google/devtools/build/zip/ZipFileEntry.java
+++ b/src/java_tools/singlejar/java/com/google/devtools/build/zip/ZipFileEntry.java
@@ -437,4 +437,9 @@ public final class ZipFileEntry {
EnumSet<Feature> getFeatureSet() {
return featureSet;
}
+
+ @Override
+ public String toString() {
+ return "ZipFileEntry[" + name + "]";
+ }
}
diff --git a/src/main/java/BUILD b/src/main/java/BUILD
index 8b7ad922c6..0b1045c385 100644
--- a/src/main/java/BUILD
+++ b/src/main/java/BUILD
@@ -155,6 +155,7 @@ java_library(
":shell",
":unix",
":vfs",
+ "//src/java_tools/singlejar:zip",
"//src/main/protobuf:proto_build",
"//src/main/protobuf:proto_bundlemerge",
"//src/main/protobuf:proto_crosstool_config",
@@ -165,7 +166,6 @@ java_library(
"//src/tools/xcode-common/java/com/google/devtools/build/xcode/common",
"//src/tools/xcode-common/java/com/google/devtools/build/xcode/util",
"//third_party:aether",
- "//third_party:apache_commons_compress",
"//third_party:apache_commons_pool2",
"//third_party:auto_value",
"//third_party:gson",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
index c43801a102..f742683b1a 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
@@ -25,12 +25,14 @@ import com.google.devtools.build.lib.bazel.commands.FetchCommand;
import com.google.devtools.build.lib.bazel.repository.HttpArchiveFunction;
import com.google.devtools.build.lib.bazel.repository.HttpDownloadFunction;
import com.google.devtools.build.lib.bazel.repository.HttpJarFunction;
+import com.google.devtools.build.lib.bazel.repository.JarFunction;
import com.google.devtools.build.lib.bazel.repository.LocalRepositoryFunction;
import com.google.devtools.build.lib.bazel.repository.MavenJarFunction;
import com.google.devtools.build.lib.bazel.repository.NewHttpArchiveFunction;
import com.google.devtools.build.lib.bazel.repository.NewLocalRepositoryFunction;
import com.google.devtools.build.lib.bazel.repository.RepositoryDelegatorFunction;
import com.google.devtools.build.lib.bazel.repository.RepositoryFunction;
+import com.google.devtools.build.lib.bazel.repository.ZipFunction;
import com.google.devtools.build.lib.bazel.rules.android.AndroidNdkRepositoryFunction;
import com.google.devtools.build.lib.bazel.rules.android.AndroidNdkRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.android.AndroidSdkRepositoryFunction;
@@ -134,6 +136,8 @@ public class BazelRepositoryModule extends BlazeModule {
// Helper SkyFunctions.
builder.put(SkyFunctionName.computed(HttpDownloadFunction.NAME), new HttpDownloadFunction());
+ builder.put(JarFunction.NAME, new JarFunction());
+ builder.put(ZipFunction.NAME, new ZipFunction());
return builder.build();
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/DecompressorFactory.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/DecompressorFactory.java
deleted file mode 100644
index d421b49ad9..0000000000
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/DecompressorFactory.java
+++ /dev/null
@@ -1,233 +0,0 @@
-// Copyright 2014 Google Inc. 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.repository;
-
-import com.google.devtools.build.lib.bazel.rules.workspace.HttpArchiveRule;
-import com.google.devtools.build.lib.bazel.rules.workspace.HttpJarRule;
-import com.google.devtools.build.lib.bazel.rules.workspace.NewHttpArchiveRule;
-import com.google.devtools.build.lib.vfs.FileSystemUtils;
-import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.PathFragment;
-
-import org.apache.commons.compress.archivers.ArchiveException;
-import org.apache.commons.compress.archivers.ArchiveInputStream;
-import org.apache.commons.compress.archivers.ArchiveStreamFactory;
-import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
-import org.apache.commons.compress.utils.IOUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.Charset;
-
-/**
- * Creates decompressors to use on archive. Use {@link DecompressorFactory#create} to get the
- * correct type of decompressor for the input archive, then call
- * {@link Decompressor#decompress} to decompress it.
- */
-public abstract class DecompressorFactory {
-
- public static Decompressor create(
- String targetKind, String targetName, Path archivePath, Path repositoryPath)
- throws DecompressorException {
- String baseName = archivePath.getBaseName();
-
- if (targetKind.startsWith(HttpJarRule.NAME + " ")) {
- if (baseName.endsWith(".jar")) {
- return new JarDecompressor(targetKind, targetName, archivePath, repositoryPath);
- } else {
- throw new DecompressorException(
- String.format("Expected %s %s to create file with a .jar suffix (got %s)",
- HttpJarRule.NAME, targetName, archivePath));
- }
- }
-
- if (targetKind.startsWith(HttpArchiveRule.NAME + " ")
- || targetKind.startsWith(NewHttpArchiveRule.NAME + " ")) {
- if (baseName.endsWith(".zip") || baseName.endsWith(".jar")) {
- return new ZipDecompressor(archivePath);
- } else {
- throw new DecompressorException(
- String.format("Expected %s %s to create file with a .zip or .jar suffix (got %s)",
- HttpArchiveRule.NAME, targetName, archivePath));
- }
- }
-
- throw new DecompressorException(String.format("No decompressor found for %s rule %s (got %s)",
- targetKind, targetName, archivePath));
- }
-
- /**
- * General decompressor for an archive. Should be overridden for each specific archive type.
- */
- public abstract static class Decompressor {
- protected final Path archiveFile;
-
- private Decompressor(Path archiveFile) {
- this.archiveFile = archiveFile;
- }
-
- /**
- * This is overridden by archive-specific decompression logic. Often this logic will create
- * files and directories under the {@link Decompressor#archiveFile}'s parent directory.
- *
- * @return the path to the repository directory. That is, the returned path will be a directory
- * containing a WORKSPACE file.
- */
- public abstract Path decompress() throws DecompressorException;
- }
-
- /**
- * Decompressor for jar files.
- *
- * <p>This is actually a bit of a misnomer, as .jars aren't decompressed. This does create a
- * repository a BUILD file for them, though, making the java_import target @&lt;jar&gt;//jar:jar
- * available for users to depend on.</p>
- */
- static class JarDecompressor extends Decompressor {
- private final String targetKind;
- private final String targetName;
- private final Path repositoryDir;
-
- public JarDecompressor(
- String targetKind, String targetName, Path archiveFile, Path repositoryDir) {
- super(archiveFile);
- this.targetKind = targetKind;
- this.targetName = targetName;
- this.repositoryDir = repositoryDir;
- }
-
- /**
- * The .jar can be used compressed, so this just exposes it in a way Bazel can use.
- *
- * <p>It moves the jar from some-name/x/y/z/foo.jar to some-name/jar/foo.jar and creates a
- * BUILD file containing one entry: the .jar.
- */
- @Override
- public Path decompress() throws DecompressorException {
- // Example: archiveFile is .external-repository/some-name/foo.jar.
- String baseName = archiveFile.getBaseName();
-
- try {
- FileSystemUtils.createDirectoryAndParents(repositoryDir);
- // .external-repository/some-name/WORKSPACE.
- Path workspaceFile = repositoryDir.getRelative("WORKSPACE");
- FileSystemUtils.writeContent(workspaceFile, Charset.forName("UTF-8"), String.format(
- "# DO NOT EDIT: automatically generated WORKSPACE file for %s rule %s\n",
- targetKind, targetName));
- // .external-repository/some-name/jar.
- Path jarDirectory = repositoryDir.getRelative("jar");
- FileSystemUtils.createDirectoryAndParents(jarDirectory);
- // .external-repository/some-name/repository/jar/foo.jar is a symbolic link to the jar in
- // .external-repository/some-name.
- Path jarSymlink = jarDirectory.getRelative(baseName);
- if (!jarSymlink.exists()) {
- jarSymlink.createSymbolicLink(archiveFile);
- }
- // .external-repository/some-name/repository/jar/BUILD defines the //jar target.
- Path buildFile = jarDirectory.getRelative("BUILD");
- FileSystemUtils.writeLinesAs(buildFile, Charset.forName("UTF-8"),
- "# DO NOT EDIT: automatically generated BUILD file for " + targetKind + " rule "
- + targetName,
- "java_import(",
- " name = 'jar',",
- " jars = ['" + baseName + "'],",
- " visibility = ['//visibility:public']",
- ")");
- } catch (IOException e) {
- throw new DecompressorException(
- "Error auto-creating jar repo structure: " + e.getMessage());
- }
- return repositoryDir;
- }
- }
-
- /**
- * Decompressor for zip files.
- */
- private static class ZipDecompressor extends Decompressor {
- public ZipDecompressor(Path archiveFile) {
- super(archiveFile);
- }
-
- /**
- * This unzips the zip file to a sibling directory of {@link Decompressor#archiveFile}. The
- * zip file is expected to have the WORKSPACE file at the top level, e.g.:
- *
- * <pre>
- * $ unzip -lf some-repo.zip
- * Archive: ../repo.zip
- * Length Date Time Name
- * --------- ---------- ----- ----
- * 0 2014-11-20 15:50 WORKSPACE
- * 0 2014-11-20 16:10 foo/
- * 236 2014-11-20 15:52 foo/BUILD
- * ...
- * </pre>
- */
- @Override
- public Path decompress() throws DecompressorException {
- Path destinationDirectory = archiveFile.getParentDirectory();
- try (InputStream is = new FileInputStream(archiveFile.getPathString())) {
- ArchiveInputStream in = new ArchiveStreamFactory().createArchiveInputStream(
- ArchiveStreamFactory.ZIP, is);
- ZipArchiveEntry entry = (ZipArchiveEntry) in.getNextEntry();
- while (entry != null) {
- extractZipEntry(in, entry, destinationDirectory);
- entry = (ZipArchiveEntry) in.getNextEntry();
- }
- } catch (IOException | ArchiveException e) {
- throw new DecompressorException(
- String.format("Error extracting %s to %s: %s",
- archiveFile, destinationDirectory, e.getMessage()));
- }
- return destinationDirectory;
- }
-
- private void extractZipEntry(
- ArchiveInputStream in, ZipArchiveEntry entry, Path destinationDirectory)
- throws IOException, DecompressorException {
- PathFragment relativePath = new PathFragment(entry.getName());
- if (relativePath.isAbsolute()) {
- throw new DecompressorException(
- String.format("Failed to extract %s, zipped paths cannot be absolute", relativePath));
- }
- Path outputPath = destinationDirectory.getRelative(relativePath);
- FileSystemUtils.createDirectoryAndParents(outputPath.getParentDirectory());
- if (entry.isDirectory()) {
- FileSystemUtils.createDirectoryAndParents(outputPath);
- } else {
- try (OutputStream out = new FileOutputStream(new File(outputPath.getPathString()))) {
- IOUtils.copy(in, out);
- } catch (IOException e) {
- throw new DecompressorException(
- String.format("Error writing %s from %s", outputPath, archiveFile));
- }
- }
- }
- }
-
- /**
- * Exceptions thrown when something goes wrong decompressing an archive.
- */
- public static class DecompressorException extends Exception {
- public DecompressorException(String message) {
- super(message);
- }
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/DecompressorValue.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/DecompressorValue.java
new file mode 100644
index 0000000000..0ba8ef9797
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/DecompressorValue.java
@@ -0,0 +1,162 @@
+// Copyright 2015 Google Inc. 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.repository;
+
+import com.google.devtools.build.lib.bazel.rules.workspace.HttpArchiveRule;
+import com.google.devtools.build.lib.bazel.rules.workspace.HttpJarRule;
+import com.google.devtools.build.lib.bazel.rules.workspace.MavenJarRule;
+import com.google.devtools.build.lib.bazel.rules.workspace.NewHttpArchiveRule;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * The contents of decompressed archive.
+ */
+public class DecompressorValue implements SkyValue {
+
+ private final Path directory;
+
+ /**
+ * @param repositoryPath
+ */
+ public DecompressorValue(Path repositoryPath) {
+ directory = repositoryPath;
+ }
+
+ public Path getDirectory() {
+ return directory;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other == null || !(other instanceof DecompressorValue)) {
+ return false;
+ }
+
+ return directory.equals(((DecompressorValue) other).directory);
+ }
+
+ @Override
+ public int hashCode() {
+ return directory.hashCode();
+ }
+
+ public static SkyKey key(
+ String targetKind, String targetName, Path archivePath, Path repositoryPath)
+ throws IOException {
+ String baseName = archivePath.getBaseName();
+
+ if (targetKind.startsWith(HttpJarRule.NAME + " ")
+ || targetKind.equals(MavenJarRule.NAME)) {
+ if (baseName.endsWith(".jar")) {
+ return new SkyKey(JarFunction.NAME,
+ new DecompressorDescriptor(targetKind, targetName, archivePath, repositoryPath));
+ } else {
+ throw new IOException(
+ String.format("Expected %s %s to create file with a .jar suffix (got %s)",
+ targetKind, targetName, archivePath));
+ }
+ }
+
+ if (targetKind.startsWith(HttpArchiveRule.NAME + " ")
+ || targetKind.startsWith(NewHttpArchiveRule.NAME + " ")) {
+ if (baseName.endsWith(".zip") || baseName.endsWith(".jar")) {
+ return new SkyKey(ZipFunction.NAME,
+ new DecompressorDescriptor(targetKind, targetName, archivePath, repositoryPath));
+ } else {
+ throw new IOException(
+ String.format("Expected %s %s to create file with a .zip or .jar suffix (got %s)",
+ HttpArchiveRule.NAME, targetName, archivePath));
+ }
+ }
+
+ throw new IOException(String.format("No decompressor found for %s rule %s (got %s)",
+ targetKind, targetName, archivePath));
+ }
+
+ /**
+ * Description of an archive to be decompressed for use in a SkyKey.
+ * TODO(bazel-team): this should be an autovalue class.
+ */
+ public static class DecompressorDescriptor {
+ private final String targetKind;
+ private final String targetName;
+ private final Path archivePath;
+ private final Path repositoryPath;
+
+ public DecompressorDescriptor(String targetKind, String targetName, Path archivePath,
+ Path repositoryPath) {
+ this.targetKind = targetKind;
+ this.targetName = targetName;
+ this.archivePath = archivePath;
+ this.repositoryPath = repositoryPath;
+ }
+
+ public String targetKind() {
+ return targetKind;
+ }
+
+ public String targetName() {
+ return targetName;
+ }
+
+ public Path archivePath() {
+ return archivePath;
+ }
+
+ public Path repositoryPath() {
+ return repositoryPath;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other == null || !(other instanceof DecompressorDescriptor)) {
+ return false;
+ }
+
+ DecompressorDescriptor descriptor = (DecompressorDescriptor) other;
+ return targetKind.equals(descriptor.targetKind)
+ && targetName.equals(descriptor.targetName)
+ && archivePath.equals(descriptor.archivePath)
+ && repositoryPath.equals(descriptor.repositoryPath);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(targetKind, targetName, archivePath, repositoryPath);
+ }
+ }
+
+ /**
+ * Exceptions thrown when something goes wrong decompressing an archive.
+ */
+ static class DecompressorException extends Exception {
+ public DecompressorException(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpArchiveFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpArchiveFunction.java
index 94bb25de15..b5cdb9bde6 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpArchiveFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpArchiveFunction.java
@@ -15,15 +15,11 @@
package com.google.devtools.build.lib.bazel.repository;
import com.google.devtools.build.lib.analysis.RuleDefinition;
-import com.google.devtools.build.lib.bazel.repository.DecompressorFactory.DecompressorException;
import com.google.devtools.build.lib.bazel.rules.workspace.HttpArchiveRule;
-import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.PackageIdentifier.RepositoryName;
import com.google.devtools.build.lib.packages.Rule;
-import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.skyframe.FileValue;
import com.google.devtools.build.lib.skyframe.RepositoryValue;
-import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.skyframe.SkyFunctionException;
@@ -33,8 +29,6 @@ import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
/**
* Downloads a file over HTTP.
@@ -76,26 +70,23 @@ public class HttpArchiveFunction extends RepositoryFunction {
if (directoryValue == null) {
return null;
}
- AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
- URL url = null;
- try {
- url = new URL(mapper.get("url", Type.STRING));
- } catch (MalformedURLException e) {
- throw new RepositoryFunctionException(
- new EvalException(rule.getLocation(), "Error parsing URL: " + e.getMessage()),
- Transience.PERSISTENT);
- }
- String sha256 = mapper.get("sha256", Type.STRING);
- HttpDownloader downloader = new HttpDownloader(url, sha256, outputDirectory);
+
try {
- Path archiveFile = downloader.download();
- outputDirectory = DecompressorFactory.create(
- rule.getTargetKind(), rule.getName(), archiveFile, outputDirectory).decompress();
+ HttpDownloadValue downloadValue = (HttpDownloadValue) env.getValueOrThrow(
+ HttpDownloadFunction.key(rule, outputDirectory), IOException.class);
+ if (downloadValue == null) {
+ return null;
+ }
+
+ DecompressorValue value = (DecompressorValue) env.getValueOrThrow(DecompressorValue.key(
+ rule.getTargetKind(), rule.getName(), downloadValue.getPath(), outputDirectory),
+ IOException.class);
+ if (value == null) {
+ return null;
+ }
} catch (IOException e) {
// Assumes all IO errors transient.
throw new RepositoryFunctionException(e, Transience.TRANSIENT);
- } catch (DecompressorException e) {
- throw new RepositoryFunctionException(new IOException(e.getMessage()), Transience.TRANSIENT);
}
return RepositoryValue.create(directoryValue);
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadFunction.java
index dc84864498..022d0dc000 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadFunction.java
@@ -101,7 +101,7 @@ public class HttpDownloadFunction implements SkyFunction {
if (obj == this) {
return true;
}
- if (!(obj instanceof HttpDescriptor)) {
+ if (obj == null || !(obj instanceof HttpDescriptor)) {
return false;
}
HttpDescriptor other = (HttpDescriptor) obj;
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadValue.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadValue.java
index 39f3f24a4b..547e571720 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadValue.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/HttpDownloadValue.java
@@ -38,12 +38,11 @@ public class HttpDownloadValue implements SkyValue {
if (this == other) {
return true;
}
-
- if (other instanceof HttpDownloadValue) {
- HttpDownloadValue otherValue = (HttpDownloadValue) other;
- return this.path.equals(otherValue.path);
+ if (other == null || !(other instanceof HttpDownloadValue)) {
+ return false;
}
- return false;
+ HttpDownloadValue otherValue = (HttpDownloadValue) other;
+ return this.path.equals(otherValue.path);
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/JarFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/JarFunction.java
new file mode 100644
index 0000000000..e519a171db
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/JarFunction.java
@@ -0,0 +1,91 @@
+// Copyright 2015 Google Inc. 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.repository;
+
+import com.google.devtools.build.lib.bazel.repository.DecompressorValue.DecompressorDescriptor;
+import com.google.devtools.build.lib.bazel.repository.RepositoryFunction.RepositoryFunctionException;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import javax.annotation.Nullable;
+
+/**
+ * Creates a repository for a jar file.
+ */
+public class JarFunction implements SkyFunction {
+
+ public static final SkyFunctionName NAME = SkyFunctionName.computed("JAR_FUNCTION");
+
+ /**
+ * The .jar can be used compressed, so this just exposes it in a way Bazel can use.
+ *
+ * <p>It moves the jar from some-name/x/y/z/foo.jar to some-name/jar/foo.jar and creates a
+ * BUILD file containing one entry: the .jar.</p>
+ */
+ @Override
+ @Nullable
+ public SkyValue compute(SkyKey skyKey, Environment env) throws RepositoryFunctionException {
+ DecompressorDescriptor descriptor = (DecompressorDescriptor) skyKey.argument();
+ // Example: archiveFile is external/some-name/foo.jar.
+ String baseName = descriptor.archivePath().getBaseName();
+
+ try {
+ FileSystemUtils.createDirectoryAndParents(descriptor.repositoryPath());
+ // external/some-name/WORKSPACE.
+ Path workspaceFile = descriptor.repositoryPath().getRelative("WORKSPACE");
+ FileSystemUtils.writeContent(workspaceFile, Charset.forName("UTF-8"), String.format(
+ "# DO NOT EDIT: automatically generated WORKSPACE file for %s rule %s\n",
+ descriptor.targetKind(), descriptor.targetName()));
+ // external/some-name/jar.
+ Path jarDirectory = descriptor.repositoryPath().getRelative("jar");
+ FileSystemUtils.createDirectoryAndParents(jarDirectory);
+ // external/some-name/repository/jar/foo.jar is a symbolic link to the jar in
+ // external/some-name.
+ Path jarSymlink = jarDirectory.getRelative(baseName);
+ if (!jarSymlink.exists()) {
+ jarSymlink.createSymbolicLink(descriptor.archivePath());
+ }
+ // external/some-name/repository/jar/BUILD defines the //jar target.
+ Path buildFile = jarDirectory.getRelative("BUILD");
+ FileSystemUtils.writeLinesAs(buildFile, Charset.forName("UTF-8"),
+ "# DO NOT EDIT: automatically generated BUILD file for " + descriptor.targetKind()
+ + " rule " + descriptor.targetName(),
+ "java_import(",
+ " name = 'jar',",
+ " jars = ['" + baseName + "'],",
+ " visibility = ['//visibility:public']",
+ ")");
+ } catch (IOException e) {
+ throw new RepositoryFunctionException(new IOException(
+ "Error auto-creating jar repo structure: " + e.getMessage()), Transience.TRANSIENT);
+ }
+ return new DecompressorValue(descriptor.repositoryPath());
+ }
+
+ @Override
+ @Nullable
+ public String extractTag(SkyKey skyKey) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java
index 8861beb8ab..8af654f7da 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java
@@ -22,8 +22,6 @@ import com.google.common.collect.Lists;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.devtools.build.lib.analysis.RuleDefinition;
-import com.google.devtools.build.lib.bazel.repository.DecompressorFactory.DecompressorException;
-import com.google.devtools.build.lib.bazel.repository.DecompressorFactory.JarDecompressor;
import com.google.devtools.build.lib.bazel.rules.workspace.MavenJarRule;
import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.AttributeMap;
@@ -88,10 +86,8 @@ public class MavenJarFunction extends HttpArchiveFunction {
return downloader;
}
- @VisibleForTesting
SkyValue createOutputTree(MavenDownloader downloader, Environment env)
throws RepositoryFunctionException {
-
FileValue outputDirectoryValue = createDirectory(downloader.getOutputDirectory(), env);
if (outputDirectoryValue == null) {
return null;
@@ -105,16 +101,18 @@ public class MavenJarFunction extends HttpArchiveFunction {
}
// Add a WORKSPACE file & BUILD file to the Maven jar.
- JarDecompressor decompressor = new JarDecompressor(
- MavenJarRule.NAME, downloader.getName(), repositoryJar,
- outputDirectoryValue.realRootedPath().asPath());
- Path repositoryDirectory = null;
+ DecompressorValue value;
try {
- repositoryDirectory = decompressor.decompress();
- } catch (DecompressorException e) {
- throw new RepositoryFunctionException(new IOException(e.getMessage()), Transience.TRANSIENT);
+ value = (DecompressorValue) env.getValueOrThrow(DecompressorValue.key(
+ MavenJarRule.NAME, downloader.getName(), repositoryJar,
+ outputDirectoryValue.realRootedPath().asPath()), IOException.class);
+ if (value == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ throw new RepositoryFunctionException(e, Transience.TRANSIENT);
}
- FileValue repositoryFileValue = getRepositoryDirectory(repositoryDirectory, env);
+ FileValue repositoryFileValue = getRepositoryDirectory(value.getDirectory(), env);
if (repositoryFileValue == null) {
return null;
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/NewHttpArchiveFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/NewHttpArchiveFunction.java
index 3000a95150..30ad5c888a 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/NewHttpArchiveFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/NewHttpArchiveFunction.java
@@ -75,18 +75,21 @@ public class NewHttpArchiveFunction extends HttpArchiveFunction {
}
// Decompress.
- Path decompressedDirectory;
+ DecompressorValue decompressed;
try {
- decompressedDirectory = DecompressorFactory.create(
- rule.getTargetKind(), rule.getName(), downloadedFileValue.getPath(), outputDirectory)
- .decompress();
- } catch (DecompressorFactory.DecompressorException e) {
+ decompressed = (DecompressorValue) env.getValueOrThrow(
+ DecompressorValue.key(rule.getTargetKind(), rule.getName(),
+ downloadedFileValue.getPath(), outputDirectory), IOException.class);
+ if (decompressed == null) {
+ return null;
+ }
+ } catch (IOException e) {
throw new RepositoryFunctionException(
new IOException(e.getMessage()), Transience.TRANSIENT);
}
// Add WORKSPACE and BUILD files.
- createWorkspaceFile(decompressedDirectory, rule);
+ createWorkspaceFile(decompressed.getDirectory(), rule);
return symlinkBuildFile(rule, getWorkspace(), repositoryDirectory, env);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java
index efc2beb7d2..b4a02a2a80 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java
@@ -171,11 +171,6 @@ public abstract class RepositoryFunction implements SkyFunction {
if (createSymbolicLink(buildFilePath, buildFileTarget, env) == null) {
return null;
}
-
- if (buildFileValue == null) {
- return null;
- }
-
return RepositoryValue.createNew(directoryValue, buildFileValue);
}
@@ -201,7 +196,7 @@ public abstract class RepositoryFunction implements SkyFunction {
* .external-repository/
* x/
* WORKSPACE
- * BUILD -> <build_root>/x.BUILD
+ * BUILD -> &lt;build_root&gt;/x.BUILD
* z -> /some/path/to/y/z
* w -> /some/path/to/y/w
* v -> /some/path/to/y/v
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/ZipFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/ZipFunction.java
new file mode 100644
index 0000000000..168220cb8f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/ZipFunction.java
@@ -0,0 +1,106 @@
+// Copyright 2015 Google Inc. 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.repository;
+
+import com.google.devtools.build.lib.bazel.repository.DecompressorValue.DecompressorDescriptor;
+import com.google.devtools.build.lib.bazel.repository.RepositoryFunction.RepositoryFunctionException;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import com.google.devtools.build.zip.ZipFileEntry;
+import com.google.devtools.build.zip.ZipReader;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collection;
+
+import javax.annotation.Nullable;
+
+/**
+ * Creates a repository by decompressing a zip file.
+ */
+public class ZipFunction implements SkyFunction {
+
+ public static final SkyFunctionName NAME = SkyFunctionName.computed("ZIP_FUNCTION");
+
+ /**
+ * This unzips the zip file to a sibling directory of {@link DecompressorDescriptor#archivePath}.
+ * The zip file is expected to have the WORKSPACE file at the top level, e.g.:
+ *
+ * <pre>
+ * $ unzip -lf some-repo.zip
+ * Archive: ../repo.zip
+ * Length Date Time Name
+ * --------- ---------- ----- ----
+ * 0 2014-11-20 15:50 WORKSPACE
+ * 0 2014-11-20 16:10 foo/
+ * 236 2014-11-20 15:52 foo/BUILD
+ * ...
+ * </pre>
+ */
+ @Override
+ @Nullable
+ public SkyValue compute(SkyKey skyKey, Environment env) throws RepositoryFunctionException {
+ DecompressorDescriptor descriptor = (DecompressorDescriptor) skyKey.argument();
+ Path destinationDirectory = descriptor.archivePath().getParentDirectory();
+ try (ZipReader reader = new ZipReader(descriptor.archivePath().getPathFile())) {
+ Collection<ZipFileEntry> entries = reader.entries();
+ for (ZipFileEntry entry : entries) {
+ extractZipEntry(reader, entry, destinationDirectory);
+ }
+ } catch (IOException e) {
+ throw new RepositoryFunctionException(new IOException(
+ String.format("Error extracting %s to %s: %s",
+ descriptor.archivePath(), destinationDirectory, e.getMessage())),
+ Transience.TRANSIENT);
+ }
+ return new DecompressorValue(destinationDirectory);
+ }
+
+ private void extractZipEntry(ZipReader reader, ZipFileEntry entry, Path destinationDirectory)
+ throws IOException {
+ PathFragment relativePath = new PathFragment(entry.getName());
+ if (relativePath.isAbsolute()) {
+ throw new IOException(
+ String.format("Failed to extract %s, zipped paths cannot be absolute", relativePath));
+ }
+ Path outputPath = destinationDirectory.getRelative(relativePath);
+ FileSystemUtils.createDirectoryAndParents(outputPath.getParentDirectory());
+ // Posix permissions are in the high-order 2 bytes of the external attributes. After this
+ // operation, permissions holds 0100755 (or 040755 for directories).
+ int permissions = entry.getExternalAttributes() >>> 16;
+ boolean isDirectory = (permissions & 040000) == 040000;
+ if (isDirectory) {
+ FileSystemUtils.createDirectoryAndParents(outputPath);
+ } else {
+ File outputFile = outputPath.getPathFile();
+ Files.copy(reader.getInputStream(entry), outputFile.toPath());
+ outputPath.chmod(permissions);
+ }
+ }
+
+ @Override
+ @Nullable
+ public String extractTag(SkyKey skyKey) {
+ return null;
+ }
+
+}
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index 9b976d9d8a..12a9bdd259 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -52,6 +52,7 @@ genrule(
name = "doc-srcs",
testonly = 1,
srcs = [
+ "//src/java_tools/singlejar:srcs",
"//src/main/protobuf:srcs",
"//src/main/java:srcs",
"//src/tools/xcode-common:srcs",
diff --git a/src/test/shell/bazel/external_integration_test.sh b/src/test/shell/bazel/external_integration_test.sh
index e6c02ea455..32757b7503 100755
--- a/src/test/shell/bazel/external_integration_test.sh
+++ b/src/test/shell/bazel/external_integration_test.sh
@@ -141,7 +141,11 @@ filegroup(
)
EOF
what_does_the_fox_say="Fraka-kaka-kaka-kaka-kow"
- echo $what_does_the_fox_say > fox/male
+ cat > fox/male <<EOF
+#!/bin/bash
+echo $what_does_the_fox_say
+EOF
+ chmod +x fox/male
# Add some padding to the .zip to test that Bazel's download logic can
# handle breaking a response into chunks.
dd if=/dev/zero of=fox/padding bs=1024 count=10240
@@ -166,11 +170,10 @@ EOF
cat > zoo/female.sh <<EOF
#!/bin/bash
-cat external/endangered/fox/male
+./external/endangered/fox/male
EOF
chmod +x zoo/female.sh
- bazel fetch //zoo:breeding-program || fail "Fetch failed"
bazel run //zoo:breeding-program >& $TEST_log \
|| echo "Expected build/run to succeed"
kill_nc
@@ -403,7 +406,7 @@ function test_new_remote_repo() {
local what_does_the_fox_say="Fraka-kaka-kaka-kaka-kow"
echo $what_does_the_fox_say > fox/male
local repo2_zip=$TEST_TMPDIR/fox.zip
- rm $repo2_zip
+ rm -f $repo2_zip
zip -r $repo2_zip fox
local sha256=$(sha256sum $repo2_zip | cut -f 1 -d ' ')
serve_file $repo2_zip
@@ -424,7 +427,6 @@ new_http_archive(
sha256 = '$sha256',
build_file = 'fox.BUILD'
)
-bind(name = 'stud', actual = '@endangered//:fox')
EOF
mkdir -p zoo
@@ -432,7 +434,7 @@ EOF
sh_binary(
name = "breeding-program",
srcs = ["female.sh"],
- data = ["//external:stud"],
+ data = ["@endangered//:fox"],
)
EOF
@@ -442,8 +444,6 @@ cat external/endangered/fox/male
EOF
chmod +x zoo/female.sh
- bazel clean --expunge
- bazel fetch //zoo:breeding-program || fail "Fetch failed"
bazel run //zoo:breeding-program >& $TEST_log \
|| echo "Expected build/run to succeed"
kill_nc
diff --git a/third_party/BUILD b/third_party/BUILD
index 146f63dd4e..0f11d72beb 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -47,11 +47,6 @@ java_import(
)
java_import(
- name = "apache_commons_compress",
- jars = ["apache_commons_compress/apache-commons-compress-1.9.jar"],
-)
-
-java_import(
name = "apache_commons_logging",
jars = ["apache_commons_logging/commons-logging-1.1.1.jar"],
)