blob: bca191b07a015472cad9f994f1c27ecd80964455 [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.model;
import com.facebook.buck.io.ProjectFilesystem;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.Set;
/**
* Class to allow looking up parents and children of build files.
* E.g. for a directory structure that looks like:
* <pre>
* foo/BUCK
* foo/bar/baz/BUCK
* foo/bar/qux/BUCK
* </pre>
* <p>
* foo/BUCK is the parent of foo/bar/baz/BUCK and foo/bar/qux/BUCK.
*/
public class FilesystemBackedBuildFileTree extends BuildFileTree {
private final ProjectFilesystem projectFilesystem;
private final String buildFileName;
public FilesystemBackedBuildFileTree(ProjectFilesystem projectFilesystem, String buildFileName) {
this.projectFilesystem = projectFilesystem;
this.buildFileName = buildFileName;
}
/**
* @return paths relative to BuildTarget that contain their own build files.
*/
@Override
public Collection<Path> getChildPaths(BuildTarget target) {
// Crawl the subdirectories of target's base path, looking for build files.
// When we find one, we can stop crawling anything under the directory it's in.
final ImmutableSet.Builder<Path> childPaths = ImmutableSet.builder();
final Path basePath = target.getBasePath();
final Set<Path> ignoredPaths = projectFilesystem.getIgnorePaths();
try {
projectFilesystem.walkRelativeFileTree(basePath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
if (ignoredPaths.contains(dir)) {
return FileVisitResult.SKIP_SUBTREE;
}
if (dir.equals(basePath)) {
return FileVisitResult.CONTINUE;
}
Path buildFile = dir.resolve(buildFileName);
if (projectFilesystem.isFile(buildFile)) {
childPaths.add(basePath.relativize(dir));
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
return childPaths.build();
}
/**
* Returns the base path for a given path. The base path is the nearest directory at or
* above filePath that contains a build file. If no base directory is found, returns an empty
* path.
*/
@Override
public Optional<Path> getBasePathOfAncestorTarget(Path filePath) {
while (filePath != null) {
// If filePath names a directory with a build file, filePath is a base path.
// If filePath or any of its parents are in ignoredPaths, we should keep looking.
if (projectFilesystem.isFile(filePath.resolve(buildFileName)) &&
!projectFilesystem.isIgnored(filePath)) {
return Optional.of(filePath);
}
// If filePath is root, we're done looking.
if (filePath.equals(projectFilesystem.getRootPath())) {
break;
}
// filePath isn't a base path; look at its parent.
filePath = filePath.getParent();
}
// No build file found in any directory, check the project root
Path rootBuckFile = Paths.get(buildFileName);
if (projectFilesystem.isFile(rootBuckFile) &&
!projectFilesystem.isIgnored(rootBuckFile)) {
return Optional.of(Paths.get(""));
}
// filePath does not fall under any build file
return Optional.absent();
}
}