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