Commit | Line | Data |
---|---|---|
bae7f79e ILT |
1 | // dirsearch.cc -- directory searching for gold |
2 | ||
3 | #include "gold.h" | |
4 | ||
5 | #include <cerrno> | |
c33feb2b | 6 | #include <cassert> |
bae7f79e ILT |
7 | #include <sys/types.h> |
8 | #include <dirent.h> | |
9 | ||
10 | #include "gold-threads.h" | |
11 | #include "dirsearch.h" | |
12 | ||
13 | namespace | |
14 | { | |
15 | ||
16 | // Read all the files in a directory. | |
17 | ||
18 | class Dir_cache | |
19 | { | |
20 | public: | |
21 | Dir_cache(const char* dirname) | |
22 | : dirname_(dirname), files_() | |
23 | { } | |
24 | ||
25 | // Read the files in the directory. | |
26 | void read_files(); | |
27 | ||
28 | // Return whether a file (a base name) is present in the directory. | |
29 | bool find(const std::string&) const; | |
30 | ||
31 | private: | |
32 | // We can not copy this class. | |
33 | Dir_cache(const Dir_cache&); | |
34 | Dir_cache& operator=(const Dir_cache&); | |
35 | ||
36 | const char* dirname_; | |
37 | Unordered_set<std::string> files_; | |
38 | }; | |
39 | ||
40 | void | |
41 | Dir_cache::read_files() | |
42 | { | |
43 | DIR* d = opendir(this->dirname_); | |
44 | if (d == NULL) | |
45 | { | |
46 | // We ignore directories which do not exist. | |
47 | if (errno == ENOENT) | |
48 | return; | |
49 | ||
50 | char *s = NULL; | |
51 | if (asprintf(&s, _("can not read directory %s"), this->dirname_) < 0) | |
52 | gold::gold_nomem(); | |
53 | gold::gold_fatal(s, true); | |
54 | } | |
55 | ||
56 | dirent* de; | |
57 | while ((de = readdir(d)) != NULL) | |
58 | this->files_.insert(std::string(de->d_name)); | |
59 | ||
60 | if (closedir(d) != 0) | |
61 | gold::gold_fatal("closedir failed", true); | |
62 | } | |
63 | ||
64 | bool | |
65 | Dir_cache::find(const std::string& basename) const | |
66 | { | |
67 | return this->files_.find(basename) != this->files_.end(); | |
68 | } | |
69 | ||
70 | // A mapping from directory names to caches. A lock permits | |
71 | // concurrent update. There is no lock for read operations--some | |
72 | // other mechanism must be used to prevent reads from conflicting with | |
73 | // writes. | |
74 | ||
75 | class Dir_caches | |
76 | { | |
77 | public: | |
78 | Dir_caches() | |
79 | : lock_(), caches_() | |
80 | { } | |
81 | ||
82 | ~Dir_caches(); | |
83 | ||
84 | // Add a cache for a directory. | |
85 | void add(const char*); | |
86 | ||
87 | // Look up a directory in the cache. This much be locked against | |
88 | // calls to Add. | |
89 | Dir_cache* lookup(const char*) const; | |
90 | ||
91 | private: | |
92 | // We can not copy this class. | |
93 | Dir_caches(const Dir_caches&); | |
94 | Dir_caches& operator=(const Dir_caches&); | |
95 | ||
96 | typedef Unordered_map<const char*, Dir_cache*> Cache_hash; | |
97 | ||
98 | gold::Lock lock_; | |
99 | Cache_hash caches_; | |
100 | }; | |
101 | ||
102 | Dir_caches::~Dir_caches() | |
103 | { | |
104 | for (Cache_hash::iterator p = this->caches_.begin(); | |
105 | p != this->caches_.end(); | |
106 | ++p) | |
107 | delete p->second; | |
108 | } | |
109 | ||
110 | void | |
111 | Dir_caches::add(const char* dirname) | |
112 | { | |
113 | { | |
114 | gold::Hold_lock hl(this->lock_); | |
115 | if (this->lookup(dirname) != NULL) | |
116 | return; | |
117 | } | |
118 | ||
119 | Dir_cache* cache = new Dir_cache(dirname); | |
120 | ||
121 | cache->read_files(); | |
122 | ||
123 | { | |
124 | gold::Hold_lock hl(this->lock_); | |
125 | ||
126 | std::pair<const char*, Dir_cache*> v(dirname, cache); | |
127 | std::pair<Cache_hash::iterator, bool> p = this->caches_.insert(v); | |
128 | assert(p.second); | |
129 | } | |
130 | } | |
131 | ||
132 | Dir_cache* | |
133 | Dir_caches::lookup(const char* dirname) const | |
134 | { | |
135 | Cache_hash::const_iterator p = this->caches_.find(dirname); | |
136 | if (p == this->caches_.end()) | |
137 | return NULL; | |
138 | return p->second; | |
139 | } | |
140 | ||
141 | // The caches. | |
142 | ||
143 | Dir_caches caches; | |
144 | ||
145 | // A Task to read the directory. | |
146 | ||
147 | class Dir_cache_task : public gold::Task | |
148 | { | |
149 | public: | |
150 | Dir_cache_task(const char* dir, gold::Task_token& token) | |
151 | : dir_(dir), token_(token) | |
152 | { } | |
153 | ||
154 | Is_runnable_type is_runnable(gold::Workqueue*); | |
155 | ||
156 | gold::Task_locker* locks(gold::Workqueue*); | |
157 | ||
158 | void run(gold::Workqueue*); | |
159 | ||
160 | private: | |
161 | const char* dir_; | |
162 | gold::Task_token& token_; | |
163 | }; | |
164 | ||
165 | // We can always run the task to read the directory. | |
166 | ||
167 | gold::Task::Is_runnable_type | |
168 | Dir_cache_task::is_runnable(gold::Workqueue*) | |
169 | { | |
170 | return IS_RUNNABLE; | |
171 | } | |
172 | ||
173 | // Return the locks to hold. We use a blocker lock to prevent file | |
174 | // lookups from starting until the directory contents have been read. | |
175 | ||
176 | gold::Task_locker* | |
177 | Dir_cache_task::locks(gold::Workqueue* workqueue) | |
178 | { | |
179 | return new gold::Task_locker_block(this->token_, workqueue); | |
180 | } | |
181 | ||
182 | // Run the task--read the directory contents. | |
183 | ||
184 | void | |
185 | Dir_cache_task::run(gold::Workqueue*) | |
186 | { | |
187 | caches.add(this->dir_); | |
188 | } | |
189 | ||
190 | } | |
191 | ||
192 | namespace gold | |
193 | { | |
194 | ||
195 | Dirsearch::Dirsearch() | |
196 | : directories_(), token_() | |
197 | { | |
198 | } | |
199 | ||
200 | void | |
201 | Dirsearch::add(Workqueue* workqueue, const char* d) | |
202 | { | |
203 | this->directories_.push_back(d); | |
204 | this->token_.add_blocker(); | |
205 | workqueue->queue(new Dir_cache_task(d, this->token_)); | |
206 | } | |
207 | ||
208 | void | |
209 | Dirsearch::add(Workqueue* workqueue, const General_options::Dir_list& list) | |
210 | { | |
211 | for (General_options::Dir_list::const_iterator p = list.begin(); | |
212 | p != list.end(); | |
213 | ++p) | |
214 | this->add(workqueue, *p); | |
215 | } | |
216 | ||
217 | std::string | |
218 | Dirsearch::find(const std::string& n1, const std::string& n2) const | |
219 | { | |
220 | assert(!this->token_.is_blocked()); | |
221 | ||
222 | for (std::list<const char*>::const_iterator p = this->directories_.begin(); | |
223 | p != this->directories_.end(); | |
224 | ++p) | |
225 | { | |
226 | Dir_cache* pdc = caches.lookup(*p); | |
227 | assert(pdc != NULL); | |
228 | if (pdc->find(n1)) | |
229 | return std::string(*p) + '/' + n1; | |
230 | if (!n2.empty() && pdc->find(n2)) | |
231 | return std::string(*p) + '/' + n2; | |
232 | } | |
233 | ||
234 | return std::string(); | |
235 | } | |
236 | ||
237 | } // End namespace gold. |