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