blob: 49bc367189c815d0f9a925cb765fac9741988f80 [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 "vc_flags.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "cmdline_parser.h"
#include "compiler_flags.h"
#include "file.h"
#include "file_helper.h"
#include "flag_parser.h"
#include "glog/logging.h"
#include "glog/stl_logging.h"
#include "known_warning_options.h"
#include "path.h"
#include "path_resolver.h"
#include "path_util.h"
using std::string;
namespace devtools_goma {
namespace {
// Normalize paths surrounded by '"' to paths without it.
// e.g. "c:\Windows\Program Files" -> c:\Windows\Program Files.
string NormalizeWin32Path(absl::string_view path) {
// TODO: omit orphan '"' at the end of path?
if (absl::StartsWith(path, "\"")) {
if (absl::EndsWith(path, "\"")) {
path = path.substr(1, path.length() - 2);
} else {
path = path.substr(1);
}
}
return string(path);
}
string ToNormalizedBasename(absl::string_view in) {
// Note file::Basename does not understand "\\" as a path delimiter
// on non-Windows.
return absl::AsciiStrToLower(GetBasename(in));
}
} // namespace
class Win32PathNormalizer : public FlagParser::Callback {
public:
// Returns parsed flag value of value for flag.
string ParseFlagValue(const FlagParser::Flag& flag,
const string& value) override;
};
string Win32PathNormalizer::ParseFlagValue(const FlagParser::Flag& /* flag */,
const string& value) {
return NormalizeWin32Path(value);
}
/* static */
bool VCFlags::IsVCCommand(absl::string_view arg) {
// As a substring "cl" would be found in other commands like "clang" or
// "nacl-gcc". Also, "cl" is case-insensitive on Windows and can be postfixed
// with ".exe".
const string& s = ToNormalizedBasename(arg);
return s == "cl.exe" || s == "cl";
}
/* static */
bool VCFlags::IsClangClCommand(absl::string_view arg) {
const string& s = ToNormalizedBasename(arg);
return s == "clang-cl.exe" || s == "clang-cl";
}
/* static */
string VCFlags::GetCompilerName(absl::string_view arg) {
if (IsClangClCommand(arg)) {
return "clang-cl";
}
return "cl.exe";
}
string VCFlags::compiler_name() const {
return GetCompilerName(compiler_name_);
}
VCFlags::VCFlags(const std::vector<string>& args, const string& cwd)
: CompilerFlags(args, cwd),
is_cplusplus_(true),
ignore_stdinc_(false),
has_Brepro_(false),
require_mspdbserv_(false) {
bool result =
ExpandArgs(cwd, args, &expanded_args_, &optional_input_filenames_);
if (!result) {
Fail("Unable to expand args", args);
return;
}
FlagParser parser;
DefineFlags(&parser);
Win32PathNormalizer normalizer;
// Compile only, no link
FlagParser::Flag* flag_c = parser.AddBoolFlag("c");
// Preprocess only, do not compile
FlagParser::Flag* flag_E = parser.AddBoolFlag("E");
FlagParser::Flag* flag_EP = parser.AddBoolFlag("EP");
FlagParser::Flag* flag_P = parser.AddBoolFlag("P");
// Ignore "standard places".
FlagParser::Flag* flag_X = parser.AddBoolFlag("X");
// Compile file as .c
FlagParser::Flag* flag_Tc = parser.AddFlag("Tc");
// Compile all files as .c
FlagParser::Flag* flag_TC = parser.AddBoolFlag("TC");
// Compile file as .cpp
FlagParser::Flag* flag_Tp = parser.AddFlag("Tp");
// Compile all files as .cpp
FlagParser::Flag* flag_TP = parser.AddBoolFlag("TP");
// Specify output.
FlagParser::Flag* flag_o = parser.AddFlag("o"); // obsoleted but always there
FlagParser::Flag* flag_Fo = parser.AddPrefixFlag("Fo"); // obj file path
FlagParser::Flag* flag_Fe = parser.AddPrefixFlag("Fe"); // exe file path
// Optimization prefix
parser.AddPrefixFlag("O")->SetOutput(&compiler_info_flags_);
// M[DT]d? define _DEBUG, _MT, and _DLL.
parser.AddPrefixFlag("MD")->SetOutput(&compiler_info_flags_);
parser.AddPrefixFlag("MT")->SetOutput(&compiler_info_flags_);
// standard
parser.AddBoolFlag("permissive-")->SetOutput(&compiler_info_flags_);
parser.AddPrefixFlag("std:")->SetOutput(&compiler_info_flags_);
// Additional include path.
parser.AddFlag("I")->SetValueOutputWithCallback(&normalizer, &include_dirs_);
MacroStore<true> defined_macro_store(&commandline_macros_);
MacroStore<false> undefined_macro_store(&commandline_macros_);
parser.AddFlag("D")->SetCallbackForParsedArgs(&defined_macro_store);
parser.AddFlag("U")->SetCallbackForParsedArgs(&undefined_macro_store);
// specifies the architecture for code generation.
// It is passed to compiler_info_flags_ to get macros.
parser.AddFlag("arch")->SetOutput(&compiler_info_flags_);
// Flags that affects predefined macros
FlagParser::Flag* flag_ZI = parser.AddBoolFlag("ZI");
FlagParser::Flag* flag_RTC = parser.AddPrefixFlag("RTC");
FlagParser::Flag* flag_Zc_wchar_t = parser.AddBoolFlag("Zc:wchar_t");
FlagParser::Flag* flag_Zi = parser.AddBoolFlag("Zi");
parser.AddFlag("FI")->SetValueOutputWithCallback(nullptr, &root_includes_);
FlagParser::Flag* flag_Yc = parser.AddPrefixFlag("Yc");
FlagParser::Flag* flag_Yu = parser.AddPrefixFlag("Yu");
FlagParser::Flag* flag_Fp = parser.AddPrefixFlag("Fp");
// Machine options used by clang-cl.
FlagParser::Flag* flag_m = parser.AddFlag("m");
FlagParser::Flag* flag_fmsc_version = parser.AddPrefixFlag("fmsc-version=");
FlagParser::Flag* flag_fms_compatibility_version =
parser.AddPrefixFlag("fms-compatibility-version=");
FlagParser::Flag* flag_fsanitize = parser.AddFlag("fsanitize");
FlagParser::Flag* flag_fno_sanitize_blacklist = nullptr;
FlagParser::Flag* flag_fsanitize_blacklist = nullptr;
FlagParser::Flag* flag_mllvm = parser.AddFlag("mllvm");
FlagParser::Flag* flag_isystem = parser.AddFlag("isystem");
// TODO: check -iquote?
// http://clang.llvm.org/docs/UsersManual.html#id8
FlagParser::Flag* flag_imsvc = parser.AddFlag("imsvc");
FlagParser::Flag* flag_std = parser.AddFlag("std"); // e.g. -std=c11
std::vector<string> incremental_linker_flags;
parser.AddBoolFlag("Brepro")->SetOutput(&incremental_linker_flags);
parser.AddBoolFlag("Brepro-")->SetOutput(&incremental_linker_flags);
if (compiler_name() == "clang-cl") {
flag_m->SetOutput(&compiler_info_flags_);
flag_fmsc_version->SetOutput(&compiler_info_flags_);
flag_fms_compatibility_version->SetOutput(&compiler_info_flags_);
flag_fsanitize->SetOutput(&compiler_info_flags_);
// TODO: do we need to support more sanitize options?
flag_fno_sanitize_blacklist = parser.AddBoolFlag("fno-sanitize-blacklist");
flag_fsanitize_blacklist = parser.AddFlag("fsanitize-blacklist=");
flag_mllvm->SetOutput(&compiler_info_flags_);
flag_isystem->SetOutput(&compiler_info_flags_);
flag_imsvc->SetOutput(&compiler_info_flags_);
flag_std->SetOutput(&compiler_info_flags_);
parser.AddBoolFlag("w")->SetOutput(&compiler_info_flags_);
// Make these understood.
parser.AddBoolFlag(
"fansi-escape-codes"); // Use ANSI escape codes for diagnostics
parser.AddBoolFlag(
"fdiagnostics-absolute-paths"); // Print absolute paths in diagnostics
// Make it understand Xclang.
parser.AddFlag("Xclang")->SetOutput(&compiler_info_flags_);
parser.AddBoolFlag("mincremental-linker-compatible")
->SetOutput(&incremental_linker_flags);
parser.AddBoolFlag("mno-incremental-linker-compatible")
->SetOutput(&incremental_linker_flags);
}
parser.AddNonFlag()->SetOutput(&input_filenames_);
parser.Parse(expanded_args_);
unknown_flags_ = parser.unknown_flag_args();
is_successful_ = true;
lang_ = "c++";
// CL.exe default to C++ unless /Tc /TC specified,
// or the file is named .c and /Tp /TP are not specified.
if (flag_Tc->seen() || flag_TC->seen() ||
((!input_filenames_.empty() &&
GetExtension(input_filenames_[0]) == "c") &&
!flag_TP->seen() && !flag_Tp->seen())) {
is_cplusplus_ = false;
lang_ = "c";
}
// Handle implicit macros, lang_ must not change after this.
// See http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.90).aspx
if (lang_ == "c++") {
implicit_macros_.append("#define __cplusplus\n");
}
if (flag_ZI->seen()) {
implicit_macros_.append("#define _VC_NODEFAULTLIB\n");
}
if (flag_RTC->seen()) {
implicit_macros_.append("#define __MSVC_RUNTIME_CHECKS\n");
}
if (flag_Zc_wchar_t->seen()) {
implicit_macros_.append("#define _NATIVE_WCHAR_T_DEFINED\n");
implicit_macros_.append("#define _WCHAR_T_DEFINED\n");
}
// Debug information format.
// http://msdn.microsoft.com/en-us/library/958x11bc.aspx
// For VC, /Zi and /ZI generated PDB.
// For clang-cl, /Zi is alias to /Z7. /ZI is not supported.
// Probably OK to deal them as the same?
// See https://msdn.microsoft.com/en-us/library/958x11bc.aspx,
// and http://clang.llvm.org/docs/UsersManual.html
if (compiler_name() != "clang-cl" && (flag_Zi->seen() || flag_ZI->seen())) {
require_mspdbserv_ = true;
}
if (flag_fsanitize_blacklist && flag_fsanitize_blacklist->seen() &&
!flag_fno_sanitize_blacklist->seen()) {
// TODO: follow -fno-sanitize-blacklist spec.
// http://clang.llvm.org/docs/UsersManual.html:
// > -fno-sanitize-blacklist: don't use blacklist file,
// > if it was specified *earlier in the command line*.
const std::vector<string>& values = flag_fsanitize_blacklist->values();
std::copy(values.begin(), values.end(),
back_inserter(optional_input_filenames_));
}
if (flag_X->seen()) {
ignore_stdinc_ = true;
compiler_info_flags_.push_back("/X");
}
if (flag_EP->seen() || flag_E->seen()) {
return; // output to stdout
}
if (flag_Yc->seen()) {
creating_pch_ = flag_Yc->GetLastValue();
}
if (flag_Yu->seen()) {
using_pch_ = flag_Yu->GetLastValue();
}
if (flag_Fp->seen()) {
using_pch_filename_ = flag_Fp->GetLastValue();
}
if (!incremental_linker_flags.empty()) {
const string& last = incremental_linker_flags.back();
if (last == "-mno-incremental-linker-compatible" || last == "/Brepro" ||
last == "-Brepro") {
has_Brepro_ = true;
}
}
string new_extension = ".obj";
string force_output;
if (flag_Fo->seen())
force_output = flag_Fo->GetLastValue();
if (flag_P->seen()) {
new_extension = ".i";
// any option to control output filename?
force_output = "";
} else if (!flag_c->seen()) {
new_extension = ".exe";
if (flag_Fe->seen()) {
force_output = flag_Fe->GetLastValue();
} else {
force_output = "";
}
}
// Single file with designated destination
if (input_filenames_.size() == 1) {
if (force_output.empty() && flag_o->seen()) {
force_output = flag_o->GetLastValue();
}
if (!force_output.empty()) {
output_files_.push_back(ComposeOutputFilePath(
input_filenames_[0], force_output, new_extension));
}
if (!output_files_.empty()) {
return;
}
}
for (const auto& input_filename : input_filenames_) {
output_files_.push_back(
ComposeOutputFilePath(input_filename, force_output, new_extension));
}
}
bool VCFlags::IsClientImportantEnv(const char* env) const {
if (IsServerImportantEnv(env)) {
return true;
}
// We don't override these variables in goma server.
// So, these are client important, but don't send to server.
static const char* kCheckEnvs[] = {
"PATHEXT=", "SystemDrive=", "SystemRoot=",
};
for (const char* check_env : kCheckEnvs) {
if (absl::StartsWithIgnoreCase(env, check_env)) {
return true;
}
}
return false;
}
bool VCFlags::IsServerImportantEnv(const char* env) const {
static const char* kCheckEnvs[] = {
"INCLUDE=", "LIB=", "MSC_CMD_FLAGS=",
"VCINSTALLDIR=", "VSINSTALLDIR=", "WindowsSdkDir=",
};
for (const char* check_env : kCheckEnvs) {
if (absl::StartsWithIgnoreCase(env, check_env)) {
return true;
}
}
return false;
}
// static
void VCFlags::DefineFlags(FlagParser* parser) {
FlagParser::Options* opts = parser->mutable_options();
// define all known flags of cl.exe here.
// undefined flag here would be treated as non flag arg
// if the arg begins with alt_flag_prefix.
// b/18063824
// https://code.google.com/p/chromium/issues/detail?id=427942
opts->flag_prefix = '-';
opts->alt_flag_prefix = '/';
opts->allows_nonspace_arg = true;
// http://msdn.microsoft.com//library/fwkeyyhe.aspx
// note: some bool flag may take - as suffix even if it is documented
// on the above URL? clang-cl defines such flag.
parser->AddBoolFlag("?"); // alias of help
parser->AddPrefixFlag("AI"); // specifies a directory to search for #using
parser->AddPrefixFlag("analyze"); // enable code analysis
parser->AddPrefixFlag("arch"); // specifies the architecture for code gen
parser->AddBoolFlag("await"); // enable resumable functions extension
parser->AddBoolFlag("bigobj"); // increases the num of addressable sections
parser->AddBoolFlag("C"); // preserves comments during preprocessing
parser->AddBoolFlag("c"); // compile only
parser->AddPrefixFlag("cgthreads"); // specify num of cl.exe threads
parser->AddPrefixFlag("clr");
parser->AddPrefixFlag("constexpr"); // constexpr options
parser->AddFlag("D"); // define macro
parser->AddPrefixFlag("doc"); // process documentation comments
// /diagnostics:<args,...> controls the format of diagnostic messages
parser->AddPrefixFlag("diagnostics:");
parser->AddBoolFlag("E"); // preprocess to stdout
parser->AddPrefixFlag("EH"); // exception ahdling model
parser->AddBoolFlag("EP"); // disable linemarker output and preprocess
parser->AddPrefixFlag("errorReport");
parser->AddFlag("F"); // set stack size
parser->AddPrefixFlag("favor"); // optimize for architecture specifics
parser->AddPrefixFlag("FA"); // output assembly code file
parser->AddPrefixFlag("Fa"); // output assembly code to this file
parser->AddBoolFlag("FC"); // full path of source code in diagnostic text
parser->AddPrefixFlag("Fd"); // set pdb file name
parser->AddPrefixFlag("Fe"); // set output executable file or directory
parser->AddFlag("FI"); // include file before parsing
parser->AddPrefixFlag("Fi"); // set preprocess output file name
parser->AddPrefixFlag("Fm"); // set map file name
parser->AddPrefixFlag("Fo"); // set output object file or directory
parser->AddPrefixFlag("fp"); // specify floating proint behavior
parser->AddPrefixFlag("Fp"); // set pch file name
parser->AddPrefixFlag("FR"); // .sbr file
parser->AddPrefixFlag("Fr"); // .sbr file without info on local var
parser->AddBoolFlag("FS"); // force synchronous PDB writes
parser->AddFlag("FU"); // #using
parser->AddBoolFlag("Fx"); // merges injected code
parser->AddBoolFlag("GA"); // optimize for win app
parser->AddBoolFlag("Gd"); // calling convention
parser->AddBoolFlag("Ge"); // enable stack probes
parser->AddBoolFlag("GF"); // enable string pool
parser->AddBoolFlag("GF-"); // disable string pooling
parser->AddBoolFlag("GH"); // call hook function _pexit
parser->AddBoolFlag("Gh"); // call hook function _penter
parser->AddBoolFlag("GL"); // enables whole program optimization
parser->AddBoolFlag("GL-");
parser->AddBoolFlag("Gm"); // enables minimal rebuild
parser->AddBoolFlag("Gm-");
parser->AddBoolFlag("GR"); // enable emission of RTTI data
parser->AddBoolFlag("GR-"); // disable emission of RTTI data
parser->AddBoolFlag("Gr"); // calling convention
parser->AddBoolFlag("GS"); // buffer security check
parser->AddBoolFlag("GS-");
parser->AddPrefixFlag("Gs"); // controls stack probes
parser->AddBoolFlag("GT"); // fibre safety thread-local storage
parser->AddBoolFlag("guard:cf"); // enable control flow guard
parser->AddBoolFlag("guard:cf-"); // disable control flow guard
parser->AddBoolFlag("Gv"); // calling convention
parser->AddBoolFlag("Gw"); // put each data item in its own section
parser->AddBoolFlag("Gw-"); // don't put each data item in its own section
parser->AddBoolFlag("GX"); // enable exception handling
parser->AddBoolFlag("Gy"); // put each function in its own section
parser->AddBoolFlag("Gy-"); // don't put each function in its own section
parser->AddBoolFlag("GZ"); // same as /RTC
parser->AddBoolFlag("Gz"); // calling convention
parser->AddPrefixFlag("H"); // restricts the length of external names
parser->AddBoolFlag("HELP"); // alias of help
parser->AddBoolFlag("help"); // display available options
parser->AddBoolFlag("homeparams"); // copy register parameters to stack
parser->AddBoolFlag("hotpatch"); // create hotpatchable image
parser->AddFlag("I"); // add directory to include search path
parser->AddBoolFlag("J"); // make char type unsinged
parser->AddBoolFlag("kernel"); // create kernel mode binary
parser->AddBoolFlag("kernel-");
parser->AddBoolFlag("LD"); // create DLL
parser->AddBoolFlag("LDd"); // create debug DLL
parser->AddFlag("link"); // forward options to the linker
parser->AddBoolFlag("LN");
parser->AddPrefixFlag("MD"); // use DLL run time
// MD, MDd
parser->AddPrefixFlag("MP"); // build with multiple process
parser->AddPrefixFlag("MT"); // use static run time
// MT, MTd
parser->AddBoolFlag("nologo");
parser->AddPrefixFlag("O"); // optimization level
// O1, O2
// Ob[012], Od, Oi, Oi-, Os, Ot, Ox, Oy, Oy-
parser->AddBoolFlag("openmp");
parser->AddBoolFlag("P"); // preprocess to file
// set standard-conformance mode (feature set subject to change)
parser->AddBoolFlag("permissive-");
parser->AddPrefixFlag("Q");
// Qfast_transcendentals, QIfirst, Qimprecise_fwaits, Qpar
// Qsafe_fp_loads, Qrev-report:n
parser->AddPrefixFlag("RTC"); // run time error check
parser->AddBoolFlag("sdl"); // additional security check
parser->AddBoolFlag("sdl-");
parser->AddBoolFlag("showIncludes"); // print info about included files
parser->AddPrefixFlag("std:"); // C++ standard version
parser->AddFlag("Tc"); // specify a C source file
parser->AddBoolFlag("TC"); // treat all source files as C
parser->AddFlag("Tp"); // specify a C++ source file
parser->AddBoolFlag("TP"); // treat all source files as C++
parser->AddFlag("U"); // undefine macro
parser->AddBoolFlag("u"); // remove all predefined macros
parser->AddPrefixFlag("V"); // Sets the version string
parser->AddPrefixFlag("vd"); // control vtordisp placement
// for member pointers.
parser->AddBoolFlag("vmb"); // use a best-case representation method
parser->AddBoolFlag("vmg"); // use a most-general representation
// set the default most-general representation
parser->AddBoolFlag("vmm"); // to multiple inheritance
parser->AddBoolFlag("vms"); // to single inheritance
parser->AddBoolFlag("vmv"); // to virtual inheritance
parser->AddBoolFlag("volatile");
parser->AddPrefixFlag("W"); // warning
// W0, W1, W2, W3, W4, Wall, WX, WX-, WL, Wp64
parser->AddPrefixFlag("w"); // disable warning
// wd4005, ...
parser->AddBoolFlag("X"); // ignore standard include paths
parser->AddBoolFlag("Y-"); // ignore precompiled header
parser->AddPrefixFlag("Yc"); // create precompiled header
parser->AddBoolFlag("Yd"); // place debug information
parser->AddPrefixFlag("Yl"); // inject PCH reference for debug library
parser->AddPrefixFlag("Yu"); // use precompiled header
parser->AddBoolFlag("Z7"); // debug information format
parser->AddBoolFlag("Za"); // disable language extensions
parser->AddPrefixFlag("Zc"); // conformance
// line number only debug information; b/30077868
parser->AddBoolFlag("Zd");
parser->AddBoolFlag("Ze"); // enable microsoft extensions
parser->AddBoolFlag("ZH:SHA_256"); // use SHA256 for file checksum
parser->AddBoolFlag("Zg"); // generate function prototype
parser->AddBoolFlag("ZI"); // produce pdb
parser->AddBoolFlag("Zi"); // enable debug information
parser->AddBoolFlag("Zl"); // omit default library name
parser->AddPrefixFlag("Zm"); // specify precompiled header memory limit
parser->AddBoolFlag("Zo"); // enhance optimized debugging
parser->AddBoolFlag("Zo-");
parser->AddPrefixFlag("Zp"); // default maximum struct packing alignment
// Zp1, Zp2, Zp4, Zp8, Zp16
parser->AddFlag("Zs"); // syntax check only
parser->AddPrefixFlag("ZW"); // windows runtime compilation
// New flags from VS2015 Update 2
parser->AddPrefixFlag("source-charset:"); // set source character set.
parser->AddPrefixFlag("execution-charset:"); // set execution character set.
parser->AddBoolFlag("utf-8"); // set both character set to utf-8.
parser->AddBoolFlag("validate-charset"); // validate utf-8 files.
parser->AddBoolFlag("validate-charset-");
// /d2XXX is undocument flag for debugging.
// See b/27777598, b/68147091
parser->AddPrefixFlag("d2");
// Brepro is undocument flag for reproducible build?
// https://github.com/llvm-project/llvm-project-20170507/blob/3e1fa78737e3b303558e6310c49d31c31827a2bf/clang/include/clang/Driver/CLCompatOptions.td#L55
parser->AddBoolFlag("Brepro");
parser->AddBoolFlag("Brepro-");
// also see clang-cl
// http://llvm.org/klaus/clang/blob/master/include/clang/Driver/CLCompatOptions.td
parser->AddFlag("o"); // set output file or directory
parser->AddBoolFlag("fallback");
parser->AddBoolFlag("G1");
parser->AddBoolFlag("G2");
parser->AddFlag("imsvc"); // both -imsvc, /imsvc.
// clang-cl flags. only accepts if it starts with '-'.
opts->flag_prefix = '-';
opts->alt_flag_prefix = '\0';
parser->AddFlag("m");
parser->AddPrefixFlag("fmsc-version="); // -fmsc-version=<arg>
parser->AddPrefixFlag(
"fms-compatibility-version="); // -fms-compatibility-version=<arg>
parser->AddFlag("fsanitize");
parser->AddBoolFlag("fcolor-diagnostics"); // Use color for diagnostics
parser->AddBoolFlag(
"fno-standalone-debug"); // turn on the vtable-based optimization
parser->AddBoolFlag(
"fstandalone-debug"); // turn off the vtable-based optimization
parser->AddBoolFlag("gcolumn-info"); // debug information (-g)
parser->AddBoolFlag("gline-tables-only"); // debug information (-g)
parser->AddFlag("Xclang");
parser->AddFlag("isystem");
parser->AddPrefixFlag("-analyze"); // enable code analysis (--analyze)
opts->flag_prefix = '-';
opts->alt_flag_prefix = '/';
}
// static
bool VCFlags::ExpandArgs(const string& cwd,
const std::vector<string>& args,
std::vector<string>* expanded_args,
std::vector<string>* optional_input_filenames) {
// Expand arguments which start with '@'.
for (const auto& arg : args) {
if (absl::StartsWith(arg, "@")) {
const string& source_list_filename =
PathResolver::PlatformConvert(arg.substr(1));
string source_list;
if (!ReadFileToString(
file::JoinPathRespectAbsolute(cwd, source_list_filename),
&source_list)) {
LOG(ERROR) << "failed to read: " << source_list_filename;
return false;
}
if (optional_input_filenames) {
optional_input_filenames->push_back(source_list_filename);
}
if (source_list[0] == '\xff' && source_list[1] == '\xfe') {
// UTF-16LE.
// do we need to handle FEFF(UTF-16BE) case or others?
// TODO: handle real wide character.
// use WideCharToMultiByte on Windows, and iconv on posix?
VLOG(1) << "Convert WC to MB in @" << source_list_filename;
string source_list_mb;
// We don't need BOM (the first 2 bytes: 0xFF 0xFE)
source_list_mb.resize(source_list.size() / 2 - 1);
for (size_t i = 2; i < source_list.size(); i += 2) {
source_list_mb[i / 2 - 1] = source_list[i];
if (source_list[i + 1] != 0) {
LOG(ERROR) << "failed to convert:" << source_list_filename;
return false;
}
}
source_list.swap(source_list_mb);
VLOG(1) << "source_list:" << source_list;
}
if (!ParseWinCommandLineToArgv(source_list, expanded_args)) {
LOG(WARNING) << "failed to parse command line: " << source_list;
return false;
}
VLOG(1) << "expanded_args:" << *expanded_args;
} else {
expanded_args->push_back(arg);
}
}
return true;
}
// static
string VCFlags::ComposeOutputFilePath(const string& input_file_name,
const string& output_file_or_dir,
const string& output_file_ext) {
string input_file = NormalizeWin32Path(input_file_name);
string output_target = NormalizeWin32Path(output_file_or_dir);
bool output_is_dir = false;
if (output_target.length() &&
output_target[output_target.length() - 1] == '\\') {
output_is_dir = true;
}
if (output_target.length() && !output_is_dir) {
return output_target;
}
// We need only the filename part of input file
size_t begin = input_file.find_last_of("/\\");
size_t end = input_file.rfind('.');
begin = (begin == string::npos) ? 0 : begin + 1;
end = (end == string::npos) ? input_file_name.size() : end;
string new_output;
if (end > begin) {
new_output = input_file.substr(begin, end - begin);
new_output.append(output_file_ext);
if (output_target.length() && output_is_dir) {
new_output = output_target + new_output;
}
} else {
new_output = output_target;
}
return new_output;
}
} // namespace devtools_goma