/*
* 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.d;
import com.facebook.buck.cli.BuckConfig;
import com.facebook.buck.io.ExecutableFinder;
import com.facebook.buck.io.FileFinder;
import com.facebook.buck.log.Logger;
import com.facebook.buck.rules.HashedFileTool;
import com.facebook.buck.rules.Tool;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.environment.Architecture;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.stream.StreamSupport;
public class DBuckConfig {
private static final Path DEFAULT_D_COMPILER = Paths.get("dmd");
private static final Logger LOG = Logger.get(DBuckConfig.class);
private final BuckConfig delegate;
public DBuckConfig(BuckConfig delegate) {
this.delegate = delegate;
}
/** @return a Tool representing the D compiler to be used. */
Tool getDCompiler() {
return new HashedFileTool(getDCompilerPath());
}
/** @return a list of flags that must be passed to the compiler. */
ImmutableList<String> getBaseCompilerFlags() {
// If flags are configured in buckconfig, return those.
// Else, return an empty list (no flags), as that should normally work.
return delegate
.getOptionalListWithoutComments("d", "base_compiler_flags", ' ')
.orElse(ImmutableList.of());
}
/** @return a list of flags that must be passed to the linker to link D binaries. */
public ImmutableList<String> getLinkerFlags() {
Optional<ImmutableList<String>> configuredFlags =
delegate.getOptionalListWithoutComments("d", "linker_flags", ' ');
if (configuredFlags.isPresent()) {
return configuredFlags.get();
} else {
// No flags configured; generate them based on library paths.
ImmutableList.Builder<String> builder = ImmutableList.builder();
builder.addAll(
StreamSupport.stream(getBaseLibraryPaths().spliterator(), false)
.map(input -> "-L" + input)
.iterator());
builder.add("-lphobos2", "-lpthread", "-lm");
return builder.build();
}
}
/**
* @return a list of paths to be searched for libraries, in addition to paths that may be
* introduced by rules.
*/
private Iterable<Path> getBaseLibraryPaths() {
Optional<ImmutableList<String>> configuredPaths =
delegate.getOptionalListWithoutComments("d", "library_path", ':');
if (configuredPaths.isPresent()) {
return FluentIterable.from(configuredPaths.get()).transform(input -> Paths.get(input));
}
// No paths configured. Make an educated guess and return that.
// We search, in order:
// 1. a lib directory next to the directory where the compiler is
// 2. /usr/local/lib
// 3. /usr/lib
// 4. /usr/local/lib/${arch}-${platform}
// 5. /usr/lib/${arch}-${platform}
// For the platform names, both versions with and without "-gnu" are tried.
Path compilerPath = getDCompilerPath();
try {
compilerPath = compilerPath.toRealPath();
} catch (IOException e) {
LOG.debug(
"Could not resolve " + compilerPath + " to real path (likely cause: it does not exist)");
}
Path usrLib = Paths.get("/usr", "lib");
Path usrLocalLib = Paths.get("/usr", "local", "lib");
Architecture architecture = delegate.getArchitecture();
String platformName = architecture.toString() + "-" + delegate.getPlatform().getAutoconfName();
String platformNameGnu = platformName + "-gnu";
ImmutableSet<Path> searchPath =
ImmutableSet.<Path>builder()
.add(compilerPath.getParent().resolve(Paths.get("..", "lib")).normalize())
.add(usrLocalLib)
.add(usrLib)
.add(usrLocalLib.resolve(platformName))
.add(usrLocalLib.resolve(platformNameGnu))
.add(usrLib.resolve(platformName))
.add(usrLib.resolve(platformNameGnu))
.build();
ImmutableSet<String> fileNames =
FileFinder.combine(ImmutableSet.of("lib"), "phobos2", ImmutableSet.of(".a", ".so"));
Optional<Path> phobosPath =
FileFinder.getOptionalFile(fileNames, searchPath, Files::isRegularFile);
if (phobosPath.isPresent()) {
LOG.debug("Detected path to Phobos: " + phobosPath.get());
} else {
throw new HumanReadableException("Phobos not found, and not configured using d.library_path");
}
return ImmutableList.of(phobosPath.get().getParent());
}
/** @return the Path to the D compiler. */
private Path getDCompilerPath() {
Path compilerPath =
delegate.getPath("d", "compiler", /*isCellRootRelative=*/ false).orElse(DEFAULT_D_COMPILER);
return new ExecutableFinder().getExecutable(compilerPath, delegate.getEnvironment());
}
}