[clang] [clang][ssaf] Extract shared tool utilities into `clang-ssaf-tool-common` (PR #187439)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 19 09:30:09 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Aviral Goel (aviralg)
<details>
<summary>Changes</summary>
This patch factors the duplicated code from `clang-ssaf-linker` and `clang-ssaf-format` into a new static library `clangSSAFToolCommon` at `clang/tools/clang-ssaf-tool-common/`. This includes identical `fail()` overloads, a format registry cache (getFormatForExtension), the `SummaryFile` abstraction, shared error message strings, `printVersion`, and near-identical `main()` initialization sequences.
---
Patch is 25.49 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/187439.diff
8 Files Affected:
- (modified) clang/tools/CMakeLists.txt (+1)
- (modified) clang/tools/clang-ssaf-format/CMakeLists.txt (+1)
- (modified) clang/tools/clang-ssaf-format/SSAFFormat.cpp (+10-120)
- (modified) clang/tools/clang-ssaf-linker/CMakeLists.txt (+1)
- (modified) clang/tools/clang-ssaf-linker/SSAFLinker.cpp (+17-109)
- (added) clang/tools/clang-ssaf-tool-common/CMakeLists.txt (+17)
- (added) clang/tools/clang-ssaf-tool-common/SSAFToolUtils.cpp (+127)
- (added) clang/tools/clang-ssaf-tool-common/SSAFToolUtils.h (+115)
``````````diff
diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt
index 891043ec31f77..b63778991e99f 100644
--- a/clang/tools/CMakeLists.txt
+++ b/clang/tools/CMakeLists.txt
@@ -17,6 +17,7 @@ add_clang_subdirectory(clang-offload-bundler)
add_clang_subdirectory(clang-scan-deps)
add_clang_subdirectory(clang-ssaf-format)
add_clang_subdirectory(clang-ssaf-linker)
+add_clang_subdirectory(clang-ssaf-tool-common)
add_clang_subdirectory(clang-sycl-linker)
add_clang_subdirectory(clang-installapi)
if(HAVE_CLANG_REPL_SUPPORT)
diff --git a/clang/tools/clang-ssaf-format/CMakeLists.txt b/clang/tools/clang-ssaf-format/CMakeLists.txt
index f64d12eb3fac0..0eba14190e4ba 100644
--- a/clang/tools/clang-ssaf-format/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-format/CMakeLists.txt
@@ -11,4 +11,5 @@ clang_target_link_libraries(clang-ssaf-format
PRIVATE
clangBasic
clangScalableStaticAnalysisFrameworkCore
+ clangSSAFToolCommon
)
diff --git a/clang/tools/clang-ssaf-format/SSAFFormat.cpp b/clang/tools/clang-ssaf-format/SSAFFormat.cpp
index d91ff9402f7ec..e1267c93d4768 100644
--- a/clang/tools/clang-ssaf-format/SSAFFormat.cpp
+++ b/clang/tools/clang-ssaf-format/SSAFFormat.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "SSAFToolUtils.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummaryEncoding.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/TUSummaryEncoding.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
@@ -19,15 +20,12 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/Process.h"
-#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <optional>
@@ -36,6 +34,7 @@
using namespace llvm;
using namespace clang::ssaf;
+using namespace clang::ssaf::tool;
namespace {
@@ -84,87 +83,18 @@ cl::opt<bool> ListFormats("list",
"analyses, then exit"),
cl::init(false), cl::cat(SsafFormatCategory));
-llvm::StringRef ToolName;
-
-void printVersion(llvm::raw_ostream &OS) { OS << ToolName << " 0.1\n"; }
-
//===----------------------------------------------------------------------===//
// Error Messages
//===----------------------------------------------------------------------===//
-namespace ErrorMessages {
-
-constexpr const char *FailedToLoadPlugin = "failed to load plugin '{0}': {1}";
-
-constexpr const char *CannotValidateSummary =
- "failed to validate summary '{0}': {1}";
-
-constexpr const char *ExtensionNotSupplied = "Extension not supplied";
-
-constexpr const char *NoFormatForExtension =
- "Format not registered for extension '{0}'";
-
-constexpr const char *OutputDirectoryMissing =
- "Parent directory does not exist";
+namespace LocalErrorMessages {
constexpr const char *OutputFileAlreadyExists = "Output file already exists";
constexpr const char *InputOutputSamePath =
"Input and Output resolve to the same path";
-} // namespace ErrorMessages
-
-//===----------------------------------------------------------------------===//
-// Diagnostic Utilities
-//===----------------------------------------------------------------------===//
-
-[[noreturn]] void fail(const char *Msg) {
- llvm::WithColor::error(llvm::errs(), ToolName) << Msg << "\n";
- llvm::sys::Process::Exit(1);
-}
-
-template <typename... Ts>
-[[noreturn]] void fail(const char *Fmt, Ts &&...Args) {
- std::string Message = llvm::formatv(Fmt, std::forward<Ts>(Args)...);
- fail(Message.data());
-}
-
-[[noreturn]] void fail(llvm::Error Err) {
- fail(toString(std::move(Err)).data());
-}
-
-//===----------------------------------------------------------------------===//
-// Format Registry
-//===----------------------------------------------------------------------===//
-
-// FIXME: This will be revisited after we add support for registering formats
-// with extensions.
-SerializationFormat *getFormatForExtension(llvm::StringRef Extension) {
- static llvm::SmallVector<
- std::pair<std::string, std::unique_ptr<SerializationFormat>>, 4>
- ExtensionFormatList;
-
- // Most recently used format is most likely to be reused again.
- auto ReversedList = llvm::reverse(ExtensionFormatList);
- auto It = llvm::find_if(ReversedList, [&](const auto &Entry) {
- return Entry.first == Extension;
- });
- if (It != ReversedList.end()) {
- return It->second.get();
- }
-
- if (!isFormatRegistered(Extension)) {
- return nullptr;
- }
-
- auto Format = makeFormat(Extension);
- SerializationFormat *Result = Format.get();
- assert(Result);
-
- ExtensionFormatList.emplace_back(Extension, std::move(Format));
-
- return Result;
-}
+} // namespace LocalErrorMessages
//===----------------------------------------------------------------------===//
// Format Listing
@@ -295,47 +225,10 @@ void listFormats() {
printFormats(Formats, computePrintLayout(Formats));
}
-//===----------------------------------------------------------------------===//
-// Plugin Loading
-//===----------------------------------------------------------------------===//
-
-void loadPlugins() {
- for (const auto &PluginPath : LoadPlugins) {
- std::string ErrMsg;
- if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(PluginPath.c_str(),
- &ErrMsg)) {
- fail(ErrorMessages::FailedToLoadPlugin, PluginPath, ErrMsg);
- }
- }
-}
-
//===----------------------------------------------------------------------===//
// Input Validation
//===----------------------------------------------------------------------===//
-struct SummaryFile {
- std::string Path;
- SerializationFormat *Format = nullptr;
-
- static SummaryFile fromPath(llvm::StringRef Path) {
- llvm::StringRef Extension = path::extension(Path);
- if (Extension.empty()) {
- fail(ErrorMessages::CannotValidateSummary, Path,
- ErrorMessages::ExtensionNotSupplied);
- }
-
- Extension = Extension.drop_front();
- SerializationFormat *Format = getFormatForExtension(Extension);
- if (!Format) {
- std::string Msg =
- llvm::formatv(ErrorMessages::NoFormatForExtension, Extension);
- fail(ErrorMessages::CannotValidateSummary, Path, Msg);
- }
-
- return {Path.str(), Format};
- }
-};
-
struct FormatInput {
SummaryFile InputFile;
std::optional<SummaryFile> OutputFile;
@@ -391,12 +284,12 @@ FormatInput validateInput() {
if (RealOutputPath == FI.InputFile.Path) {
fail(ErrorMessages::CannotValidateSummary, OutputPath,
- ErrorMessages::InputOutputSamePath);
+ LocalErrorMessages::InputOutputSamePath);
}
if (fs::exists(RealOutputPath)) {
fail(ErrorMessages::CannotValidateSummary, OutputPath,
- ErrorMessages::OutputFileAlreadyExists);
+ LocalErrorMessages::OutputFileAlreadyExists);
}
FI.OutputFile = SummaryFile::fromPath(RealOutputPath);
@@ -458,15 +351,12 @@ void convert(const FormatInput &FI) {
//===----------------------------------------------------------------------===//
int main(int argc, const char **argv) {
- InitLLVM X(argc, argv);
- // path::stem strips the .exe extension on Windows so ToolName is consistent.
- ToolName = path::stem(argv[0]);
+ llvm::StringRef ToolHeading = "SSAF Format";
- cl::HideUnrelatedOptions(SsafFormatCategory);
- cl::SetVersionPrinter(printVersion);
- cl::ParseCommandLineOptions(argc, argv, "SSAF Format\n");
+ InitLLVM X(argc, argv);
+ initTool(argc, argv, "0.1", SsafFormatCategory, ToolHeading);
- loadPlugins();
+ loadPlugins(LoadPlugins);
if (ListFormats) {
listFormats();
diff --git a/clang/tools/clang-ssaf-linker/CMakeLists.txt b/clang/tools/clang-ssaf-linker/CMakeLists.txt
index 0f6041fb109c9..ed317cb0fb182 100644
--- a/clang/tools/clang-ssaf-linker/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-linker/CMakeLists.txt
@@ -11,4 +11,5 @@ clang_target_link_libraries(clang-ssaf-linker
PRIVATE
clangBasic
clangScalableStaticAnalysisFrameworkCore
+ clangSSAFToolCommon
)
diff --git a/clang/tools/clang-ssaf-linker/SSAFLinker.cpp b/clang/tools/clang-ssaf-linker/SSAFLinker.cpp
index 3c57a2fe2cf98..1b48060cf9795 100644
--- a/clang/tools/clang-ssaf-linker/SSAFLinker.cpp
+++ b/clang/tools/clang-ssaf-linker/SSAFLinker.cpp
@@ -11,10 +11,10 @@
//
//===----------------------------------------------------------------------===//
+#include "SSAFToolUtils.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/EntityLinker.h"
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/TUSummaryEncoding.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
#include "llvm/ADT/STLExtras.h"
@@ -24,7 +24,6 @@
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/Process.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
@@ -34,6 +33,7 @@
using namespace llvm;
using namespace clang::ssaf;
+using namespace clang::ssaf::tool;
namespace fs = llvm::sys::fs;
namespace path = llvm::sys::path;
@@ -59,53 +59,12 @@ cl::opt<bool> Verbose("verbose", cl::desc("Enable verbose output"),
cl::opt<bool> Time("time", cl::desc("Enable timing"), cl::init(false),
cl::cat(SsafLinkerCategory));
-//===----------------------------------------------------------------------===//
-// Error Messages
-//===----------------------------------------------------------------------===//
-
-namespace ErrorMessages {
-
-constexpr const char *CannotValidateSummary =
- "failed to validate summary '{0}': {1}";
-
-constexpr const char *OutputDirectoryMissing =
- "Parent directory does not exist";
-
-constexpr const char *OutputDirectoryNotWritable =
- "Parent directory is not writable";
-
-constexpr const char *ExtensionNotSupplied = "Extension not supplied";
-
-constexpr const char *NoFormatForExtension =
- "Format not registered for extension '{0}'";
-
-constexpr const char *LinkingSummary = "Linking summary '{0}'";
-
-} // namespace ErrorMessages
-
//===----------------------------------------------------------------------===//
// Diagnostic Utilities
//===----------------------------------------------------------------------===//
constexpr unsigned IndentationWidth = 2;
-llvm::StringRef ToolName;
-
-template <typename... Ts> [[noreturn]] void fail(const char *Msg) {
- llvm::WithColor::error(llvm::errs(), ToolName) << Msg << "\n";
- llvm::sys::Process::Exit(1);
-}
-
-template <typename... Ts>
-[[noreturn]] void fail(const char *Fmt, Ts &&...Args) {
- std::string Message = llvm::formatv(Fmt, std::forward<Ts>(Args)...);
- fail(Message.data());
-}
-
-template <typename... Ts> [[noreturn]] void fail(llvm::Error Err) {
- fail(toString(std::move(Err)).data());
-}
-
template <typename... Ts>
void info(unsigned IndentationLevel, const char *Fmt, Ts &&...Args) {
if (Verbose) {
@@ -115,74 +74,29 @@ void info(unsigned IndentationLevel, const char *Fmt, Ts &&...Args) {
}
}
-//===----------------------------------------------------------------------===//
-// Format Registry
-//===----------------------------------------------------------------------===//
-
-SerializationFormat *getFormatForExtension(llvm::StringRef Extension) {
- static llvm::SmallVector<
- std::pair<std::string, std::unique_ptr<SerializationFormat>>, 4>
- ExtensionFormatList;
-
- // Most recently used format is most likely to be reused again.
- auto ReversedList = llvm::reverse(ExtensionFormatList);
- auto It = llvm::find_if(ReversedList, [&](const auto &Entry) {
- return Entry.first == Extension;
- });
- if (It != ReversedList.end()) {
- return It->second.get();
- }
-
- if (!isFormatRegistered(Extension)) {
- return nullptr;
- }
-
- auto Format = makeFormat(Extension);
- SerializationFormat *Result = Format.get();
- assert(Result);
-
- ExtensionFormatList.emplace_back(Extension, std::move(Format));
-
- return Result;
-}
-
//===----------------------------------------------------------------------===//
// Data Structures
//===----------------------------------------------------------------------===//
-struct SummaryFile {
- std::string Path;
- SerializationFormat *Format = nullptr;
-
- static SummaryFile fromPath(llvm::StringRef Path) {
- llvm::StringRef Extension = path::extension(Path);
- if (Extension.empty()) {
- fail(ErrorMessages::CannotValidateSummary, Path,
- ErrorMessages::ExtensionNotSupplied);
- }
- Extension = Extension.drop_front();
- SerializationFormat *Format = getFormatForExtension(Extension);
- if (!Format) {
- std::string BadExtension =
- llvm::formatv(ErrorMessages::NoFormatForExtension, Extension);
- fail(ErrorMessages::CannotValidateSummary, Path, BadExtension);
- }
- return {Path.str(), Format};
- }
-};
-
struct LinkerInput {
std::vector<SummaryFile> InputFiles;
SummaryFile OutputFile;
std::string LinkUnitName;
};
-static void printVersion(llvm::raw_ostream &OS) { OS << ToolName << " 0.1\n"; }
-
//===----------------------------------------------------------------------===//
// Pipeline
//===----------------------------------------------------------------------===//
+namespace LocalErrorMessages {
+
+constexpr const char *OutputDirectoryNotWritable =
+ "Parent directory is not writable";
+
+constexpr const char *LinkingSummary = "Linking summary '{0}'";
+
+} // namespace LocalErrorMessages
+
LinkerInput validate(llvm::TimerGroup &TG) {
llvm::Timer TValidate("validate", "Validate Input", TG);
LinkerInput LI;
@@ -199,7 +113,7 @@ LinkerInput validate(llvm::TimerGroup &TG) {
if (fs::access(DirToCheck, fs::AccessMode::Write)) {
fail(ErrorMessages::CannotValidateSummary, OutputPath,
- ErrorMessages::OutputDirectoryNotWritable);
+ LocalErrorMessages::OutputDirectoryNotWritable);
}
LI.OutputFile = SummaryFile::fromPath(OutputPath);
@@ -264,7 +178,7 @@ void link(const LinkerInput &LI, llvm::TimerGroup &TG) {
if (auto Err = EL.link(std::move(Summary))) {
fail(ErrorBuilder::wrap(std::move(Err))
- .context(ErrorMessages::LinkingSummary, InputFile.Path)
+ .context(LocalErrorMessages::LinkingSummary, InputFile.Path)
.build());
}
}
@@ -290,18 +204,12 @@ void link(const LinkerInput &LI, llvm::TimerGroup &TG) {
//===----------------------------------------------------------------------===//
int main(int argc, const char **argv) {
- InitLLVM X(argc, argv);
- // path::stem strips the .exe extension on Windows so ToolName is consistent.
- ToolName = llvm::sys::path::stem(argv[0]);
+ llvm::StringRef ToolHeading = "SSAF Linker";
- // Hide options unrelated to clang-ssaf-linker from --help output.
- cl::HideUnrelatedOptions(SsafLinkerCategory);
- // Register a custom version printer for the --version flag.
- cl::SetVersionPrinter(printVersion);
- // Parse command-line arguments and exit with an error if they are invalid.
- cl::ParseCommandLineOptions(argc, argv, "SSAF Linker\n");
+ InitLLVM X(argc, argv);
+ initTool(argc, argv, "0.1", SsafLinkerCategory, ToolHeading);
- llvm::TimerGroup LinkerTimers(ToolName, "SSAF Linker");
+ llvm::TimerGroup LinkerTimers(ToolName, ToolHeading);
LinkerInput LI;
{
diff --git a/clang/tools/clang-ssaf-tool-common/CMakeLists.txt b/clang/tools/clang-ssaf-tool-common/CMakeLists.txt
new file mode 100644
index 0000000000000..67715b7aa7e19
--- /dev/null
+++ b/clang/tools/clang-ssaf-tool-common/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangSSAFToolCommon
+ SSAFToolUtils.cpp
+ )
+
+clang_target_link_libraries(clangSSAFToolCommon
+ PUBLIC
+ clangBasic
+ clangScalableStaticAnalysisFrameworkCore
+ )
+
+target_include_directories(clangSSAFToolCommon
+ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+ )
diff --git a/clang/tools/clang-ssaf-tool-common/SSAFToolUtils.cpp b/clang/tools/clang-ssaf-tool-common/SSAFToolUtils.cpp
new file mode 100644
index 0000000000000..fe86b5d484a97
--- /dev/null
+++ b/clang/tools/clang-ssaf-tool-common/SSAFToolUtils.cpp
@@ -0,0 +1,127 @@
+//===- SSAFToolUtils.cpp - Shared utilities for SSAF tools ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SSAFToolUtils.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <memory>
+#include <string>
+
+namespace clang::ssaf::tool {
+
+namespace path = llvm::sys::path;
+
+llvm::StringRef ToolName;
+static llvm::StringRef ToolVersion;
+
+void printVersion(llvm::raw_ostream &OS) {
+ OS << ToolName << " " << ToolVersion << "\n";
+}
+
+[[noreturn]] void fail(const char *Msg) {
+ llvm::WithColor::error(llvm::errs(), ToolName) << Msg << "\n";
+ llvm::sys::Process::Exit(1);
+}
+
+[[noreturn]] void fail(llvm::Error Err) {
+ std::string Message = llvm::toString(std::move(Err));
+ fail(Message.data());
+}
+
+void loadPlugins(llvm::ArrayRef<std::string> Paths) {
+ for (const std::string &PluginPath : Paths) {
+ std::string ErrMsg;
+ if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(PluginPath.c_str(),
+ &ErrMsg)) {
+ fail(ErrorMessages::FailedToLoadPlugin, PluginPath, ErrMsg);
+ }
+ }
+}
+
+void initTool(int argc, const char **argv, llvm::StringRef Version,
+ llvm::cl::OptionCategory &Category, llvm::StringRef ToolHeading) {
+ // path::stem strips the .exe extension on Windows so ToolName is consistent.
+ ToolName = path::stem(argv[0]);
+
+ // Set tool version for the version printer.
+ ToolVersion = Version;
+
+ // Hide options unrelated to the tool from --help output.
+ llvm::cl::HideUnrelatedOptions(Category);
+
+ // Register a custom version printer for the --version flag.
+ llvm::cl::SetVersionPrinter(printVersion);
+
+ // Parse command-line arguments and exit with an error if they are invalid.
+ std::string Overview = (ToolHeading + "\n").str();
+ llvm::cl::ParseCommandLineOptions(argc, argv, Overview);
+}
+
+// FIXME: This will be revisited after we add support for registering formats
+// with extensions.
+SerializationFormat *getFormatForExtension(llvm::StringRef Extension) {
+ // This cache is not thread-safe. SSAF tools are single-threaded CLIs, so
+ // concurrent calls to this function are not expected.
+
+ // Realistically, we don't expect to encounter more than four registered
+ // formats.
+ static llvm::SmallVector<
+ std::pair<std::string, std::unique_ptr<SerializationFormat>>, 4>
+ ExtensionFormatList;
+
+ // Most recently used format is most likely to be reused again.
+ auto ReversedList = llvm::reverse(ExtensionFormatList);
+ auto It = llvm::find_if(ReversedList, [&](const auto &Entry) {
+ return Entry.first == Extension;
+ });
+ if (It != ReversedList.end()) {
+ return It->second.get();
+ }
+
+ if (!isFormatRegistered(Extension)) {
+ return nullptr;
+ }
+
+ auto Format = makeFormat(Extension);
+...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/187439
More information about the cfe-commits
mailing list