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