/*
* 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;
import com.facebook.buck.io.ProjectFilesystem.CopySourceMode;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.StepExecutionResult;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import java.io.File;
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 ProjectFilesystem filesystem;
private final Path source;
private final Path destination;
private final CopySourceMode copySourceMode;
private CopyStep(
ProjectFilesystem filesystem, Path source, Path destination, CopySourceMode copySourceMode) {
this.filesystem = filesystem;
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(ProjectFilesystem filesystem, Path source, Path destination) {
return new CopyStep(filesystem, 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(
ProjectFilesystem filesystem, 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(filesystem, 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.
// N.B., on Windows, java.nio.AbstractPath does not resolve *
// as a path, causing InvalidPathException. Since this is purely a
// description, manually create the source argument.
args.add(source.toString() + File.separator + "*");
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 StepExecutionResult execute(ExecutionContext context) {
try {
filesystem.copy(source, destination, copySourceMode);
return StepExecutionResult.SUCCESS;
} catch (IOException e) {
context.logError(e, "Failed when trying to copy: %s", getDescription(context));
return StepExecutionResult.ERROR;
}
}
}