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
*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
, const Token
&IncludeTok
, StringRef FileName
,
70 bool IsAngled
, CharSourceRange FilenameRange
, OptionalFileEntryRef File
,
71 StringRef SearchPath
, StringRef RelativePath
, const Module
*Imported
,
72 SrcMgr::CharacteristicKind FileType
) {
76 SourceManager
&SM
= PP
->getSourceManager();
77 OptionalFileEntryRef FromFile
=
78 SM
.getFileEntryRefForID(SM
.getFileID(SM
.getExpansionLoc(HashLoc
)));
82 Dependencies
[*FromFile
].push_back(*File
);
84 AllFiles
.insert(*File
);
85 AllFiles
.insert(*FromFile
);
89 DependencyGraphCallback::writeNodeReference(raw_ostream
&OS
,
90 const FileEntry
*Node
) {
91 OS
<< "header_" << Node
->getUID();
95 void DependencyGraphCallback::OutputGraphFile() {
97 llvm::raw_fd_ostream
OS(OutputFile
, EC
, llvm::sys::fs::OF_TextWithCRLF
);
99 PP
->getDiagnostics().Report(diag::err_fe_error_opening
) << OutputFile
104 OS
<< "digraph \"dependencies\" {\n";
107 for (unsigned I
= 0, N
= AllFiles
.size(); I
!= N
; ++I
) {
108 // Write the node itself.
110 writeNodeReference(OS
, AllFiles
[I
]);
111 OS
<< " [ shape=\"box\", label=\"";
112 StringRef FileName
= AllFiles
[I
].getName();
113 if (FileName
.startswith(SysRoot
))
114 FileName
= FileName
.substr(SysRoot
.size());
116 OS
<< DOT::EscapeString(std::string(FileName
)) << "\"];\n";
120 for (DependencyMap::iterator F
= Dependencies
.begin(),
121 FEnd
= Dependencies
.end();
123 for (unsigned I
= 0, N
= F
->second
.size(); I
!= N
; ++I
) {
125 writeNodeReference(OS
, F
->first
);
127 writeNodeReference(OS
, F
->second
[I
]);