blob: c818e29c95f8fa9ee2213e8dec64c4e74481c874 [file] [log] [blame]
// Copyright 2018 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 "gcc_compiler_info_builder.h"
#include "absl/strings/match.h"
#include "autolock_timer.h"
#include "clang_compiler_info_builder_helper.h"
#include "counterz.h"
#include "gcc_flags.h"
#include "glog/logging.h"
#include "glog/stl_logging.h"
#include "nacl_compiler_info_builder_helper.h"
#include "path.h"
#include "path_resolver.h"
#include "util.h"
#ifdef __linux__
#include "chromeos_compiler_info_builder_helper.h"
#endif
#ifdef _WIN32
#include "posix_helper_win.h"
#endif // _WIN32
namespace devtools_goma {
namespace {
class GetClangPluginPath : public FlagParser::Callback {
public:
explicit GetClangPluginPath(std::vector<string>* subprograms)
: load_seen_(false), subprograms_(subprograms) {}
~GetClangPluginPath() override {}
string ParseFlagValue(const FlagParser::Flag& flag ALLOW_UNUSED,
const string& value) override {
if (load_seen_) {
load_seen_ = false;
if (!used_plugin_.insert(value).second) {
LOG(INFO) << "The same plugin is trying to be added more than twice."
<< " Let us ignore it to reduce subprogram spec size."
<< " path=" << value;
}
subprograms_->push_back(value);
}
if (value == "-load") {
load_seen_ = true;
}
return value;
}
private:
bool load_seen_;
std::vector<string>* subprograms_;
std::set<string> used_plugin_;
};
bool AddSubprogramInfo(
const string& user_specified_path,
const string& abs_path,
google::protobuf::RepeatedPtrField<CompilerInfoData::SubprogramInfo>* ss) {
CompilerInfoData::SubprogramInfo* s = ss->Add();
if (!CxxCompilerInfoBuilder::SubprogramInfoFromPath(user_specified_path,
abs_path, s)) {
ss->RemoveLast();
return false;
}
return true;
}
// Execute GCC and get the string output for GCC version
bool GetGccVersion(const string& bare_gcc,
const std::vector<string>& compiler_info_envs,
const string& cwd,
string* version) {
std::vector<string> argv;
argv.push_back(bare_gcc);
argv.push_back("-dumpversion");
std::vector<string> env(compiler_info_envs);
env.push_back("LC_ALL=C");
int32_t status = 0;
string dumpversion_output;
{
GOMA_COUNTERZ("ReadCommandOutput(dumpversion)");
dumpversion_output = ReadCommandOutput(bare_gcc, argv, env, cwd,
MERGE_STDOUT_STDERR, &status);
}
if (status != 0) {
LOG(ERROR) << "ReadCommandOutput exited with non zero status code."
<< " bare_gcc=" << bare_gcc << " status=" << status
<< " argv=" << argv << " env=" << env << " cwd=" << cwd
<< " dumpversion_output=" << dumpversion_output;
return false;
}
argv[1] = "--version";
string version_output;
{
GOMA_COUNTERZ("ReadCommandOutput(version)");
version_output = ReadCommandOutput(bare_gcc, argv, env, cwd,
MERGE_STDOUT_STDERR, &status);
}
if (status != 0) {
LOG(ERROR) << "ReadCommandOutput exited with non zero status code."
<< " bare_gcc=" << bare_gcc << " status=" << status
<< " argv=" << argv << " env=" << env << " cwd=" << cwd
<< " version_output=" << version_output;
return false;
}
if (dumpversion_output.empty() || version_output.empty()) {
LOG(ERROR) << "dumpversion_output or version_output is empty."
<< " bare_gcc=" << bare_gcc << " status=" << status
<< " argv=" << argv << " env=" << env << " cwd=" << cwd
<< " dumpversion_output=" << dumpversion_output
<< " version_output=" << version_output;
return false;
}
*version = GetCxxCompilerVersionFromCommandOutputs(
bare_gcc, dumpversion_output, version_output);
return true;
}
// Execute GCC and get the string output for GCC target architecture
// This target is used to pick the same compiler in the backends, so
// we don't need to use compiler_info_flags here.
bool GetGccTarget(const string& bare_gcc,
const std::vector<string>& compiler_info_envs,
const string& cwd,
string* target) {
std::vector<string> argv;
argv.push_back(bare_gcc);
argv.push_back("-dumpmachine");
std::vector<string> env(compiler_info_envs);
env.push_back("LC_ALL=C");
int32_t status = 0;
string gcc_output;
{
GOMA_COUNTERZ("ReadCommandOutput(dumpmachine)");
gcc_output = ReadCommandOutput(bare_gcc, argv, env, cwd,
MERGE_STDOUT_STDERR, &status);
}
if (status != 0) {
LOG(ERROR) << "ReadCommandOutput exited with non zero status code."
<< " bare_gcc=" << bare_gcc << " status=" << status
<< " argv=" << argv << " env=" << env << " cwd=" << cwd
<< " gcc_output=" << gcc_output;
return false;
}
*target = GetFirstLine(gcc_output);
return !target->empty();
}
bool IsExecutable(const string& cwd, const string& path) {
const string abs_path = file::JoinPathRespectAbsolute(cwd, path);
return access(abs_path.c_str(), X_OK) == 0;
}
#if defined(__linux__) || defined(__MACH__)
string GetRealClangPath(const string& normal_gcc_path,
const string& cwd,
const std::vector<string>& envs) {
std::vector<string> argv;
argv.push_back(normal_gcc_path);
argv.push_back("-xc");
argv.push_back("-v");
argv.push_back("-E");
argv.push_back("/dev/null");
int32_t status = 0;
string v_output;
{
GOMA_COUNTERZ("ReadCommandOutput(-xc -v)");
v_output = ReadCommandOutput(normal_gcc_path, argv, envs, cwd,
MERGE_STDOUT_STDERR, &status);
}
LOG_IF(ERROR, status != 0)
<< "ReadCommandOutput exited with non zero status code."
<< " normal_gcc_path=" << normal_gcc_path << " status=" << status
<< " argv=" << argv << " envs=" << envs << " cwd=" << cwd
<< " v_output=" << v_output;
const string clang_path =
ClangCompilerInfoBuilderHelper::ParseRealClangPath(v_output);
if (!clang_path.empty() && IsExecutable(cwd, clang_path)) {
return clang_path;
}
return string();
}
#endif
} // anonymous namespace
void GCCCompilerInfoBuilder::SetTypeSpecificCompilerInfo(
const CompilerFlags& flags,
const string& local_compiler_path,
const string& abs_local_compiler_path,
const std::vector<string>& compiler_info_envs,
CompilerInfoData* data) const {
// Some compilers uses wrapper script to set build target, and in such a
// situation, build target could be different.
// To make goma backend use proper wrapper script, or set proper -target,
// we should need to use local_compiler_path instead of real path.
bool has_version = GetGccVersion(abs_local_compiler_path, compiler_info_envs,
flags.cwd(), data->mutable_version());
bool has_target = GetGccTarget(abs_local_compiler_path, compiler_info_envs,
flags.cwd(), data->mutable_target());
const GCCFlags& gcc_flags = static_cast<const GCCFlags&>(flags);
// If input is LLVM IR, we assume it ThinLTO backend phase.
// The phase should not use system include paths, predefined macro and
// features.
//
// See also:
// http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html
const bool is_input_ir = gcc_flags.lang() == "ir";
// TODO: As we have -x flags in compiler_info,
// include_processor don't need to have 2 kinds of
// system include paths (C and C++).
// However, we still need them because backend
// should set them using different ways
// (-isystem and CPLUS_INCLUDE_PATH).
// Once b/5218687 is fixed, we should
// be able to eliminate cxx_system_include_paths.
if (!is_input_ir &&
!ClangCompilerInfoBuilderHelper::SetBasicCompilerInfo(
local_compiler_path, gcc_flags.compiler_info_flags(),
compiler_info_envs, gcc_flags.cwd(), "-x" + flags.lang(),
gcc_flags.resource_dir(), gcc_flags.is_cplusplus(),
gcc_flags.has_nostdinc(), data)) {
DCHECK(data->has_error_message());
// If error occurred in SetBasicCompilerInfo, we do not need to
// continue.
return;
}
#ifdef _WIN32
// In the (build: Windows, target: NaCl (not PNaCl)) compile,
// include paths under toolchain root are shown as relative path from it.
if (GCCFlags::IsNaClGCCCommand(local_compiler_path)) {
data->mutable_cxx()->set_toolchain_root(
NaClCompilerInfoBuilderHelper::GetNaClToolchainRoot(
local_compiler_path));
}
#endif
if (!has_version) {
AddErrorMessage("Failed to get version for " + data->real_compiler_path(),
data);
LOG(ERROR) << data->error_message();
return;
}
if (!has_target) {
AddErrorMessage("Failed to get target for " + data->real_compiler_path(),
data);
LOG(ERROR) << data->error_message();
return;
}
if (!GetExtraSubprograms(local_compiler_path, gcc_flags, compiler_info_envs,
data)) {
std::ostringstream ss;
ss << "Failed to get subprograms for " << data->real_compiler_path();
AddErrorMessage(ss.str(), data);
LOG(ERROR) << data->error_message();
return;
}
// Hack for GCC 5's has_include and has_include_next support.
// GCC has built-in macro that defines __has_include to __has_include__
// and __has_include_next to __has_include_next__.
// https://gcc.gnu.org/viewcvs/gcc/trunk/gcc/c-family/c-cppbuiltin.c?revision=229533&view=markup#l794
// However, __has_include__ and __has_include_next__ are usable but not
// defined.
// https://gcc.gnu.org/viewcvs/gcc/trunk/libcpp/init.c?revision=229154&view=markup#l376
// i.e.
// if we execute gcc -E to followings, we only get
// "__has_include__(<stddef.h>)"
// #ifdef __has_include__
// "__has_include__"
// #endif
// #ifdef __has_include__(<stddef.h>)
// "__has_include__(<stddef.h>)"
// #endif
// See also: b/25581637
//
// Note that I do not think we need version check because:
// 1. __has_include is the new feature and old version does not have it.
// 2. I can hardly think they change their implementation as far as
// I guessed from the code
if (data->name() == "gcc" || data->name() == "g++") {
bool has_include = false;
bool has_include__ = false;
bool has_include_next = false;
bool has_include_next__ = false;
for (const auto& m : data->cxx().supported_predefined_macros()) {
if (m == "__has_include")
has_include = true;
if (m == "__has_include__")
has_include__ = true;
if (m == "__has_include_next")
has_include_next = true;
if (m == "__has_include_next__")
has_include_next__ = true;
}
if (has_include && !has_include__ &&
(data->cxx().predefined_macros().find("__has_include__") !=
string::npos)) {
data->mutable_cxx()->add_hidden_predefined_macros("__has_include__");
}
if (has_include_next && !has_include_next__ &&
(data->cxx().predefined_macros().find("__has_include_next__") !=
string::npos)) {
data->mutable_cxx()->add_hidden_predefined_macros("__has_include_next__");
}
}
// --- Experimental. Add compiler resource.
std::vector<string> resource_paths_to_collect;
// local compiler.
// The server assumes the first resource path is always the local compiler.
// So we have to start from a local compiler.
resource_paths_to_collect.push_back(local_compiler_path);
// real compiler if it differs from local compiler.
// When clang++ is local compiler, the real compiler is usually clang, and
// clang++ is just a symlink to clang. In that case, we don't need to collect
// real compiler. We think the files are the same if hash is the same.
if (local_compiler_path != data->real_compiler_path() &&
data->local_compiler_hash() != data->hash()) {
resource_paths_to_collect.push_back(data->real_compiler_path());
}
// subprograms.
for (const auto& subprogram : data->subprograms()) {
resource_paths_to_collect.push_back(subprogram.user_specified_path());
}
// TODO: Currently GCCCompilerInfoBuilder covers all
// gcc/clang/nacl-clang/pnacl-clang compilers. However, it's better to have
// a subclass for each type of compiler to support type specific procedures.
if (GCCFlags::IsPNaClClangCommand(local_compiler_path)) {
NaClCompilerInfoBuilderHelper::CollectPNaClClangResources(
local_compiler_path, flags.cwd(), &resource_paths_to_collect);
}
if (GCCFlags::IsNaClGCCCommand(local_compiler_path)) {
NaClCompilerInfoBuilderHelper::CollectNaClGccResources(
local_compiler_path, flags.cwd(), &resource_paths_to_collect);
}
if (GCCFlags::IsNaClClangCommand(local_compiler_path)) {
NaClCompilerInfoBuilderHelper::CollectNaClClangResources(
local_compiler_path, flags.cwd(), &resource_paths_to_collect);
}
#ifdef __linux__
if (ChromeOSCompilerInfoBuilderHelper::IsSimpleChromeClangCommand(
local_compiler_path, data->real_compiler_path())) {
if (!ChromeOSCompilerInfoBuilderHelper::CollectSimpleChromeClangResources(
local_compiler_path, data->real_compiler_path(),
&resource_paths_to_collect)) {
AddErrorMessage("failed to add simple chrome resources", data);
LOG(ERROR)
<< "failed to add simple chrome resources: local_compiler_path="
<< local_compiler_path
<< " real_compiler_path=" << data->real_compiler_path();
return;
}
}
if (ChromeOSCompilerInfoBuilderHelper::IsClangInChrootEnv(
local_compiler_path)) {
if (!ChromeOSCompilerInfoBuilderHelper::CollectChrootClangResources(
local_compiler_path, data->real_compiler_path(),
&resource_paths_to_collect)) {
AddErrorMessage("failed to add chromeos chroot env clang resources",
data);
LOG(ERROR) << "failed to add chromeos chroot env clang resources: "
"local_compiler_path="
<< local_compiler_path
<< " real_compiler_path=" << data->real_compiler_path();
return;
}
ChromeOSCompilerInfoBuilderHelper::SetAdditionalFlags(
local_compiler_path, data->mutable_additional_flags());
}
#endif // __linux__
absl::flat_hash_set<string> visited_paths;
for (const auto& resource_path : resource_paths_to_collect) {
if (!AddResourceAsExecutableBinary(resource_path, gcc_flags, &visited_paths,
data)) {
return;
}
}
}
// static
bool GCCCompilerInfoBuilder::AddResourceAsExecutableBinary(
const string& resource_path,
const GCCFlags& gcc_flags,
absl::flat_hash_set<string>* visited_paths,
CompilerInfoData* data) {
// On Linux, MAX_NESTED_LINKS is 8, so I chose 8 here.
static const int kMaxNestedLinks = 8;
return AddResourceAsExecutableBinaryInternal(
resource_path, gcc_flags, kMaxNestedLinks, visited_paths, data);
}
// static
bool GCCCompilerInfoBuilder::AddResourceAsExecutableBinaryInternal(
const string& resource_path,
const GCCFlags& gcc_flags,
int rest_symlink_follow_count,
absl::flat_hash_set<string>* visited_paths,
CompilerInfoData* data) {
string abs_resource_path =
file::JoinPathRespectAbsolute(gcc_flags.cwd(), resource_path);
if (!visited_paths->insert(std::move(abs_resource_path)).second) {
// This path has been visited before. Don't proceed.
// This is not an error, so return true.
return true;
}
CompilerInfoData::ResourceInfo r;
if (!CompilerInfoBuilder::ResourceInfoFromPath(
gcc_flags.cwd(), resource_path, CompilerInfoData::EXECUTABLE_BINARY,
&r)) {
AddErrorMessage(
"failed to get GCC resource info for " + resource_path, data);
return false;
}
if (r.symlink_path().empty()) {
// Not a symlink. Just add it as a resource.
*data->add_resource() = std::move(r);
return true;
}
// symlink.
if (rest_symlink_follow_count <= 0) {
// Needed to follow too many symlink. Return an error.
AddErrorMessage("too deep nested symlink: " + resource_path, data);
return false;
}
string symlink_path = file::JoinPathRespectAbsolute(
file::Dirname(resource_path), r.symlink_path());
// Implementation Note: the original resource must come first. If the resource
// is a symlink, the actual file must be added after the symlink. The server
// assumes the first resource is a compiler used in a command line, even
// if it's a symlink.
*data->add_resource() = std::move(r);
return AddResourceAsExecutableBinaryInternal(symlink_path, gcc_flags,
rest_symlink_follow_count - 1,
visited_paths, data);
}
void GCCCompilerInfoBuilder::SetCompilerPath(
const CompilerFlags& flags,
const string& local_compiler_path,
const std::vector<string>& compiler_info_envs,
CompilerInfoData* data) const {
data->set_local_compiler_path(local_compiler_path);
data->set_real_compiler_path(GetRealCompilerPath(
local_compiler_path, flags.cwd(), compiler_info_envs));
}
string GCCCompilerInfoBuilder::GetCompilerName(
const CompilerInfoData& data) const {
absl::string_view base = file::Basename(data.local_compiler_path());
if (base != "cc" && base != "c++") {
// We can simply use local_compiler_path for judging compiler name
// if basename is not "cc" or "c++".
// See also b/13107706
return GCCFlags::GetCompilerName(data.local_compiler_path());
}
if (!GCCFlags::IsClangCommand(data.real_compiler_path())) {
return GCCFlags::GetCompilerName(data.real_compiler_path());
}
// clang++ is usually symlink to clang, and real compiler path is
// usually be clang. It does not usually reflect what we expect as a
// compiler name.
string real_name = GCCFlags::GetCompilerName(data.real_compiler_path());
if (base == "cc") {
return real_name;
}
if (real_name == "clang") {
return string("clang++");
}
LOG(WARNING) << "Cannot detect compiler name:"
<< " local=" << data.local_compiler_path()
<< " real=" << data.real_compiler_path();
return string();
}
/* static */
bool GCCCompilerInfoBuilder::GetExtraSubprograms(
const string& normal_gcc_path,
const GCCFlags& gcc_flags,
const std::vector<string>& compiler_info_envs,
CompilerInfoData* compiler_info) {
// TODO: support linker subprograms on linking.
std::vector<string> clang_plugins;
std::vector<string> B_options;
bool no_integrated_as = false;
std::set<string> known_subprograms;
ParseSubprogramFlags(normal_gcc_path, gcc_flags, &clang_plugins, &B_options,
&no_integrated_as);
for (const auto& path : clang_plugins) {
string absolute_path = file::JoinPathRespectAbsolute(gcc_flags.cwd(), path);
if (!known_subprograms.insert(absolute_path).second) {
LOG(INFO) << "ignored duplicated subprogram: " << absolute_path;
continue;
}
if (!AddSubprogramInfo(path, absolute_path,
compiler_info->mutable_subprograms())) {
LOG(ERROR) << "invalid plugin:"
<< " absolute_path=" << absolute_path
<< " normal_gcc_path=" << normal_gcc_path
<< " compiler_info_flags=" << gcc_flags.compiler_info_flags();
return false;
}
}
std::vector<string> subprogram_paths;
if (!CxxCompilerInfoBuilder::GetSubprograms(
normal_gcc_path, gcc_flags.lang(), gcc_flags.compiler_info_flags(),
compiler_info_envs, gcc_flags.cwd(), no_integrated_as,
&subprogram_paths)) {
LOG(ERROR) << "failed to get subprograms.";
return false;
}
if (no_integrated_as && !HasAsPath(subprogram_paths)) {
LOG(ERROR) << "no_integrated_as is set but we cannot find as.";
return false;
}
for (const auto& path : subprogram_paths) {
bool may_register = false;
if (no_integrated_as && absl::EndsWith(path, "as")) {
may_register = true;
} else {
// List only subprograms under -B path for backward compatibility.
// See b/63082235
for (const string& b : B_options) {
if (absl::StartsWith(path, b)) {
may_register = true;
break;
}
}
}
if (!may_register) {
LOG(INFO) << "showed up as subprogram but not sent for"
<< " backword compatibility."
<< " path=" << path << " normal_gcc_path=" << normal_gcc_path
<< " compiler_info_flags=" << gcc_flags.compiler_info_flags();
continue;
}
string absolute_path = file::JoinPathRespectAbsolute(gcc_flags.cwd(), path);
if (!known_subprograms.insert(absolute_path).second) {
LOG(INFO) << "ignored duplicated subprogram: " << absolute_path;
continue;
}
if (!AddSubprogramInfo(path, absolute_path,
compiler_info->mutable_subprograms())) {
LOG(ERROR) << "invalid subprogram:"
<< " absolute_path=" << absolute_path
<< " normal_gcc_path=" << normal_gcc_path
<< " compiler_info_flags=" << gcc_flags.compiler_info_flags();
return false;
}
}
return true;
}
/* static */
void GCCCompilerInfoBuilder::ParseSubprogramFlags(
const string& normal_gcc_path,
const GCCFlags& gcc_flags,
std::vector<string>* clang_plugins,
std::vector<string>* B_options,
bool* no_integrated_as) {
const std::vector<string>& compiler_info_flags =
gcc_flags.compiler_info_flags();
FlagParser flag_parser;
GCCFlags::DefineFlags(&flag_parser);
// Clang plugin support.
GetClangPluginPath get_clang_plugin_path(clang_plugins);
flag_parser.AddFlag("Xclang")->SetCallbackForParsedArgs(
&get_clang_plugin_path);
// Support no-integrated-as.
flag_parser.AddBoolFlag("no-integrated-as")->SetSeenOutput(no_integrated_as);
flag_parser.AddBoolFlag("fno-integrated-as")->SetSeenOutput(no_integrated_as);
// Parse -B options.
FlagParser::Flag* flag_B = flag_parser.AddBoolFlag("B");
std::vector<string> argv;
argv.push_back(normal_gcc_path);
copy(compiler_info_flags.begin(), compiler_info_flags.end(),
back_inserter(argv));
flag_parser.Parse(argv);
std::copy(flag_B->values().cbegin(), flag_B->values().cend(),
std::back_inserter(*B_options));
}
// static
bool GCCCompilerInfoBuilder::HasAsPath(
const std::vector<string>& subprogram_paths) {
for (const auto& path : subprogram_paths) {
absl::string_view basename = file::Basename(path);
if (basename == "as" || absl::EndsWith(basename, "-as")) {
return true;
}
}
return false;
}
// static
string GCCCompilerInfoBuilder::GetRealCompilerPath(
const string& normal_gcc_path,
const string& cwd,
const std::vector<string>& envs) {
#if !defined(__linux__) && !defined(__MACH__) && !defined(_WIN32)
return normal_gcc_path;
#endif
#if defined(__linux__) || defined(__MACH__)
// For whom using a wrapper script for clang.
// E.g. ChromeOS clang and Android.
//
// Since clang invokes itself as cc1, we can find its real name by capturing
// what is cc1. Exception is that it is invoked via a shell script that
// invokes loader, which might be only done by ChromeOS clang.
//
// For pnacl-clang, although we still use binary_hash of local_compiler for
// command_spec in request, we also need real compiler to check toolchain
// update for compiler_info_cache.
if (GCCFlags::IsClangCommand(normal_gcc_path)) {
const string real_path = GetRealClangPath(normal_gcc_path, cwd, envs);
if (real_path.empty()) {
LOG(WARNING) << "seems not be a clang?"
<< " normal_gcc_path=" << normal_gcc_path;
return normal_gcc_path;
}
#ifndef __linux__
return real_path;
#else
// Ubuntu Linux is required to build ChromeOS.
// We do not need to consider ChromeOS clang for Mac.
// http://www.chromium.org/chromium-os/quick-start-guide
//
// Consider the clang is ChromeOS clang, which runs via a wrapper.
// TODO: more reliable ways?
string real_chromeos_clang_path = real_path + ".elf";
if (IsExecutable(cwd, real_chromeos_clang_path)) {
return real_chromeos_clang_path;
}
return real_path;
#endif
}
#endif
#ifdef __linux__
// For ChromeOS compilers.
// Note: Ubuntu Linux is required to build ChromeOS.
// http://www.chromium.org/chromium-os/quick-start-guide
std::vector<string> argv;
argv.push_back(normal_gcc_path);
argv.push_back("-v");
int32_t status = 0;
string v_output;
{
GOMA_COUNTERZ("ReadCommandOutput(-v)");
v_output = ReadCommandOutput(normal_gcc_path, argv, envs, cwd,
MERGE_STDOUT_STDERR, &status);
}
LOG_IF(ERROR, status != 0)
<< "ReadCommandOutput exited with non zero status code."
<< " normal_gcc_path=" << normal_gcc_path << " status=" << status
<< " argv=" << argv << " envs=" << envs << " cwd=" << cwd
<< " v_output=" << v_output;
const char* kCollectGcc = "COLLECT_GCC=";
size_t index = v_output.find(kCollectGcc);
if (index == string::npos)
return normal_gcc_path;
index += strlen(kCollectGcc);
// If COLLECT_GCC is specified and gcc is accompanied by gcc.real,
// we assume the "real" one is the last binary we will run.
// TODO: More reliable ways?
const string& gcc_path =
v_output.substr(index, v_output.find_first_of("\r\n", index) - index);
const string& real_gcc_path = gcc_path + ".real";
if (IsExecutable(cwd, real_gcc_path)) {
return real_gcc_path;
}
return gcc_path;
#endif
#ifdef __MACH__
if (file::Dirname(normal_gcc_path) != "/usr/bin") {
return normal_gcc_path;
}
const string clang_path = GetRealClangPath(normal_gcc_path, cwd, envs);
if (!clang_path.empty()) {
return clang_path;
}
LOG(INFO) << "The command seems not clang. Use it as-is: " << normal_gcc_path;
return normal_gcc_path;
#endif
#ifdef _WIN32
// For Windows nacl-{gcc,g++}.
// The real binary is ../libexec/nacl-{gcc,g++}.exe. Binaries under
// the bin directory are just wrappers to them.
if (GCCFlags::IsNaClGCCCommand(normal_gcc_path)) {
const string& candidate_path = file::JoinPath(
NaClCompilerInfoBuilderHelper::GetNaClToolchainRoot(normal_gcc_path),
file::JoinPath("libexec", file::Basename(normal_gcc_path)));
if (IsExecutable(cwd, candidate_path)) {
return candidate_path;
}
LOG(ERROR) << "cannot find nacl-gcc's real compiler path."
<< " normal_gcc_path=" << normal_gcc_path
<< " cwd=" << cwd
<< " candidate_path=" << candidate_path;
}
return normal_gcc_path;
#endif
}
} // namespace devtools_goma