]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
feat: Add msvc_utf8 config option to allow disabling /utf-8 for MSVC (#1650)
authorMax Winkler <max.enrico.winkler@gmail.com>
Tue, 28 Oct 2025 18:46:11 +0000 (11:46 -0700)
committerGitHub <noreply@github.com>
Tue, 28 Oct 2025 18:46:11 +0000 (19:46 +0100)
Add  option to avoid adding  to msvc command line for non-utf8 source code

doc/manual.adoc
src/ccache/ccache.cpp
src/ccache/config.cpp
src/ccache/config.hpp
unittest/test_config.cpp

index 32a55ae321dbf80c05df15eab42ad8e8d2934f8d..fcfad31eaac47713c5acd0144faf67809fa09fd2 100644 (file)
@@ -907,6 +907,16 @@ file in `/etc/rsyslog.d`:
     The default prefix is "`Note: including file:`". If you use a localized
     compiler, this should be set accordingly.
 
+[#config_msvc_utf8]
+*msvc_utf8* (*CCACHE_MSVC_UTF8*)::
+
+    This option adds `/utf-8` to the msvc command line when executing the preprocessor to
+    ensure that filenames are not garbled for non-ascii characters.
+    This implicitly enables `/validate-charset` and treats the source code as utf-8 which
+    may cause compilation errors if comments in your code have characters in the [128, 255]
+    range for a given Windows system codepage which results in an invalid utf-8 sequence.
+    The default is true.
+
 [#config_namespace]
 *namespace* (*CCACHE_NAMESPACE*)::
 
@@ -1587,7 +1597,7 @@ An environment variable not supported by ccache was set.
 
 | Unsupported source encoding |
 Source file (or an included header) has unsupported encoding. ccache currently
-requires UTF-8-encoded source code for MSVC.
+requires UTF-8-encoded source code for MSVC when `msvc_utf8` is true.
 
 | Unsupported source language |
 A source language e.g. specified with `-x` was unsupported by ccache.
index e442707d641f1ac1e21ca068bdcc0db23641dbe8..681e061bafc89b4b9813bd9632af328d928e95de 100644 (file)
@@ -1412,7 +1412,9 @@ get_result_key_from_cpp(Context& ctx, util::Args& args, Hash& hash)
     // compilers that don't exit with a proper status on write error to stdout.
     // See also <https://github.com/llvm/llvm-project/issues/56499>.
     if (ctx.config.is_compiler_group_msvc()) {
-      args.push_back("-utf-8"); // Avoid garbling filenames in output
+      if (ctx.config.msvc_utf8()) {
+        args.push_back("-utf-8"); // Avoid garbling filenames in output
+      }
       args.push_back("-P");
       args.push_back(FMT("-Fi{}", preprocessed_path));
     } else {
@@ -1441,7 +1443,7 @@ get_result_key_from_cpp(Context& ctx, util::Args& args, Hash& hash)
     cpp_stderr_data = result->stderr_data;
     cpp_stdout_data = result->stdout_data;
 
-    if (ctx.config.is_compiler_group_msvc()) {
+    if (ctx.config.is_compiler_group_msvc() && ctx.config.msvc_utf8()) {
       // Check that usage of -utf-8 didn't garble the preprocessor output.
       static constexpr char warning_c4828[] =
         "warning C4828: The file contains a character starting at offset";
index dcf7c17d71b9cc616c7ec4b078cd388445705ff5..95091d2ae1b7f1c0e25014146a5eddc415c90bbd 100644 (file)
@@ -101,6 +101,7 @@ enum class ConfigItem : uint8_t {
   max_files,
   max_size,
   msvc_dep_prefix,
+  msvc_utf8,
   namespace_,
   path,
   pch_external_checksum,
@@ -157,6 +158,7 @@ const std::unordered_map<std::string_view, ConfigKeyTableEntry>
     {"max_files",                  {ConfigItem::max_files}                       },
     {"max_size",                   {ConfigItem::max_size}                        },
     {"msvc_dep_prefix",            {ConfigItem::msvc_dep_prefix}                 },
+    {"msvc_utf8",                  {ConfigItem::msvc_utf8}                       },
     {"namespace",                  {ConfigItem::namespace_}                      },
     {"path",                       {ConfigItem::path}                            },
     {"pch_external_checksum",      {ConfigItem::pch_external_checksum}           },
@@ -207,6 +209,7 @@ const std::unordered_map<std::string_view, std::string_view>
     {"MAXFILES",             "max_files"                 },
     {"MAXSIZE",              "max_size"                  },
     {"MSVC_DEP_PREFIX",      "msvc_dep_prefix"           },
+    {"MSVC_UTF8",            "msvc_utf8"                 },
     {"NAMESPACE",            "namespace"                 },
     {"PATH",                 "path"                      },
     {"PCH_EXTSUM",           "pch_external_checksum"     },
@@ -871,6 +874,9 @@ Config::get_string_value(const std::string& key) const
   case ConfigItem::msvc_dep_prefix:
     return m_msvc_dep_prefix;
 
+  case ConfigItem::msvc_utf8:
+    return format_bool(m_msvc_utf8);
+
   case ConfigItem::namespace_:
     return m_namespace;
 
@@ -1149,6 +1155,10 @@ Config::set_item(const std::string_view& key,
     m_msvc_dep_prefix = value;
     break;
 
+  case ConfigItem::msvc_utf8:
+    m_msvc_utf8 = parse_bool(value, env_var_key, negate);
+    break;
+
   case ConfigItem::namespace_:
     m_namespace = value;
     break;
index 83cd77ecef76f777a8b6b930ae74da287ba66929..6351d073b09643867189b6dced3516c2494fb519 100644 (file)
@@ -85,6 +85,7 @@ public:
   uint64_t max_files() const;
   uint64_t max_size() const;
   const std::string& msvc_dep_prefix() const;
+  bool msvc_utf8() const;
   const std::string& path() const;
   bool pch_external_checksum() const;
   const std::string& prefix_command() const;
@@ -126,6 +127,7 @@ public:
   void set_inode_cache(bool value);
   void set_max_files(uint64_t value);
   void set_msvc_dep_prefix(const std::string& value);
+  void set_msvc_utf8(bool value);
   void set_temporary_dir(const std::filesystem::path& value);
 
   // Where to write configuration changes.
@@ -206,6 +208,7 @@ private:
   uint64_t m_max_files = 0;
   uint64_t m_max_size = 5ULL * 1024 * 1024 * 1024;
   std::string m_msvc_dep_prefix = "Note: including file:";
+  bool m_msvc_utf8 = true;
   std::string m_path;
   bool m_pch_external_checksum = false;
   std::string m_prefix_command;
@@ -431,6 +434,12 @@ Config::msvc_dep_prefix() const
   return m_msvc_dep_prefix;
 }
 
+inline bool
+Config::msvc_utf8() const
+{
+  return m_msvc_utf8;
+}
+
 inline const std::string&
 Config::path() const
 {
@@ -629,6 +638,12 @@ Config::set_msvc_dep_prefix(const std::string& value)
   m_msvc_dep_prefix = value;
 }
 
+inline void
+Config::set_msvc_utf8(bool value)
+{
+  m_msvc_utf8 = value;
+}
+
 inline void
 Config::set_temporary_dir(const std::filesystem::path& value)
 {
index 572966d971996bb7b95eb1130c5ed6d1338f39f4..6624827a83760e69c960bfe9a1e868862f699887 100644 (file)
@@ -72,6 +72,7 @@ TEST_CASE("Config: default values")
   CHECK(config.max_files() == 0);
   CHECK(config.max_size() == static_cast<uint64_t>(5) * 1024 * 1024 * 1024);
   CHECK(config.msvc_dep_prefix() == "Note: including file:");
+  CHECK(config.msvc_utf8());
   CHECK(config.path().empty());
   CHECK_FALSE(config.pch_external_checksum());
   CHECK(config.prefix_command().empty());
@@ -133,6 +134,7 @@ TEST_CASE("Config::update_from_file")
         "max_files = 17\n"
         "max_size = 123M\n"
         "msvc_dep_prefix = Some other prefix:\n"
+        "msvc_utf8 = false\n"
         "path = $USER.x\n"
         "pch_external_checksum = true\n"
         "prefix_command = x$USER\n"
@@ -178,6 +180,7 @@ TEST_CASE("Config::update_from_file")
   CHECK(config.max_files() == 17);
   CHECK(config.max_size() == 123 * 1000 * 1000);
   CHECK(config.msvc_dep_prefix() == "Some other prefix:");
+  CHECK_FALSE(config.msvc_utf8());
   CHECK(config.path() == FMT("{}.x", user));
   CHECK(config.pch_external_checksum());
   CHECK(config.prefix_command() == FMT("x{}", user));
@@ -617,6 +620,7 @@ TEST_CASE("Config::visit_items")
     "max_files = 4711\n"
     "max_size = 98.7M\n"
     "msvc_dep_prefix = mdp\n"
+    "msvc_utf8 = true\n"
     "namespace = ns\n"
     "path = p\n"
     "pch_external_checksum = true\n"
@@ -680,6 +684,7 @@ TEST_CASE("Config::visit_items")
     "(test.conf) max_files = 4711",
     "(test.conf) max_size = 98.7 MB",
     "(test.conf) msvc_dep_prefix = mdp",
+    "(test.conf) msvc_utf8 = true",
     "(test.conf) namespace = ns",
     "(test.conf) path = p",
     "(test.conf) pch_external_checksum = true",