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 OptionalFileEntryRef File
, StringRef SearchPath
,
52 StringRef RelativePath
, const Module
*SuggestedModule
,
54 SrcMgr::CharacteristicKind FileType
) override
;
56 void EmbedDirective(SourceLocation HashLoc
, StringRef FileName
, bool IsAngled
,
57 OptionalFileEntryRef File
,
58 const LexEmbedParametersResult
&Params
) override
;
60 void EndOfMainFile() override
{
67 void clang::AttachDependencyGraphGen(Preprocessor
&PP
, StringRef OutputFile
,
69 PP
.addPPCallbacks(std::make_unique
<DependencyGraphCallback
>(&PP
, OutputFile
,
73 void DependencyGraphCallback::InclusionDirective(
74 SourceLocation HashLoc
, const Token
&IncludeTok
, StringRef FileName
,
75 bool IsAngled
, CharSourceRange FilenameRange
, OptionalFileEntryRef File
,
76 StringRef SearchPath
, StringRef RelativePath
, const Module
*SuggestedModule
,
77 bool ModuleImported
, SrcMgr::CharacteristicKind FileType
) {
81 SourceManager
&SM
= PP
->getSourceManager();
82 OptionalFileEntryRef FromFile
=
83 SM
.getFileEntryRefForID(SM
.getFileID(SM
.getExpansionLoc(HashLoc
)));
87 Dependencies
[*FromFile
].push_back(*File
);
89 AllFiles
.insert(*File
);
90 AllFiles
.insert(*FromFile
);
93 void DependencyGraphCallback::EmbedDirective(SourceLocation HashLoc
, StringRef
,
94 bool, OptionalFileEntryRef File
,
95 const LexEmbedParametersResult
&) {
99 SourceManager
&SM
= PP
->getSourceManager();
100 OptionalFileEntryRef FromFile
=
101 SM
.getFileEntryRefForID(SM
.getFileID(SM
.getExpansionLoc(HashLoc
)));
105 Dependencies
[*FromFile
].push_back(*File
);
107 AllFiles
.insert(*File
);
108 AllFiles
.insert(*FromFile
);
112 DependencyGraphCallback::writeNodeReference(raw_ostream
&OS
,
113 const FileEntry
*Node
) {
114 OS
<< "header_" << Node
->getUID();
118 void DependencyGraphCallback::OutputGraphFile() {
120 llvm::raw_fd_ostream
OS(OutputFile
, EC
, llvm::sys::fs::OF_TextWithCRLF
);
122 PP
->getDiagnostics().Report(diag::err_fe_error_opening
) << OutputFile
127 OS
<< "digraph \"dependencies\" {\n";
130 for (unsigned I
= 0, N
= AllFiles
.size(); I
!= N
; ++I
) {
131 // Write the node itself.
133 writeNodeReference(OS
, AllFiles
[I
]);
134 OS
<< " [ shape=\"box\", label=\"";
135 StringRef FileName
= AllFiles
[I
].getName();
136 FileName
.consume_front(SysRoot
);
138 OS
<< DOT::EscapeString(std::string(FileName
)) << "\"];\n";
142 for (DependencyMap::iterator F
= Dependencies
.begin(),
143 FEnd
= Dependencies
.end();
145 for (unsigned I
= 0, N
= F
->second
.size(); I
!= N
; ++I
) {
147 writeNodeReference(OS
, F
->first
);
149 writeNodeReference(OS
, F
->second
[I
]);