blob: bdd359153293defff9ef663f3ad6f1e4f4489f59 [file] [log] [blame]
/*
* Copyright 2012-present Facebook, Inc.
*
* 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.facebook.buck.zip;
import static com.facebook.buck.zip.ZipOutputStreams.HandleDuplicates.OVERWRITE_EXISTING;
import static java.util.logging.Level.SEVERE;
import com.facebook.buck.event.LogEvent;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.util.DirectoryTraversal;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Set;
/**
* A {@link com.facebook.buck.step.Step} that creates a ZIP archive..
*/
public class ZipStep implements Step {
public static final int MIN_COMPRESSION_LEVEL = 0;
public static final int DEFAULT_COMPRESSION_LEVEL = 6;
public static final int MAX_COMPRESSION_LEVEL = 9;
private final String absolutePathToZipFile;
private final ImmutableSet<String> paths;
private final boolean junkPaths;
private final int compressionLevel;
private final File baseDir;
/**
* Create a {@link ZipStep} to create or update a zip archive.
*
* Note that paths added to the archive are always relative to the working directory.<br/>
* For example, if you're in {@code /dir} and you add {@code file.txt}, you get
* an archive containing just the file. If you were in {@code /} and added
* {@code dir/file.txt}, you would get an archive containing the file within a directory.
*
*
* @param absolutePathToZipFile path to archive to create.
* @param paths a set of files and/or directories to work on. The entire working directory is
* assumed if this set is empty.
* @param junkPaths if {@code true}, the relative paths of added archive entries are discarded,
* i.e. they are all placed in the root of the archive.
* @param compressionLevel between 0 (store) and 9.
* @param baseDir working directory for {@code zip} command.
* If {@code null}, project directory root is used instead.
*/
public ZipStep(
String absolutePathToZipFile,
Set<String> paths,
boolean junkPaths,
int compressionLevel,
File baseDir) {
Preconditions.checkArgument(compressionLevel >= MIN_COMPRESSION_LEVEL &&
compressionLevel <= MAX_COMPRESSION_LEVEL, "compressionLevel out of bounds.");
this.absolutePathToZipFile = Preconditions.checkNotNull(absolutePathToZipFile);
this.paths = ImmutableSet.copyOf(Preconditions.checkNotNull(paths));
this.junkPaths = junkPaths;
this.compressionLevel = compressionLevel;
this.baseDir = Preconditions.checkNotNull(baseDir);
}
@Override
public int execute(ExecutionContext context) {
File original = new File(absolutePathToZipFile);
if (original.exists()) {
context.getBuckEventBus().post(
LogEvent.create(SEVERE, "Attempting to overwrite an existing zip: %s", original)
);
return 1;
}
try (
BufferedOutputStream baseOut = new BufferedOutputStream(new FileOutputStream(absolutePathToZipFile));
CustomZipOutputStream out = ZipOutputStreams.newOutputStream(baseOut, OVERWRITE_EXISTING);
) {
DirectoryTraversal traversal = new DirectoryTraversal(baseDir) {
@Override
public void visit(File file, String relativePath) throws IOException {
if (!paths.isEmpty() && !paths.contains(relativePath)) {
return;
}
String name = junkPaths ? file.getName() : relativePath;
if (file.isDirectory()) {
// Lame.
name += "/";
}
CustomZipEntry entry = new CustomZipEntry(name);
entry.setTime(file.lastModified());
entry.setSize(file.length());
entry.setCompressionLevel(compressionLevel);
out.putNextEntry(entry);
Files.copy(file.toPath(), out);
out.closeEntry();
}
};
traversal.traverse();
return 0;
} catch (IOException e) {
return 1;
}
}
@Override
public String getDescription(ExecutionContext context) {
StringBuilder args = new StringBuilder("zip ");
// Don't add extra fields, neither do the Android tools.
args.append("-X ");
// recurse
args.append("-r ");
// compression level
args.append("-").append(compressionLevel).append(" ");
// unk paths
if (junkPaths) {
args.append("-j ");
}
// destination archive
args.append(absolutePathToZipFile).append(" ");
// files to add to archive
if (paths.isEmpty()) {
// Add the contents of workingDirectory to archive.
args.append("-i* ");
args.append(". ");
} else {
// Add specified paths, relative to workingDirectory.
for (String path : paths) {
args.append(path).append(" ");
}
}
return args.toString();
}
@Override
public String getShortName() {
return "zip";
}
}