blob: 3cdb22aa7aeac113af28aeb939af825f3f7652c2 [file] [log] [blame]
// Copyright 2019 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos_compiler_info_builder_helper.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/strip.h"
#include "glog/logging.h"
#include "lib/file_helper.h"
#include "path.h"
#include <unistd.h>
namespace devtools_goma {
namespace {
bool IsKnownClangInChroot(absl::string_view local_compiler_path) {
return local_compiler_path == "/usr/bin/clang" ||
local_compiler_path == "/usr/bin/clang++" ||
local_compiler_path == "/usr/bin/x86_64-cros-linux-gnu-clang" ||
local_compiler_path == "/usr/bin/x86_64-cros-linux-gnu-clang++";
}
bool ParseEnvdPath(absl::string_view envd_path, string* path) {
// content is like
//
// ```
// PATH="/usr/x86_64-pc-linux-gnu/x86_64-cros-linux-gnu/gcc-bin/4.9.x"
// ROOTPATH="/usr/x86_64-pc-linux-gnu/x86_64-cros-linux-gnu/gcc-bin/4.9.x"
// ```
string content;
if (!ReadFileToString(envd_path, &content)) {
LOG(ERROR) << "failed to open/read " << envd_path;
return false;
}
for (absl::string_view line :
absl::StrSplit(content, absl::ByAnyChar("\r\n"), absl::SkipEmpty())) {
if (absl::ConsumePrefix(&line, "PATH=\"") &&
absl::ConsumeSuffix(&line, "\"")) {
*path = string(line);
return true;
}
}
return false;
}
} // anonymous namespace
// static
bool ChromeOSCompilerInfoBuilderHelper::IsSimpleChromeClangCommand(
absl::string_view local_compiler_path,
absl::string_view real_compiler_path) {
if (!(absl::EndsWith(local_compiler_path, "clang") ||
absl::EndsWith(local_compiler_path, "clang++"))) {
return false;
}
if (!absl::EndsWith(real_compiler_path, ".elf")) {
return false;
}
return true;
}
// static
bool ChromeOSCompilerInfoBuilderHelper::CollectSimpleChromeClangResources(
absl::string_view local_compiler_path,
absl::string_view real_compiler_path,
std::vector<string>* resource_paths) {
absl::string_view local_compiler_dir = file::Dirname(local_compiler_path);
int version;
if (!EstimateClangMajorVersion(real_compiler_path, &version)) {
LOG(ERROR) << "failed to estimate clang major version"
<< " real_compiler_path=" << real_compiler_path;
return false;
}
// if local_compiler is clang++, real_compiler is clang-<N>.elf.
// However, clang++-<N>.elf and clang-<N> are both necessary to run clang++.
if (absl::EndsWith(local_compiler_path, "clang++")) {
resource_paths->push_back(file::JoinPath(
local_compiler_dir, absl::StrCat("clang++-", version, ".elf")));
resource_paths->push_back(
file::JoinPath(local_compiler_dir, absl::StrCat("clang-", version)));
}
const string lib_dir = file::JoinPath(local_compiler_dir, "..", "..", "lib");
resource_paths->push_back(file::JoinPath(lib_dir, "ld-linux-x86-64.so.2"));
resource_paths->push_back(
file::JoinPath(lib_dir, absl::StrCat("libLLVM-", version, "svn.so")));
resource_paths->push_back(file::JoinPath(lib_dir, "libc++.so.1"));
resource_paths->push_back(file::JoinPath(lib_dir, "libc++abi.so.1"));
resource_paths->push_back(file::JoinPath(lib_dir, "libm.so.6"));
resource_paths->push_back(file::JoinPath(lib_dir, "libc.so.6"));
resource_paths->push_back(file::JoinPath(lib_dir, "libffi.so.6"));
resource_paths->push_back(file::JoinPath(lib_dir, "libz.so.1"));
resource_paths->push_back(file::JoinPath(lib_dir, "libdl.so.2"));
resource_paths->push_back(file::JoinPath(lib_dir, "libtinfo.so.5"));
resource_paths->push_back(file::JoinPath(lib_dir, "libpthread.so.0"));
resource_paths->push_back(file::JoinPath(lib_dir, "libxml2.so.2"));
return true;
}
// static
bool ChromeOSCompilerInfoBuilderHelper::EstimateClangMajorVersion(
absl::string_view real_compiler_path,
int* version) {
// Assuming real_compiler_path filename is like
// `clang-<N>.elf` or `clang-<N>`.
absl::string_view filename = file::Basename(real_compiler_path);
if (!absl::ConsumePrefix(&filename, "clang-")) {
return false;
}
// If this has .elf, remove that.
// If it doesn't exist, it's not an error.
absl::ConsumeSuffix(&filename, ".elf");
if (!absl::SimpleAtoi(filename, version)) {
return false;
}
return true;
}
// static
bool ChromeOSCompilerInfoBuilderHelper::IsClangInChrootEnv(
absl::string_view local_compiler_path) {
if (!IsKnownClangInChroot(local_compiler_path)) {
return false;
}
// chromeos chroot env should have /etc/cros_chroot_version.
if (access("/etc/cros_chroot_version", F_OK) < 0) {
return false;
}
return true;
}
// static
bool ChromeOSCompilerInfoBuilderHelper::CollectChrootClangResources(
absl::string_view local_compiler_path,
absl::string_view real_compiler_path,
std::vector<string>* resource_paths) {
constexpr absl::string_view lib_dir = "/usr/lib64";
int version;
if (!EstimateClangMajorVersion(real_compiler_path, &version)) {
LOG(ERROR) << "failed to estimate clang major version"
<< " real_compiler_path=" << real_compiler_path;
return false;
}
// TODO: Currently support only target = x86_64.
// for target=arm, we need to use other resources.
// check local_compiler_path, and if compiler name looks like arm,
// we have to use arm-like resources.
resource_paths->push_back(
file::JoinPath(lib_dir, absl::StrCat("libLLVM-", version, "svn.so")));
resource_paths->push_back(file::JoinPath(lib_dir, "libc++.so.1"));
resource_paths->push_back(file::JoinPath(lib_dir, "libc++abi.so.1"));
resource_paths->push_back(file::JoinPath(lib_dir, "libffi.so.6"));
resource_paths->push_back(file::JoinPath(lib_dir, "libxml2.so.2"));
resource_paths->push_back("/etc/env.d/gcc/.NATIVE");
resource_paths->push_back("/etc/env.d/05gcc-x86_64-cros-linux-gnu");
string path_from_envd;
if (!ParseEnvdPath("/etc/env.d/05gcc-x86_64-cros-linux-gnu",
&path_from_envd)) {
return false;
}
if (local_compiler_path == "/usr/bin/x86_64-cros-linux-gnu-clang") {
// Actually /usr/bin/clang is called.
// /usr/x86_64-pc-linux-gnu/x86_64-cros-linux-gnu/gcc-bin/4.9.x/x86_64-cros-linux-gnu-clang
// is wrapper.
resource_paths->push_back("/usr/bin/clang");
resource_paths->push_back(
file::JoinPath(path_from_envd, "x86_64-cros-linux-gnu-clang"));
} else if (local_compiler_path == "/usr/bin/x86_64-cros-linux-gnu-clang++") {
// Actually /usr/bin/clang++ is called, and /usr/bin/clang can also be
// called. The latter 2 binaries are both wrapper.
resource_paths->push_back("/usr/bin/clang");
resource_paths->push_back("/usr/bin/clang++");
resource_paths->push_back(
file::JoinPath(path_from_envd, "x86_64-cros-linux-gnu-clang"));
resource_paths->push_back(
file::JoinPath(path_from_envd, "x86_64-cros-linux-gnu-clang++"));
}
return true;
}
// static
void ChromeOSCompilerInfoBuilderHelper::SetAdditionalFlags(
absl::string_view local_compiler_path,
google::protobuf::RepeatedPtrField<std::string>* additional_flags) {
if (local_compiler_path == "/usr/bin/x86_64-cros-linux-gnu-clang" ||
local_compiler_path == "/usr/bin/x86_64-cros-linux-gnu-clang++") {
// Wrapper tries to set up ccache, but it's meaningless in goma.
// we have to set -noccache.
// TODO: chromeos toolchain should have -noccache by default
// if goma is enabled.
additional_flags->Add("-noccache");
}
}
} // namespace devtools_goma