blob: 5d765407dfc168fe4297661b6a615feb9fdfb798 [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
Chris Lattner577377e2006-10-20 04:55:45 +000046static const FileEntry *DoFrameworkLookup(const DirectoryEntry *Dir,
47 const std::string &Filename,
48 FileManager &FM) {
Chris Lattner59a9ebd2006-10-18 05:34:33 +000049 // TODO: caching.
50
51 // Framework names must have a '/' in the filename.
52 std::string::size_type SlashPos = Filename.find('/');
Chris Lattner577377e2006-10-20 04:55:45 +000053 if (SlashPos == std::string::npos) return 0;
Chris Lattner59a9ebd2006-10-18 05:34:33 +000054
55 // FrameworkName = "/System/Library/Frameworks/"
56 std::string FrameworkName = Dir->getName();
57 if (FrameworkName.empty() || FrameworkName[FrameworkName.size()-1] != '/')
58 FrameworkName += '/';
59
60 // FrameworkName = "/System/Library/Frameworks/Cocoa"
61 FrameworkName += std::string(Filename.begin(), Filename.begin()+SlashPos);
62
63 // FrameworkName = "/System/Library/Frameworks/Cocoa.framework/"
64 FrameworkName += ".framework/";
65
Chris Lattner59a9ebd2006-10-18 05:34:33 +000066 // Check "/System/Library/Frameworks/Cocoa.framework/Headers/file.h"
67 std::string HeadersFilename = FrameworkName + "Headers/" +
68 std::string(Filename.begin()+SlashPos+1, Filename.end());
Chris Lattner577377e2006-10-20 04:55:45 +000069 if (const FileEntry *FE = FM.getFile(HeadersFilename))
70 return FE;
Chris Lattner59a9ebd2006-10-18 05:34:33 +000071
72 // Check "/System/Library/Frameworks/Cocoa.framework/PrivateHeaders/file.h"
73 std::string PrivateHeadersFilename = FrameworkName + "PrivateHeaders/" +
74 std::string(Filename.begin()+SlashPos+1, Filename.end());
Chris Lattner577377e2006-10-20 04:55:45 +000075 return FM.getFile(PrivateHeadersFilename);
Chris Lattner59a9ebd2006-10-18 05:34:33 +000076}
77
78/// LookupFile - Given a "foo" or <foo> reference, look up the indicated file,
79/// return null on failure. isAngled indicates whether the file reference is
80/// for system #include's or not (i.e. using <> instead of ""). CurFileEnt, if
81/// non-null, indicates where the #including file is, in case a relative search
82/// is needed.
83const FileEntry *HeaderSearch::LookupFile(const std::string &Filename,
84 bool isAngled,
85 const DirectoryLookup *FromDir,
86 const DirectoryLookup *&CurDir,
87 const FileEntry *CurFileEnt) {
88 // If 'Filename' is absolute, check to see if it exists and no searching.
89 // FIXME: Portability. This should be a sys::Path interface, this doesn't
90 // handle things like C:\foo.txt right, nor win32 \\network\device\blah.
91 if (Filename[0] == '/') {
92 CurDir = 0;
93
94 // If this was an #include_next "/absolute/file", fail.
95 if (FromDir) return 0;
96
97 // Otherwise, just return the file.
98 return FileMgr.getFile(Filename);
99 }
100
101 // Step #0, unless disabled, check to see if the file is in the #includer's
102 // directory. This search is not done for <> headers.
Chris Lattner63dd32b2006-10-20 04:42:40 +0000103 if (CurFileEnt && !isAngled && !NoCurDirSearch) {
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000104 // Concatenate the requested file onto the directory.
105 // FIXME: Portability. Filename concatenation should be in sys::Path.
106 if (const FileEntry *FE =
107 FileMgr.getFile(CurFileEnt->getDir()->getName()+"/"+Filename)) {
108 // Leave CurDir unset.
109
110 // This file is a system header or C++ unfriendly if the old file is.
Chris Lattner63dd32b2006-10-20 04:42:40 +0000111 getFileInfo(FE).DirInfo = getFileInfo(CurFileEnt).DirInfo;
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000112 return FE;
113 }
114 }
115
116 CurDir = 0;
117
118 // If this is a system #include, ignore the user #include locs.
119 unsigned i = isAngled ? SystemDirIdx : 0;
120
121 // If this is a #include_next request, start searching after the directory the
122 // file was found in.
123 if (FromDir)
124 i = FromDir-&SearchDirs[0];
125
126 // Check each directory in sequence to see if it contains this file.
127 for (; i != SearchDirs.size(); ++i) {
128 // Concatenate the requested file onto the directory.
129 std::string SearchDir;
130
Chris Lattner577377e2006-10-20 04:55:45 +0000131 const FileEntry *FE = 0;
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000132 if (!SearchDirs[i].isFramework()) {
133 // FIXME: Portability. Adding file to dir should be in sys::Path.
Chris Lattner577377e2006-10-20 04:55:45 +0000134 std::string SearchDir = SearchDirs[i].getDir()->getName()+"/"+Filename;
135 FE = FileMgr.getFile(SearchDir);
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000136 } else {
Chris Lattner577377e2006-10-20 04:55:45 +0000137 FE = DoFrameworkLookup(SearchDirs[i].getDir(), Filename, FileMgr);
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000138 }
139
Chris Lattner577377e2006-10-20 04:55:45 +0000140 if (FE) {
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000141 CurDir = &SearchDirs[i];
142
143 // This file is a system header or C++ unfriendly if the dir is.
144 getFileInfo(FE).DirInfo = CurDir->getDirCharacteristic();
145 return FE;
146 }
147 }
148
149 // Otherwise, didn't find it.
150 return 0;
151}
152
Chris Lattner63dd32b2006-10-20 04:42:40 +0000153/// LookupSubframeworkHeader - Look up a subframework for the specified
154/// #include file. For example, if #include'ing <HIToolbox/HIToolbox.h> from
155/// within ".../Carbon.framework/Headers/Carbon.h", check to see if HIToolbox
156/// is a subframework within Carbon.framework. If so, return the FileEntry
157/// for the designated file, otherwise return null.
158const FileEntry *HeaderSearch::
159LookupSubframeworkHeader(const std::string &Filename,
160 const FileEntry *ContextFileEnt) {
161 // Framework names must have a '/' in the filename. Find it.
162 std::string::size_type SlashPos = Filename.find('/');
163 if (SlashPos == std::string::npos) return 0;
164
165 // TODO: Cache subframework.
166
167 // Look up the base framework name of the ContextFileEnt.
168 const std::string &ContextName = ContextFileEnt->getName();
169 std::string::size_type FrameworkPos = ContextName.find(".framework/");
170 // If the context info wasn't a framework, couldn't be a subframework.
171 if (FrameworkPos == std::string::npos)
172 return 0;
173
174 std::string FrameworkName(ContextName.begin(),
175 ContextName.begin()+FrameworkPos+strlen(".framework/"));
176 // Append Frameworks/HIToolbox.framework/
177 FrameworkName += "Frameworks/";
178 FrameworkName += std::string(Filename.begin(), Filename.begin()+SlashPos);
179 FrameworkName += ".framework/";
Chris Lattner577377e2006-10-20 04:55:45 +0000180
181 const FileEntry *FE = 0;
182
Chris Lattner63dd32b2006-10-20 04:42:40 +0000183 // Check ".../Frameworks/HIToolbox.framework/Headers/HIToolbox.h"
184 std::string HeadersFilename = FrameworkName + "Headers/" +
185 std::string(Filename.begin()+SlashPos+1, Filename.end());
Chris Lattner577377e2006-10-20 04:55:45 +0000186 if (!(FE = FileMgr.getFile(HeadersFilename))) {
Chris Lattner63dd32b2006-10-20 04:42:40 +0000187
188 // Check ".../Frameworks/HIToolbox.framework/PrivateHeaders/HIToolbox.h"
189 std::string PrivateHeadersFilename = FrameworkName + "PrivateHeaders/" +
190 std::string(Filename.begin()+SlashPos+1, Filename.end());
Chris Lattner577377e2006-10-20 04:55:45 +0000191 if (!(FE = FileMgr.getFile(PrivateHeadersFilename)))
Chris Lattner63dd32b2006-10-20 04:42:40 +0000192 return 0;
Chris Lattner63dd32b2006-10-20 04:42:40 +0000193 }
194
Chris Lattner577377e2006-10-20 04:55:45 +0000195 // This file is a system header or C++ unfriendly if the old file is.
196 getFileInfo(FE).DirInfo = getFileInfo(ContextFileEnt).DirInfo;
197 return FE;
Chris Lattner63dd32b2006-10-20 04:42:40 +0000198}
199
Chris Lattner59a9ebd2006-10-18 05:34:33 +0000200//===----------------------------------------------------------------------===//
201// File Info Management.
202//===----------------------------------------------------------------------===//
203
204
205/// getFileInfo - Return the PerFileInfo structure for the specified
206/// FileEntry.
207HeaderSearch::PerFileInfo &HeaderSearch::getFileInfo(const FileEntry *FE) {
208 if (FE->getUID() >= FileInfo.size())
209 FileInfo.resize(FE->getUID()+1);
210 return FileInfo[FE->getUID()];
211}
212
213/// ShouldEnterIncludeFile - Mark the specified file as a target of of a
214/// #include, #include_next, or #import directive. Return false if #including
215/// the file will have no effect or true if we should include it.
216bool HeaderSearch::ShouldEnterIncludeFile(const FileEntry *File, bool isImport){
217 ++NumIncluded; // Count # of attempted #includes.
218
219 // Get information about this file.
220 PerFileInfo &FileInfo = getFileInfo(File);
221
222 // If this is a #import directive, check that we have not already imported
223 // this header.
224 if (isImport) {
225 // If this has already been imported, don't import it again.
226 FileInfo.isImport = true;
227
228 // Has this already been #import'ed or #include'd?
229 if (FileInfo.NumIncludes) return false;
230 } else {
231 // Otherwise, if this is a #include of a file that was previously #import'd
232 // or if this is the second #include of a #pragma once file, ignore it.
233 if (FileInfo.isImport)
234 return false;
235 }
236
237 // Next, check to see if the file is wrapped with #ifndef guards. If so, and
238 // if the macro that guards it is defined, we know the #include has no effect.
239 if (FileInfo.ControllingMacro && FileInfo.ControllingMacro->getMacroInfo()) {
240 ++NumMultiIncludeFileOptzn;
241 return false;
242 }
243
244 // Increment the number of times this file has been included.
245 ++FileInfo.NumIncludes;
246
247 return true;
248}
249
250