/* * 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.cxx; import static com.facebook.buck.file.ProjectFilesystemMatchers.pathExists; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; import com.facebook.buck.cli.FakeBuckConfig; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.step.ExecutionContext; import com.facebook.buck.step.TestExecutionContext; import com.facebook.buck.testutil.TestConsole; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; public class CxxCompileStepIntegrationTest { @Rule public TemporaryPaths tmp = new TemporaryPaths(); private void assertCompDir(Path compDir, Optional<String> failure) throws Exception { ProjectFilesystem filesystem = new ProjectFilesystem(tmp.getRoot()); CxxPlatform platform = CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); // Build up the paths to various files the archive step will use. BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); Compiler compiler = platform.getCc().resolve(resolver); ImmutableList<String> compilerCommandPrefix = compiler.getCommandPrefix(pathResolver); Path output = filesystem.resolve(Paths.get("output.o")); Path depFile = filesystem.resolve(Paths.get("output.dep")); Path relativeInput = Paths.get("input.c"); Path input = filesystem.resolve(relativeInput); filesystem.writeContentsToPath("int main() {}", relativeInput); Path scratchDir = filesystem.getPath("scratchDir"); filesystem.mkdirs(scratchDir); ImmutableList.Builder<String> compilerArguments = ImmutableList.builder(); compilerArguments.add("-g"); DebugPathSanitizer sanitizer = new MungingDebugPathSanitizer(200, File.separatorChar, compDir, ImmutableBiMap.of()); // Build an archive step. CxxPreprocessAndCompileStep step = new CxxPreprocessAndCompileStep( BuildTarget.builder(tmp.getRoot(), "//foo", "bar").build(), filesystem, CxxPreprocessAndCompileStep.Operation.PREPROCESS_AND_COMPILE, output, Optional.of(depFile), relativeInput, CxxSource.Type.C, new CxxPreprocessAndCompileStep.ToolCommand( compilerCommandPrefix, compilerArguments.build(), ImmutableMap.of()), HeaderPathNormalizer.empty(pathResolver), sanitizer, scratchDir, true, compiler); // Execute the archive step and verify it ran successfully. ExecutionContext executionContext = TestExecutionContext.newInstance(); TestConsole console = (TestConsole) executionContext.getConsole(); int exitCode = step.execute(executionContext).getExitCode(); if (failure.isPresent()) { assertNotEquals("compile step succeeded", 0, exitCode); assertThat( console.getTextWrittenToStdErr(), console.getTextWrittenToStdErr(), Matchers.containsString(failure.get())); } else { assertEquals("compile step failed: " + console.getTextWrittenToStdErr(), 0, exitCode); // Verify that we find the expected compilation dir embedded in the file. String contents = new String(Files.readAllBytes(output)); assertThat(contents, Matchers.containsString(sanitizer.getCompilationDirectory())); } // Cleanup. Files.delete(input); Files.deleteIfExists(output); } @Test public void updateCompilationDir() throws Exception { assertCompDir(Paths.get("."), Optional.empty()); assertCompDir(Paths.get("blah"), Optional.empty()); } @Test public void createsAnArgfile() throws Exception { ProjectFilesystem filesystem = new ProjectFilesystem(tmp.getRoot()); CxxPlatform platform = CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); // Build up the paths to various files the archive step will use. BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); Compiler compiler = platform.getCc().resolve(resolver); ImmutableList<String> compilerCommandPrefix = compiler.getCommandPrefix(pathResolver); Path output = filesystem.resolve(Paths.get("output.o")); Path depFile = filesystem.resolve(Paths.get("output.dep")); Path relativeInput = Paths.get("input.c"); Path input = filesystem.resolve(relativeInput); filesystem.writeContentsToPath("int main() {}", relativeInput); Path scratchDir = filesystem.getPath("scratchDir"); filesystem.mkdirs(scratchDir); ImmutableList.Builder<String> compilerArguments = ImmutableList.builder(); compilerArguments.add("-g"); // Build an archive step. CxxPreprocessAndCompileStep step = new CxxPreprocessAndCompileStep( BuildTarget.builder(tmp.getRoot(), "//foo", "bar").build(), filesystem, CxxPreprocessAndCompileStep.Operation.PREPROCESS_AND_COMPILE, output, Optional.of(depFile), relativeInput, CxxSource.Type.C, new CxxPreprocessAndCompileStep.ToolCommand( compilerCommandPrefix, compilerArguments.build(), ImmutableMap.of()), HeaderPathNormalizer.empty(pathResolver), CxxPlatformUtils.DEFAULT_COMPILER_DEBUG_PATH_SANITIZER, scratchDir, true, compiler); // Execute the archive step and verify it ran successfully. ExecutionContext executionContext = TestExecutionContext.newInstance(); TestConsole console = (TestConsole) executionContext.getConsole(); int exitCode = step.execute(executionContext).getExitCode(); assertEquals("compile step failed: " + console.getTextWrittenToStdErr(), 0, exitCode); Path argfile = filesystem.resolve(scratchDir.resolve("ppandcompile.argsfile")); assertThat(filesystem, pathExists(argfile)); assertThat(Files.readAllLines(argfile, StandardCharsets.UTF_8), hasItem(equalTo("-g"))); // Cleanup. Files.delete(input); Files.deleteIfExists(output); } }