/* * Copyright 2015-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.go; import com.facebook.buck.cli.BuckConfig; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.io.ExecutableFinder; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.FlavorDomain; import com.facebook.buck.model.InternalFlavor; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.CommandTool; import com.facebook.buck.rules.HashedFileTool; import com.facebook.buck.rules.Tool; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.MoreCollectors; import com.facebook.buck.util.ProcessExecutor; import com.facebook.buck.util.ProcessExecutorParams; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.EnumSet; import java.util.Optional; public class GoBuckConfig { private static final String SECTION = "go"; private static final Path DEFAULT_GO_TOOL = Paths.get("go"); private final BuckConfig delegate; private Supplier<Path> goRootSupplier; private Supplier<Path> goToolDirSupplier; private Supplier<GoPlatformFlavorDomain> platformFlavorDomain; private Supplier<GoPlatform> defaultPlatform; public GoBuckConfig( final BuckConfig delegate, final ProcessExecutor processExecutor, final FlavorDomain<CxxPlatform> cxxPlatforms) { this.delegate = delegate; goRootSupplier = Suppliers.memoize( () -> { Optional<Path> configValue = delegate.getPath(SECTION, "root"); if (configValue.isPresent()) { return configValue.get(); } return Paths.get(getGoEnvFromTool(processExecutor, "GOROOT")); }); goToolDirSupplier = Suppliers.memoize(() -> Paths.get(getGoEnvFromTool(processExecutor, "GOTOOLDIR"))); platformFlavorDomain = Suppliers.memoize( () -> { // TODO(mikekap): Allow adding goos/goarch values from config. return new GoPlatformFlavorDomain( delegate.getPlatform(), delegate.getArchitecture(), cxxPlatforms); }); defaultPlatform = Suppliers.memoize( () -> { Optional<String> configValue = delegate.getValue(SECTION, "default_platform"); Optional<GoPlatform> platform; if (configValue.isPresent()) { platform = platformFlavorDomain.get().getValue(InternalFlavor.of(configValue.get())); if (!platform.isPresent()) { throw new HumanReadableException( "Bad go platform value for %s.default_platform = %s", SECTION, configValue); } } else { platform = platformFlavorDomain .get() .getValue(delegate.getPlatform(), delegate.getArchitecture()); if (!platform.isPresent()) { throw new HumanReadableException( "Couldn't determine default go platform for %s %s", delegate.getPlatform(), delegate.getArchitecture()); } } return platform.get(); }); } GoPlatformFlavorDomain getPlatformFlavorDomain() { return platformFlavorDomain.get(); } GoPlatform getDefaultPlatform() { return defaultPlatform.get(); } Tool getCompiler() { return getGoTool("compiler", "compile", "compiler_flags"); } Tool getAssembler() { return getGoTool("assembler", "asm", "asm_flags"); } Tool getPacker() { return getGoTool("packer", "pack", ""); } Tool getLinker() { return getGoTool("linker", "link", "linker_flags"); } Path getDefaultPackageName(BuildTarget target) { Path prefix = Paths.get(delegate.getValue(SECTION, "prefix").orElse("")); return prefix.resolve(target.getBasePath()); } ImmutableList<Path> getVendorPaths() { Optional<ImmutableList<String>> vendorPaths = delegate.getOptionalListWithoutComments(SECTION, "vendor_path", ':'); if (vendorPaths.isPresent()) { return vendorPaths.get().stream().map(Paths::get).collect(MoreCollectors.toImmutableList()); } return ImmutableList.of(); } Optional<Tool> getGoTestMainGenerator(BuildRuleResolver resolver) { return delegate.getTool(SECTION, "test_main_gen", resolver); } ImmutableList<Path> getAssemblerIncludeDirs() { // TODO(mikekap): Allow customizing this via config. return ImmutableList.of(goRootSupplier.get().resolve("pkg").resolve("include")); } private Tool getGoTool( final String configName, final String toolName, final String extraFlagsConfigKey) { Optional<Path> toolPath = delegate.getPath(SECTION, configName); if (!toolPath.isPresent()) { toolPath = Optional.of(goToolDirSupplier.get().resolve(toolName)); } CommandTool.Builder builder = new CommandTool.Builder(new HashedFileTool(toolPath.get())); if (!extraFlagsConfigKey.isEmpty()) { for (String arg : getFlags(extraFlagsConfigKey)) { builder.addArg(arg); } } builder.addEnv("GOROOT", goRootSupplier.get().toString()); return builder.build(); } private ImmutableList<String> getFlags(String key) { return ImmutableList.copyOf( Splitter.on(" ").omitEmptyStrings().split(delegate.getValue(SECTION, key).orElse(""))); } private Path getGoToolPath() { Optional<Path> goTool = delegate.getPath(SECTION, "tool"); if (goTool.isPresent()) { return goTool.get(); } // Try resolving it via the go root config var. We can't use goRootSupplier here since that // would create a recursion. Optional<Path> goRoot = delegate.getPath(SECTION, "root"); if (goRoot.isPresent()) { return goRoot.get().resolve("bin").resolve("go"); } return new ExecutableFinder().getExecutable(DEFAULT_GO_TOOL, delegate.getEnvironment()); } private String getGoEnvFromTool(ProcessExecutor processExecutor, String env) { Path goTool = getGoToolPath(); Optional<ImmutableMap<String, String>> goRootEnv = delegate.getPath(SECTION, "root").map(input -> ImmutableMap.of("GOROOT", input.toString())); try { ProcessExecutor.Result goToolResult = processExecutor.launchAndExecute( ProcessExecutorParams.builder() .addCommand(goTool.toString(), "env", env) .setEnvironment(goRootEnv) .build(), EnumSet.of(ProcessExecutor.Option.EXPECTING_STD_OUT), /* stdin */ Optional.empty(), /* timeOutMs */ Optional.empty(), /* timeoutHandler */ Optional.empty()); if (goToolResult.getExitCode() == 0) { return CharMatcher.whitespace().trimFrom(goToolResult.getStdout().get()); } else { throw new HumanReadableException(goToolResult.getStderr().get()); } } catch (InterruptedException e) { throw new RuntimeException(e); } catch (IOException e) { throw new HumanReadableException( e, "Could not run \"%s env %s\": %s", goTool, env, e.getMessage()); } } }