blob: 5f29535eb1706a609b89e34c4f68b8032abc6e53 [file] [log] [blame]
/*
* Copyright 2014-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.java;
import com.facebook.buck.io.MorePaths;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.rules.BuildTargetSourcePath;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MkdirAndSymlinkFileStep;
import com.facebook.buck.util.BuckConstant;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CopyResourcesStep implements Step {
/**
* This matches the Path for a BuildTargetSourcePath and returns the path without the
* "buck-out/XXX/" component as the first capturing group.
*/
private static final Pattern GENERATED_FILE_PATTERN = Pattern.compile(
"^(?:" +
Joiner.on('|').join(
BuckConstant.ANNOTATION_DIR,
BuckConstant.BIN_DIR,
BuckConstant.GEN_DIR
) + ")/(.*)");
private final SourcePathResolver resolver;
private final BuildTarget target;
private final Collection<? extends SourcePath> resources;
private final Path outputDirectory;
private final JavaPackageFinder javaPackageFinder;
public CopyResourcesStep(
SourcePathResolver resolver,
BuildTarget target,
Collection<? extends SourcePath> resources,
Path outputDirectory,
JavaPackageFinder javaPackageFinder) {
this.resolver = resolver;
this.target = target;
this.resources = resources;
this.outputDirectory = outputDirectory;
this.javaPackageFinder = javaPackageFinder;
}
@Override
public int execute(ExecutionContext context) throws IOException, InterruptedException {
for (Step step : buildSteps()) {
int result = step.execute(context);
if (result != 0) {
return result;
}
}
return 0;
}
@VisibleForTesting
ImmutableList<Step> buildSteps() {
ImmutableList.Builder<Step> allSteps = ImmutableList.builder();
if (resources.isEmpty()) {
return allSteps.build();
}
String targetPackageDir = javaPackageFinder.findJavaPackageForPath(
target.getBasePathWithSlash())
.replace('.', File.separatorChar);
for (SourcePath rawResource : resources) {
// If the path to the file defining this rule were:
// "first-party/orca/lib-http/tests/com/facebook/orca/BUCK"
//
// And the value of resource were:
// "first-party/orca/lib-http/tests/com/facebook/orca/protocol/base/batch_exception1.txt"
//
// Assuming that `src_roots = tests` were in the [java] section of the .buckconfig file,
// then javaPackageAsPath would be:
// "com/facebook/orca/protocol/base/"
//
// And the path that we would want to copy to the classes directory would be:
// "com/facebook/orca/protocol/base/batch_exception1.txt"
//
// Therefore, some path-wrangling is required to produce the correct string.
final Path pathToResource = resolver.getPath(rawResource);
String resource = MorePaths.pathWithUnixSeparators(pathToResource);
Matcher matcher;
if ((matcher = GENERATED_FILE_PATTERN.matcher(resource)).matches()) {
resource = matcher.group(1);
}
String javaPackageAsPath = javaPackageFinder.findJavaPackageFolderForPath(resource);
Path relativeSymlinkPath;
if ("".equals(javaPackageAsPath)) {
// In this case, the project root is acting as the default package, so the resource path
// works fine.
relativeSymlinkPath = pathToResource.getFileName();
} else {
int lastIndex = resource.lastIndexOf(javaPackageAsPath);
if (lastIndex < 0) {
Preconditions.checkState(
rawResource instanceof BuildTargetSourcePath,
"If resource path %s does not contain %s, then it must be a BuildTargetSourcePath.",
pathToResource,
javaPackageAsPath);
// Handle the case where we depend on the output of another BuildRule. In that case, just
// grab the output and put in the same package as this target would be in.
relativeSymlinkPath = Paths.get(
String.format(
"%s%s%s",
targetPackageDir,
targetPackageDir.isEmpty() ? "" : "/",
resolver.getPath(rawResource).getFileName()));
} else {
relativeSymlinkPath = Paths.get(resource.substring(lastIndex));
}
}
Path target = outputDirectory.resolve(relativeSymlinkPath);
MkdirAndSymlinkFileStep link = new MkdirAndSymlinkFileStep(pathToResource, target);
allSteps.add(link);
}
return allSteps.build();
}
@Override
public String getShortName() {
return "copy_resources";
}
@Override
public String getDescription(ExecutionContext context) {
return String.format("%s of %s", getShortName(), target);
}
}