/*
* Copyright 2016-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.io;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/** Common functions that are done with a {@link ProjectFilesystem}. */
public class MoreProjectFilesystems {
/** Utility class: do not instantiate. */
private MoreProjectFilesystems() {}
/**
* Creates a symlink at {@code
* projectFilesystem.getRootPath().resolve(pathToDesiredLinkUnderProjectRoot)} that points to
* {@code projectFilesystem.getRootPath().resolve(pathToExistingFileUnderProjectRoot)} using a
* relative symlink.
*
* @param pathToDesiredLinkUnderProjectRoot must reference a file, not a directory.
* @param pathToExistingFileUnderProjectRoot must reference a file, not a directory.
* @return the relative path from the new symlink that was created to the existing file.
*/
public static Path createRelativeSymlink(
Path pathToDesiredLinkUnderProjectRoot,
Path pathToExistingFileUnderProjectRoot,
ProjectFilesystem projectFilesystem)
throws IOException {
return MorePaths.createRelativeSymlink(
pathToDesiredLinkUnderProjectRoot,
pathToExistingFileUnderProjectRoot,
projectFilesystem.getRootPath());
}
/** @return Whether the input path directs to a file in the buck generated files folder. */
public static boolean isGeneratedFile(
ProjectFilesystem filesystem, Path pathRelativeToProjectRoot) {
return pathRelativeToProjectRoot.startsWith(filesystem.getBuckPaths().getGenDir());
}
public static boolean fileContentsDiffer(
InputStream contents, Path path, ProjectFilesystem projectFilesystem) throws IOException {
try {
// Hash the contents of the file at path so we don't have to pull the whole thing into memory.
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
byte[] pathDigest;
try (InputStream is = projectFilesystem.newFileInputStream(path)) {
pathDigest = inputStreamDigest(is, sha1);
}
// Hash 'contents' and see if the two differ.
sha1.reset();
byte[] contentsDigest = inputStreamDigest(contents, sha1);
return !Arrays.equals(pathDigest, contentsDigest);
} catch (NoSuchFileException e) {
// If the file doesn't exist, we need to create it.
return true;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
private static byte[] inputStreamDigest(InputStream inputStream, MessageDigest messageDigest)
throws IOException {
try (DigestInputStream dis = new DigestInputStream(inputStream, messageDigest)) {
byte[] buf = new byte[4096];
while (true) {
// Read the contents of the existing file so we can hash it.
if (dis.read(buf) == -1) {
break;
}
}
return dis.getMessageDigest().digest();
}
}
}