]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
refactor: Move environment utility functions to util
authorJoel Rosdahl <joel@rosdahl.net>
Fri, 7 Jul 2023 14:42:23 +0000 (16:42 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Wed, 12 Jul 2023 09:07:51 +0000 (11:07 +0200)
14 files changed:
src/Config.cpp
src/Util.cpp
src/Util.hpp
src/ccache.cpp
src/core/mainoptions.cpp
src/util/CMakeLists.txt
src/util/environment.cpp [new file with mode: 0644]
src/util/environment.hpp [new file with mode: 0644]
unittest/CMakeLists.txt
unittest/main.cpp
unittest/test_Config.cpp
unittest/test_Stat.cpp
unittest/test_Util.cpp
unittest/test_util_environment.cpp [new file with mode: 0644]

index b312196f1cc8cf3cbadd2ccd7c807ce2032e7248..971faac8edb516b43811c026137050a4ac4484d8 100644 (file)
@@ -29,6 +29,7 @@
 #include <core/wincompat.hpp>
 #include <fmtmacros.hpp>
 #include <util/Tokenizer.hpp>
+#include <util/environment.hpp>
 #include <util/expected.hpp>
 #include <util/file.hpp>
 #include <util/path.hpp>
@@ -934,7 +935,7 @@ Config::set_item(const std::string& key,
     return;
   }
 
-  std::string value = Util::expand_environment_variables(unexpanded_value);
+  std::string value = util::expand_environment_variables(unexpanded_value);
 
   switch (it->second.item) {
   case ConfigItem::absolute_paths_in_stderr:
index ec01a5d7a5adfb3fe00b011e78d055ee8dd05dc6..8eebcc0ca481245c2ec4061945f93273e508b3cd 100644 (file)
@@ -429,60 +429,6 @@ dir_name(std::string_view path)
   }
 }
 
-std::string
-expand_environment_variables(const std::string& str)
-{
-  std::string result;
-  const char* left = str.c_str();
-  const char* right = left;
-
-  while (*right) {
-    if (*right == '$') {
-      result.append(left, right - left);
-
-      if (*(right + 1) == '$') {
-        result += '$';
-        right += 2;
-        left = right;
-        continue;
-      }
-
-      left = right + 1;
-      bool curly = *left == '{';
-      if (curly) {
-        ++left;
-      }
-      right = left;
-      while (isalnum(*right) || *right == '_') {
-        ++right;
-      }
-      if (curly && *right != '}') {
-        throw core::Error(FMT("syntax error: missing '}}' after \"{}\"", left));
-      }
-      if (right == left) {
-        // Special case: don't consider a single $ the left of a variable.
-        result += '$';
-        --right;
-      } else {
-        std::string name(left, right - left);
-        const char* value = getenv(name.c_str());
-        if (!value) {
-          throw core::Error(FMT("environment variable \"{}\" not set", name));
-        }
-        result += value;
-        if (!curly) {
-          --right;
-        }
-        left = right + 1;
-      }
-    }
-    ++right;
-  }
-
-  result += left;
-  return result;
-}
-
 int
 fallocate(int fd, long new_size)
 {
@@ -1101,18 +1047,6 @@ set_umask(mode_t mask)
   return umask(mask);
 }
 
-void
-setenv(const std::string& name, const std::string& value)
-{
-#ifdef HAVE_SETENV
-  ::setenv(name.c_str(), value.c_str(), true);
-#else
-  char* string;
-  asprintf(&string, "%s=%s", name.c_str(), value.c_str());
-  putenv(string); // Leak to environment.
-#endif
-}
-
 std::vector<std::string_view>
 split_into_views(std::string_view string,
                  const char* separators,
@@ -1293,18 +1227,6 @@ unlink_tmp(const std::string& path, UnlinkLog unlink_log)
   return success;
 }
 
-void
-unsetenv(const std::string& name)
-{
-#ifdef HAVE_UNSETENV
-  ::unsetenv(name.c_str());
-#elif defined(_WIN32)
-  SetEnvironmentVariable(name.c_str(), NULL);
-#else
-  putenv(strdup(name.c_str())); // Leak to environment.
-#endif
-}
-
 void
 wipe_path(const std::string& path)
 {
index 2db92fdff181592f453d99811dabd5762cf9c144..0be214c826b36fbe3f5e79ae1f09958f137b7c63 100644 (file)
@@ -88,10 +88,6 @@ std::string_view dir_name(std::string_view path);
 // Like create_dir but throws Fatal on error.
 void ensure_dir_exists(std::string_view dir);
 
-// Expand all instances of $VAR or ${VAR}, where VAR is an environment variable,
-// in `str`. Throws `core::Error` if one of the environment variables.
-[[nodiscard]] std::string expand_environment_variables(const std::string& str);
-
 // Extends file size to at least new_size by calling posix_fallocate() if
 // supported, otherwise by writing zeros last to the file.
 //
@@ -241,9 +237,6 @@ void set_cloexec_flag(int fd);
 // Set process umask. Returns the previous mask.
 mode_t set_umask(mode_t mask);
 
-// Set environment variable `name` to `value`.
-void setenv(const std::string& name, const std::string& value);
-
 // Return size change in KiB between `old_stat`  and `new_stat`.
 inline int64_t
 size_change_kibibyte(const Stat& old_stat, const Stat& new_stat)
@@ -298,9 +291,6 @@ bool unlink_safe(const std::string& path,
 bool unlink_tmp(const std::string& path,
                 UnlinkLog unlink_log = UnlinkLog::log_failure);
 
-// Unset environment variable `name`.
-void unsetenv(const std::string& name);
-
 // Remove `path` (and its contents if it's a directory). A nonexistent path is
 // not considered an error.
 //
index be1984748ffc44af761f426f5c0ff18e00d7d45a..18338d0501a34e719e3e8a7413895638194e57f6 100644 (file)
@@ -54,6 +54,7 @@
 #include <core/types.hpp>
 #include <core/wincompat.hpp>
 #include <storage/Storage.hpp>
+#include <util/environment.hpp>
 #include <util/expected.hpp>
 #include <util/file.hpp>
 #include <util/path.hpp>
@@ -2215,7 +2216,7 @@ set_up_uncached_err()
     return nonstd::make_unexpected(Statistic::internal_error);
   }
 
-  Util::setenv("UNCACHED_ERR_FD", FMT("{}", uncached_fd));
+  util::setenv("UNCACHED_ERR_FD", FMT("{}", uncached_fd));
   return {};
 }
 
@@ -2398,7 +2399,7 @@ do_cache_compilation(Context& ctx)
   // calling ccache second time. For instance, if the real compiler is a wrapper
   // script that calls "ccache $compiler ..." we want that inner ccache call to
   // be disabled.
-  Util::setenv("CCACHE_DISABLE", "1");
+  util::setenv("CCACHE_DISABLE", "1");
 
   MTR_BEGIN("main", "process_args");
   ProcessArgsResult processed = process_args(ctx);
@@ -2413,7 +2414,7 @@ do_cache_compilation(Context& ctx)
   // VS_UNICODE_OUTPUT prevents capturing stdout/stderr, as the output is sent
   // directly to Visual Studio.
   if (ctx.config.compiler_type() == CompilerType::msvc) {
-    Util::unsetenv("VS_UNICODE_OUTPUT");
+    util::unsetenv("VS_UNICODE_OUTPUT");
   }
 
   for (const auto& name : {"DEPENDENCIES_OUTPUT", "SUNPRO_DEPENDENCIES"}) {
index b32afc5bb895cfe167e9fd4e13fbbe99cec1fe11..685b34f473cf469fbfbf25b27b7ad5f5fb95d95d 100644 (file)
@@ -44,6 +44,7 @@
 #include <storage/local/LocalStorage.hpp>
 #include <util/TextTable.hpp>
 #include <util/XXH3_128.hpp>
+#include <util/environment.hpp>
 #include <util/expected.hpp>
 #include <util/file.hpp>
 #include <util/string.hpp>
@@ -504,11 +505,11 @@ process_main_options(int argc, const char* const* argv)
 
     switch (c) {
     case 'd': // --dir
-      Util::setenv("CCACHE_DIR", arg);
+      util::setenv("CCACHE_DIR", arg);
       break;
 
     case CONFIG_PATH:
-      Util::setenv("CCACHE_CONFIGPATH", arg);
+      util::setenv("CCACHE_CONFIGPATH", arg);
       break;
 
     case RECOMPRESS_THREADS:
index d08916bbc12b2f442c94adba836e58478c2fd805..234318bea124389b261450cc9bb8df971ce821c7 100644 (file)
@@ -6,6 +6,7 @@ set(
   TextTable.cpp
   TimePoint.cpp
   Tokenizer.cpp
+  environment.cpp
   file.cpp
   path.cpp
   string.cpp
diff --git a/src/util/environment.cpp b/src/util/environment.cpp
new file mode 100644 (file)
index 0000000..1e4e70f
--- /dev/null
@@ -0,0 +1,106 @@
+// Copyright (C) 2023 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "environment.hpp"
+
+#include <Win32Util.hpp> // for asprintf
+#include <core/exceptions.hpp>
+#include <core/wincompat.hpp>
+#include <fmtmacros.hpp>
+
+namespace util {
+
+std::string
+expand_environment_variables(const std::string& str)
+{
+  std::string result;
+  const char* left = str.c_str();
+  const char* right = left;
+
+  while (*right) {
+    if (*right == '$') {
+      result.append(left, right - left);
+
+      if (*(right + 1) == '$') {
+        result += '$';
+        right += 2;
+        left = right;
+        continue;
+      }
+
+      left = right + 1;
+      bool curly = *left == '{';
+      if (curly) {
+        ++left;
+      }
+      right = left;
+      while (isalnum(*right) || *right == '_') {
+        ++right;
+      }
+      if (curly && *right != '}') {
+        throw core::Error(FMT("syntax error: missing '}}' after \"{}\"", left));
+      }
+      if (right == left) {
+        // Special case: don't consider a single $ the left of a variable.
+        result += '$';
+        --right;
+      } else {
+        std::string name(left, right - left);
+        const char* value = getenv(name.c_str());
+        if (!value) {
+          throw core::Error(FMT("environment variable \"{}\" not set", name));
+        }
+        result += value;
+        if (!curly) {
+          --right;
+        }
+        left = right + 1;
+      }
+    }
+    ++right;
+  }
+
+  result += left;
+  return result;
+}
+
+void
+setenv(const std::string& name, const std::string& value)
+{
+#ifdef HAVE_SETENV
+  ::setenv(name.c_str(), value.c_str(), true);
+#else
+  char* string;
+  asprintf(&string, "%s=%s", name.c_str(), value.c_str());
+  putenv(string); // Leak to environment.
+#endif
+}
+
+void
+unsetenv(const std::string& name)
+{
+#ifdef HAVE_UNSETENV
+  ::unsetenv(name.c_str());
+#elif defined(_WIN32)
+  SetEnvironmentVariable(name.c_str(), NULL);
+#else
+  putenv(strdup(name.c_str())); // Leak to environment.
+#endif
+}
+
+} // namespace util
diff --git a/src/util/environment.hpp b/src/util/environment.hpp
new file mode 100644 (file)
index 0000000..29a99de
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#include <string>
+
+namespace util {
+
+// Expand all instances of $VAR or ${VAR}, where VAR is an environment variable,
+// in `str`. Throws `core::Error` if one of the environment variables.
+[[nodiscard]] std::string expand_environment_variables(const std::string& str);
+
+// Set environment variable `name` to `value`.
+void setenv(const std::string& name, const std::string& value);
+
+// Unset environment variable `name`.
+void unsetenv(const std::string& name);
+
+} // namespace util
index 208dd8bbb255641df3e55d582e3591bde4e337ce..ec743312f95dff61008844ed07cd97a50e8b170f 100644 (file)
@@ -30,6 +30,7 @@ set(
   test_util_XXH3_128.cpp
   test_util_XXH3_64.cpp
   test_util_conversion.cpp
+  test_util_environment.cpp
   test_util_expected.cpp
   test_util_file.cpp
   test_util_path.cpp
index df9bf599b88e6e20b16a8112f29b467a46399e5c..d67126f7189e20a274e07f1f704c5cd326640ccb 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2010-2020 Joel Rosdahl and other contributors
+// Copyright (C) 2010-2023 Joel Rosdahl and other contributors
 //
 // See doc/AUTHORS.adoc for a complete list of contributors.
 //
@@ -20,6 +20,8 @@
 #include "../src/fmtmacros.hpp"
 #include "TestUtil.hpp"
 
+#include <util/environment.hpp>
+
 #include "third_party/fmt/core.h"
 
 #define DOCTEST_THREAD_LOCAL // Avoid MinGW thread_local bug
@@ -30,9 +32,9 @@ int
 main(int argc, char** argv)
 {
 #ifdef _WIN32
-  Util::setenv("CCACHE_DETECT_SHEBANG", "1");
+  util::setenv("CCACHE_DETECT_SHEBANG", "1");
 #endif
-  Util::unsetenv("GCC_COLORS"); // Don't confuse argument processing tests.
+  util::unsetenv("GCC_COLORS"); // Don't confuse argument processing tests.
 
   std::string dir_before = Util::get_actual_cwd();
   std::string testdir = FMT("testdir/{}", getpid());
index 6b97e4e7e75de46726feb296905d5c75258cba0a..c2f46e9d96c57de64c0051c149292fdc3207a353 100644 (file)
@@ -22,6 +22,7 @@
 #include "TestUtil.hpp"
 
 #include <core/exceptions.hpp>
+#include <util/environment.hpp>
 #include <util/file.hpp>
 
 #include "third_party/doctest.h"
@@ -87,7 +88,7 @@ TEST_CASE("Config::update_from_file")
   TestContext test_context;
 
   const char user[] = "rabbit";
-  Util::setenv("USER", user);
+  util::setenv("USER", user);
 
 #ifndef _WIN32
   std::string base_dir = FMT("/{0}/foo/{0}", user);
@@ -289,13 +290,13 @@ TEST_CASE("Config::update_from_environment")
 {
   Config config;
 
-  Util::setenv("CCACHE_COMPRESS", "1");
+  util::setenv("CCACHE_COMPRESS", "1");
   config.update_from_environment();
   CHECK(config.compression());
 
-  Util::unsetenv("CCACHE_COMPRESS");
+  util::unsetenv("CCACHE_COMPRESS");
 
-  Util::setenv("CCACHE_NOCOMPRESS", "1");
+  util::setenv("CCACHE_NOCOMPRESS", "1");
   config.update_from_environment();
   CHECK(!config.compression());
 }
index ffdb68095c1849a98d8a2eea9b6aedd3b8253d1b..fdba8c2bafac806c7be0f16973cfdf478bfd9575 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <core/exceptions.hpp>
 #include <core/wincompat.hpp>
+#include <util/environment.hpp>
 #include <util/file.hpp>
 
 #include "third_party/doctest.h"
@@ -663,14 +664,14 @@ TEST_CASE("Win32 No Sharing")
 // Instead, test a well-known junction that has existed in all Windows versions
 // since Vista. (Not present on Wine.)
 TEST_CASE("Win32 Directory Junction"
-          * doctest::skip(!win32_is_junction(Util::expand_environment_variables(
+          * doctest::skip(!win32_is_junction(util::expand_environment_variables(
             "${ALLUSERSPROFILE}\\Application Data"))))
 {
   TestContext test_context;
 
   SUBCASE("junction stat")
   {
-    auto stat = Stat::stat(Util::expand_environment_variables(
+    auto stat = Stat::stat(util::expand_environment_variables(
       "${ALLUSERSPROFILE}\\Application Data"));
     CHECK(stat);
     CHECK(stat.error_number() == 0);
@@ -686,7 +687,7 @@ TEST_CASE("Win32 Directory Junction"
 
   SUBCASE("junction lstat")
   {
-    auto stat = Stat::lstat(Util::expand_environment_variables(
+    auto stat = Stat::lstat(util::expand_environment_variables(
       "${ALLUSERSPROFILE}\\Application Data"));
     CHECK(stat);
     CHECK(stat.error_number() == 0);
index 062bad5da751c72fca3fe4beca3c85a343986528..5b583254113350f45f66ced9faea9dd0b1584716 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <core/exceptions.hpp>
 #include <core/wincompat.hpp>
+#include <util/environment.hpp>
 #include <util/file.hpp>
 
 #include "third_party/doctest.h"
@@ -146,29 +147,6 @@ TEST_CASE("Util::ensure_dir_exists")
     "Failed to create directory create/dir/file: Not a directory");
 }
 
-TEST_CASE("Util::expand_environment_variables")
-{
-  Util::setenv("FOO", "bar");
-
-  CHECK(Util::expand_environment_variables("") == "");
-  CHECK(Util::expand_environment_variables("$FOO") == "bar");
-  CHECK(Util::expand_environment_variables("$$FOO") == "$FOO");
-  CHECK(Util::expand_environment_variables("$$$FOO") == "$bar");
-  CHECK(Util::expand_environment_variables("$ $$ $") == "$ $ $");
-  CHECK(Util::expand_environment_variables("$FOO $FOO:$FOO") == "bar bar:bar");
-  CHECK(Util::expand_environment_variables("x$FOO") == "xbar");
-  CHECK(Util::expand_environment_variables("${FOO}x") == "barx");
-
-  DOCTEST_GCC_SUPPRESS_WARNING_PUSH
-  DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-result")
-  CHECK_THROWS_WITH(
-    (void)Util::expand_environment_variables("$surelydoesntexist"),
-    "environment variable \"surelydoesntexist\" not set");
-  CHECK_THROWS_WITH((void)Util::expand_environment_variables("${FOO"),
-                    "syntax error: missing '}' after \"FOO\"");
-  DOCTEST_GCC_SUPPRESS_WARNING_POP
-}
-
 TEST_CASE("Util::fallocate")
 {
   TestContext test_context;
@@ -337,7 +315,7 @@ TEST_CASE("Util::make_relative_path")
   REQUIRE(symlink("d", "s") == 0);
 #endif
   REQUIRE(chdir("d") == 0);
-  Util::setenv("PWD", apparent_cwd);
+  util::setenv("PWD", apparent_cwd);
 
   SUBCASE("No base directory")
   {
diff --git a/unittest/test_util_environment.cpp b/unittest/test_util_environment.cpp
new file mode 100644 (file)
index 0000000..0665467
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include <util/environment.hpp>
+
+#include <third_party/doctest.h>
+
+TEST_SUITE_BEGIN("util");
+
+TEST_CASE("util::expand_environment_variables")
+{
+  util::setenv("FOO", "bar");
+
+  CHECK(util::expand_environment_variables("") == "");
+  CHECK(util::expand_environment_variables("$FOO") == "bar");
+  CHECK(util::expand_environment_variables("$$FOO") == "$FOO");
+  CHECK(util::expand_environment_variables("$$$FOO") == "$bar");
+  CHECK(util::expand_environment_variables("$ $$ $") == "$ $ $");
+  CHECK(util::expand_environment_variables("$FOO $FOO:$FOO") == "bar bar:bar");
+  CHECK(util::expand_environment_variables("x$FOO") == "xbar");
+  CHECK(util::expand_environment_variables("${FOO}x") == "barx");
+
+  DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+  DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-result")
+  CHECK_THROWS_WITH(
+    (void)util::expand_environment_variables("$surelydoesntexist"),
+    "environment variable \"surelydoesntexist\" not set");
+  CHECK_THROWS_WITH((void)util::expand_environment_variables("${FOO"),
+                    "syntax error: missing '}' after \"FOO\"");
+  DOCTEST_GCC_SUPPRESS_WARNING_POP
+}
+
+TEST_SUITE_END();