]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
Add unittest for find_compiler (#670)
authorAlexander Lanin <alex@lanin.de>
Mon, 28 Sep 2020 13:39:55 +0000 (15:39 +0200)
committerGitHub <noreply@github.com>
Mon, 28 Sep 2020 13:39:55 +0000 (15:39 +0200)
Co-authored-by: Joel Rosdahl <joel@rosdahl.net>
src/Config.hpp
src/Util.cpp
src/Util.hpp
src/ccache.cpp
src/ccache.hpp
unittest/CMakeLists.txt
unittest/test_ccache.cpp [new file with mode: 0644]

index 3397db9c2eb1f44dfe839c700c7006b751a01233..bc8bb4fc9da4ae8516a3ca66542757ca8871e91a 100644 (file)
@@ -79,6 +79,7 @@ public:
   void set_base_dir(const std::string& value);
   void set_cache_dir(const std::string& value);
   void set_cpp_extension(const std::string& value);
+  void set_compiler(const std::string& value);
   void set_depend_mode(bool value);
   void set_debug(bool value);
   void set_direct_mode(bool value);
@@ -415,6 +416,12 @@ Config::set_cpp_extension(const std::string& value)
   m_cpp_extension = value;
 }
 
+inline void
+Config::set_compiler(const std::string& value)
+{
+  m_compiler = value;
+}
+
 inline void
 Config::set_depend_mode(bool value)
 {
index 79350729057b7542332921afe34ab5e786595cbb..0a579952ff8bcb9c2d82e5a0ac48e6269fded2d3 100644 (file)
@@ -1239,13 +1239,14 @@ rename(const std::string& oldpath, const std::string& newpath)
 }
 
 bool
-same_program_name(const std::string& program_name,
-                  const std::string& canonical_program_name)
+same_program_name(nonstd::string_view program_name,
+                  nonstd::string_view canonical_program_name)
 {
 #ifdef _WIN32
   std::string lowercase_program_name = Util::to_lowercase(program_name);
   return lowercase_program_name == canonical_program_name
-         || lowercase_program_name == (canonical_program_name + ".exe");
+         || lowercase_program_name
+              == fmt::format("{}.exe", canonical_program_name);
 #else
   return program_name == canonical_program_name;
 #endif
index d2c7bff8f56dee549054cdba33b8da4437e429ca..b3ab9e9bc2bf1c1625bedace25d4fef18efe2a0c 100644 (file)
@@ -388,8 +388,8 @@ void rename(const std::string& oldpath, const std::string& newpath);
 // Detmine if `program_name` is equal to `canonical_program_name`. On Windows,
 // this means performing a case insensitive equality check with and without a
 // ".exe" suffix. On non-Windows, it is a simple equality check.
-bool same_program_name(const std::string& program_name,
-                       const std::string& canonical_program_name);
+bool same_program_name(nonstd::string_view program_name,
+                       nonstd::string_view canonical_program_name);
 
 // Send `text` to STDERR_FILENO, optionally stripping ANSI color sequences if
 // `ctx.args_info.strip_diagnostics_colors` is true and rewriting paths to
index 1c6ea5c4a040f359c2349f131b21330374b22544..d620ce9c0f1cf039f275a44edf548755b2db9e86 100644 (file)
@@ -1749,13 +1749,17 @@ from_cache(Context& ctx, FromCacheCallMode mode)
                                            : Statistic::preprocessed_cache_hit;
 }
 
-// Find the real compiler. We just search the PATH to find an executable of the
-// same name that isn't a link to ourselves.
-static void
-find_compiler(Context& ctx, const char* const* argv)
+// Find the real compiler and put it into ctx.orig_args[0]. We just search the
+// PATH to find an executable of the same name that isn't a link to ourselves.
+// Pass find_executable function as second parameter.
+void
+find_compiler(Context& ctx,
+              const FindExecutableFunction& find_executable_function)
 {
+  const std::string orig_first_arg = ctx.orig_args[0];
+
   // We might be being invoked like "ccache gcc -c foo.c".
-  std::string base(Util::base_name(argv[0]));
+  std::string base(Util::base_name(ctx.orig_args[0]));
   if (Util::same_program_name(base, CCACHE_NAME)) {
     ctx.orig_args.pop_front();
     if (Util::is_full_path(ctx.orig_args[0])) {
@@ -1769,11 +1773,11 @@ find_compiler(Context& ctx, const char* const* argv)
     base = ctx.config.compiler();
   }
 
-  std::string compiler = find_executable(ctx, base, CCACHE_NAME);
+  std::string compiler = find_executable_function(ctx, base, CCACHE_NAME);
   if (compiler.empty()) {
     throw Fatal("Could not find compiler \"{}\" in PATH", base);
   }
-  if (compiler == argv[0]) {
+  if (compiler == orig_first_arg) {
     throw Fatal(
       "Recursive invocation (the name of the ccache binary must be \"{}\")",
       CCACHE_NAME);
@@ -2133,7 +2137,7 @@ cache_compilation(int argc, const char* const* argv)
     initialize(ctx, argc, argv);
 
     MTR_BEGIN("main", "find_compiler");
-    find_compiler(ctx, argv);
+    find_compiler(ctx, &find_executable);
     MTR_END("main", "find_compiler");
 
     try {
index 70d397b523d0afa45080677a20f8835990bb6ed5..627f623b6cf86dedc1e545ba29ab0a400e447f98 100644 (file)
 
 #include "system.hpp"
 
+#include <functional>
+#include <string>
+
+class Context;
+
 extern const char CCACHE_VERSION[];
 
 enum class GuessedCompiler { clang, gcc, nvcc, pump, unknown };
@@ -44,3 +49,12 @@ const uint32_t SLOPPY_CLANG_INDEX_STORE = 1 << 7;
 const uint32_t SLOPPY_LOCALE = 1 << 8;
 // Allow caching even if -fmodules is used.
 const uint32_t SLOPPY_MODULES = 1 << 9;
+
+using FindExecutableFunction =
+  std::function<std::string(const Context& ctx,
+                            const std::string& name,
+                            const std::string& exclude_name)>;
+
+// Tested by unit tests.
+void find_compiler(Context& ctx,
+                   const FindExecutableFunction& find_executable_function);
index aa1fa8f527f94ce67daf8941eec592b70542c320..9be4d8563ff763710bf769dc10b585251bbf0cff 100644 (file)
@@ -17,6 +17,7 @@ set(
   test_Util.cpp
   test_ZstdCompression.cpp
   test_argprocessing.cpp
+  test_ccache.cpp
   test_compopt.cpp
   test_hashutil.cpp)
 
diff --git a/unittest/test_ccache.cpp b/unittest/test_ccache.cpp
new file mode 100644 (file)
index 0000000..18b361f
--- /dev/null
@@ -0,0 +1,147 @@
+// Copyright (C) 2020 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 "Context.hpp"
+#include "TestUtil.hpp"
+#include "ccache.hpp"
+
+#include "third_party/doctest.h"
+#include "third_party/nonstd/optional.hpp"
+
+#ifdef MYNAME
+#  define CCACHE_NAME MYNAME
+#else
+#  define CCACHE_NAME "ccache"
+#endif
+
+TEST_SUITE_BEGIN("ccache");
+
+// Wraps find_compiler in a test friendly interface.
+static std::string
+helper(const char* args,
+       const char* config_compiler,
+       const char* find_executable_return_string = nullptr)
+{
+  const auto find_executable_stub =
+    [&find_executable_return_string](
+      const Context&, const std::string& s, const std::string&) -> std::string {
+    return find_executable_return_string ? find_executable_return_string
+                                         : "resolved_" + s;
+  };
+
+  Context ctx;
+  ctx.config.set_compiler(config_compiler);
+  ctx.orig_args = Args::from_string(args);
+  find_compiler(ctx, find_executable_stub);
+  return ctx.orig_args.to_string();
+}
+
+TEST_CASE("find_compiler")
+{
+  SUBCASE("no config")
+  {
+    // In case the first parameter is gcc it must be a link to ccache, so
+    // find_compiler should call find_executable to locate the next best "gcc"
+    // and return that value.
+    CHECK(helper("gcc", "") == "resolved_gcc");
+    CHECK(helper("relative/gcc", "") == "resolved_gcc");
+    CHECK(helper("/absolute/gcc", "") == "resolved_gcc");
+
+    // In case the first parameter is ccache, resolve the second parameter to
+    // the real compiler unless it's a relative or absolute path.
+    CHECK(helper(CCACHE_NAME " gcc", "") == "resolved_gcc");
+    CHECK(helper(CCACHE_NAME " rel/gcc", "") == "rel/gcc");
+    CHECK(helper(CCACHE_NAME " /abs/gcc", "") == "/abs/gcc");
+
+    CHECK(helper("rel/" CCACHE_NAME " gcc", "") == "resolved_gcc");
+    CHECK(helper("rel/" CCACHE_NAME " rel/gcc", "") == "rel/gcc");
+    CHECK(helper("rel/" CCACHE_NAME " /abs/gcc", "") == "/abs/gcc");
+
+    CHECK(helper("/abs/" CCACHE_NAME " gcc", "") == "resolved_gcc");
+    CHECK(helper("/abs/" CCACHE_NAME " rel/gcc", "") == "rel/gcc");
+    CHECK(helper("/abs/" CCACHE_NAME " /abs/gcc", "") == "/abs/gcc");
+
+    // If gcc points back to ccache throw, unless either ccache or gcc is a
+    // relative or absolute path. If ccache *and* compiler have a relative or
+    // absolute path, call ccache from PATH.
+    CHECK_THROWS(helper(CCACHE_NAME " gcc", "", CCACHE_NAME));
+    CHECK(helper(CCACHE_NAME " rel/gcc", "", CCACHE_NAME) == "rel/gcc");
+    CHECK(helper(CCACHE_NAME " /abs/gcc", "", CCACHE_NAME) == "/abs/gcc");
+
+    CHECK(helper("rel/" CCACHE_NAME " gcc", "", CCACHE_NAME) == "ccache");
+    CHECK(helper("rel/" CCACHE_NAME " rel/gcc", "", CCACHE_NAME) == "rel/gcc");
+    CHECK(helper("rel/" CCACHE_NAME " /a/gcc", "", CCACHE_NAME) == "/a/gcc");
+
+    CHECK(helper("/abs/" CCACHE_NAME " gcc", "", CCACHE_NAME) == "ccache");
+    CHECK(helper("/abs/" CCACHE_NAME " rel/gcc", "", CCACHE_NAME) == "rel/gcc");
+    CHECK(helper("/abs/" CCACHE_NAME " /a/gcc", "", CCACHE_NAME) == "/a/gcc");
+
+    // If compiler is not found then throw, unless the compiler has a relative
+    // or absolute path.
+    CHECK_THROWS(helper(CCACHE_NAME " gcc", "", ""));
+    CHECK(helper(CCACHE_NAME " rel/gcc", "", "") == "rel/gcc");
+    CHECK(helper(CCACHE_NAME " /abs/gcc", "", "") == "/abs/gcc");
+
+    CHECK_THROWS(helper("rel/" CCACHE_NAME " gcc", "", ""));
+    CHECK(helper("rel/" CCACHE_NAME " rel/gcc", "", "") == "rel/gcc");
+    CHECK(helper("rel/" CCACHE_NAME " /abs/gcc", "", "") == "/abs/gcc");
+
+    CHECK_THROWS(helper("/abs/" CCACHE_NAME " gcc", "", ""));
+    CHECK(helper("/abs/" CCACHE_NAME " rel/gcc", "", "") == "rel/gcc");
+    CHECK(helper("/abs/" CCACHE_NAME " /abs/gcc", "", "") == "/abs/gcc");
+  }
+
+  SUBCASE("config")
+  {
+    // In case the first parameter is gcc it must be a link to ccache so use
+    // config value instead.
+    CHECK(helper("gcc", "config") == "resolved_config");
+    CHECK(helper("gcc", "rel/config") == "resolved_rel/config");
+    CHECK(helper("gcc", "/abs/config") == "resolved_/abs/config");
+    CHECK(helper("rel/gcc", "config") == "resolved_config");
+    CHECK(helper("rel/gcc", "rel/config") == "resolved_rel/config");
+    CHECK(helper("rel/gcc", "/abs/config") == "resolved_/abs/config");
+    CHECK(helper("/abs/gcc", "config") == "resolved_config");
+    CHECK(helper("/abs/gcc", "rel/config") == "resolved_rel/config");
+    CHECK(helper("/abs/gcc", "/abs/config") == "resolved_/abs/config");
+
+    // In case the first parameter is ccache, use the configuration value unless
+    // the second parameter is a relative or absolute path.
+    CHECK(helper(CCACHE_NAME " gcc", "config") == "resolved_config");
+    CHECK(helper(CCACHE_NAME " gcc", "rel/config") == "resolved_rel/config");
+    CHECK(helper(CCACHE_NAME " gcc", "/abs/config") == "resolved_/abs/config");
+    CHECK(helper(CCACHE_NAME " rel/gcc", "config") == "rel/gcc");
+    CHECK(helper(CCACHE_NAME " /abs/gcc", "config") == "/abs/gcc");
+
+    // Same as above with relative path to ccache.
+    CHECK(helper("r/" CCACHE_NAME " gcc", "conf") == "resolved_conf");
+    CHECK(helper("r/" CCACHE_NAME " gcc", "rel/conf") == "resolved_rel/conf");
+    CHECK(helper("r/" CCACHE_NAME " gcc", "/abs/conf") == "resolved_/abs/conf");
+    CHECK(helper("r/" CCACHE_NAME " rel/gcc", "conf") == "rel/gcc");
+    CHECK(helper("r/" CCACHE_NAME " /abs/gcc", "conf") == "/abs/gcc");
+
+    // Same as above with absolute path to ccache.
+    CHECK(helper("/a/" CCACHE_NAME " gcc", "conf") == "resolved_conf");
+    CHECK(helper("/a/" CCACHE_NAME " gcc", "rel/conf") == "resolved_rel/conf");
+    CHECK(helper("/a/" CCACHE_NAME " gcc", "/a/conf") == "resolved_/a/conf");
+    CHECK(helper("/a/" CCACHE_NAME " rel/gcc", "conf") == "rel/gcc");
+    CHECK(helper("/a/" CCACHE_NAME " /abs/gcc", "conf") == "/abs/gcc");
+  }
+}
+
+TEST_SUITE_END();