// Copyright 2015 The Bazel Authors. All rights reserved. // // 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.google.devtools.build.lib.packages.util; import com.google.devtools.build.lib.testutil.BlazeTestUtils; import com.google.devtools.build.lib.testutil.TestConstants; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; import java.io.IOException; import javax.annotation.Nullable; /** * Configuration for the mock client setup that we use for testing. */ public final class MockToolsConfig { private final Path rootDirectory; private final boolean realFileSystem; // Allow the injection of the runfiles directory where actual tools are found. // TestUtil.getRunfilesDir() caches the value of the "TEST_SRCDIR" system property, which makes // it impossible to change if it doesn't get set early in test configuration setup. private final Path runfilesDirectory; public MockToolsConfig(Path rootDirectory) { this(rootDirectory, false, null); } public MockToolsConfig(Path rootDirectory, boolean realFileSystem) { this(rootDirectory, realFileSystem, null); } public MockToolsConfig( Path rootDirectory, boolean realFileSystem, @Nullable Path runfilesDirectoryOpt) { this.rootDirectory = rootDirectory; this.realFileSystem = realFileSystem; if (!realFileSystem) { this.runfilesDirectory = null; } else if (runfilesDirectoryOpt == null) { this.runfilesDirectory = rootDirectory.getRelative(BlazeTestUtils.runfilesDir()); } else { this.runfilesDirectory = runfilesDirectoryOpt; } } public boolean isRealFileSystem() { return realFileSystem; } public Path getPath(String relativePath) { return rootDirectory.getRelative(relativePath); } public Path create(String relativePath, String... lines) throws IOException { Path path = rootDirectory.getRelative(relativePath); if (!path.exists()) { FileSystemUtils.writeIsoLatin1(path, lines); } else if (lines.length > 0) { String existingContent = new String(FileSystemUtils.readContentAsLatin1(path)); StringBuilder newContent = new StringBuilder(); for (String line : lines) { newContent.append(line); newContent.append(System.lineSeparator()); } if (!newContent.toString().equals(existingContent)) { throw new IOException( "Conflict: '" + relativePath + "':\n'" + newContent + "'\n vs \n'" + existingContent + "'"); } } return path; } public Path overwrite(String relativePath, String... lines) throws IOException { Path path = rootDirectory.getRelative(relativePath); if (path.exists()) { FileSystemUtils.deleteTree(path); } return create(relativePath, lines); } /** * Links a tool into the workspace by creating a symbolic link to a real file. The target location * in the workspace uses the same relative path as the given path to the tool in the runfiles * tree. Use this if you do not need to rename or relocate the file, i.e., if the location in the * workspace and the runfiles tree matches. Otherwise use {@link #linkTool(String, String)}. * * @param relativePath the relative path within the runfiles tree of the current test * @throws IOException */ public void linkTool(String relativePath) throws IOException { Preconditions.checkState(realFileSystem); linkTool(relativePath, relativePath); } /** * Links a tool into the workspace by creating a symbolic link to a real file. * * @param relativePath the relative path within the runfiles tree of the current test * @param dest the relative path in the mock client * @throws IOException */ public void linkTool(String relativePath, String dest) throws IOException { Preconditions.checkState(realFileSystem); Path target = runfilesDirectory.getRelative(TestConstants.WORKSPACE_NAME + "/" + relativePath); if (!target.exists()) { // In some cases we run tests in a special client with a ../READONLY/ path where we may also // find the runfiles. Try that, too. Path readOnlyClientPath = rootDirectory.getRelative( "../READONLY/" + TestConstants.WORKSPACE_NAME + "/" + relativePath); if (!readOnlyClientPath.exists()) { throw new IOException("target does not exist " + target); } else { target = readOnlyClientPath; } } Path path = rootDirectory.getRelative(dest); FileSystemUtils.createDirectoryAndParents(path.getParentDirectory()); path.delete(); path.createSymbolicLink(target); } /** * Convenience method to link multiple tools. Same as calling {@link #linkTool(String)} for each * parameter. */ public void linkTools(String... tools) throws IOException { for (String tool : tools) { linkTool(tool); } } }