422f4cde194b73aeb94000ef27e452184654d1c7
[reactos.git] / reactos / tools / rbuild / automaticdependency.cpp
1 #include "pch.h"
2 #include <assert.h>
3
4 #include "rbuild.h"
5
6 /* Read at most this amount of bytes from each file and assume that all #includes are located within this block */
7 #define MAX_BYTES_TO_READ 4096
8
9 using std::string;
10 using std::vector;
11 using std::map;
12
13 SourceFile::SourceFile ( AutomaticDependency* automaticDependency,
14 Module& module,
15 const string& filename,
16 SourceFile* parent,
17 bool isNonAutomaticDependency )
18 : automaticDependency ( automaticDependency ),
19 module ( module ),
20 filename ( filename ),
21 isNonAutomaticDependency ( isNonAutomaticDependency ),
22 youngestLastWriteTime ( 0 )
23 {
24 if ( parent != NULL )
25 parents.push_back ( parent );
26 GetDirectoryAndFilenameParts ();
27 }
28
29 void
30 SourceFile::GetDirectoryAndFilenameParts ()
31 {
32 size_t index = filename.find_last_of ( CSEP );
33 if ( index != string::npos )
34 {
35 directoryPart = filename.substr ( 0, index );
36 filenamePart = filename.substr ( index + 1, filename.length () - index );
37 }
38 else
39 {
40 directoryPart = "";
41 filenamePart = filename;
42 }
43 }
44
45 void
46 SourceFile::Close ()
47 {
48 buf.resize ( 0 );
49 p = end = NULL;
50 }
51
52 void
53 SourceFile::Open ()
54 {
55 struct stat statbuf;
56
57 Close ();
58 FILE* f = fopen ( filename.c_str (), "rb" );
59 if ( !f )
60 throw FileNotFoundException ( filename );
61
62 if ( fstat ( fileno ( f ), &statbuf ) != 0 )
63 {
64 fclose ( f );
65 throw AccessDeniedException ( filename );
66 }
67 lastWriteTime = statbuf.st_mtime;
68
69 unsigned long len = (unsigned long) filelen ( f );
70 if ( len > MAX_BYTES_TO_READ )
71 len = MAX_BYTES_TO_READ;
72 buf.resize ( len );
73 fread ( &buf[0], 1, len, f );
74 fclose ( f );
75 p = buf.c_str ();
76 end = p + len;
77 }
78
79 void
80 SourceFile::SkipWhitespace ()
81 {
82 while ( p < end && isspace ( *p ))
83 p++;
84 }
85
86 bool
87 SourceFile::ReadInclude ( string& filename,
88 bool& includeNext)
89 {
90 while ( p < end )
91 {
92 if ( ( *p == '#') && ( end - p > 13 ) )
93 {
94 bool include = false;
95 p++;
96 SkipWhitespace ();
97 if ( *p == 'i' )
98 {
99 if ( strncmp ( p, "include ", 8 ) == 0 )
100 {
101 p += 8;
102 includeNext = false;
103 include = true;
104 }
105 if ( strncmp ( p, "include_next ", 13 ) == 0 )
106 {
107 p += 13;
108 includeNext = true;
109 include = true;
110 }
111
112 if ( include )
113 {
114 SkipWhitespace ();
115 if ( p < end )
116 {
117 register char ch = *p;
118 if ( ch == '<' || ch == '"' )
119 {
120 p++;
121 filename.resize ( MAX_PATH );
122 int i = 0;
123 ch = *p;
124 while ( p < end && ch != '>' && ch != '"' && ch != '\n' )
125 {
126 filename[i++] = *p;
127 p++;
128 if ( p < end )
129 ch = *p;
130 }
131 filename.resize ( i );
132 return true;
133 }
134 }
135 }
136 }
137 }
138 p++;
139 }
140 filename = "";
141 includeNext = false;
142 return false;
143 }
144
145 bool
146 SourceFile::IsParentOf ( const SourceFile* parent,
147 const SourceFile* child )
148 {
149 size_t i;
150 for ( i = 0; i < child->parents.size (); i++ )
151 {
152 if ( child->parents[i] != NULL )
153 {
154 if ( child->parents[i] == parent )
155 return true;
156 }
157 }
158 for ( i = 0; i < child->parents.size (); i++ )
159 {
160 if ( child->parents[i] != NULL )
161 {
162 if ( IsParentOf ( parent,
163 child->parents[i] ) )
164 return true;
165 }
166 }
167 return false;
168 }
169
170 bool
171 SourceFile::IsIncludedFrom ( const string& normalizedFilename )
172 {
173 if ( normalizedFilename == filename )
174 return true;
175
176 SourceFile* sourceFile = automaticDependency->RetrieveFromCache ( normalizedFilename );
177 if ( sourceFile == NULL )
178 return false;
179
180 return IsParentOf ( sourceFile,
181 this );
182 }
183
184 SourceFile*
185 SourceFile::GetParentSourceFile ()
186 {
187 if ( isNonAutomaticDependency )
188 return NULL;
189 return this;
190 }
191
192 bool
193 SourceFile::CanProcessFile ( const string& extension )
194 {
195 if ( extension == ".h" || extension == ".H" )
196 return true;
197 if ( extension == ".c" || extension == ".C" )
198 return true;
199 if ( extension == ".cpp" || extension == ".CPP" )
200 return true;
201 if ( extension == ".rc" || extension == ".RC" )
202 return true;
203 if ( extension == ".s" || extension == ".S" )
204 return true;
205 return false;
206 }
207
208 SourceFile*
209 SourceFile::ParseFile ( const string& normalizedFilename )
210 {
211 string extension = GetExtension ( normalizedFilename );
212 if ( CanProcessFile ( extension ) )
213 {
214 if ( IsIncludedFrom ( normalizedFilename ) )
215 return NULL;
216
217 SourceFile* sourceFile = automaticDependency->RetrieveFromCacheOrParse ( module,
218 normalizedFilename,
219 GetParentSourceFile () );
220 return sourceFile;
221 }
222 return NULL;
223 }
224
225 void
226 SourceFile::Parse ()
227 {
228 Open ();
229 while ( p < end )
230 {
231 string includedFilename ( "" );
232
233 bool includeNext;
234 while ( ReadInclude ( includedFilename,
235 includeNext ) )
236 {
237 string resolvedFilename ( "" );
238 bool locatedFile = automaticDependency->LocateIncludedFile ( this,
239 module,
240 includedFilename,
241 includeNext,
242 resolvedFilename );
243 if ( locatedFile )
244 {
245 SourceFile* sourceFile = ParseFile ( resolvedFilename );
246 if ( sourceFile != NULL )
247 files.push_back ( sourceFile );
248 }
249 }
250 p++;
251 }
252 Close ();
253 }
254
255 string
256 SourceFile::Location () const
257 {
258 int line = 1;
259 const char* end_of_line = strchr ( buf.c_str (), '\n' );
260 while ( end_of_line && end_of_line < p )
261 {
262 ++line;
263 end_of_line = strchr ( end_of_line + 1, '\n' );
264 }
265 return ssprintf ( "%s(%i)",
266 filename.c_str (),
267 line );
268 }
269
270
271 AutomaticDependency::AutomaticDependency ( const Project& project )
272 : project ( project )
273 {
274 }
275
276 AutomaticDependency::~AutomaticDependency ()
277 {
278 std::map<std::string, SourceFile*>::iterator theIterator;
279 for ( theIterator = sourcefile_map.begin (); theIterator != sourcefile_map.end (); theIterator++ )
280 delete theIterator->second;
281 }
282
283 void
284 AutomaticDependency::Process ()
285 {
286 for ( size_t i = 0; i < project.modules.size (); i++ )
287 ProcessModule ( *project.modules[i] );
288 }
289
290 void
291 AutomaticDependency::GetModuleFiles ( Module& module,
292 vector<File*>& files ) const
293 {
294 for ( size_t i = 0; i < module.non_if_data.files.size (); i++ )
295 files.push_back ( module.non_if_data.files[i] );
296
297 /* FIXME: Collect files in IFs here */
298
299 if ( module.pch != NULL )
300 files.push_back ( &module.pch->file );
301 }
302
303 void
304 AutomaticDependency::ProcessModule ( Module& module )
305 {
306 vector<File*> files;
307 GetModuleFiles ( module, files );
308 for ( size_t i = 0; i < files.size (); i++ )
309 ProcessFile ( module, *files[i] );
310 }
311
312 void
313 AutomaticDependency::ProcessFile ( Module& module,
314 const File& file )
315 {
316 string normalizedFilename = NormalizeFilename ( file.name );
317 RetrieveFromCacheOrParse ( module,
318 normalizedFilename,
319 NULL );
320 }
321
322 bool
323 AutomaticDependency::LocateIncludedFile ( const string& directory,
324 const string& includedFilename,
325 string& resolvedFilename )
326 {
327 string normalizedFilename = NormalizeFilename ( directory + SSEP + includedFilename );
328 FILE* f = fopen ( normalizedFilename.c_str (), "rb" );
329 if ( f != NULL )
330 {
331 fclose ( f );
332 resolvedFilename = normalizedFilename;
333 return true;
334 }
335 resolvedFilename = "";
336 return false;
337 }
338
339 string
340 AutomaticDependency::GetFilename ( const string& filename )
341 {
342 size_t index = filename.find_last_of ( CSEP );
343 if (index == string::npos)
344 return filename;
345 else
346 return filename.substr ( index + 1,
347 filename.length () - index - 1);
348 }
349
350 bool
351 AutomaticDependency::LocateIncludedFile ( SourceFile* sourceFile,
352 Module& module,
353 const string& includedFilename,
354 bool includeNext,
355 string& resolvedFilename )
356 {
357 size_t i, j;
358 const vector<Include*>* pincludes;
359 for ( i = 0; i < 2; i++ )
360 {
361 if ( !i )
362 pincludes = &module.non_if_data.includes;
363 else
364 pincludes = &module.project.non_if_data.includes;
365 const vector<Include*>& includes = *pincludes;
366 for ( j = 0; j < includes.size (); j++ )
367 {
368 Include& include = *includes[j];
369 if ( LocateIncludedFile ( include.directory,
370 includedFilename,
371 resolvedFilename ) )
372 {
373 if ( includeNext && stricmp ( resolvedFilename.c_str (),
374 sourceFile->filename.c_str () ) == 0 )
375 continue;
376 return true;
377 }
378 }
379 }
380
381 resolvedFilename = "";
382 return false;
383 }
384
385 SourceFile*
386 AutomaticDependency::RetrieveFromCacheOrParse ( Module& module,
387 const string& filename,
388 SourceFile* parentSourceFile )
389 {
390 SourceFile* sourceFile = sourcefile_map[filename];
391 if ( sourceFile == NULL )
392 {
393 sourceFile = new SourceFile ( this,
394 module,
395 filename,
396 parentSourceFile,
397 false );
398 sourcefile_map[filename] = sourceFile;
399 sourceFile->Parse ();
400 }
401 else if ( parentSourceFile != NULL )
402 sourceFile->parents.push_back ( parentSourceFile );
403 return sourceFile;
404 }
405
406 SourceFile*
407 AutomaticDependency::RetrieveFromCache ( const string& filename )
408 {
409 return sourcefile_map[filename];
410 }
411
412 void
413 AutomaticDependency::CheckAutomaticDependencies ( bool verbose )
414 {
415 struct utimbuf timebuf;
416 for ( size_t mi = 0; mi < project.modules.size (); mi++ )
417 {
418 vector<File*> files;
419 GetModuleFiles ( *project.modules[mi], files );
420 for ( size_t fi = 0; fi < files.size (); fi++ )
421 {
422 File& file = *files[fi];
423 string normalizedFilename = NormalizeFilename ( file.name );
424
425 SourceFile* sourceFile = RetrieveFromCache ( normalizedFilename );
426 if ( sourceFile != NULL )
427 {
428 CheckAutomaticDependenciesForFile ( sourceFile );
429 assert ( sourceFile->youngestLastWriteTime != 0 );
430 if ( sourceFile->youngestLastWriteTime > sourceFile->lastWriteTime )
431 {
432 if ( verbose )
433 {
434 printf ( "Marking %s for rebuild due to younger file %s\n",
435 sourceFile->filename.c_str (),
436 sourceFile->youngestFile->filename.c_str () );
437 }
438 timebuf.actime = sourceFile->youngestLastWriteTime;
439 timebuf.modtime = sourceFile->youngestLastWriteTime;
440 utime ( sourceFile->filename.c_str (),
441 &timebuf );
442 }
443 }
444 }
445 }
446 }
447
448 void
449 AutomaticDependency::CheckAutomaticDependenciesForFile ( SourceFile* sourceFile )
450 {
451 if ( sourceFile->youngestLastWriteTime > 0 )
452 return;
453
454 if ( sourceFile->files.size () == 0 )
455 {
456 sourceFile->youngestLastWriteTime = sourceFile->lastWriteTime;
457 sourceFile->youngestFile = sourceFile;
458 return;
459 }
460
461 for ( size_t i = 0; i < sourceFile->files.size (); i++ )
462 {
463 SourceFile* childSourceFile = sourceFile->files[i];
464
465 CheckAutomaticDependenciesForFile ( childSourceFile );
466 if ( ( childSourceFile->youngestLastWriteTime > sourceFile->youngestLastWriteTime ) ||
467 ( childSourceFile->lastWriteTime > sourceFile->youngestLastWriteTime ) )
468 {
469 if ( childSourceFile->youngestLastWriteTime > childSourceFile->lastWriteTime )
470 {
471 sourceFile->youngestLastWriteTime = childSourceFile->youngestLastWriteTime;
472 sourceFile->youngestFile = childSourceFile->youngestFile;
473 }
474 else
475 {
476 sourceFile->youngestLastWriteTime = childSourceFile->lastWriteTime;
477 sourceFile->youngestFile = childSourceFile;
478 }
479 }
480 }
481 }