- The separator (slash or back slash), exepostfix and exeprefix are initialized from...
[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 size_t i;
380 if ( searchCurrentDirectory )
381 includes.push_back( &currentDirectory );
382 for ( i = 0; i < module.non_if_data.includes.size (); i++ )
383 includes.push_back( module.non_if_data.includes[i] );
384 for ( i = 0; i < module.project.non_if_data.includes.size (); i++ )
385 includes.push_back( module.project.non_if_data.includes[i] );
386 }
387
388 bool
389 AutomaticDependency::LocateIncludedFile ( SourceFile* sourceFile,
390 const Module& module,
391 const string& includedFilename,
392 bool searchCurrentDirectory,
393 bool includeNext,
394 string& resolvedFilename )
395 {
396 vector<Include*> includes;
397 Include currentDirectory ( module.project, ".", sourceFile->directoryPart );
398 GetIncludeDirectories ( includes, module, currentDirectory, searchCurrentDirectory );
399 for ( size_t j = 0; j < includes.size (); j++ )
400 {
401 Include& include = *includes[j];
402 if ( LocateIncludedFile ( include.directory,
403 includedFilename,
404 resolvedFilename ) )
405 {
406 if ( includeNext && stricmp ( resolvedFilename.c_str (),
407 sourceFile->filename.c_str () ) == 0 )
408 continue;
409 return true;
410 }
411 }
412 resolvedFilename = "";
413 return false;
414 }
415
416 SourceFile*
417 AutomaticDependency::RetrieveFromCacheOrParse ( const Module& module,
418 const string& filename,
419 SourceFile* parentSourceFile )
420 {
421 SourceFile* sourceFile = sourcefile_map[filename];
422 if ( sourceFile == NULL )
423 {
424 sourceFile = new SourceFile ( this,
425 module,
426 filename,
427 parentSourceFile,
428 false );
429 sourcefile_map[filename] = sourceFile;
430 sourceFile->Parse ();
431 }
432 else if ( parentSourceFile != NULL )
433 sourceFile->parents.push_back ( parentSourceFile );
434 return sourceFile;
435 }
436
437 SourceFile*
438 AutomaticDependency::RetrieveFromCache ( const string& filename )
439 {
440 return sourcefile_map[filename];
441 }
442
443 void
444 AutomaticDependency::CheckAutomaticDependencies ( bool verbose )
445 {
446 ParseFiles ();
447 for ( size_t mi = 0; mi < project.modules.size (); mi++ )
448 {
449 Module& module = *project.modules[mi];
450 CheckAutomaticDependencies ( module, verbose );
451 }
452 }
453
454 void
455 AutomaticDependency::GetModulesToCheck ( Module& module, vector<const Module*>& modules )
456 {
457 modules.push_back ( &module );
458 for ( size_t i = 0; i < module.non_if_data.libraries.size (); i++ )
459 {
460 Library& library = *module.non_if_data.libraries[i];
461 if ( library.importedModule->type != ObjectLibrary )
462 break;
463 modules.push_back ( library.importedModule );
464 }
465
466 /* FIXME: Collect libraries in IFs here */
467 }
468
469 void
470 AutomaticDependency::CheckAutomaticDependenciesForModule ( Module& module,
471 bool verbose )
472 {
473 size_t mi;
474 vector<const Module*> modules;
475 GetModulesToCheck ( module, modules );
476 for ( mi = 0; mi < modules.size (); mi++ )
477 ParseFiles ( *modules[mi] );
478 for ( mi = 0; mi < modules.size (); mi++ )
479 CheckAutomaticDependencies ( *modules[mi], verbose );
480 }
481
482 void
483 AutomaticDependency::CheckAutomaticDependencies ( const Module& module,
484 bool verbose )
485 {
486 struct utimbuf timebuf;
487 vector<File*> files;
488 GetModuleFiles ( module, files );
489 for ( size_t fi = 0; fi < files.size (); fi++ )
490 {
491 File& file = *files[fi];
492 string normalizedFilename = NormalizeFilename ( file.name );
493
494 SourceFile* sourceFile = RetrieveFromCache ( normalizedFilename );
495 if ( sourceFile != NULL )
496 {
497 CheckAutomaticDependenciesForFile ( sourceFile );
498 assert ( sourceFile->youngestLastWriteTime != 0 );
499 if ( sourceFile->youngestLastWriteTime > sourceFile->lastWriteTime )
500 {
501 if ( verbose )
502 {
503 printf ( "Marking %s for rebuild due to younger file %s\n",
504 sourceFile->filename.c_str (),
505 sourceFile->youngestFile->filename.c_str () );
506 }
507 timebuf.actime = sourceFile->youngestLastWriteTime;
508 timebuf.modtime = sourceFile->youngestLastWriteTime;
509 utime ( sourceFile->filename.c_str (),
510 &timebuf );
511 }
512 }
513 }
514 }
515
516 void
517 AutomaticDependency::CheckAutomaticDependenciesForFile ( SourceFile* sourceFile )
518 {
519 if ( sourceFile->youngestLastWriteTime > 0 )
520 return;
521
522 if ( sourceFile->files.size () == 0 )
523 {
524 sourceFile->youngestLastWriteTime = sourceFile->lastWriteTime;
525 sourceFile->youngestFile = sourceFile;
526 return;
527 }
528
529 for ( size_t i = 0; i < sourceFile->files.size (); i++ )
530 {
531 SourceFile* childSourceFile = sourceFile->files[i];
532
533 CheckAutomaticDependenciesForFile ( childSourceFile );
534 if ( ( childSourceFile->youngestLastWriteTime > sourceFile->youngestLastWriteTime ) ||
535 ( childSourceFile->lastWriteTime > sourceFile->youngestLastWriteTime ) )
536 {
537 if ( childSourceFile->youngestLastWriteTime > childSourceFile->lastWriteTime )
538 {
539 sourceFile->youngestLastWriteTime = childSourceFile->youngestLastWriteTime;
540 sourceFile->youngestFile = childSourceFile->youngestFile;
541 }
542 else
543 {
544 sourceFile->youngestLastWriteTime = childSourceFile->lastWriteTime;
545 sourceFile->youngestFile = childSourceFile;
546 }
547 }
548 }
549 }