https://github.com/aviralg updated 
https://github.com/llvm/llvm-project/pull/208799

>From 95c81aab0cfd6dc81d9434b01f8fad44a6806104 Mon Sep 17 00:00:00 2001
From: Aviral Goel <[email protected]>
Date: Fri, 10 Jul 2026 11:20:08 -0700
Subject: [PATCH] [clang][ssaf] Add static-library create subcommand

This change adds `clang-ssaf-linker static-library create` for bundling TU
summaries into a StaticLibrary archive. The linker has been refactored to
dispatch on `cl::SubCommand`; invocations without a subcommand still run the
existing linker pipeline unchanged for backward compatibility.
---
 .../Core/EntityLinker/StaticLibrary.h         |   9 +-
 .../Core/EntityLinker/TUSummaryEncoding.h     |   6 +
 .../Outputs/lib-explicit-triple.json          |  21 +
 .../Outputs/lib-namespace-override.json       |  21 +
 .../ssaf-linker/Outputs/libempty.json         |  21 +
 .../Scalable/ssaf-linker/Outputs/libtwo.json  | 640 ++++++++++++++++++
 .../Analysis/Scalable/ssaf-linker/help.test   |  10 +-
 .../ssaf-linker/static-library-create.test    |  98 +++
 .../ssaf-linker/static-library-time.test      |  19 +
 .../ssaf-linker/static-library-verbose.test   |  23 +
 clang/tools/clang-ssaf-linker/CMakeLists.txt  |   1 +
 clang/tools/clang-ssaf-linker/LinkerCommon.h  |  47 ++
 clang/tools/clang-ssaf-linker/SSAFLinker.cpp  | 143 ++--
 .../StaticLibraryCreateCLI.cpp                | 214 ++++++
 .../StaticLibraryCreateCLI.h                  |  77 +++
 15 files changed, 1297 insertions(+), 53 deletions(-)
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-linker/Outputs/lib-explicit-triple.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-linker/Outputs/lib-namespace-override.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-linker/Outputs/libempty.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-linker/Outputs/libtwo.json
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-linker/static-library-create.test
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-linker/static-library-time.test
 create mode 100644 
clang/test/Analysis/Scalable/ssaf-linker/static-library-verbose.test
 create mode 100644 clang/tools/clang-ssaf-linker/LinkerCommon.h
 create mode 100644 clang/tools/clang-ssaf-linker/StaticLibraryCreateCLI.cpp
 create mode 100644 clang/tools/clang-ssaf-linker/StaticLibraryCreateCLI.h

diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
index 4ed34c9ec68cf..7358a73c32da1 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h
@@ -23,6 +23,8 @@
 
 namespace clang::ssaf {
 
+class StaticLibraryCreateCLI;
+
 /// Represents a static library of translation unit summary encodings.
 ///
 /// A StaticLibrary bundles member translation units without performing
@@ -35,12 +37,13 @@ namespace clang::ssaf {
 /// expressed as a fat wrapper around per-architecture StaticLibrary
 /// instances rather than as a single mixed-architecture library.
 ///
-/// Members are stored as encoded TUSummaryEncoding objects: the archiver
-/// tool never decodes per-entity payloads, and the linker consumes them
-/// as-is during its selective inclusion pass.
+/// Members are stored as encoded TUSummaryEncoding objects: the
+/// static-library tool never decodes per-entity payloads, and the linker
+/// consumes them as-is during its selective inclusion pass.
 class StaticLibrary {
   friend class MultiArchStaticLibrary;
   friend class SerializationFormat;
+  friend class StaticLibraryCreateCLI;
   friend class TestFixture;
 
   /// Orders members by their TUNamespace. As a nested struct of
diff --git 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
index 2ece195ed1fd0..4a5ebc0d486ff 100644
--- 
a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
+++ 
b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h
@@ -26,6 +26,11 @@
 
 namespace clang::ssaf {
 
+// Forward declared so it can be friended. Defined in
+// clang/tools/clang-ssaf-linker/. Needs access to TUNamespace to report
+// the offending member in a duplicate-member error during assembly.
+class StaticLibraryCreateCLI;
+
 /// Represents a translation unit summary in its serialized encoding.
 ///
 /// TUSummaryEncoding holds entity summary data in a format-specific encoding
@@ -36,6 +41,7 @@ class TUSummaryEncoding {
   friend class EntityLinker;
   friend class SerializationFormat;
   friend class StaticLibrary;
+  friend class StaticLibraryCreateCLI;
   friend class TestFixture;
 
   // Target triple of the translation unit.
diff --git 
a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lib-explicit-triple.json 
b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lib-explicit-triple.json
new file mode 100644
index 0000000000000..57be3f594b8cb
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lib-explicit-triple.json
@@ -0,0 +1,21 @@
+{
+  "members": [
+    {
+      "data": [],
+      "id_table": [],
+      "linkage_table": [],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "empty.cpp"
+      },
+      "type": "TUSummary"
+    }
+  ],
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "lib-explicit-triple"
+  },
+  "target_triple": "arm64-apple-macosx",
+  "type": "StaticLibrary"
+}
diff --git 
a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lib-namespace-override.json 
b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lib-namespace-override.json
new file mode 100644
index 0000000000000..0f8e9a8f26b47
--- /dev/null
+++ 
b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/lib-namespace-override.json
@@ -0,0 +1,21 @@
+{
+  "members": [
+    {
+      "data": [],
+      "id_table": [],
+      "linkage_table": [],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "empty.cpp"
+      },
+      "type": "TUSummary"
+    }
+  ],
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "my-static-lib"
+  },
+  "target_triple": "arm64-apple-macosx",
+  "type": "StaticLibrary"
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/libempty.json 
b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/libempty.json
new file mode 100644
index 0000000000000..ccd2e650980b7
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/libempty.json
@@ -0,0 +1,21 @@
+{
+  "members": [
+    {
+      "data": [],
+      "id_table": [],
+      "linkage_table": [],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "empty.cpp"
+      },
+      "type": "TUSummary"
+    }
+  ],
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libempty"
+  },
+  "target_triple": "arm64-apple-macosx",
+  "type": "StaticLibrary"
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Outputs/libtwo.json 
b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/libtwo.json
new file mode 100644
index 0000000000000..7c4d80720ec57
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-linker/Outputs/libtwo.json
@@ -0,0 +1,640 @@
+{
+  "members": [
+    {
+      "data": [
+        {
+          "summary_data": [
+            {
+              "entity_id": 0,
+              "entity_summary": {
+                "call_count": 3,
+                "callees": [
+                  {
+                    "@": 1
+                  },
+                  {
+                    "@": 2
+                  },
+                  {
+                    "@": 3
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 1,
+              "entity_summary": {
+                "call_count": 2,
+                "callees": [
+                  {
+                    "@": 0
+                  },
+                  {
+                    "@": 4
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 2,
+              "entity_summary": {
+                "call_count": 1,
+                "callees": [
+                  {
+                    "@": 5
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 3,
+              "entity_summary": {
+                "call_count": 2,
+                "callees": [
+                  {
+                    "@": 0
+                  },
+                  {
+                    "@": 1
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 4,
+              "entity_summary": {
+                "call_count": 1,
+                "callees": [
+                  {
+                    "@": 3
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 5,
+              "entity_summary": {
+                "call_count": 3,
+                "callees": [
+                  {
+                    "@": 0
+                  },
+                  {
+                    "@": 3
+                  },
+                  {
+                    "@": 4
+                  }
+                ]
+              }
+            }
+          ],
+          "summary_name": "Analysis1"
+        },
+        {
+          "summary_data": [
+            {
+              "entity_id": 0,
+              "entity_summary": {
+                "direct": {
+                  "@": 3
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 1
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 4
+                    },
+                    "level": 2
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 1,
+              "entity_summary": {
+                "direct": {
+                  "@": 0
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 2
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 5
+                    },
+                    "level": 2
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 2,
+              "entity_summary": {
+                "direct": {
+                  "@": 1
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 3
+                    },
+                    "level": 1
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 3,
+              "entity_summary": {
+                "direct": {
+                  "@": 4
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 0
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 2
+                    },
+                    "level": 2
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 4,
+              "entity_summary": {
+                "direct": {
+                  "@": 5
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 1
+                    },
+                    "level": 1
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 5,
+              "entity_summary": {
+                "direct": {
+                  "@": 2
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 0
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 3
+                    },
+                    "level": 2
+                  },
+                  {
+                    "entity": {
+                      "@": 4
+                    },
+                    "level": 3
+                  }
+                ]
+              }
+            }
+          ],
+          "summary_name": "Analysis2"
+        }
+      ],
+      "id_table": [
+        {
+          "id": 0,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@shared_ext#"
+          }
+        },
+        {
+          "id": 1,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@shared_int#"
+          }
+        },
+        {
+          "id": 2,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@shared_none#"
+          }
+        },
+        {
+          "id": 3,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@unique_ext_tu1#"
+          }
+        },
+        {
+          "id": 4,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@unique_int_tu1#"
+          }
+        },
+        {
+          "id": 5,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@unique_none_tu1#"
+          }
+        }
+      ],
+      "linkage_table": [
+        {
+          "id": 0,
+          "linkage": {
+            "type": "External"
+          }
+        },
+        {
+          "id": 1,
+          "linkage": {
+            "type": "Internal"
+          }
+        },
+        {
+          "id": 2,
+          "linkage": {
+            "type": "None"
+          }
+        },
+        {
+          "id": 3,
+          "linkage": {
+            "type": "External"
+          }
+        },
+        {
+          "id": 4,
+          "linkage": {
+            "type": "Internal"
+          }
+        },
+        {
+          "id": 5,
+          "linkage": {
+            "type": "None"
+          }
+        }
+      ],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "tu1.cpp"
+      },
+      "type": "TUSummary"
+    },
+    {
+      "data": [
+        {
+          "summary_data": [
+            {
+              "entity_id": 0,
+              "entity_summary": {
+                "call_count": 3,
+                "callees": [
+                  {
+                    "@": 1
+                  },
+                  {
+                    "@": 2
+                  },
+                  {
+                    "@": 5
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 1,
+              "entity_summary": {
+                "call_count": 2,
+                "callees": [
+                  {
+                    "@": 0
+                  },
+                  {
+                    "@": 3
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 2,
+              "entity_summary": {
+                "call_count": 1,
+                "callees": [
+                  {
+                    "@": 4
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 3,
+              "entity_summary": {
+                "call_count": 2,
+                "callees": [
+                  {
+                    "@": 0
+                  },
+                  {
+                    "@": 2
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 4,
+              "entity_summary": {
+                "call_count": 1,
+                "callees": [
+                  {
+                    "@": 3
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 5,
+              "entity_summary": {
+                "call_count": 3,
+                "callees": [
+                  {
+                    "@": 1
+                  },
+                  {
+                    "@": 2
+                  },
+                  {
+                    "@": 3
+                  }
+                ]
+              }
+            }
+          ],
+          "summary_name": "Analysis1"
+        },
+        {
+          "summary_data": [
+            {
+              "entity_id": 0,
+              "entity_summary": {
+                "direct": {
+                  "@": 3
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 2
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 5
+                    },
+                    "level": 2
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 1,
+              "entity_summary": {
+                "direct": {
+                  "@": 0
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 4
+                    },
+                    "level": 1
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 2,
+              "entity_summary": {
+                "direct": {
+                  "@": 5
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 0
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 3
+                    },
+                    "level": 2
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 3,
+              "entity_summary": {
+                "direct": {
+                  "@": 1
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 2
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 0
+                    },
+                    "level": 2
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 4,
+              "entity_summary": {
+                "direct": {
+                  "@": 2
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 5
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 3
+                    },
+                    "level": 2
+                  }
+                ]
+              }
+            },
+            {
+              "entity_id": 5,
+              "entity_summary": {
+                "direct": {
+                  "@": 4
+                },
+                "indirect": [
+                  {
+                    "entity": {
+                      "@": 1
+                    },
+                    "level": 1
+                  },
+                  {
+                    "entity": {
+                      "@": 2
+                    },
+                    "level": 2
+                  },
+                  {
+                    "entity": {
+                      "@": 0
+                    },
+                    "level": 3
+                  }
+                ]
+              }
+            }
+          ],
+          "summary_name": "Analysis2"
+        }
+      ],
+      "id_table": [
+        {
+          "id": 0,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@shared_ext#"
+          }
+        },
+        {
+          "id": 1,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@shared_int#"
+          }
+        },
+        {
+          "id": 2,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@shared_none#"
+          }
+        },
+        {
+          "id": 3,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@unique_ext_tu2#"
+          }
+        },
+        {
+          "id": 4,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@unique_int_tu2#"
+          }
+        },
+        {
+          "id": 5,
+          "name": {
+            "suffix": "",
+            "usr": "c:@F@unique_none_tu2#"
+          }
+        }
+      ],
+      "linkage_table": [
+        {
+          "id": 0,
+          "linkage": {
+            "type": "External"
+          }
+        },
+        {
+          "id": 1,
+          "linkage": {
+            "type": "Internal"
+          }
+        },
+        {
+          "id": 2,
+          "linkage": {
+            "type": "None"
+          }
+        },
+        {
+          "id": 3,
+          "linkage": {
+            "type": "External"
+          }
+        },
+        {
+          "id": 4,
+          "linkage": {
+            "type": "Internal"
+          }
+        },
+        {
+          "id": 5,
+          "linkage": {
+            "type": "None"
+          }
+        }
+      ],
+      "target_triple": "arm64-apple-macosx",
+      "tu_namespace": {
+        "kind": "CompilationUnit",
+        "name": "tu2.cpp"
+      },
+      "type": "TUSummary"
+    }
+  ],
+  "namespace": {
+    "kind": "StaticLibrary",
+    "name": "libtwo"
+  },
+  "target_triple": "arm64-apple-macosx",
+  "type": "StaticLibrary"
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-linker/help.test 
b/clang/test/Analysis/Scalable/ssaf-linker/help.test
index cb071a7b22d7b..6f897f4ca9228 100644
--- a/clang/test/Analysis/Scalable/ssaf-linker/help.test
+++ b/clang/test/Analysis/Scalable/ssaf-linker/help.test
@@ -5,7 +5,13 @@
 
 // CHECK:      OVERVIEW: SSAF Linker
 // CHECK-EMPTY:
-// CHECK-NEXT: USAGE: clang-ssaf-linker{{(\.exe)?}} [options] <input files>
+// CHECK-NEXT: USAGE: clang-ssaf-linker{{(\.exe)?}} [subcommand] [options] 
<input files>
+// CHECK-EMPTY:
+// CHECK-NEXT: SUBCOMMANDS:
+// CHECK-EMPTY:
+// CHECK-NEXT:   static-library - Operations on StaticLibraries
+// CHECK-EMPTY:
+// CHECK-NEXT:   Type "clang-ssaf-linker{{(\.exe)?}} <subcommand> --help" to 
get more help on a specific subcommand
 // CHECK-EMPTY:
 // CHECK-NEXT: OPTIONS:
 // CHECK-NEXT:   -h                  - Alias for --help
@@ -18,4 +24,4 @@
 // CHECK-NEXT:   --print-options     - Print non-default options after command 
line parsing
 // CHECK-NEXT:   --time              - Enable timing
 // CHECK-NEXT:   --verbose           - Enable verbose output
-// CHECK-NEXT:   --version           - Display the version of this program
\ No newline at end of file
+// CHECK-NEXT:   --version           - Display the version of this program
diff --git 
a/clang/test/Analysis/Scalable/ssaf-linker/static-library-create.test 
b/clang/test/Analysis/Scalable/ssaf-linker/static-library-create.test
new file mode 100644
index 0000000000000..112d05ea50428
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-linker/static-library-create.test
@@ -0,0 +1,98 @@
+// Tests for `clang-ssaf-linker static-library create`.
+//
+// These tests exercise the CLI shape (subcommand dispatch, verb positional,
+// flag surface) and the happy / error paths of StaticLibrary construction.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+
+// ============================================================================
+// Happy path: single TU input
+// ============================================================================
+
+// The single-input case produces a StaticLibrary whose namespace name is
+// derived from the output file stem (`libempty`), whose target triple is
+// inferred from the input, and whose single member is the input TU.
+// RUN: clang-ssaf-linker static-library create %S/Inputs/tu-empty.json -o 
%t/libempty.json
+// RUN: diff %S/Outputs/libempty.json %t/libempty.json
+
+// ============================================================================
+// Happy path: multiple TU inputs
+// ============================================================================
+
+// Multiple inputs are bundled together; every member's target triple must
+// match, and every member's TUNamespace must be unique.
+// RUN: clang-ssaf-linker static-library create %S/Inputs/tu-1.json 
%S/Inputs/tu-2.json -o %t/libtwo.json
+// RUN: diff %S/Outputs/libtwo.json %t/libtwo.json
+
+// ============================================================================
+// Explicit --namespace overrides the output-stem default
+// ============================================================================
+
+// RUN: clang-ssaf-linker static-library create %S/Inputs/tu-empty.json 
--namespace my-static-lib -o %t/lib-namespace-override.json
+// RUN: diff %S/Outputs/lib-namespace-override.json 
%t/lib-namespace-override.json
+
+// ============================================================================
+// Explicit --target-triple matches inputs
+// ============================================================================
+
+// When explicit and matching, the flag is accepted and the produced
+// StaticLibrary is identical to the inferred-triple case.
+// RUN: clang-ssaf-linker static-library create %S/Inputs/tu-empty.json 
--target-triple arm64-apple-macosx -o %t/lib-explicit-triple.json
+// RUN: diff %S/Outputs/lib-explicit-triple.json %t/lib-explicit-triple.json
+
+// ============================================================================
+// Explicit --target-triple disagrees with input
+// ============================================================================
+
+// RUN: not clang-ssaf-linker static-library create %S/Inputs/tu-empty.json 
--target-triple x86_64-apple-macosx -o %t/lib-mismatch.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=EXPLICIT-MISMATCH
+// EXPLICIT-MISMATCH: target triple 'arm64-apple-macosx' from TU summary 
'{{.*}}tu-empty.json' does not match expected triple 'x86_64-apple-macosx'
+
+// ============================================================================
+// Duplicate TU namespaces across inputs
+// ============================================================================
+
+// Passing the same TU summary twice trips duplicate-member detection.
+// RUN: not clang-ssaf-linker static-library create %S/Inputs/tu-empty.json 
%S/Inputs/tu-empty.json -o %t/lib-dup.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=DUP-MEMBER
+// DUP-MEMBER: duplicate TU summary member with namespace 
BuildNamespace(CompilationUnit, empty.cpp)
+
+// ============================================================================
+// Empty input list
+// ============================================================================
+
+// The subcommand requires at least one positional input after the verb.
+// RUN: not clang-ssaf-linker static-library create -o 
%t/lib-empty-inputs.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=NO-INPUTS
+// NO-INPUTS: no input TU summaries: at least one input is required
+
+// ============================================================================
+// Unknown verb
+// ============================================================================
+
+// RUN: not clang-ssaf-linker static-library extract %S/Inputs/tu-empty.json 
-o %t/lib-unknown.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=UNKNOWN-VERB
+// UNKNOWN-VERB: unknown static-library verb 'extract': expected 'create'
+
+// ============================================================================
+// Round-trip: produced StaticLibrary parses cleanly with clang-ssaf-format
+// ============================================================================
+
+// The linker's output must be a valid StaticLibrary artifact that
+// clang-ssaf-format can read back and write out byte-identical (its own
+// reader-writer round-trip). This ties the linker to the same on-disk
+// invariants the format tool enforces.
+// RUN: clang-ssaf-linker static-library create %S/Inputs/tu-1.json 
%S/Inputs/tu-2.json -o %t/librt.json
+// RUN: clang-ssaf-format --type static-library %t/librt.json -o 
%t/librt-roundtrip.json
+// RUN: diff %t/librt.json %t/librt-roundtrip.json
+
+// ============================================================================
+// Backward-compat: no subcommand still runs `link`
+// ============================================================================
+
+// Existing invocations that don't name a subcommand fall back to the linker
+// pipeline unchanged.
+// RUN: mkdir -p %t/default-cmd
+// RUN: clang-ssaf-linker %S/Inputs/tu-empty.json -o 
%t/default-cmd/lu-empty.json
+// RUN: diff %S/Outputs/lu-empty.json %t/default-cmd/lu-empty.json
diff --git a/clang/test/Analysis/Scalable/ssaf-linker/static-library-time.test 
b/clang/test/Analysis/Scalable/ssaf-linker/static-library-time.test
new file mode 100644
index 0000000000000..fb0e7f0695a2b
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-linker/static-library-time.test
@@ -0,0 +1,19 @@
+// Test the --time flag on the `static-library create` subcommand.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+
+// RUN: clang-ssaf-linker static-library create --time \
+// RUN:     %S/Inputs/tu-1.json %S/Inputs/tu-2.json -o %t/libt.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines
+
+// CHECK:      
===-------------------------------------------------------------------------===
+// CHECK-NEXT: {{[ ]+}}SSAF Linker
+// CHECK-NEXT: 
===-------------------------------------------------------------------------===
+// CHECK-NEXT:   Total Execution Time: {{[0-9.]+}} seconds ({{[0-9.]+}} wall 
clock)
+// CHECK:      {{.*}}---Wall Time---{{.*}}
+// CHECK-DAG:  {{.*}}Validate Input
+// CHECK-DAG:  {{.*}}Read Summaries
+// CHECK-DAG:  {{.*}}Assemble StaticLibrary
+// CHECK-DAG:  {{.*}}Write StaticLibrary
+// CHECK:      {{.*}}Total
diff --git 
a/clang/test/Analysis/Scalable/ssaf-linker/static-library-verbose.test 
b/clang/test/Analysis/Scalable/ssaf-linker/static-library-verbose.test
new file mode 100644
index 0000000000000..42b4a1cdb7365
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-linker/static-library-verbose.test
@@ -0,0 +1,23 @@
+// Test the --verbose flag on the `static-library create` subcommand.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+
+// RUN: clang-ssaf-linker static-library create --verbose \
+// RUN:     %S/Inputs/tu-1.json %S/Inputs/tu-2.json -o %t/libv.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines
+
+// CHECK:      note: - Creating StaticLibrary started.
+// CHECK-NEXT: note:   - Validating input.
+// CHECK-NEXT: note:     - Validated output path '{{.*}}libv.json'.
+// CHECK-NEXT: note:     - Validated 2 input summary paths.
+// CHECK-NEXT: note:     - Namespace name: 'libv'.
+// CHECK-NEXT: note:   - Bundling StaticLibrary objects.
+// CHECK-NEXT: note:     - Bundling members.
+// CHECK-NEXT: note:       - [1/2] Reading '{{.*}}tu-1.json'.
+// CHECK-NEXT: note:       - [1/2] Assembling '{{.*}}tu-1.json'.
+// CHECK-NEXT: note:       - [2/2] Reading '{{.*}}tu-2.json'.
+// CHECK-NEXT: note:       - [2/2] Assembling '{{.*}}tu-2.json'.
+// CHECK-NEXT: note:     - Target triple: 'arm64-apple-macosx'.
+// CHECK-NEXT: note:     - Writing StaticLibrary to '{{.*}}libv.json'.
+// CHECK-NEXT: note: - Creating StaticLibrary finished.
diff --git a/clang/tools/clang-ssaf-linker/CMakeLists.txt 
b/clang/tools/clang-ssaf-linker/CMakeLists.txt
index a4d371d908373..c315a0b0faf32 100644
--- a/clang/tools/clang-ssaf-linker/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-linker/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_tool(clang-ssaf-linker
   SSAFLinker.cpp
+  StaticLibraryCreateCLI.cpp
   )
 
 clang_target_link_libraries(clang-ssaf-linker
diff --git a/clang/tools/clang-ssaf-linker/LinkerCommon.h 
b/clang/tools/clang-ssaf-linker/LinkerCommon.h
new file mode 100644
index 0000000000000..12993c71047df
--- /dev/null
+++ b/clang/tools/clang-ssaf-linker/LinkerCommon.h
@@ -0,0 +1,47 @@
+//===- LinkerCommon.h -------------------------------------------*- C++ 
-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+//  Shared cl::opt globals and diagnostic helpers used by every
+//  clang-ssaf-linker action's translation unit. The globals themselves
+//  are defined in SSAFLinker.cpp; this header only extern-declares them
+//  so per-action TUs can reference them without spelling out the
+//  boilerplate.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_LINKERCOMMON_H
+#define LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_LINKERCOMMON_H
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+#include <utility>
+
+// Shared cl::opt globals, defined in SSAFLinker.cpp.
+extern llvm::cl::OptionCategory SsafLinkerCategory;
+extern llvm::cl::SubCommand StaticLibraryCmd;
+extern llvm::cl::opt<bool> Verbose;
+extern llvm::cl::opt<bool> Time;
+
+// Indent width, in spaces, per level of nesting in verbose output.
+inline constexpr unsigned IndentationWidth = 2;
+
+/// Prints one indented note to stderr when `--verbose` is set. No-op
+/// otherwise.
+template <typename... Ts>
+void info(unsigned IndentationLevel, const char *Fmt, Ts &&...Args) {
+  if (Verbose) {
+    llvm::WithColor::note()
+        << std::string(IndentationLevel * IndentationWidth, ' ') << "- "
+        << llvm::formatv(Fmt, std::forward<Ts>(Args)...) << "\n";
+  }
+}
+
+#endif // LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_LINKERCOMMON_H
diff --git a/clang/tools/clang-ssaf-linker/SSAFLinker.cpp 
b/clang/tools/clang-ssaf-linker/SSAFLinker.cpp
index a8bf6c97a37b8..9afb2ccbf235e 100644
--- a/clang/tools/clang-ssaf-linker/SSAFLinker.cpp
+++ b/clang/tools/clang-ssaf-linker/SSAFLinker.cpp
@@ -6,11 +6,18 @@
 //
 
//===----------------------------------------------------------------------===//
 //
-//  This file implements the SSAF entity linker tool that performs entity
-//  linking across multiple TU summaries using the EntityLinker framework.
+//  This file implements the SSAF entity linker tool. Its default behavior
+//  is to link N TU summaries into one LU summary via the EntityLinker
+//  framework. It also provides the `static-library` subcommand for
+//  bundling TU summaries into a StaticLibrary (SSAF's analogue of
+//  `ar` / `libtool -static`) without performing entity resolution; the
+//  create action's logic lives in StaticLibraryCreateCLI.
 //
 
//===----------------------------------------------------------------------===//
 
+#include "LinkerCommon.h"
+#include "StaticLibraryCreateCLI.h"
+
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h"
 #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
 #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
@@ -19,13 +26,9 @@
 #include "clang/ScalableStaticAnalysis/Tool/Utils.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Timer.h"
-#include "llvm/Support/WithColor.h"
-#include "llvm/Support/raw_ostream.h"
 #include <memory>
 #include <string>
 
@@ -34,14 +37,28 @@ using namespace clang::ssaf;
 
 namespace path = llvm::sys::path;
 
-namespace {
-
 
//===----------------------------------------------------------------------===//
 // Command-Line Options
+//
+// These globals are visible to StaticLibraryCreateCLI.cpp via `extern`
+// declarations. Any addition or rename must be mirrored there.
 
//===----------------------------------------------------------------------===//
 
 cl::OptionCategory SsafLinkerCategory("clang-ssaf-linker options");
 
+// The `static-library` subcommand groups all StaticLibrary operations.
+// The first positional argument selects the verb (currently only
+// `create`) and the driver dispatches on it. Declared before the
+// options that reference it so those options can attach to its scope.
+
+cl::SubCommand StaticLibraryCmd("static-library",
+                                "Operations on StaticLibraries");
+
+namespace {
+
+// The `link` action is the default when no subcommand is named. It
+// parses against these top-level options.
+
 cl::list<std::string> InputPaths(cl::Positional, cl::desc("<input files>"),
                                  cl::OneOrMore, cl::cat(SsafLinkerCategory));
 
@@ -49,11 +66,43 @@ cl::opt<std::string> OutputPath("o", cl::desc("Output file 
path"),
                                 cl::value_desc("path"), cl::Required,
                                 cl::cat(SsafLinkerCategory));
 
+} // namespace
+
+// --verbose and --time apply to every subcommand: they're one conceptual
+// switch each for the whole tool. Attaching a single cl::opt to both the
+// top-level scope and every named subcommand lets a single global back
+// both `clang-ssaf-linker --verbose ...` and
+// `clang-ssaf-linker static-library create --verbose ...`.
+
 cl::opt<bool> Verbose("verbose", cl::desc("Enable verbose output"),
-                      cl::init(false), cl::cat(SsafLinkerCategory));
+                      cl::init(false), cl::cat(SsafLinkerCategory),
+                      cl::sub(cl::SubCommand::getTopLevel()),
+                      cl::sub(StaticLibraryCmd));
 
 cl::opt<bool> Time("time", cl::desc("Enable timing"), cl::init(false),
-                   cl::cat(SsafLinkerCategory));
+                   cl::cat(SsafLinkerCategory),
+                   cl::sub(cl::SubCommand::getTopLevel()),
+                   cl::sub(StaticLibraryCmd));
+
+namespace {
+
+// The verb positional under the `static-library` subcommand. Selecting
+// which action to run happens here; the actions themselves live in their
+// own translation units and read their own cl::opt globals.
+
+cl::opt<std::string> StaticLibraryVerb(cl::Positional, cl::Required,
+                                       cl::sub(StaticLibraryCmd),
+                                       cl::desc("<verb>"),
+                                       cl::value_desc("create"),
+                                       cl::cat(SsafLinkerCategory));
+
+//===----------------------------------------------------------------------===//
+// StaticLibrary Verbs
+//===----------------------------------------------------------------------===//
+
+// Verb strings for the `static-library` subcommand. Kept in sync with
+// UnknownStaticLibraryVerb below.
+constexpr const char *StaticLibraryCreateVerb = "create";
 
 
//===----------------------------------------------------------------------===//
 // Error Messages
@@ -63,25 +112,13 @@ namespace LocalErrorMessages {
 
 constexpr const char *LinkingSummary = "Linking summary '{0}'";
 
-} // namespace LocalErrorMessages
-
-//===----------------------------------------------------------------------===//
-// Diagnostic Utilities
-//===----------------------------------------------------------------------===//
-
-constexpr unsigned IndentationWidth = 2;
+constexpr const char *UnknownStaticLibraryVerb =
+    "unknown static-library verb '{0}': expected 'create'";
 
-template <typename... Ts>
-void info(unsigned IndentationLevel, const char *Fmt, Ts &&...Args) {
-  if (Verbose) {
-    llvm::WithColor::note()
-        << std::string(IndentationLevel * IndentationWidth, ' ') << "- "
-        << llvm::formatv(Fmt, std::forward<Ts>(Args)...) << "\n";
-  }
-}
+} // namespace LocalErrorMessages
 
 
//===----------------------------------------------------------------------===//
-// Data Structures
+// link action
 
//===----------------------------------------------------------------------===//
 
 struct LinkerInput {
@@ -90,11 +127,7 @@ struct LinkerInput {
   std::string LinkUnitName;
 };
 
-//===----------------------------------------------------------------------===//
-// Pipeline
-//===----------------------------------------------------------------------===//
-
-LinkerInput validate(llvm::TimerGroup &TG) {
+LinkerInput validateLinkInput(llvm::TimerGroup &TG) {
   llvm::Timer TValidate("validate", "Validate Input", TG);
   LinkerInput LI;
 
@@ -119,7 +152,16 @@ LinkerInput validate(llvm::TimerGroup &TG) {
   return LI;
 }
 
-void link(const LinkerInput &LI, llvm::TimerGroup &TG) {
+void runLink(llvm::TimerGroup &TG) {
+  info(0, "Linking started.");
+
+  LinkerInput LI;
+  {
+    info(1, "Validating input.");
+    LI = validateLinkInput(TG);
+  }
+
+  info(1, "Linking input.");
   info(2, "Constructing linker.");
 
   // TODO: The linker currently uses a hardcoded target triple. Architecture
@@ -178,6 +220,22 @@ void link(const LinkerInput &LI, llvm::TimerGroup &TG) {
       fail(std::move(Err));
     }
   }
+
+  info(0, "Linking finished.");
+}
+
+//===----------------------------------------------------------------------===//
+// static-library subcommand dispatch
+//===----------------------------------------------------------------------===//
+
+void runStaticLibrary(llvm::TimerGroup &TG) {
+  if (StaticLibraryVerb == StaticLibraryCreateVerb) {
+    StaticLibraryCreateCLI SLC;
+    SLC.run(TG);
+    return;
+  }
+  fail(LocalErrorMessages::UnknownStaticLibraryVerb,
+       StaticLibraryVerb.getValue());
 }
 
 } // namespace
@@ -192,23 +250,12 @@ int main(int argc, const char **argv) {
   InitLLVM X(argc, argv);
   initTool(argc, argv, "0.1", SsafLinkerCategory, ToolHeading);
 
-  llvm::TimerGroup LinkerTimers(getToolName(), ToolHeading);
-  LinkerInput LI;
-
-  {
-    info(0, "Linking started.");
-
-    {
-      info(1, "Validating input.");
-      LI = validate(LinkerTimers);
-    }
-
-    {
-      info(1, "Linking input.");
-      link(LI, LinkerTimers);
-    }
+  llvm::TimerGroup Timers(getToolName(), ToolHeading);
 
-    info(0, "Linking finished.");
+  if (StaticLibraryCmd) {
+    runStaticLibrary(Timers);
+  } else {
+    runLink(Timers);
   }
 
   return 0;
diff --git a/clang/tools/clang-ssaf-linker/StaticLibraryCreateCLI.cpp 
b/clang/tools/clang-ssaf-linker/StaticLibraryCreateCLI.cpp
new file mode 100644
index 0000000000000..86b4e15863760
--- /dev/null
+++ b/clang/tools/clang-ssaf-linker/StaticLibraryCreateCLI.cpp
@@ -0,0 +1,214 @@
+//===- StaticLibraryCreateCLI.cpp 
-----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+//  Implements the `static-library create` CLI action. Owns the
+//  subcommand's cl::opt globals and splits the work into an ordered
+//  set of phase methods (validate, bundle, write). The bundle phase
+//  streams inputs one at a time — read then insert — mirroring the
+//  `link` action's per-input read+link loop.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StaticLibraryCreateCLI.h"
+
+#include "LinkerCommon.h"
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
+#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h"
+#include "clang/ScalableStaticAnalysis/Core/Support/ErrorBuilder.h"
+#include "clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Path.h"
+#include <memory>
+
+using namespace llvm;
+using namespace clang::ssaf;
+
+namespace path = llvm::sys::path;
+
+//===----------------------------------------------------------------------===//
+// Command-Line Options
+//
+// The subcommand and the verb positional live in SSAFLinker.cpp because
+// they drive dispatch; every other option is owned here since it is
+// consumed only by this action. Shared globals come from LinkerCommon.h.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+cl::list<std::string> StaticLibraryInputs(cl::Positional,
+                                          cl::sub(StaticLibraryCmd),
+                                          cl::desc("<TU summary files>"),
+                                          cl::cat(SsafLinkerCategory));
+
+cl::opt<std::string> StaticLibraryOutput("o", cl::Required,
+                                         cl::sub(StaticLibraryCmd),
+                                         cl::desc("Output file path"),
+                                         cl::value_desc("path"),
+                                         cl::cat(SsafLinkerCategory));
+
+cl::opt<std::string> StaticLibraryNamespace(
+    "namespace", cl::sub(StaticLibraryCmd),
+    cl::desc("Namespace name for the StaticLibrary (defaults to output "
+             "file stem)"),
+    cl::value_desc("name"), cl::cat(SsafLinkerCategory));
+
+cl::opt<std::string> StaticLibraryTriple(
+    "target-triple", cl::sub(StaticLibraryCmd),
+    cl::desc("Target triple (defaults to inputs' triple; must match all "
+             "inputs when set)"),
+    cl::value_desc("triple"), cl::cat(SsafLinkerCategory));
+
+//===----------------------------------------------------------------------===//
+// Error Messages
+//===----------------------------------------------------------------------===//
+
+constexpr const char *ReadingSummary = "Reading TU summary '{0}'";
+
+constexpr const char *TargetTripleMismatch =
+    "target triple '{0}' from TU summary '{1}' does not match expected "
+    "triple '{2}'";
+
+constexpr const char *NoInputs =
+    "no input TU summaries: at least one input is required";
+
+constexpr const char *DuplicateMember =
+    "duplicate TU summary member with namespace {0}";
+
+//===----------------------------------------------------------------------===//
+// Helpers
+//===----------------------------------------------------------------------===//
+
+BuildNamespace makeStaticLibraryNamespace(llvm::StringRef Name) {
+  return BuildNamespace(BuildNamespaceKind::StaticLibrary, Name);
+}
+
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// StaticLibraryCreateCLI
+//===----------------------------------------------------------------------===//
+
+void StaticLibraryCreateCLI::run(llvm::TimerGroup &TG) {
+  info(0, "Creating StaticLibrary started.");
+
+  {
+    info(1, "Validating input.");
+    validate(TG);
+  }
+
+  info(1, "Bundling StaticLibrary objects.");
+  StaticLibrary Result = bundle(TG);
+  write(TG, Result);
+
+  info(0, "Creating StaticLibrary finished.");
+}
+
+void StaticLibraryCreateCLI::validate(llvm::TimerGroup &TG) {
+  llvm::Timer TValidate("validate", "Validate Input", TG);
+  llvm::TimeRegion _(Time ? &TValidate : nullptr);
+
+  OutputFile = FormatFile::fromOutputPath(StaticLibraryOutput);
+  info(2, "Validated output path '{0}'.", OutputFile.Path);
+
+  if (StaticLibraryInputs.empty()) {
+    fail(NoInputs);
+  }
+  for (const auto &InputPath : StaticLibraryInputs) {
+    InputFiles.push_back(FormatFile::fromInputPath(InputPath));
+  }
+  info(2, "Validated {0} input summary paths.", InputFiles.size());
+
+  NamespaceName = StaticLibraryNamespace.empty()
+                      ? path::stem(OutputFile.Path).str()
+                      : StaticLibraryNamespace;
+  info(2, "Namespace name: '{0}'.", NamespaceName);
+
+  if (!StaticLibraryTriple.empty()) {
+    ResolvedTriple = llvm::Triple(StaticLibraryTriple);
+  }
+}
+
+StaticLibrary StaticLibraryCreateCLI::bundle(llvm::TimerGroup &TG) {
+  llvm::Timer TRead("read", "Read Summaries", TG);
+  llvm::Timer TAssemble("assemble", "Assemble StaticLibrary", TG);
+
+  // If the target triple came from --target-triple, we can build the
+  // StaticLibrary upfront; otherwise its triple is the first member's
+  // and construction has to wait until we've read that member.
+  std::optional<StaticLibrary> Result;
+  if (ResolvedTriple) {
+    Result.emplace(*ResolvedTriple, makeStaticLibraryNamespace(NamespaceName));
+  }
+
+  info(2, "Bundling members.");
+
+  for (auto [Index, InputFile] : llvm::enumerate(InputFiles)) {
+    std::unique_ptr<TUSummaryEncoding> Member;
+
+    {
+      info(3, "[{0}/{1}] Reading '{2}'.", (Index + 1), InputFiles.size(),
+           InputFile.Path);
+      llvm::TimeRegion _(Time ? &TRead : nullptr);
+
+      auto ExpectedSummary =
+          InputFile.Format->readTUSummaryEncoding(InputFile.Path);
+      if (!ExpectedSummary) {
+        fail(ErrorBuilder::wrap(ExpectedSummary.takeError())
+                 .context(ReadingSummary, InputFile.Path)
+                 .build());
+      }
+      Member = 
std::make_unique<TUSummaryEncoding>(std::move(*ExpectedSummary));
+    }
+
+    // Resolve (or verify) the target triple and, if this is the first
+    // input under triple inference, construct the StaticLibrary now.
+    const llvm::Triple &MemberTriple = Member->getTargetTriple();
+    if (!Result) {
+      ResolvedTriple = MemberTriple;
+      Result.emplace(*ResolvedTriple,
+                     makeStaticLibraryNamespace(NamespaceName));
+    } else if (MemberTriple != Result->TargetTriple) {
+      fail(TargetTripleMismatch, llvm::Triple::normalize(MemberTriple.str()),
+           InputFile.Path, 
llvm::Triple::normalize(Result->TargetTriple.str()));
+    }
+
+    {
+      info(3, "[{0}/{1}] Assembling '{2}'.", (Index + 1), InputFiles.size(),
+           InputFile.Path);
+      llvm::TimeRegion _(Time ? &TAssemble : nullptr);
+
+      // StaticLibraryCreateCLI is a friend of StaticLibrary and
+      // TUSummaryEncoding: we can insert into the private members set
+      // and copy the member's private TUNamespace for the error
+      // message, all without exposing mutating API on those types.
+      auto MemberNamespace = Member->TUNamespace;
+      auto [It, Inserted] = Result->Members.insert(std::move(Member));
+      if (!Inserted) {
+        fail(DuplicateMember, MemberNamespace);
+      }
+    }
+  }
+
+  info(2, "Target triple: '{0}'.",
+       llvm::Triple::normalize(ResolvedTriple->str()));
+
+  return std::move(*Result);
+}
+
+void StaticLibraryCreateCLI::write(llvm::TimerGroup &TG,
+                                   const StaticLibrary &Result) {
+  info(2, "Writing StaticLibrary to '{0}'.", OutputFile.Path);
+
+  llvm::Timer TWrite("write", "Write StaticLibrary", TG);
+  llvm::TimeRegion _(Time ? &TWrite : nullptr);
+
+  if (auto Err =
+          OutputFile.Format->writeStaticLibrary(Result, OutputFile.Path)) {
+    fail(std::move(Err));
+  }
+}
diff --git a/clang/tools/clang-ssaf-linker/StaticLibraryCreateCLI.h 
b/clang/tools/clang-ssaf-linker/StaticLibraryCreateCLI.h
new file mode 100644
index 0000000000000..ddd9df87cb2bd
--- /dev/null
+++ b/clang/tools/clang-ssaf-linker/StaticLibraryCreateCLI.h
@@ -0,0 +1,77 @@
+//===- StaticLibraryCreateCLI.h ---------------------------------*- C++ 
-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+//  Declares the CLI action class for `clang-ssaf-linker static-library
+//  create`: validates inputs and output, streams TU summaries into a
+//  StaticLibrary one at a time (reading and inserting each in turn),
+//  and serializes the result.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_STATICLIBRARYCREATECLI_H
+#define LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_STATICLIBRARYCREATECLI_H
+
+#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
+#include "clang/ScalableStaticAnalysis/Tool/Utils.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/TargetParser/Triple.h"
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace clang::ssaf {
+
+/// Runs the `static-library create` action for `clang-ssaf-linker`.
+///
+/// The pipeline is split into phases so each is readable on its own.
+/// Non-recoverable errors call fail() from Tool/Utils.h and terminate
+/// the process.
+///
+/// The class is a friend of the StaticLibrary and TUSummaryEncoding
+/// model types so it can assemble a StaticLibrary in place (private
+/// member-set insertion plus reading each encoding's private
+/// TUNamespace for duplicate-member error reporting) without exposing
+/// mutating API on those types.
+class StaticLibraryCreateCLI {
+public:
+  /// Orchestrator: validate → bundle → write.
+  void run(llvm::TimerGroup &TG);
+
+private:
+  /// Validates the output path and every input path, and resolves the
+  /// namespace name (defaulting to the output file stem when
+  /// `--namespace` is unset) and the explicit target triple from
+  /// `--target-triple` (if any).
+  void validate(llvm::TimerGroup &TG);
+
+  /// Reads each validated input file and inserts it into a StaticLibrary
+  /// one at a time, mirroring the `link` action's per-input
+  /// read+link streaming pattern. Resolves the target triple from
+  /// `--target-triple` if set, else from the first input; every
+  /// subsequent input must match. Triple mismatches include the
+  /// offending input file's path in the error.
+  StaticLibrary bundle(llvm::TimerGroup &TG);
+
+  /// Serializes the StaticLibrary to the validated output path.
+  void write(llvm::TimerGroup &TG, const StaticLibrary &Result);
+
+  // State populated during validate() and consumed by later phases.
+  FormatFile OutputFile;
+  std::vector<FormatFile> InputFiles;
+  std::string NamespaceName;
+
+  // Set by validate() only when `--target-triple` was passed. bundle()
+  // uses it to construct the StaticLibrary up front and to verify every
+  // input's triple; when unset, bundle() populates it from the first
+  // input and every subsequent input must match.
+  std::optional<llvm::Triple> ResolvedTriple;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_STATICLIBRARYCREATECLI_H

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to