| // Copyright 2014 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.xcode.zip; |
| |
| import static com.google.devtools.build.singlejar.ZipCombiner.DOS_EPOCH; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.singlejar.ZipCombiner; |
| import com.google.devtools.build.xcode.util.Value; |
| import com.google.devtools.build.zip.ZipFileEntry; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.file.FileVisitResult; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.SimpleFileVisitor; |
| import java.nio.file.attribute.BasicFileAttributes; |
| |
| /** |
| * Describes an entry in a zip file when the zip file is being created. |
| */ |
| public class ZipInputEntry extends Value<ZipInputEntry> { |
| /** |
| * The external file attribute used for files by default. This indicates a non-executable regular |
| * file that is readable by group and world. |
| */ |
| public static final int DEFAULT_EXTERNAL_FILE_ATTRIBUTE = (0100644 << 16); |
| |
| /** |
| * An external file attribute that indicates an executable regular file that is readable and |
| * executable by group and world. |
| */ |
| public static final int EXECUTABLE_EXTERNAL_FILE_ATTRIBUTE = (0100755 << 16); |
| |
| /** |
| * Made by version of .ipa files built by Xcode. Upper byte indicates Unix host. Lower byte |
| * indicates version of encoding software (note that 0x1e = 30 = (3.0 * 10), so 0x1e translates |
| * to 3.0). The Unix host value in the upper byte is what causes the external file attribute to |
| * be interpreted as POSIX permission and file type bits. |
| */ |
| public static final short MADE_BY_VERSION = (short) 0x031e; |
| |
| private final Path source; |
| private final String zipPath; |
| private final int externalFileAttribute; |
| |
| public ZipInputEntry(Path source, String zipPath) { |
| this(source, zipPath, DEFAULT_EXTERNAL_FILE_ATTRIBUTE); |
| } |
| |
| public ZipInputEntry(Path source, String zipPath, int externalFileAttribute) { |
| super(source, zipPath, externalFileAttribute); |
| this.source = source; |
| this.zipPath = zipPath; |
| this.externalFileAttribute = externalFileAttribute; |
| } |
| |
| /** |
| * The location of the source file to place in the zip. |
| */ |
| public Path getSource() { |
| return source; |
| } |
| |
| /** |
| * The full path of the item in the zip. |
| */ |
| public String getZipPath() { |
| return zipPath; |
| } |
| |
| /** |
| * The external file attribute field of the zip entry in the central directory record. On |
| * Unix-originated .zips, this corresponds to the permission bits (e.g. 0755 for an excutable |
| * file). |
| */ |
| public int getExternalFileAttribute() { |
| return externalFileAttribute; |
| } |
| |
| /** |
| * Adds this entry to a zip using the given {@code ZipCombiner}. Entry can be either a directory |
| * or a file. |
| */ |
| public void add(ZipCombiner combiner) throws IOException { |
| ZipFileEntry entry = new ZipFileEntry(zipPath); |
| if (Files.isDirectory(source)) { |
| String name = entry.getName(); |
| if (!name.endsWith("/")) { |
| name = name + "/"; |
| } |
| combiner.addDirectory(name, DOS_EPOCH); |
| return; |
| } |
| try (InputStream inputStream = Files.newInputStream(source)) { |
| entry.setTime(DOS_EPOCH.getTime()); |
| entry.setVersion(MADE_BY_VERSION); |
| entry.setExternalAttributes(externalFileAttribute); |
| combiner.addFile(entry, inputStream); |
| } |
| } |
| |
| public static void addAll(ZipCombiner combiner, Iterable<ZipInputEntry> inputs) |
| throws IOException { |
| for (ZipInputEntry input : inputs) { |
| input.add(combiner); |
| } |
| } |
| |
| /** |
| * Returns the entries to place in a zip file as if the zip file mirrors some directory structure. |
| * For instance, if {@code rootDirectory} is /tmp/foo, and the following files exist: |
| * <ul> |
| * <li>/tmp/foo/a |
| * <li>/tmp/foo/bar/c |
| * <li>/tmp/foo/baz/d |
| * </ul> |
| * This function will return entries which point to these files and have in-zip paths of: |
| * <ul> |
| * <li>a |
| * <li>bar/c |
| * <li>baz/d |
| * </ul> |
| * Note that currently this doesn't add directory entries. |
| */ |
| public static Iterable<ZipInputEntry> fromDirectory(final Path rootDirectory) throws IOException { |
| final ImmutableList.Builder<ZipInputEntry> zipInputs = new ImmutableList.Builder<>(); |
| Files.walkFileTree(rootDirectory, new SimpleFileVisitor<Path>() { |
| @Override |
| public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { |
| // TODO(bazel-team): Set the external file attribute based on the attribute of the |
| // permissions of the file on-disk. |
| zipInputs.add(new ZipInputEntry(file, rootDirectory.relativize(file).toString())); |
| return FileVisitResult.CONTINUE; |
| } |
| }); |
| return zipInputs.build(); |
| } |
| } |