/* * Copyright 2013-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.d; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import com.facebook.buck.cli.BuckConfig; import com.facebook.buck.cli.FakeBuckConfig; import com.facebook.buck.io.MoreFiles; 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.rules.Tool; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.environment.Platform; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import org.junit.Rule; import org.junit.Test; /** * Tests that the autodetection in DBuckConfig works and that explicitly configured values are * respected. */ public class DBuckConfigTest { @Rule public TemporaryPaths tmp = new TemporaryPaths(); private Path makeFakeExecutable(Path directory, String baseName) throws IOException { Path dmd = directory.resolve(baseName + (Platform.detect() == Platform.WINDOWS ? ".exe" : "")); Files.createFile(dmd); MoreFiles.makeExecutable(dmd); return dmd; } @Test public void testCompilerInPath() throws IOException { Path yooserBeen = tmp.newFolder("yooser", "been"); Path dmd = makeFakeExecutable(yooserBeen, "dmd"); BuckConfig delegate = FakeBuckConfig.builder() .setEnvironment(ImmutableMap.of("PATH", yooserBeen.toRealPath().toString())) .build(); DBuckConfig dBuckConfig = new DBuckConfig(delegate); assertEquals(dmd.toRealPath().toString(), toolPath(dBuckConfig.getDCompiler())); } @Test public void testCompilerNotInPath() throws IOException { Path yooserBeen = tmp.newFolder("yooser", "been"); Path userBean = tmp.newFolder("user", "bean").toRealPath(); makeFakeExecutable(yooserBeen, "dmd"); BuckConfig delegate = FakeBuckConfig.builder() .setEnvironment(ImmutableMap.of("PATH", userBean.toString())) .build(); DBuckConfig dBuckConfig = new DBuckConfig(delegate); String msg = ""; Tool compiler = null; try { compiler = dBuckConfig.getDCompiler(); } catch (HumanReadableException e) { msg = e.getMessage(); } // OS X searches the paths in /etc/paths in addition to those specified in // the PATH environment variable. Since dmd may be on one of those paths, // we may find it there, resulting in no exception being thrown. When this // happens, assert that the compiler we found is at neither of the paths // we specify in the test. if (delegate.getPlatform() == Platform.MACOS && msg.length() == 0) { assertNotNull(compiler); assertFalse(toolPath(compiler).contains(userBean.toString())); assertFalse(toolPath(compiler).contains(yooserBeen.toString())); } else { assertEquals("Unable to locate dmd on PATH, or it's not marked as being executable", msg); } } @Test public void testCompilerOverridden() throws IOException { Path yooserBeen = tmp.newFolder("yooser", "been"); makeFakeExecutable(yooserBeen, "dmd"); Path ldc = makeFakeExecutable(yooserBeen, "ldc"); BuckConfig delegate = FakeBuckConfig.builder() .setEnvironment(ImmutableMap.of("PATH", yooserBeen.toRealPath().toString())) .setSections("[d]", "compiler=" + ldc.toRealPath()) .build(); DBuckConfig dBuckConfig = new DBuckConfig(delegate); assertEquals(ldc.toRealPath().toString(), toolPath(dBuckConfig.getDCompiler())); } @Test public void testDCompilerFlagsOverridden() throws IOException { BuckConfig delegate = FakeBuckConfig.builder().setSections("[d]", "base_compiler_flags=-g -O3").build(); DBuckConfig dBuckConfig = new DBuckConfig(delegate); ImmutableList<String> compilerFlags = dBuckConfig.getBaseCompilerFlags(); assertContains(compilerFlags, "-g"); assertContains(compilerFlags, "-O3"); } @Test public void testDLinkerFlagsOverridden() throws IOException { Path yooserBin = tmp.newFolder("yooser", "bin"); Path yooserLib = tmp.newFolder("yooser", "lib"); makeFakeExecutable(yooserBin, "dmd"); Path phobos2So = yooserLib.resolve("libphobos2.so"); Files.createFile(phobos2So); BuckConfig delegate = FakeBuckConfig.builder() .setEnvironment(ImmutableMap.of("PATH", yooserBin.toRealPath().toString())) .setSections( "[d]", "linker_flags = -L/opt/doesnotexist/dmd/lib \"-L/path with spaces\"") .build(); DBuckConfig dBuckConfig = new DBuckConfig(delegate); ImmutableList<String> linkerFlags = dBuckConfig.getLinkerFlags(); assertContains(linkerFlags, "-L/opt/doesnotexist/dmd/lib"); assertContains(linkerFlags, "-L/path with spaces"); assertDoesNotContain(linkerFlags, "-L" + yooserLib.toRealPath()); } @Test public void testDRuntimeNearCompiler() throws IOException { Path yooserBin = tmp.newFolder("yooser", "bin"); Path yooserLib = tmp.newFolder("yooser", "lib"); makeFakeExecutable(yooserBin, "dmd"); Path phobos2So = yooserLib.resolve("libphobos2.so"); Files.createFile(phobos2So); BuckConfig delegate = FakeBuckConfig.builder() .setEnvironment(ImmutableMap.of("PATH", yooserBin.toRealPath().toString())) .build(); DBuckConfig dBuckConfig = new DBuckConfig(delegate); ImmutableList<String> linkerFlags = dBuckConfig.getLinkerFlags(); assertContains(linkerFlags, "-L" + yooserLib.toRealPath()); } private static <T> void assertContains(Collection<T> haystack, T needle) { assertTrue(needle.toString() + " is in " + haystack.toString(), haystack.contains(needle)); } private static <T> void assertDoesNotContain(Collection<T> haystack, T needle) { assertFalse(needle.toString() + " is not in " + haystack.toString(), haystack.contains(needle)); } /** Returns the path of a Tool. */ private String toolPath(Tool tool) { BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); return tool.getCommandPrefix(new SourcePathResolver(new SourcePathRuleFinder(resolver))).get(0); } }