blob: eacdcd46c14522d02fb05841711926b54b4663d0 [file] [log] [blame]
// 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();
}
}