Skip to content

Commit 3ddbf65

Browse files
committed
[clang][modules-driver] Add dependency scan and dependency graph
This patch is part of a series to support driver-managed module builds. It adds support for the discovery of module dependencies from within the driver and for generation of the module dependency graph. The dependency scan and graph support both Clang modules and C++ named modules. The generated dependency graph can be output in Graphviz DOT format as a remark. RFC discussing linking the driver against additional libraries: https://discourse.llvm.org/t/rfc-driver-link-the-driver-against-clangdependencyscanning-clangast-clangfrontend-clangserialization-and-clanglex RFC for driver-managed module builds: https://discourse.llvm.org/t/rfc-modules-support-simple-c-20-modules-use-from-the-clang-driver-without-a-build-system
1 parent f1e56ac commit 3ddbf65

File tree

8 files changed

+1400
-1
lines changed

8 files changed

+1400
-1
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,18 @@ def remark_found_cxx20_module_usage : Remark<
587587
def remark_performing_driver_managed_module_build : Remark<
588588
"performing driver managed module build">,
589589
InGroup<ModulesDriver>;
590+
def err_failed_depdendency_scan : Error<
591+
"failed to perform dependency scan">;
592+
def remark_failed_dependency_scan_for_input : Remark<
593+
"dependency scan failed for source input '%0'">,
594+
InGroup<ModulesDriver>;
595+
def err_mod_graph_named_module_redefinition : Error<
596+
"duplicate definitions of C++20 named module '%0' in '%1' and '%2'">;
597+
def err_building_depdendency_graph : Error<
598+
"failed to construct the module dependency graph">;
599+
def remark_printing_module_graph : Remark<
600+
"printing module dependency graph">,
601+
InGroup<ModulesDriver>;
590602

591603
def warn_drv_delayed_template_parsing_after_cxx20 : Warning<
592604
"-fdelayed-template-parsing is deprecated after C++20">,
Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
//===- DependencyScanner.h - Module dependency discovery --------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// \file
10+
/// This file defines the module dependency graph and dependency-scanning
11+
/// functionality.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_CLANG_DRIVER_DEPENDENCYSCANNER_H
16+
#define LLVM_CLANG_DRIVER_DEPENDENCYSCANNER_H
17+
18+
#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
19+
#include "llvm/ADT/DirectedGraph.h"
20+
#include "llvm/Option/ArgList.h"
21+
#include "llvm/Support/Allocator.h"
22+
23+
namespace llvm::opt {
24+
class DerivedArgList;
25+
} // namespace llvm::opt
26+
27+
namespace clang {
28+
class DiagnosticsEngine;
29+
namespace driver {
30+
class Driver;
31+
} // namespace driver
32+
} // namespace clang
33+
34+
namespace clang::driver::dependencies {
35+
36+
using ClangModuleDeps = tooling::dependencies::ModuleDeps;
37+
using ClangModuleGraph = tooling::dependencies::ModuleDepsGraph;
38+
using tooling::dependencies::ModuleID;
39+
using tooling::dependencies::TranslationUnitDeps;
40+
41+
//===----------------------------------------------------------------------===//
42+
// Dependency Scan
43+
//===----------------------------------------------------------------------===//
44+
45+
/// Represents the dependency scanning result for a single source input.
46+
struct TranslationUnitScanResult {
47+
TranslationUnitScanResult(size_t JobIndex, std::string &&Filename,
48+
TranslationUnitDeps &&TUDeps)
49+
: JobIndex(JobIndex), Filename(std::move(Filename)),
50+
TUDeps(std::move(TUDeps)) {}
51+
/// Index of the job associated with this scan result, which the driver will
52+
/// later create.
53+
size_t JobIndex;
54+
55+
/// The source input for which the scan was run.
56+
std::string Filename;
57+
58+
/// The full dependencies and Clang module graph for this input.
59+
TranslationUnitDeps TUDeps;
60+
};
61+
62+
/// Computes module dependencies for the given driver command line.
63+
///
64+
/// \param ClangExecutable - The path to the main clang executable.
65+
/// \param Diags - The driver's diagnostics engine.
66+
/// \param Args - The driver's command line.
67+
///
68+
/// \returns A vector of scan results (one per scannable source input), or an
69+
/// error if any input fails to scan. The order of scan results is
70+
/// deterministic.
71+
llvm::Expected<llvm::SmallVector<TranslationUnitScanResult, 0>>
72+
scanDependencies(llvm::StringRef ClangExecutable,
73+
clang::DiagnosticsEngine &Diags,
74+
const llvm::opt::DerivedArgList &Args);
75+
76+
//===----------------------------------------------------------------------===//
77+
// Module Dependency Graph
78+
//===----------------------------------------------------------------------===//
79+
80+
class MDGNode;
81+
class MDGEdge;
82+
using MDGNodeBase = llvm::DGNode<MDGNode, MDGEdge>;
83+
using MDGEdgeBase = llvm::DGEdge<MDGNode, MDGEdge>;
84+
using ModuleDepGraphBase = llvm::DirectedGraph<MDGNode, MDGEdge>;
85+
86+
/// Abstract base class for all node kinds in the module dependency graph.
87+
class MDGNode : public MDGNodeBase {
88+
public:
89+
enum class NodeKind {
90+
Root,
91+
ClangModule,
92+
NamedCXXModule,
93+
NonModule,
94+
};
95+
96+
explicit MDGNode(NodeKind Kind) : Kind(Kind) {}
97+
98+
/// Returns this node's kind.
99+
NodeKind getKind() const { return Kind; }
100+
101+
/// Returns the list of Clang modules this module/translation unit directly
102+
/// depends on.
103+
virtual llvm::ArrayRef<ModuleID> getClangModuleDeps() const { return {}; }
104+
105+
/// Returns the list of C++20 named modules this translation unit directly
106+
/// depends on.
107+
virtual llvm::ArrayRef<std::string> getCXXNamedModuleDeps() const {
108+
return {};
109+
}
110+
111+
protected:
112+
virtual ~MDGNode() = 0;
113+
114+
private:
115+
const NodeKind Kind;
116+
};
117+
118+
/// Represents the root node of the module dependency graph.
119+
///
120+
/// The root node only serves as an entry point for graph traversal.
121+
/// It should have an edge to each node that would otherwise have no incoming
122+
/// edges, ensuring there is always a path from the root to any node in the
123+
/// graph.
124+
/// There should be exactly one such root node in a given graph.
125+
class RootMDGNode final : public MDGNode {
126+
public:
127+
RootMDGNode() : MDGNode(NodeKind::Root) {}
128+
129+
/// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
130+
static bool classof(const MDGNode *N) {
131+
return N->getKind() == NodeKind::Root;
132+
}
133+
};
134+
135+
/// Base class defining common functionality for nodes that represent a
136+
/// translation unit from a command line source input.
137+
class SourceInputBackedMDGNode : public MDGNode {
138+
protected:
139+
explicit SourceInputBackedMDGNode(
140+
const NodeKind Kind, const TranslationUnitScanResult &BackingScanResult)
141+
: MDGNode(Kind), BackingScanResult(&BackingScanResult) {}
142+
143+
/// The backing scan result, owned by the module dependency graph.
144+
const TranslationUnitScanResult *BackingScanResult;
145+
146+
public:
147+
/// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
148+
static bool classof(const MDGNode *N) {
149+
auto K = N->getKind();
150+
return K == NodeKind::NonModule || K == NodeKind::NamedCXXModule;
151+
}
152+
153+
/// Returns the path of this translation unit's main source file.
154+
llvm::StringRef getFilename() const { return BackingScanResult->Filename; }
155+
156+
/// Returns the index of the -cc1 driver job for this translation unit, which
157+
/// the driver will later create.
158+
size_t getJobIndex() const { return BackingScanResult->JobIndex; }
159+
160+
llvm::ArrayRef<std::string> getCXXNamedModuleDeps() const override {
161+
return BackingScanResult->TUDeps.NamedModuleDeps;
162+
}
163+
164+
llvm::ArrayRef<ModuleID> getClangModuleDeps() const override {
165+
return BackingScanResult->TUDeps.ClangModuleDeps;
166+
}
167+
};
168+
169+
/// Subclass of MDGNode representing a translation unit that does not provide
170+
/// any module.
171+
class NonModuleMDGNode final : public SourceInputBackedMDGNode {
172+
public:
173+
explicit NonModuleMDGNode(const TranslationUnitScanResult &BackingScanResult)
174+
: SourceInputBackedMDGNode(NodeKind::NonModule, BackingScanResult) {}
175+
176+
/// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
177+
static bool classof(const MDGNode *N) {
178+
return N->getKind() == NodeKind::NonModule;
179+
}
180+
};
181+
182+
/// Subclass of MDGNode representing a translation unit that provides a C++20
183+
/// named module.
184+
class NamedCXXModuleMDGNode final : public SourceInputBackedMDGNode {
185+
public:
186+
explicit NamedCXXModuleMDGNode(
187+
const TranslationUnitScanResult &BackingScanResult)
188+
: SourceInputBackedMDGNode(NodeKind::NamedCXXModule, BackingScanResult) {}
189+
190+
/// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
191+
static bool classof(const MDGNode *N) {
192+
return N->getKind() == NodeKind::NamedCXXModule;
193+
}
194+
195+
/// Returns the unique identifier for the named C++ module this translation
196+
/// unit exports.
197+
const ModuleID &getModuleID() const { return BackingScanResult->TUDeps.ID; }
198+
};
199+
200+
/// Subclass of MDGNode representing a Clang module unit.
201+
class ClangModuleMDGNode final : public MDGNode {
202+
public:
203+
explicit ClangModuleMDGNode(const ClangModuleDeps &BackingModuleDeps)
204+
: MDGNode(NodeKind::ClangModule), BackingModuleDeps(&BackingModuleDeps) {}
205+
206+
/// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc.
207+
static bool classof(const MDGNode *N) {
208+
return N->getKind() == NodeKind::ClangModule;
209+
}
210+
211+
/// Returns the unique identifier for this Clang module.
212+
const ModuleID &getModuleID() const { return BackingModuleDeps->ID; }
213+
214+
/// Returns the list of Clang modules this module unit directly depends on.
215+
llvm::ArrayRef<ModuleID> getClangModuleDeps() const override {
216+
return BackingModuleDeps->ClangModuleDeps;
217+
}
218+
219+
private:
220+
/// The backing scan result, owned by the module dependency graph.
221+
const ClangModuleDeps *BackingModuleDeps;
222+
};
223+
224+
/// Represents an import relation in the module dependency graph, directed from
225+
/// the imported module to the importer.
226+
class MDGEdge : public MDGEdgeBase {
227+
public:
228+
explicit MDGEdge(MDGNode &N) : MDGEdgeBase(N) {}
229+
MDGEdge() = delete;
230+
};
231+
232+
namespace detail {
233+
class ModuleDepGraphBuilder;
234+
} // namespace detail
235+
236+
/// A directed graph describing module import relationships.
237+
///
238+
/// The graph owns its nodes, edges, and the dependency scan results from which
239+
/// it was created.
240+
/// Non-root nodes provide a view into the backing scan results.
241+
class ModuleDepGraph : public ModuleDepGraphBase {
242+
public:
243+
explicit ModuleDepGraph(
244+
llvm::SmallVectorImpl<TranslationUnitScanResult> &&ScanResults)
245+
: BackingScanResults(std::move(ScanResults)) {}
246+
247+
MDGNode *getRoot() { return Root; }
248+
249+
const MDGNode *getRoot() const { return Root; }
250+
251+
private:
252+
friend class detail::ModuleDepGraphBuilder;
253+
254+
llvm::BumpPtrAllocator BumpPtrAlloc;
255+
llvm::SmallVector<TranslationUnitScanResult, 0> BackingScanResults;
256+
RootMDGNode *Root = nullptr;
257+
};
258+
259+
/// Build a module dependency graph from the given \c ScanResults.
260+
///
261+
/// \returns The constructed graph, or an error if conflicting module
262+
/// definitions are found.
263+
llvm::Expected<ModuleDepGraph>
264+
buildModuleDepGraph(llvm::SmallVectorImpl<TranslationUnitScanResult> &&Scans,
265+
DiagnosticsEngine &Diags);
266+
267+
/// Writes the module dependency graph to the given output stream.
268+
void writeModuleDepGraph(raw_ostream &OS, const ModuleDepGraph &G);
269+
270+
} // namespace clang::driver::dependencies
271+
272+
//===----------------------------------------------------------------------===//
273+
// Module Dependency Graph: GraphTraits specializations
274+
//===----------------------------------------------------------------------===//
275+
276+
namespace llvm {
277+
/// Non-const versions of the GraphTraits specializations for ModuleDepGraph.
278+
template <> struct GraphTraits<clang::driver::dependencies::MDGNode *> {
279+
using NodeTy = clang::driver::dependencies::MDGNode;
280+
using NodeRef = NodeTy *;
281+
282+
static NodeRef MDGGetTargetNode(clang::driver::dependencies::MDGEdgeBase *E) {
283+
return &E->getTargetNode();
284+
}
285+
286+
// Provide a mapped iterator so that GraphTraits-based implementations can
287+
// find the target nodes without explicitly going through the edges.
288+
using ChildIteratorType =
289+
mapped_iterator<NodeTy::iterator, decltype(&MDGGetTargetNode)>;
290+
using ChildEdgeIteratorType = NodeTy::iterator;
291+
292+
static NodeRef getEntryNode(NodeRef N) { return N; }
293+
294+
static ChildIteratorType child_begin(NodeRef N) {
295+
return ChildIteratorType(N->begin(), &MDGGetTargetNode);
296+
}
297+
298+
static ChildIteratorType child_end(NodeRef N) {
299+
return ChildIteratorType(N->end(), &MDGGetTargetNode);
300+
}
301+
302+
static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
303+
return N->begin();
304+
}
305+
static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }
306+
};
307+
308+
template <>
309+
struct GraphTraits<clang::driver::dependencies::ModuleDepGraph *>
310+
: GraphTraits<clang::driver::dependencies::MDGNode *> {
311+
using GraphTy = clang::driver::dependencies::ModuleDepGraph;
312+
using GraphRef = GraphTy *;
313+
using NodeRef = clang::driver::dependencies::MDGNode *;
314+
315+
using nodes_iterator = GraphTy::iterator;
316+
317+
static NodeRef getEntryNode(GraphRef G) { return G->getRoot(); }
318+
319+
static nodes_iterator nodes_begin(GraphRef G) { return G->begin(); }
320+
321+
static nodes_iterator nodes_end(GraphRef G) { return G->end(); }
322+
};
323+
324+
/// Const versions of the GraphTraits specializations for ModuleDepGraph.
325+
template <> struct GraphTraits<const clang::driver::dependencies::MDGNode *> {
326+
using NodeTy = const clang::driver::dependencies::MDGNode;
327+
using NodeRef = NodeTy *;
328+
329+
static NodeRef
330+
MDGGetTargetNode(const clang::driver::dependencies::MDGEdgeBase *E) {
331+
return &E->getTargetNode();
332+
}
333+
334+
// Provide a mapped iterator so that GraphTraits-based implementations can
335+
// find the target nodes without explicitly going through the edges.
336+
using ChildIteratorType =
337+
mapped_iterator<NodeTy::const_iterator, decltype(&MDGGetTargetNode)>;
338+
using ChildEdgeIteratorType = NodeTy::const_iterator;
339+
340+
static NodeRef getEntryNode(NodeRef N) { return N; }
341+
342+
static ChildIteratorType child_begin(NodeRef N) {
343+
return ChildIteratorType(N->begin(), &MDGGetTargetNode);
344+
}
345+
346+
static ChildIteratorType child_end(NodeRef N) {
347+
return ChildIteratorType(N->end(), &MDGGetTargetNode);
348+
}
349+
350+
static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
351+
return N->begin();
352+
}
353+
354+
static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); }
355+
};
356+
357+
template <>
358+
struct GraphTraits<const clang::driver::dependencies::ModuleDepGraph *>
359+
: GraphTraits<const clang::driver::dependencies::MDGNode *> {
360+
using GraphTy = const clang::driver::dependencies::ModuleDepGraph;
361+
using GraphRef = GraphTy *;
362+
using NodeRef = const clang::driver::dependencies::MDGNode *;
363+
364+
using nodes_iterator = GraphTy::const_iterator;
365+
366+
static NodeRef getEntryNode(GraphRef G) { return G->getRoot(); }
367+
368+
static nodes_iterator nodes_begin(GraphRef G) { return G->begin(); }
369+
370+
static nodes_iterator nodes_end(GraphRef G) { return G->end(); }
371+
};
372+
} // namespace llvm
373+
374+
#endif

0 commit comments

Comments
 (0)