blob: c30f36528b851bea5a07a68918dd209e8460d78c [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.io.ProjectFilesystem.CopySourceMode;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
public class CopyStep implements Step {
/**
* When copying a directory, this specifies whether only
* the contents of the directory should be copied, or
* if the directory itself should be included.
*/
public enum DirectoryMode {
/**
* Copy only the contents of the directory (recursively). If
* copying a source directory 'foo' with a file 'bar.txt' to a
* destination directory 'dest', this will create
* 'dest/bar.txt'.
*/
CONTENTS_ONLY,
/**
* Copy the directory and its contents (recursively). If copying
* a source directory 'foo' with a file 'bar.txt' to a
* destination directory 'dest', this will create
* 'dest/foo/bar.txt'.
*/
DIRECTORY_AND_CONTENTS
}
private final Path source;
private final Path destination;
private final CopySourceMode copySourceMode;
private CopyStep(Path source, Path destination, CopySourceMode copySourceMode) {
this.source = source;
this.destination = destination;
this.copySourceMode = copySourceMode;
}
/**
* Creates a CopyStep which copies a single file from 'source' to
* 'destination'.
*/
public static CopyStep forFile(Path source, Path destination) {
return new CopyStep(source, destination, CopySourceMode.FILE);
}
/**
* Creates a CopyStep which recursively copies a directory from
* 'source' to 'destination'. See {@link DirectoryMode} for options
* to control the copy.
*/
public static CopyStep forDirectory(
Path source,
Path destination,
DirectoryMode directoryMode) {
CopySourceMode copySourceMode;
switch (directoryMode) {
case CONTENTS_ONLY:
copySourceMode = CopySourceMode.DIRECTORY_CONTENTS_ONLY;
break;
case DIRECTORY_AND_CONTENTS:
copySourceMode = CopySourceMode.DIRECTORY_AND_CONTENTS;
break;
default:
throw new IllegalArgumentException("Invalid directory mode: " + directoryMode);
}
return new CopyStep(source, destination, copySourceMode);
}
@Override
public String getShortName() {
return "cp";
}
@Override
public String getDescription(ExecutionContext context) {
ImmutableList.Builder<String> args = ImmutableList.builder();
args.add("cp");
switch (copySourceMode) {
case FILE:
args.add(source.toString());
break;
case DIRECTORY_AND_CONTENTS:
args.add("-R");
args.add(source.toString());
break;
case DIRECTORY_CONTENTS_ONLY:
args.add("-R");
// BSD and GNU cp have different behaviors with -R:
// http://jondavidjohn.com/blog/2012/09/linux-vs-osx-the-cp-command
//
// To work around this, we use "sourceDir/*" as the source to
// copy in this mode.
args.add(source.resolve("*").toString());
break;
}
args.add(destination.toString());
return Joiner.on(" ").join(args.build());
}
@VisibleForTesting
Path getSource() {
return source;
}
@VisibleForTesting
Path getDestination() {
return destination;
}
public boolean isRecursive() {
return copySourceMode != CopySourceMode.FILE;
}
@Override
public int execute(ExecutionContext context) {
try {
context.getProjectFilesystem().copy(source, destination, copySourceMode);
return 0;
} catch (IOException e) {
context.logError(e, "Failed when trying to copy: %s", getDescription(context));
return 1;
}
}
}