# \[RFC\] Running SSAF over whole projects: clang-ssaf-orchestrator-make and clang-ssaf-driver

**URL:** <https://discourse.llvm.org/t/rfc-running-ssaf-over-whole-projects-clang-ssaf-orchestrator-make-and-clang-ssaf-driver/92595>\
**Category:** Clang Frontend\
**Created:** [October 24, 2026, 2:43am UTC](https://discourse.llvm.org/t/rfc-running-ssaf-over-whole-projects-clang-ssaf-orchestrator-make-and-clang-ssaf-driver/92595 "2026-10-24T02:43:06Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![jkorous](https://avatars.discourse-cdn.com/v4/letter/j/47e85d/32.png) [@jkorous](https://discourse.llvm.org/u/jkorous)\
**Post date:** [October 24, 2026, 2:43am UTC](https://discourse.llvm.org/t/rfc-running-ssaf-over-whole-projects-clang-ssaf-orchestrator-make-and-clang-ssaf-driver/92595/1 "2026-10-24T02:43:06Z")

</div>

# [RFC] Running SSAF over whole projects: clang-ssaf-orchestrator-make and clang-ssaf-driver

## TL;DR

We’d like to add two command-line tools:

- `clang-ssaf-orchestrator-make` runs an SSAF workflow over a project built  
with make.
- `clang-ssaf-driver` is the user interface for SSAF analyses and  
transformations. The user picks a transformation, gives it the build  
command, and gets back the diagnostics or patch and a report respectively.

## The problem

SSAF ([RFC](https://discourse.llvm.org/t/rfc-scalable-static-analysis-framework/88678))  
consists of several components. Each of them works on a single translation  
unit or a single link unit:

| Step | Tool | Runs once per |
| --- | --- | --- |
| Summary extraction | `clang --ssaf-extract-summaries=` | compilation |
| Entity linking | `clang-ssaf-linker` | link |
| Whole-program analysis | `clang-ssaf-analyzer` | top-most link unit |
| Source-edit generation | `clang --ssaf-source-transformation=` | compilation, in a second build |
| Edit and report merging | `clang-ssaf-src-edit-merge`, `clang-ssaf-report-merge`, `clang-ssaf-conflict-merge` | link, in the second build |

To run them over a project you have to somehow follow its build graph. Each  
compilation needs its summary extracted, and each link needs the summaries of  
its inputs linked, just like the build links the object files. A  
transformation then needs a second pass over the whole thing, because source  
edits are generated from the analysis result, and we only have that once the  
whole program has been summarized.

## The proposal

`clang-ssaf-orchestrator-make` integrates SSAF with make. It runs the build  
and, for every step that produces a binary build artifact, runs the  
corresponding SSAF step:

```console
$ clang-ssaf-orchestrator-make \
    --extract-summaries=PointerFlow,TypeConstrainedPointers,UnsafeBufferUsage,VirtualMethod \
    --analysis=UnsafeBufferReachableAnalysisResult \
    -o ssaf-output -- make -j8

```

`clang-ssaf-driver` is meant for end users and hides the implementation  
details. A transformation author defines a transformation as a {summary  
extraction, analysis, transformation} triple and gives it a user-facing name.  
`cpp-bounded-buffers` is the first one. Analyses will work the same way,  
defined as a {summary extraction, analysis} pair with a name.

```console
$ clang-ssaf-driver --transformation=cpp-bounded-buffers -- make -j8

```

The relationship between the two tools is similar to the one between  
`cc1` and the clang driver.

## Implementation

### Make orchestrator

The orchestrator is based on toolchain interposition. The build runs as usual, and the  
orchestrator gets involved whenever the build invokes a compiler or a linker.

Before starting the build, the orchestrator creates a directory of shims and  
prepends it to `PATH`. The shims are symlinks named after tools like `clang++`  
or `ld`, so when the build invokes one of these tools, it actually runs the  
shim. Each shim first runs the real tool with the original arguments, and  
then the corresponding SSAF step:

- Compilation: the shim asks the build’s compiler driver (with `-###`) which  
frontend jobs the command consists of, and runs each of them again with the  
SSAF clang and the `--ssaf-*` options added. This way summary extraction  
sees exactly what the build compiled.
- Link: the shim records which binary artifacts were linked, and runs `clang-ssaf-linker` with the SSAF build artifacts related to the binary build artifacts. The final link, whose output isn’t  
consumed by anything else, is the top-most link unit, and `clang-ssaf-analyzer` runs  
on its summary.
- Transformation: the orchestrator runs the build a second time. This time the  
shims generate source edits for each compilation, and merge the edits and  
reports at each link.

Static libraries work the same way. An `ar` shim links the summaries of the  
archive members, and a link that uses the archive picks up that summary.

We plan to add shims for other toolchain components as necessary. For  
example, a shim for `lipo` on macOS, which combines binaries built for  
different architectures into one universal binary.

The orchestrator keeps track of which SSAF artifacts belong to which binary  
build artifact: the summary of an object file, the linked summary of a  
program, and in the second pass their source edits and reports. The orchestrator also  
records which artifacts are input to each link. This information is used to pass the right inputs to SSAF tools.

Longer term, we want a build-system integration layer, with one integration  
per build system, each running the same SSAF steps at the same points in the  
build. Interposition is not the right answer for every build system.  
For example, for Swift Build, we are integrating SSAF directly in the build system implementation.

[Swift Build Integration RFC](https://forums.swift.org/t/integrating-whole-program-analysis-with-swift-build/86166)

### Driver

The driver puts user-facing names to analysis and transformation workflow implementations: which summaries to  
extract, which analysis to run, and for transformations, which transformation  
to apply. It then runs the build-system integration for the given build command (currently only the make  
orchestrator) with that workflow and passes the integration’s own options.  
Once integrations for other build systems are available, the driver will use  
them the same way.

For transformation, the driver writes the merged source edits as a patch  
that can be applied with `patch`. It prints the transformation report to  
stderr in the same format as compiler diagnostics, and also writes it as  
SARIF. For analysis, only the diagnostics are emitted.

#### Scope

- POSIX only. Windows is not supported.
- A single build sub-tree. The build has to end in a single link, so a `make all`  
that builds several programs isn’t supported for now. In the future we plan  
to support more kinds of build steps that aggregate build artifacts, such as combining  
multi-architecture libraries.

Both tools are written in Python and only use the standard library.

## PRs

- `clang-ssaf-orchestrator-make`:  
[#232543](https://github.com/llvm/llvm-project/pull/232543)
- `clang-ssaf-driver`:  
[#232549](https://github.com/llvm/llvm-project/pull/232549)

## Questions

1. Is `clang/tools` the right place for these tools, next to  
`clang-ssaf-linker` and `clang-ssaf-analyzer`? Or should they go to  
`clang-tools-extra`, or somewhere else?
2. Is it OK for them to be written in Python? There’s a precedent in  
`scan-build-py`.
