blob: b9c7d4cc11fb501fdca50692a76b739065882f16 [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.step.fs;
import com.facebook.buck.shell.ShellStep;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.util.Verbosity;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.File;
import java.util.Set;
/**
* A {@link com.facebook.buck.step.Step} that creates or updates a ZIP archive using {@code zip}.
*
* @see <a href="http://www.info-zip.org/mans/zip.html">ZIP</a>
*/
public class ZipStep extends ShellStep {
public static final int MIN_COMPRESSION_LEVEL = 0;
public static final int DEFAULT_COMPRESSION_LEVEL = 6;
public static final int MAX_COMPRESSION_LEVEL = 9;
/**
* These map to {@code zip} 'command modes' (as seen in man page).
* @see <a href="http://www.info-zip.org/mans/zip.html">ZIP</a>
*/
public static enum Mode {
/**
* Update existing entries and add new files.
* If the archive does not exist create it. This is the default mode.
*/
ADD(""),
/**
* Update existing entries if newer on the file system and add new files.
* If the archive does not exist issue warning then create a new archive.
*/
UPDATE("-u"),
/**
* Update existing entries of an archive if newer on the file system.
* Does not add new files to the archive.
*/
FRESHEN("-f"),
/**
* Select entries in an existing archive and delete them.
*/
DELETE("-d");
public final String arg;
private Mode(String arg) {
this.arg = arg;
}
}
private final Mode mode;
private final String pathToZipFile;
private final ImmutableSet<String> paths;
private final boolean junkPaths;
private final int compressionLevel;
/**
* 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 mode one of {@link ZipStep.Mode#ADD}, {@link ZipStep.Mode#UPDATE UPDATE},
* {@link ZipStep.Mode#FRESHEN FRESHEN} or {@link ZipStep.Mode#DELETE DELETE}, as in the
* {@code zip} command.
* @param pathToZipFile path to archive to create or update
* @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 workingDirectory working directory for {@code zip} command.
* If {@code null}, project directory root is used instead.
*
* @see <a href="http://www.info-zip.org/mans/zip.html">ZIP</a>
*/
public ZipStep(
Mode mode,
String pathToZipFile,
Set<String> paths,
boolean junkPaths,
int compressionLevel,
File workingDirectory) {
super(workingDirectory);
Preconditions.checkArgument(compressionLevel >= MIN_COMPRESSION_LEVEL &&
compressionLevel <= MAX_COMPRESSION_LEVEL, "compressionLevel out of bounds.");
this.mode = mode;
this.pathToZipFile = Preconditions.checkNotNull(pathToZipFile);
this.paths = ImmutableSet.copyOf(Preconditions.checkNotNull(paths));
this.junkPaths = junkPaths;
this.compressionLevel = compressionLevel;
}
/**
* Create a {@link ZipStep} that adds an entire directory to an archive. The files directly in
* the directory will appear in the root of the archive. Default compression level is assumed.
*
* @param pathToZipFile path to archive to create or update
* @param directoryToAdd directory to add to the archive
*
* @see #ZipStep(Mode, String, Set, boolean, int, File)
*/
public ZipStep(String pathToZipFile, File directoryToAdd) {
this(
Mode.ADD,
pathToZipFile,
ImmutableSet.<String>of() /* pathsToAdd */,
false /* junkPaths */,
DEFAULT_COMPRESSION_LEVEL /* compressionLevel */,
Preconditions.checkNotNull(directoryToAdd));
}
@Override
protected ImmutableList<String> getShellCommandInternal(ExecutionContext context) {
ImmutableList.Builder<String> args = ImmutableList.builder();
args.add("zip");
// What to do?
if (mode != Mode.ADD) {
args.add(mode.arg);
}
Verbosity verbosity = context.getVerbosity();
if (!verbosity.shouldUseVerbosityFlagIfAvailable()) {
if (verbosity.shouldPrintStandardInformation()) {
args.add("-q");
} else {
args.add("-qq");
}
}
// Don't add extra fields, neither do the Android tools.
args.add("-X");
// recurse
args.add("-r");
// compression level
args.add("-" + compressionLevel);
// unk paths
if (junkPaths) {
args.add("-j");
}
// destination archive
args.add(pathToZipFile);
// files to add to archive
if (paths.isEmpty()) {
// Add the contents of workingDirectory to archive.
args.add("-i*");
args.add(".");
} else {
// Add specified paths, relative to workingDirectory.
args.addAll(paths);
}
return args.build();
}
@Override
public String getShortName() {
return "zip";
}
}