[clang] Support target names with dots in more utilities (PR #65812)

via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 18 17:19:57 PDT 2023


https://github.com/dankm updated https://github.com/llvm/llvm-project/pull/65812

>From 5f5e57a5c01088e7f0b9276ed02dd436a3f9cc0a Mon Sep 17 00:00:00 2001
From: Dan McGregor <dan.mcgregor at usask.ca>
Date: Mon, 14 Aug 2023 18:44:08 -0600
Subject: [PATCH 1/2] Support: hoist lld's executable name code to Path

Instead of using custom code to find the program name throughout
the codebase, write one function as a path helper to consistently
determine the program name. This globally correctly finds target
names with dots in them (ie freebsd13.2).
---
 lld/COFF/Driver.cpp                            |  2 +-
 lld/Common/Args.cpp                            |  6 ------
 lld/ELF/Driver.cpp                             |  2 +-
 lld/MachO/Driver.cpp                           |  2 +-
 lld/include/lld/Common/Args.h                  |  2 --
 lld/wasm/Driver.cpp                            |  2 +-
 lldb/tools/driver/Driver.cpp                   |  2 +-
 llvm/include/llvm/Support/Path.h               | 15 +++++++++++++++
 llvm/lib/Support/Path.cpp                      |  7 +++++++
 llvm/tools/llvm-ar/llvm-ar.cpp                 |  3 ++-
 llvm/tools/llvm-cov/llvm-cov.cpp               |  2 +-
 llvm/tools/llvm-driver/llvm-driver.cpp         |  2 +-
 llvm/tools/llvm-objcopy/llvm-objcopy.cpp       |  2 +-
 llvm/tools/llvm-objdump/llvm-objdump.cpp       |  2 +-
 llvm/tools/llvm-rc/llvm-rc.cpp                 |  2 +-
 llvm/tools/llvm-readobj/llvm-readobj.cpp       |  2 +-
 llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp |  2 +-
 17 files changed, 36 insertions(+), 21 deletions(-)

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index d7476e91e03e384..03a63bf3a306b4d 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -68,7 +68,7 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
   auto *ctx = new COFFLinkerContext;
 
   ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput);
-  ctx->e.logName = args::getFilenameWithoutExe(args[0]);
+  ctx->e.logName = sys::path::program_name(args[0]);
   ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now"
                                  " (use /errorlimit:0 to see all errors)";
 
diff --git a/lld/Common/Args.cpp b/lld/Common/Args.cpp
index 48c934df3a2c931..b02d023fe19aeb5 100644
--- a/lld/Common/Args.cpp
+++ b/lld/Common/Args.cpp
@@ -85,9 +85,3 @@ std::vector<StringRef> lld::args::getLines(MemoryBufferRef mb) {
   }
   return ret;
 }
-
-StringRef lld::args::getFilenameWithoutExe(StringRef path) {
-  if (path.ends_with_insensitive(".exe"))
-    return sys::path::stem(path);
-  return sys::path::filename(path);
-}
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 9d167293574fa64..957d0da1589caf8 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -143,7 +143,7 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
 
     SharedFile::vernauxNum = 0;
   };
-  ctx->e.logName = args::getFilenameWithoutExe(args[0]);
+  ctx->e.logName = sys::path::program_name(args[0]);
   ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use "
                                  "--error-limit=0 to see all errors)";
 
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 00ff3439a043be9..ed0b66d4b2ec6ba 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -1428,7 +1428,7 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
     InputFile::resetIdCount();
   };
 
-  ctx->e.logName = args::getFilenameWithoutExe(argsArr[0]);
+  ctx->e.logName = sys::path::program_name(argsArr[0]);
 
   MachOOptTable parser;
   InputArgList args = parser.parse(argsArr.slice(1));
diff --git a/lld/include/lld/Common/Args.h b/lld/include/lld/Common/Args.h
index 60f83fbbbf1a3c9..8822707a6a1f212 100644
--- a/lld/include/lld/Common/Args.h
+++ b/lld/include/lld/Common/Args.h
@@ -38,8 +38,6 @@ uint64_t getZOptionValue(llvm::opt::InputArgList &args, int id, StringRef key,
 
 std::vector<StringRef> getLines(MemoryBufferRef mb);
 
-StringRef getFilenameWithoutExe(StringRef path);
-
 } // namespace args
 } // namespace lld
 
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index c2f5f0185781f36..f13aa520f35b100 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -89,7 +89,7 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
   auto *ctx = new CommonLinkerContext;
 
   ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput);
-  ctx->e.logName = args::getFilenameWithoutExe(args[0]);
+  ctx->e.logName = sys::path::program_name(args[0]);
   ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use "
                                  "-error-limit=0 to see all errors)";
 
diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp
index f8058f868d53ffe..2fb760ca97a001d 100644
--- a/lldb/tools/driver/Driver.cpp
+++ b/lldb/tools/driver/Driver.cpp
@@ -754,7 +754,7 @@ int main(int argc, char const *argv[]) {
   ArrayRef<const char *> arg_arr = ArrayRef(argv + 1, argc - 1);
   opt::InputArgList input_args =
       T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount);
-  llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]);
+  llvm::StringRef argv0 = llvm::sys::path::program_name(argv[0]);
 
   if (input_args.hasArg(OPT_help)) {
     printHelp(T, argv0);
diff --git a/llvm/include/llvm/Support/Path.h b/llvm/include/llvm/Support/Path.h
index ce69f32b6cc81ba..3ef5f78086c47ad 100644
--- a/llvm/include/llvm/Support/Path.h
+++ b/llvm/include/llvm/Support/Path.h
@@ -390,6 +390,21 @@ StringRef stem(StringRef path, Style style = Style::native);
 /// @result The extension of \a path.
 StringRef extension(StringRef path, Style style = Style::native);
 
+/// Get the program's name
+///
+/// If the path ends with the string ".exe", returns the stem of
+/// \a path. Otherwise returns the filename of \a path.
+///
+/// @code
+///   /foo/prog.exe => prog
+///   /bar/prog     => prog
+///   /foo/prog1.2  => prog1.2
+/// @endcode
+///
+/// @param path Input path.
+/// @result The filename of \a path without any ".exe" component.
+StringRef program_name(StringRef path, Style style = Style::native);
+
 /// Check whether the given char is a path separator on the host OS.
 ///
 /// @param value a character
diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp
index 7a57c104ef10e7b..11c874524265f2e 100644
--- a/llvm/lib/Support/Path.cpp
+++ b/llvm/lib/Support/Path.cpp
@@ -600,6 +600,13 @@ StringRef extension(StringRef path, Style style) {
   return fname.substr(pos);
 }
 
+StringRef program_name(StringRef path, Style style) {
+  // In the future this may need to be extended to other program suffixes.
+  if (path.ends_with_insensitive(".exe"))
+    return stem(path, style);
+  return filename(path, style);
+}
+
 bool is_separator(char value, Style style) {
   if (value == '/')
     return true;
diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp
index 9889ad9de627d51..68b79cb007d1e98 100644
--- a/llvm/tools/llvm-ar/llvm-ar.cpp
+++ b/llvm/tools/llvm-ar/llvm-ar.cpp
@@ -1508,7 +1508,8 @@ int llvm_ar_main(int argc, char **argv, const llvm::ToolContext &) {
   llvm::InitializeAllTargetMCs();
   llvm::InitializeAllAsmParsers();
 
-  Stem = sys::path::stem(ToolName);
+  Stem = sys::path::program_name(ToolName);
+
   auto Is = [](StringRef Tool) {
     // We need to recognize the following filenames.
     //
diff --git a/llvm/tools/llvm-cov/llvm-cov.cpp b/llvm/tools/llvm-cov/llvm-cov.cpp
index 5ada55789b24677..e0b9e75f57c441f 100644
--- a/llvm/tools/llvm-cov/llvm-cov.cpp
+++ b/llvm/tools/llvm-cov/llvm-cov.cpp
@@ -59,7 +59,7 @@ int main(int argc, const char **argv) {
   InitLLVM X(argc, argv);
 
   // If argv[0] is or ends with 'gcov', always be gcov compatible
-  if (sys::path::stem(argv[0]).ends_with_insensitive("gcov"))
+  if (sys::path::program_name(argv[0]).ends_with_insensitive("gcov"))
     return gcovMain(argc, argv);
 
   // Check if we are invoking a specific tool command.
diff --git a/llvm/tools/llvm-driver/llvm-driver.cpp b/llvm/tools/llvm-driver/llvm-driver.cpp
index a0f1ca831d93b6a..ffb3bc0f3a6fdc3 100644
--- a/llvm/tools/llvm-driver/llvm-driver.cpp
+++ b/llvm/tools/llvm-driver/llvm-driver.cpp
@@ -48,7 +48,7 @@ static int findTool(int Argc, char **Argv, const char *Argv0) {
     return 0;
   }
 
-  StringRef Stem = sys::path::stem(ToolName);
+  StringRef Stem = sys::path::program_name(ToolName);
   auto Is = [=](StringRef Tool) {
     auto IsImpl = [=](StringRef Stem) {
       auto I = Stem.rfind_insensitive(Tool);
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index 2afa97601f5cfd8..9c51958198cb109 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -74,7 +74,7 @@ static ErrorSuccess reportWarning(Error E) {
 }
 
 static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) {
-  StringRef Stem = sys::path::stem(ToolName);
+  StringRef Stem = sys::path::program_name(ToolName);
   auto Is = [=](StringRef Tool) {
     // We need to recognize the following filenames:
     //
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index e4e1ceb9b3f97f6..f6a8417afd72c26 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -3297,7 +3297,7 @@ int llvm_objdump_main(int argc, char **argv, const llvm::ToolContext &) {
   std::unique_ptr<CommonOptTable> T;
   OptSpecifier Unknown, HelpFlag, HelpHiddenFlag, VersionFlag;
 
-  StringRef Stem = sys::path::stem(ToolName);
+  StringRef Stem = sys::path::program_name(ToolName);
   auto Is = [=](StringRef Tool) {
     // We need to recognize the following filenames:
     //
diff --git a/llvm/tools/llvm-rc/llvm-rc.cpp b/llvm/tools/llvm-rc/llvm-rc.cpp
index b955347f2a8646e..2f93a4931f55a6e 100644
--- a/llvm/tools/llvm-rc/llvm-rc.cpp
+++ b/llvm/tools/llvm-rc/llvm-rc.cpp
@@ -276,7 +276,7 @@ void preprocess(StringRef Src, StringRef Dst, const RcOptions &Opts,
 }
 
 static std::pair<bool, std::string> isWindres(llvm::StringRef Argv0) {
-  StringRef ProgName = llvm::sys::path::stem(Argv0);
+  StringRef ProgName = sys::path::program_name(Argv0);
   // x86_64-w64-mingw32-windres -> x86_64-w64-mingw32, windres
   // llvm-rc -> "", llvm-rc
   // aarch64-w64-mingw32-llvm-windres-10.exe -> aarch64-w64-mingw32, llvm-windres
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index ca633ceff90800e..2735d31202c6e70 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -657,7 +657,7 @@ int llvm_readobj_main(int argc, char **argv, const llvm::ToolContext &) {
     return 0;
   }
 
-  if (sys::path::stem(argv[0]).contains("readelf"))
+  if (sys::path::filename(argv[0]).contains("readelf"))
     opts::Output = opts::GNU;
   parseOptions(Args);
 
diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
index 78a0e6772f3fb36..e06a4a0b8fa4827 100644
--- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -432,7 +432,7 @@ int llvm_symbolizer_main(int argc, char **argv, const llvm::ToolContext &) {
   sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded);
 
   ToolName = argv[0];
-  bool IsAddr2Line = sys::path::stem(ToolName).contains("addr2line");
+  bool IsAddr2Line = sys::path::filename(ToolName).contains("addr2line");
   BumpPtrAllocator A;
   StringSaver Saver(A);
   SymbolizerOptTable Tbl;

>From f10871b4fc2387a54aed5d121da2129e3e13419e Mon Sep 17 00:00:00 2001
From: Dan McGregor <dan.mcgregor at usask.ca>
Date: Thu, 14 Sep 2023 13:16:04 -0600
Subject: [PATCH 2/2] Add tests for program_name path function.

Simple clang test for now, tests in lld and other tools coming soon.
---
 clang/lib/Driver/ToolChain.cpp              |  9 +-----
 clang/unittests/Driver/ToolChainTest.cpp    |  5 +++
 llvm/test/tools/llvm-ar/tool-name.test      |  5 +++
 llvm/test/tools/llvm-objcopy/tool-name.test |  4 +--
 llvm/test/tools/llvm-objdump/tool-name.test |  4 +--
 llvm/test/tools/llvm-ranlib/tool-name.test  |  3 ++
 llvm/unittests/Support/Path.cpp             | 34 +++++++++++++++++++++
 7 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 410757d5f9e1843..7712ff71e9f0c05 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -326,7 +326,7 @@ static const DriverSuffix *FindDriverSuffix(StringRef ProgName, size_t &Pos) {
 /// Normalize the program name from argv[0] by stripping the file extension if
 /// present and lower-casing the string on Windows.
 static std::string normalizeProgramName(llvm::StringRef Argv0) {
-  std::string ProgName = std::string(llvm::sys::path::filename(Argv0));
+  std::string ProgName = std::string(sys::path::program_name(Argv0));
   if (is_style_windows(llvm::sys::path::Style::native)) {
     // Transform to lowercase for case insensitive file systems.
     std::transform(ProgName.begin(), ProgName.end(), ProgName.begin(),
@@ -345,13 +345,6 @@ static const DriverSuffix *parseDriverSuffix(StringRef ProgName, size_t &Pos) {
   // added via -target as implicit first argument.
   const DriverSuffix *DS = FindDriverSuffix(ProgName, Pos);
 
-  if (!DS && ProgName.endswith(".exe")) {
-    // Try again after stripping the executable suffix:
-    // clang++.exe -> clang++
-    ProgName = ProgName.drop_back(StringRef(".exe").size());
-    DS = FindDriverSuffix(ProgName, Pos);
-  }
-
   if (!DS) {
     // Try again after stripping any trailing version number:
     // clang++3.5 -> clang++
diff --git a/clang/unittests/Driver/ToolChainTest.cpp b/clang/unittests/Driver/ToolChainTest.cpp
index ae567abb81a9bb4..1542b099c5fd1b7 100644
--- a/clang/unittests/Driver/ToolChainTest.cpp
+++ b/clang/unittests/Driver/ToolChainTest.cpp
@@ -430,6 +430,11 @@ TEST(ToolChainTest, GetTargetAndMode) {
   EXPECT_STREQ(Res.DriverMode, "--driver-mode=g++");
   EXPECT_TRUE(Res.TargetIsValid);
 
+  Res = ToolChain::getTargetAndModeFromProgramName(
+      "x86_64-unknown-freebsd13.2-clang-c++");
+  EXPECT_TRUE(Res.TargetPrefix == "x86_64-unknown-freebsd13.2");
+  EXPECT_TRUE(Res.TargetIsValid);
+
   Res = ToolChain::getTargetAndModeFromProgramName(
       "x86_64-linux-gnu-clang-c++-tot");
   EXPECT_TRUE(Res.TargetPrefix == "x86_64-linux-gnu");
diff --git a/llvm/test/tools/llvm-ar/tool-name.test b/llvm/test/tools/llvm-ar/tool-name.test
index f91ef716d5c3adb..d7d8bc2a44a7323 100644
--- a/llvm/test/tools/llvm-ar/tool-name.test
+++ b/llvm/test/tools/llvm-ar/tool-name.test
@@ -5,11 +5,15 @@
 # RUN: mkdir %t
 # RUN: ln -s llvm-ar %t/llvm-ar-9
 # RUN: ln -s llvm-ar %t/ar.exe
+# RUN: ln -s llvm-ar %t/x86_64-portbld-freebsd13.2-llvm-ar
+# RUN: ln -s llvm-ar %t/x86_64-portbld-freebsd13.2-llvm-ar.exe
 # RUN: ln -s llvm-ar %t/arm-pokymllib32-linux-gnueabi-llvm-ar-9
 
 # RUN: llvm-ar h | FileCheck %s --check-prefix=DEFAULT
 # RUN: %t/llvm-ar-9 h | FileCheck %s --check-prefix=VERSION
 # RUN: %t/ar.exe h | FileCheck %s --check-prefix=SUFFIX
+# RUN: %t/x86_64-portbld-freebsd13.2-llvm-ar h | FileCheck %s --check-prefix=TARGETDOT
+# RUN: %t/x86_64-portbld-freebsd13.2-llvm-ar.exe h | FileCheck %s --check-prefix=TARGETDOT
 ## Ensure that the "lib" substring does not result in misidentification as the
 ## llvm-lib tool.
 # RUN: %t/arm-pokymllib32-linux-gnueabi-llvm-ar-9 h | FileCheck %s --check-prefix=ARM
@@ -17,4 +21,5 @@
 # DEFAULT: USAGE: llvm-ar{{ }}
 # VERSION: USAGE: llvm-ar-9{{ }}
 # SUFFIX: USAGE: ar{{ }}
+# TARGETDOT: USAGE: x86_64-portbld-freebsd13.2-llvm-ar{{ }}
 # ARM: USAGE: arm-pokymllib32-linux-gnueabi-llvm-ar-9{{ }}
diff --git a/llvm/test/tools/llvm-objcopy/tool-name.test b/llvm/test/tools/llvm-objcopy/tool-name.test
index 6a1f72588e23d8f..655fa9bf9a36085 100644
--- a/llvm/test/tools/llvm-objcopy/tool-name.test
+++ b/llvm/test/tools/llvm-objcopy/tool-name.test
@@ -5,11 +5,11 @@
 # RUN: mkdir %t
 
 # RUN: ln -s llvm-objcopy %t/llvm-objcopy-11.exe
-# RUN: ln -s llvm-objcopy %t/powerpc64-unknown-freebsd13-objcopy
+# RUN: ln -s llvm-objcopy %t/powerpc64-unknown-freebsd13.0-objcopy
 
 # RUN: llvm-objcopy --help | FileCheck --check-prefix=OBJCOPY %s
 # RUN: %t/llvm-objcopy-11.exe --help | FileCheck --check-prefix=OBJCOPY %s
-# RUN: %t/powerpc64-unknown-freebsd13-objcopy --help | FileCheck --check-prefix=OBJCOPY %s
+# RUN: %t/powerpc64-unknown-freebsd13.0-objcopy --help | FileCheck --check-prefix=OBJCOPY %s
 
 # OBJCOPY: OVERVIEW: llvm-objcopy tool
 
diff --git a/llvm/test/tools/llvm-objdump/tool-name.test b/llvm/test/tools/llvm-objdump/tool-name.test
index 8aefce82c83b8de..eaf881fb6ba8f19 100644
--- a/llvm/test/tools/llvm-objdump/tool-name.test
+++ b/llvm/test/tools/llvm-objdump/tool-name.test
@@ -5,10 +5,10 @@
 # RUN: mkdir %t
 
 # RUN: ln -s llvm-objdump %t/llvm-otool-11.exe
-# RUN: ln -s llvm-objdump %t/powerpc64-unknown-freebsd13-objdump
+# RUN: ln -s llvm-objdump %t/powerpc64-unknown-freebsd13.0-objdump
 
 # RUN: %t/llvm-otool-11.exe --help | FileCheck --check-prefix=OTOOL %s
-# RUN: %t/powerpc64-unknown-freebsd13-objdump --help | \
+# RUN: %t/powerpc64-unknown-freebsd13.0-objdump --help | \
 # RUN:     FileCheck --check-prefix=OBJDUMP %s
 
 # OBJDUMP: OVERVIEW: llvm object file dumper
diff --git a/llvm/test/tools/llvm-ranlib/tool-name.test b/llvm/test/tools/llvm-ranlib/tool-name.test
index d58783e4667b3f1..217758d2d8c65b7 100644
--- a/llvm/test/tools/llvm-ranlib/tool-name.test
+++ b/llvm/test/tools/llvm-ranlib/tool-name.test
@@ -5,11 +5,14 @@
 # RUN: mkdir %t
 # RUN: ln -s llvm-ranlib %t/llvm-ranlib-9
 # RUN: ln -s llvm-ranlib %t/ranlib.exe
+# RUN: ln -s llvm-ranlib %t/x86_64-unknown-freebsd13.2-llvm-ranlib
 
 # RUN: llvm-ranlib -h | FileCheck %s --check-prefix=DEFAULT
 # RUN: %t/llvm-ranlib-9 -h | FileCheck %s --check-prefix=VERSION
 # RUN: %t/ranlib.exe -h | FileCheck %s --check-prefix=SUFFIX
+# RUN: %t/x86_64-unknown-freebsd13.2-llvm-ranlib -h | FileCheck %s --check-prefix=TRIPLE
 
 # DEFAULT: USAGE: llvm-ranlib{{ }}
 # VERSION: USAGE: llvm-ranlib-9{{ }}
 # SUFFIX: USAGE: ranlib{{ }}
+# TRIPLE: USAGE: x86_64-unknown-freebsd13.2-llvm-ranlib{{ }}
diff --git a/llvm/unittests/Support/Path.cpp b/llvm/unittests/Support/Path.cpp
index 35a01aa27667933..80f12d130831211 100644
--- a/llvm/unittests/Support/Path.cpp
+++ b/llvm/unittests/Support/Path.cpp
@@ -1696,6 +1696,40 @@ TEST(Support, ReplacePathPrefix) {
   EXPECT_EQ(Path, "C:\\old/foo\\bar");
 }
 
+TEST(Support, FindProgramName) {
+  StringRef WindowsProgName =
+      path::program_name("C:\\Test\\foo.exe", path::Style::windows);
+  EXPECT_EQ(WindowsProgName, "foo");
+
+  StringRef WindowsProgNameManyDots = path::program_name(
+      "C:\\Test.7\\x86_64-freebsd14.0-clang.exe", path::Style::windows);
+  EXPECT_EQ(WindowsProgNameManyDots, "x86_64-freebsd14.0-clang");
+
+  StringRef PosixProgName =
+      path::program_name("/var/empty/clang.exe", path::Style::posix);
+  EXPECT_EQ(PosixProgName, "clang");
+
+  StringRef PosixProgNameManyDotsExe = path::program_name(
+      "/llvm-test16.4/x86_64-portbld-freebsd13.2-llvm-ar.exe",
+      path::Style::posix);
+  EXPECT_EQ(PosixProgNameManyDotsExe, "x86_64-portbld-freebsd13.2-llvm-ar");
+
+  StringRef PosixProgNameManyDots = path::program_name(
+      "/llvm-test16.4/x86_64-portbld-freebsd13.2-llvm-ar", path::Style::posix);
+  EXPECT_EQ(PosixProgNameManyDots, "x86_64-portbld-freebsd13.2-llvm-ar");
+
+  StringRef PosixProgNameSh =
+      path::program_name("/llvm-test16.4/x86_64-portbld-freebsd13.2-llvm-ar.sh",
+                         path::Style::posix);
+  EXPECT_EQ(PosixProgNameSh, "x86_64-portbld-freebsd13.2-llvm-ar.sh");
+
+  // TODO: determine if this is correct. What happens on windows with an executable
+  // named ".exe"?
+  StringRef OnlyExe =
+      path::program_name("/var/empty/.exe", path::Style::posix);
+  EXPECT_EQ(OnlyExe, "");
+}
+
 TEST_F(FileSystemTest, OpenFileForRead) {
   // Create a temp file.
   int FileDescriptor;



More information about the cfe-commits mailing list