1 //===--- DependencyGraph.cpp - Generate dependency file -------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This code generates a header dependency graph in DOT format, for use
10 // with, e.g., GraphViz.
12 //===----------------------------------------------------------------------===//
14 #include "clang/Frontend/Utils.h"
15 #include "clang/Basic/FileManager.h"
16 #include "clang/Basic/SourceManager.h"
17 #include "clang/Frontend/FrontendDiagnostic.h"
18 #include "clang/Lex/PPCallbacks.h"
19 #include "clang/Lex/Preprocessor.h"
20 #include "llvm/ADT/SetVector.h"
21 #include "llvm/Support/GraphWriter.h"
22 #include "llvm/Support/raw_ostream.h"
24 using namespace clang
;
25 namespace DOT
= llvm::DOT
;
28 class DependencyGraphCallback
: public PPCallbacks
{
29 const Preprocessor
*PP
;
30 std::string OutputFile
;
32 llvm::SetVector
<FileEntryRef
> AllFiles
;
34 llvm::DenseMap
<FileEntryRef
, SmallVector
<FileEntryRef
, 2>>;
36 DependencyMap Dependencies
;
39 raw_ostream
&writeNodeReference(raw_ostream
&OS
,
40 const FileEntry
*Node
);
41 void OutputGraphFile();
44 DependencyGraphCallback(const Preprocessor
*_PP
, StringRef OutputFile
,
46 : PP(_PP
), OutputFile(OutputFile
.str()), SysRoot(SysRoot
.str()) { }
48 void InclusionDirective(SourceLocation HashLoc
, const Token
&IncludeTok
,
49 StringRef FileName
, bool IsAngled
,
50 CharSourceRange FilenameRange
,
51 Optional
<FileEntryRef
> File
, StringRef SearchPath
,
52 StringRef RelativePath
, const Module
*Imported
,
53 SrcMgr::CharacteristicKind FileType
) override
;
55 void EndOfMainFile() override
{
62 void clang::AttachDependencyGraphGen(Preprocessor
&PP
, StringRef OutputFile
,
64 PP
.addPPCallbacks(std::make_unique
<DependencyGraphCallback
>(&PP
, OutputFile
,
68 void DependencyGraphCallback::InclusionDirective(
69 SourceLocation HashLoc
,
70 const Token
&IncludeTok
,
73 CharSourceRange FilenameRange
,
74 Optional
<FileEntryRef
> File
,
76 StringRef RelativePath
,
77 const Module
*Imported
,
78 SrcMgr::CharacteristicKind FileType
) {
82 SourceManager
&SM
= PP
->getSourceManager();
83 Optional
<FileEntryRef
> FromFile
=
84 SM
.getFileEntryRefForID(SM
.getFileID(SM
.getExpansionLoc(HashLoc
)));
88 Dependencies
[*FromFile
].push_back(*File
);
90 AllFiles
.insert(*File
);
91 AllFiles
.insert(*FromFile
);
95 DependencyGraphCallback::writeNodeReference(raw_ostream
&OS
,
96 const FileEntry
*Node
) {
97 OS
<< "header_" << Node
->getUID();
101 void DependencyGraphCallback::OutputGraphFile() {
103 llvm::raw_fd_ostream
OS(OutputFile
, EC
, llvm::sys::fs::OF_TextWithCRLF
);
105 PP
->getDiagnostics().Report(diag::err_fe_error_opening
) << OutputFile
110 OS
<< "digraph \"dependencies\" {\n";
113 for (unsigned I
= 0, N
= AllFiles
.size(); I
!= N
; ++I
) {
114 // Write the node itself.
116 writeNodeReference(OS
, AllFiles
[I
]);
117 OS
<< " [ shape=\"box\", label=\"";
118 StringRef FileName
= AllFiles
[I
].getName();
119 if (FileName
.startswith(SysRoot
))
120 FileName
= FileName
.substr(SysRoot
.size());
122 OS
<< DOT::EscapeString(std::string(FileName
)) << "\"];\n";
126 for (DependencyMap::iterator F
= Dependencies
.begin(),
127 FEnd
= Dependencies
.end();
129 for (unsigned I
= 0, N
= F
->second
.size(); I
!= N
; ++I
) {
131 writeNodeReference(OS
, F
->first
);
133 writeNodeReference(OS
, F
->second
[I
]);