aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/xcode-common/java/com/google/devtools/build/xcode/zip/ZipInputEntry.java
blob: e6272ce7be12316639b87e1540db2d9fffcbb817 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 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.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();
  }
}