blob: 3148f79f55ff1cb9065a4b25430d04f5b8a328f4 [file] [log] [blame]
Chris Lattner59a9ebd2006-10-18 05:34:33 +00001//===--- HeaderSearch.cpp - Resolve Header File Locations ---===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file was developed by Chris Lattner and is distributed under
6// the University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the DirectoryLookup and HeaderSearch interfaces.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Basic/FileManager.h"
15#include "clang/Lex/HeaderSearch.h"
16#include "clang/Lex/IdentifierTable.h"
17#include "llvm/System/Path.h"
18#include <iostream>
19using namespace llvm;
20using namespace clang;
21
22void HeaderSearch::PrintStats() {
23 std::cerr << "\n*** HeaderSearch Stats:\n";
24 std::cerr << FileInfo.size() << " files tracked.\n";
25 unsigned NumOnceOnlyFiles = 0, MaxNumIncludes = 0, NumSingleIncludedFiles = 0;
26 for (unsigned i = 0, e = FileInfo.size(); i != e; ++i) {
27 NumOnceOnlyFiles += FileInfo[i].isImport;
28 if (MaxNumIncludes < FileInfo[i].NumIncludes)
29 MaxNumIncludes = FileInfo[i].NumIncludes;
30 NumSingleIncludedFiles += FileInfo[i].NumIncludes == 1;
31 }
32 std::cerr << " " << NumOnceOnlyFiles << " #import/#pragma once files.\n";
33 std::cerr << " " << NumSingleIncludedFiles << " included exactly once.\n";
34 std::cerr << " " << MaxNumIncludes << " max times a file is included.\n";
35
36 std::cerr << " " << NumIncluded << " #include/#include_next/#import.\n";
37 std::cerr << " " << NumMultiIncludeFileOptzn << " #includes skipped due to"
Chris Lattner63dd32b2006-10-20 04:42:40 +000038 << " the multi-include optimization.\n";
Chris Lattner59a9ebd2006-10-18 05:34:33 +000039
40}
41
42//===----------------------------------------------------------------------===//
43// Header File Location.
44//===----------------------------------------------------------------------===//
45
46static std::string DoFrameworkLookup(const DirectoryEntry *Dir,
47 const std::string &Filename) {
48 // TODO: caching.
49
50 // Framework names must have a '/' in the filename.
51 std::string::size_type SlashPos = Filename.find('/');
52 if (SlashPos == std::string::npos) return "";
53
54 // FrameworkName = "/System/Library/Frameworks/"
55 std::string FrameworkName = Dir->getName();
56 if (FrameworkName.empty() || FrameworkName[FrameworkName.size()-1] != '/')
57 FrameworkName += '/';
58
59 // FrameworkName = "/System/Library/Frameworks/Cocoa"
60 FrameworkName += std::string(Filename.begin(), Filename.begin()+SlashPos);
61
62 // FrameworkName = "/System/Library/Frameworks/Cocoa.framework/"
63 FrameworkName += ".framework/";
64
65 // If the dir doesn't exist, give up.
66 if (!sys::Path(FrameworkName).exists()) return "";
67
68 // Check "/System/Library/Frameworks/Cocoa.framework/Headers/file.h"
69 std::string HeadersFilename = FrameworkName + "Headers/" +
70 std::string(Filename.begin()+SlashPos+1, Filename.end());
Chris Lattner63dd32b2006-10-20 04:42:40 +000071 if (sys::Path(HeadersFilename).exists())
72 return HeadersFilename;
Chris Lattner59a9ebd2006-10-18 05:34:33 +000073
74 // Check "/System/Library/Frameworks/Cocoa.framework/PrivateHeaders/file.h"
75 std::string PrivateHeadersFilename = FrameworkName + "PrivateHeaders/" +
76 std::string(Filename.begin()+SlashPos+1, Filename.end());
Chris Lattner63dd32b2006-10-20 04:42:40 +000077 if (sys::Path(PrivateHeadersFilename).exists())
78 return PrivateHeadersFilename;
Chris Lattner59a9ebd2006-10-18 05:34:33 +000079
80 return "";
81}
82
83/// LookupFile - Given a "foo" or <foo> reference, look up the indicated file,
84/// return null on failure. isAngled indicates whether the file reference is
85/// for system #include's or not (i.e. using <> instead of ""). CurFileEnt, if
86/// non-null, indicates where the #including file is, in case a relative search
87/// is needed.
88const FileEntry *HeaderSearch::LookupFile(const std::string &Filename,
89 bool isAngled,
90 const DirectoryLookup *FromDir,
91 const DirectoryLookup *&CurDir,
92 const FileEntry *CurFileEnt) {
93 // If 'Filename' is absolute, check to see if it exists and no searching.
94 // FIXME: Portability. This should be a sys::Path interface, this doesn't
95 // handle things like C:\foo.txt right, nor win32 \\network\device\blah.
96 if (Filename[0] == '/') {
97 CurDir = 0;
98
99 // If this was an #include_next "/absolute/file", fail.
100 if (FromDir) return 0;
101
102 // Otherwise, just return the file.
103 return FileMgr.getFile(Filename);
104 }
105
106 // Step #0, unless disabled, check to see if the file is in the #includer's
107 // directory. This search is not done for <> headers.
Chris Lattner63dd32b2006-10-20 04:42:40 +0000108 if (CurFileEnt && !isAngled && !NoCurDirSearch) {
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000109 // Concatenate the requested file onto the directory.
110 // FIXME: Portability. Filename concatenation should be in sys::Path.
111 if (const FileEntry *FE =
112 FileMgr.getFile(CurFileEnt->getDir()->getName()+"/"+Filename)) {
113 // Leave CurDir unset.
114
115 // This file is a system header or C++ unfriendly if the old file is.
Chris Lattner63dd32b2006-10-20 04:42:40 +0000116 getFileInfo(FE).DirInfo = getFileInfo(CurFileEnt).DirInfo;
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000117 return FE;
118 }
119 }
120
121 CurDir = 0;
122
123 // If this is a system #include, ignore the user #include locs.
124 unsigned i = isAngled ? SystemDirIdx : 0;
125
126 // If this is a #include_next request, start searching after the directory the
127 // file was found in.
128 if (FromDir)
129 i = FromDir-&SearchDirs[0];
130
131 // Check each directory in sequence to see if it contains this file.
132 for (; i != SearchDirs.size(); ++i) {
133 // Concatenate the requested file onto the directory.
134 std::string SearchDir;
135
136 if (!SearchDirs[i].isFramework()) {
137 // FIXME: Portability. Adding file to dir should be in sys::Path.
138 SearchDir = SearchDirs[i].getDir()->getName()+"/"+Filename;
139 } else {
140 SearchDir = DoFrameworkLookup(SearchDirs[i].getDir(), Filename);
141 if (SearchDir.empty()) continue;
142 }
143
144 if (const FileEntry *FE = FileMgr.getFile(SearchDir)) {
145 CurDir = &SearchDirs[i];
146
147 // This file is a system header or C++ unfriendly if the dir is.
148 getFileInfo(FE).DirInfo = CurDir->getDirCharacteristic();
149 return FE;
150 }
151 }
152
153 // Otherwise, didn't find it.
154 return 0;
155}
156
Chris Lattner63dd32b2006-10-20 04:42:40 +0000157/// LookupSubframeworkHeader - Look up a subframework for the specified
158/// #include file. For example, if #include'ing <HIToolbox/HIToolbox.h> from
159/// within ".../Carbon.framework/Headers/Carbon.h", check to see if HIToolbox
160/// is a subframework within Carbon.framework. If so, return the FileEntry
161/// for the designated file, otherwise return null.
162const FileEntry *HeaderSearch::
163LookupSubframeworkHeader(const std::string &Filename,
164 const FileEntry *ContextFileEnt) {
165 // Framework names must have a '/' in the filename. Find it.
166 std::string::size_type SlashPos = Filename.find('/');
167 if (SlashPos == std::string::npos) return 0;
168
169 // TODO: Cache subframework.
170
171 // Look up the base framework name of the ContextFileEnt.
172 const std::string &ContextName = ContextFileEnt->getName();
173 std::string::size_type FrameworkPos = ContextName.find(".framework/");
174 // If the context info wasn't a framework, couldn't be a subframework.
175 if (FrameworkPos == std::string::npos)
176 return 0;
177
178 std::string FrameworkName(ContextName.begin(),
179 ContextName.begin()+FrameworkPos+strlen(".framework/"));
180 // Append Frameworks/HIToolbox.framework/
181 FrameworkName += "Frameworks/";
182 FrameworkName += std::string(Filename.begin(), Filename.begin()+SlashPos);
183 FrameworkName += ".framework/";
184
185 // Check ".../Frameworks/HIToolbox.framework/Headers/HIToolbox.h"
186 std::string HeadersFilename = FrameworkName + "Headers/" +
187 std::string(Filename.begin()+SlashPos+1, Filename.end());
188 if (!sys::Path(HeadersFilename).exists()) {
189
190 // Check ".../Frameworks/HIToolbox.framework/PrivateHeaders/HIToolbox.h"
191 std::string PrivateHeadersFilename = FrameworkName + "PrivateHeaders/" +
192 std::string(Filename.begin()+SlashPos+1, Filename.end());
193 if (!sys::Path(PrivateHeadersFilename).exists())
194 return 0;
195 HeadersFilename = PrivateHeadersFilename;
196 }
197
198
199 // Concatenate the requested file onto the directory.
200 if (const FileEntry *FE = FileMgr.getFile(HeadersFilename)) {
201 // This file is a system header or C++ unfriendly if the old file is.
202 getFileInfo(FE).DirInfo = getFileInfo(ContextFileEnt).DirInfo;
203 return FE;
204 }
205
206 return 0;
207}
208
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000209//===----------------------------------------------------------------------===//
210// File Info Management.
211//===----------------------------------------------------------------------===//
212
213
214/// getFileInfo - Return the PerFileInfo structure for the specified
215/// FileEntry.
216HeaderSearch::PerFileInfo &HeaderSearch::getFileInfo(const FileEntry *FE) {
217 if (FE->getUID() >= FileInfo.size())
218 FileInfo.resize(FE->getUID()+1);
219 return FileInfo[FE->getUID()];
220}
221
222/// ShouldEnterIncludeFile - Mark the specified file as a target of of a
223/// #include, #include_next, or #import directive. Return false if #including
224/// the file will have no effect or true if we should include it.
225bool HeaderSearch::ShouldEnterIncludeFile(const FileEntry *File, bool isImport){
226 ++NumIncluded; // Count # of attempted #includes.
227
228 // Get information about this file.
229 PerFileInfo &FileInfo = getFileInfo(File);
230
231 // If this is a #import directive, check that we have not already imported
232 // this header.
233 if (isImport) {
234 // If this has already been imported, don't import it again.
235 FileInfo.isImport = true;
236
237 // Has this already been #import'ed or #include'd?
238 if (FileInfo.NumIncludes) return false;
239 } else {
240 // Otherwise, if this is a #include of a file that was previously #import'd
241 // or if this is the second #include of a #pragma once file, ignore it.
242 if (FileInfo.isImport)
243 return false;
244 }
245
246 // Next, check to see if the file is wrapped with #ifndef guards. If so, and
247 // if the macro that guards it is defined, we know the #include has no effect.
248 if (FileInfo.ControllingMacro && FileInfo.ControllingMacro->getMacroInfo()) {
249 ++NumMultiIncludeFileOptzn;
250 return false;
251 }
252
253 // Increment the number of times this file has been included.
254 ++FileInfo.NumIncludes;
255
256 return true;
257}
258
259