If true, ccache will write results to remote storage even for local storage
cache hits. The default is false.
+[#config_response_file_format]
+*response_file_format* (*CCACHE_RESPONSE_FILE_FORMAT*)::
+
+ Ccache normally guesses the response file format based on the compiler type. The
+ *response_file_format* option lets you force the response file quoting behavior.
+ This can be useful if the compiler supports both posix and windows response file
+ quoting. Possible values are:
++
+--
+*auto*::
+ Guess one of the formats below based on the compiler type. This is the default.
+*posix*::
+ Posix quoting behavior.
+*windows*::
+ Windows quoting behavior.
+--
+
[#config_run_second_cpp]
*run_second_cpp* (*CCACHE_CPP2* or *CCACHE_NOCPP2*, see _<<Boolean values>>_ above)::
if (argpath[-1] == '-') {
++argpath;
}
- auto file_args = Args::from_atfile(argpath,
- config.is_compiler_group_msvc()
- ? Args::AtFileFormat::msvc
- : Args::AtFileFormat::gcc);
+ auto file_args = Args::from_atfile(argpath, config.atfile_format());
if (!file_args) {
LOG("Couldn't read arg file {}", argpath);
return Statistic::bad_compiler_arguments;
// Argument is a comma-separated list of files.
auto paths = util::split_into_strings(args[i], ",");
for (auto it = paths.rbegin(); it != paths.rend(); ++it) {
- auto file_args = Args::from_atfile(*it);
+ auto file_args = Args::from_atfile(*it, AtFileFormat::gcc);
if (!file_args) {
LOG("Couldn't read CUDA options file {}", *it);
return Statistic::bad_compiler_arguments;
#include "args.hpp"
+#include <ccache/config.hpp>
#include <ccache/core/exceptions.hpp>
+#include <ccache/util/assertions.hpp>
#include <ccache/util/file.hpp>
#include <ccache/util/logging.hpp>
#include <ccache/util/string.hpp>
std::optional<Args>
Args::from_atfile(const std::string& filename, AtFileFormat format)
{
+ ASSERT(format != AtFileFormat::auto_guess);
+
const auto argtext = util::read_file<std::string>(filename);
if (!argtext) {
LOG("Failed to read atfile {}: {}", filename, argtext.error());
switch (*pos) {
case '\\':
switch (format) {
+ case AtFileFormat::auto_guess:
+ ASSERT(false); // Can't happen
+ break;
case AtFileFormat::gcc:
pos++;
if (*pos == '\0') {
#include <string_view>
#include <vector>
+enum class AtFileFormat;
+
class Args
{
public:
- enum class AtFileFormat {
- gcc, // '\'' and '"' quote, '\\' escapes any character
- msvc, // '"' quotes, '\\' escapes only '"' and '\\'
- };
-
Args() = default;
Args(const Args& other) = default;
Args(Args&& other) noexcept;
static Args from_argv(int argc, const char* const* argv);
static Args from_string(std::string_view command);
- static std::optional<Args>
- from_atfile(const std::string& filename,
- AtFileFormat format = AtFileFormat::gcc);
+ static std::optional<Args> from_atfile(const std::string& filename,
+ AtFileFormat format);
Args& operator=(const Args& other) = default;
Args& operator=(Args&& other) noexcept;
remote_only,
remote_storage,
reshare,
+ response_file_format,
run_second_cpp,
sloppiness,
stats,
{"remote_only", {ConfigItem::remote_only}},
{"remote_storage", {ConfigItem::remote_storage}},
{"reshare", {ConfigItem::reshare}},
+ {"response_file_format", {ConfigItem::response_file_format}},
{"run_second_cpp", {ConfigItem::run_second_cpp}},
{"secondary_storage", {ConfigItem::remote_storage, "remote_storage"}},
{"sloppiness", {ConfigItem::sloppiness}},
{"REMOTE_ONLY", "remote_only"},
{"REMOTE_STORAGE", "remote_storage"},
{"RESHARE", "reshare"},
+ {"RESPONSE_FILE_FORMAT", "response_file_format"},
{"SECONDARY_STORAGE", "remote_storage"}, // Alias for CCACHE_REMOTE_STORAGE
{"SLOPPINESS", "sloppiness"},
{"STATS", "stats"},
{"UMASK", "umask"},
};
+AtFileFormat
+parse_response_file_format(const std::string& value)
+{
+ if (value == "windows") {
+ return AtFileFormat::msvc;
+ } else if (value == "posix") {
+ return AtFileFormat::gcc;
+ } else {
+ return AtFileFormat::auto_guess;
+ }
+}
+
bool
parse_bool(const std::string& value,
const std::optional<std::string> env_var_key,
#endif
}
+std::string
+atfile_format_to_string(AtFileFormat atfile_format)
+{
+ switch (atfile_format) {
+ case AtFileFormat::auto_guess:
+ return "auto";
+ case AtFileFormat::gcc:
+ return "posix";
+ case AtFileFormat::msvc:
+ return "windows";
+ }
+
+ ASSERT(false);
+}
+
std::string
compiler_type_to_string(CompilerType compiler_type)
{
case ConfigItem::reshare:
return format_bool(m_reshare);
+ case ConfigItem::response_file_format:
+ return atfile_format_to_string(m_atfile_format);
+
case ConfigItem::run_second_cpp:
return format_bool(m_run_second_cpp);
m_reshare = parse_bool(value, env_var_key, negate);
break;
+ case ConfigItem::response_file_format:
+ m_atfile_format = parse_response_file_format(value);
+ break;
+
case ConfigItem::run_second_cpp:
m_run_second_cpp = parse_bool(value, env_var_key, negate);
break;
other
};
+enum class AtFileFormat {
+ gcc, // '\'' and '"' quote, '\\' escapes any character
+ msvc, // '"' quotes, '\\' escapes only '"' and '\\'
+ auto_guess,
+};
+
std::string compiler_type_to_string(CompilerType compiler_type);
class Config : util::NonCopyable
void read(const std::vector<std::string>& cmdline_config_settings = {});
bool absolute_paths_in_stderr() const;
+ AtFileFormat atfile_format() const;
const std::filesystem::path& base_dir() const;
const std::filesystem::path& cache_dir() const;
const std::string& compiler() const;
std::filesystem::path m_system_config_path;
bool m_absolute_paths_in_stderr = false;
+ AtFileFormat m_atfile_format = AtFileFormat::auto_guess;
std::filesystem::path m_base_dir;
std::filesystem::path m_cache_dir;
std::string m_compiler;
return m_absolute_paths_in_stderr;
}
+inline AtFileFormat
+Config::atfile_format() const
+{
+ if (m_atfile_format != AtFileFormat::auto_guess) {
+ return m_atfile_format;
+ }
+
+ if (is_compiler_group_msvc()) {
+ return AtFileFormat::msvc;
+ }
+
+ return AtFileFormat::gcc;
+}
+
inline const std::filesystem::path&
Config::base_dir() const
{
#include "testutil.hpp"
#include <ccache/args.hpp>
+#include <ccache/config.hpp>
#include <ccache/util/file.hpp>
#include <doctest/doctest.h>
SUBCASE("Nonexistent file")
{
- CHECK(Args::from_atfile("at_file") == std::nullopt);
+ CHECK(Args::from_atfile("at_file", AtFileFormat::gcc) == std::nullopt);
}
SUBCASE("Empty")
{
util::write_file("at_file", "");
- args = *Args::from_atfile("at_file");
+ args = *Args::from_atfile("at_file", AtFileFormat::gcc);
CHECK(args.size() == 0);
}
SUBCASE("One argument without newline")
{
util::write_file("at_file", "foo");
- args = *Args::from_atfile("at_file");
+ args = *Args::from_atfile("at_file", AtFileFormat::gcc);
CHECK(args.size() == 1);
CHECK(args[0] == "foo");
}
SUBCASE("One argument with newline")
{
util::write_file("at_file", "foo\n");
- args = *Args::from_atfile("at_file");
+ args = *Args::from_atfile("at_file", AtFileFormat::gcc);
CHECK(args.size() == 1);
CHECK(args[0] == "foo");
}
SUBCASE("Multiple simple arguments")
{
util::write_file("at_file", "x y z\n");
- args = *Args::from_atfile("at_file");
+ args = *Args::from_atfile("at_file", AtFileFormat::gcc);
CHECK(args.size() == 3);
CHECK(args[0] == "x");
CHECK(args[1] == "y");
"at_file",
"first\rsec\\\tond\tthi\\\\rd\nfourth \tfif\\ th \"si'x\\\" th\""
" 'seve\nth'\\");
- args = *Args::from_atfile("at_file");
+ args = *Args::from_atfile("at_file", AtFileFormat::gcc);
CHECK(args.size() == 7);
CHECK(args[0] == "first");
CHECK(args[1] == "sec\tond");
SUBCASE("Ignore single quote in MSVC format")
{
util::write_file("at_file", "'a b'");
- args = *Args::from_atfile("at_file", Args::AtFileFormat::msvc);
+ args = *Args::from_atfile("at_file", AtFileFormat::msvc);
CHECK(args.size() == 2);
CHECK(args[0] == "'a");
CHECK(args[1] == "b'");
SUBCASE("Backslash as directory separator in MSVC format")
{
util::write_file("at_file", R"("-DDIRSEP='A\B\C'")");
- args = *Args::from_atfile("at_file", Args::AtFileFormat::msvc);
+ args = *Args::from_atfile("at_file", AtFileFormat::msvc);
CHECK(args.size() == 1);
CHECK(args[0] == R"(-DDIRSEP='A\B\C')");
}
SUBCASE("Backslash before quote in MSVC format")
{
util::write_file("at_file", R"(/Fo"N.dir\Release\\")");
- args = *Args::from_atfile("at_file", Args::AtFileFormat::msvc);
+ args = *Args::from_atfile("at_file", AtFileFormat::msvc);
CHECK(args.size() == 1);
CHECK(args[0] == R"(/FoN.dir\Release\)");
}
SUBCASE("Arguments on multiple lines in MSVC format")
{
util::write_file("at_file", "a\nb");
- args = *Args::from_atfile("at_file", Args::AtFileFormat::msvc);
+ args = *Args::from_atfile("at_file", AtFileFormat::msvc);
CHECK(args.size() == 2);
CHECK(args[0] == "a");
CHECK(args[1] == "b");
R"(\ \\ '\\' "\\" '"\\"' "'\\'" '''\\''' ''"\\"'' '"'\\'"' '""\\""' "''\\''" "'"\\"'" ""'\\'"" """\\""" )"
R"(\'\' '\'\'' "\'\'" ''\'\''' '"\'\'"' "'\'\''" ""\'\'"" '''\'\'''' ''"\'\'"'' '"'\'\''"' '""\'\'""' "''\'\'''" "'"\'\'"'" ""'\'\''"" """\'\'""" )"
R"(\"\" '\"\"' "\"\"" ''\"\"'' '"\"\""' "'\"\"'" ""\"\""" '''\"\"''' ''"\"\""'' '"'\"\"'"' '""\"\"""' "''\"\"''" "'"\"\""'" ""'\"\"'"" """\"\"""")");
- args = *Args::from_atfile("at_file", Args::AtFileFormat::msvc);
+ args = *Args::from_atfile("at_file", AtFileFormat::msvc);
CHECK(args.size() == 44);
CHECK(args[0] == R"(\)");
CHECK(args[1] == R"(\\)");
R"(a\\\b d"e f"g h )"
R"(a\\\"b c d )"
R"(a\\\\"b c" d e)");
- args = *Args::from_atfile("at_file", Args::AtFileFormat::msvc);
+ args = *Args::from_atfile("at_file", AtFileFormat::msvc);
CHECK(args.size() == 12);
CHECK(args[0] == R"(abc)");
CHECK(args[1] == R"(d)");
CHECK(!config.compression());
}
+TEST_CASE("Config::response_file_format")
+{
+ Config config;
+
+ SUBCASE("from config gcc")
+ {
+ util::write_file("ccache.conf", "response_file_format = posix");
+ CHECK(config.update_from_file("ccache.conf"));
+
+ CHECK(config.atfile_format() == AtFileFormat::gcc);
+ }
+
+ SUBCASE("from config msvc")
+ {
+ util::write_file("ccache.conf", "response_file_format = windows");
+ CHECK(config.update_from_file("ccache.conf"));
+
+ CHECK(config.atfile_format() == AtFileFormat::msvc);
+ }
+
+ SUBCASE("from config msvc with clang compiler")
+ {
+ util::write_file("ccache.conf",
+ "response_file_format = windows\ncompiler_type = clang");
+ CHECK(config.update_from_file("ccache.conf"));
+
+ CHECK(config.atfile_format() == AtFileFormat::msvc);
+ }
+
+ SUBCASE("guess from compiler gcc")
+ {
+ util::write_file("ccache.conf", "compiler_type = clang");
+ CHECK(config.update_from_file("ccache.conf"));
+
+ CHECK(config.atfile_format() == AtFileFormat::gcc);
+ }
+
+ SUBCASE("guess from compiler msvc")
+ {
+ util::write_file("ccache.conf", "compiler_type = msvc");
+ CHECK(config.update_from_file("ccache.conf"));
+
+ CHECK(config.atfile_format() == AtFileFormat::msvc);
+ }
+}
+
TEST_CASE("Config::set_value_in_file")
{
TestContext test_context;
"remote_only = true\n"
"remote_storage = rs\n"
"reshare = true\n"
+ "response_file_format = posix\n"
"run_second_cpp = false\n"
"sloppiness = include_file_mtime, include_file_ctime, time_macros,"
" file_stat_matches, file_stat_matches_ctime, pch_defines, system_headers,"
"(test.conf) remote_only = true",
"(test.conf) remote_storage = rs",
"(test.conf) reshare = true",
+ "(test.conf) response_file_format = posix",
"(test.conf) run_second_cpp = false",
"(test.conf) sloppiness = clang_index_store, file_stat_matches,"
" file_stat_matches_ctime, gcno_cwd, include_file_ctime,"