2 * Copyright (C) 2005 Casper S. Hornstrup
3 * Copyright (C) 2007 Hervé Poussineau
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.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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
32 SourceFile::SourceFile ( AutomaticDependency
* automaticDependency
,
37 automaticDependency ( automaticDependency
),
39 youngestLastWriteTime ( 0 )
42 parents
.push_back ( parent
);
56 string filename
= file
.GetFullPath ();
59 FILE* f
= fopen ( filename
.c_str (), "rb" );
61 throw FileNotFoundException ( filename
);
63 if ( fstat ( fileno ( f
), &statbuf
) != 0 )
66 throw AccessDeniedException ( filename
);
68 lastWriteTime
= statbuf
.st_mtime
;
70 unsigned long len
= (unsigned long) filelen ( f
);
71 if ( len
> MAX_BYTES_TO_READ
)
72 len
= MAX_BYTES_TO_READ
;
74 fread ( &buf
[0], 1, len
, f
);
81 SourceFile::SkipWhitespace ()
83 while ( p
< end
&& isspace ( *p
))
88 SourceFile::ReadInclude ( string
& filename
,
89 bool& searchCurrentDirectory
,
94 if ( p
!= buf
.c_str () )
96 /* Go to end of line */
97 while ( *p
!= '\n' && p
< end
)
101 if ( ( end
- p
> 13 ) && ( *p
== '#') )
103 bool include
= false;
108 if ( strncmp ( p
, "include ", 8 ) == 0 )
114 if ( strncmp ( p
, "include_next ", 13 ) == 0 )
126 register char ch
= *p
;
127 if ( ch
== '<' || ch
== '"' )
129 searchCurrentDirectory
= (ch
== '"');
131 filename
.resize ( MAX_PATH
);
134 while ( p
< end
&& ch
!= '>' && ch
!= '"' && ch
!= '\n' )
141 filename
.resize ( i
);
151 searchCurrentDirectory
= false;
157 SourceFile::IsParentOf ( const SourceFile
* parent
,
158 const SourceFile
* child
)
161 for ( i
= 0; i
< child
->parents
.size (); i
++ )
163 if ( child
->parents
[i
] != NULL
)
165 if ( child
->parents
[i
] == parent
)
169 for ( i
= 0; i
< child
->parents
.size (); i
++ )
171 if ( child
->parents
[i
] != NULL
)
173 if ( IsParentOf ( parent
,
174 child
->parents
[i
] ) )
182 SourceFile::IsIncludedFrom ( const File
& file
)
184 SourceFile
* sourceFile
= automaticDependency
->RetrieveFromCache ( file
);
185 if ( sourceFile
== NULL
)
188 if ( sourceFile
== this )
191 return IsParentOf ( sourceFile
,
196 SourceFile::CanProcessFile ( const File
& file
)
198 string extension
= GetExtension ( file
.file
);
199 std::transform ( extension
.begin (), extension
.end (), extension
.begin (), tolower
);
200 if ( extension
== ".h" )
202 if ( extension
== ".c" )
204 if ( extension
== ".cpp" )
206 if ( extension
== ".rc" )
208 if ( extension
== ".s" )
210 if ( extension
== ".nls" )
212 if ( extension
== ".idl" )
214 if ( automaticDependency
->project
.configuration
.Verbose
)
215 printf ( "Skipping file %s, as its extension is not recognized\n", file
.file
.name
.c_str () );
220 SourceFile::ParseFile ( const File
& file
)
222 if ( !CanProcessFile ( file
) )
225 if ( IsIncludedFrom ( file
) )
228 SourceFile
* sourceFile
= automaticDependency
->RetrieveFromCacheOrParse ( module
,
240 string includedFilename
;
242 bool searchCurrentDirectory
;
244 while ( ReadInclude ( includedFilename
,
245 searchCurrentDirectory
,
248 File
resolvedFile ( SourceDirectory
, "", "", false, "", false ) ;
249 bool locatedFile
= automaticDependency
->LocateIncludedFile ( this,
252 searchCurrentDirectory
,
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 (),
267 this->file
.file
.name
.c_str () );
274 AutomaticDependency::AutomaticDependency ( const Project
& project
)
275 : project ( project
)
279 AutomaticDependency::~AutomaticDependency ()
281 std::map
<std::string
, SourceFile
*>::iterator theIterator
;
282 for ( theIterator
= sourcefile_map
.begin (); theIterator
!= sourcefile_map
.end (); theIterator
++ )
283 delete theIterator
->second
;
287 AutomaticDependency::ParseFiles ()
289 for ( size_t i
= 0; i
< project
.modules
.size (); i
++ )
290 ParseFiles ( *project
.modules
[i
] );
294 AutomaticDependency::GetModuleFiles ( const Module
& module
,
295 vector
<File
*>& files
) const
297 for ( size_t i
= 0; i
< module
.non_if_data
.files
.size (); i
++ )
298 files
.push_back ( module
.non_if_data
.files
[i
] );
300 /* FIXME: Collect files in IFs here */
302 if ( module
.pch
!= NULL
)
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
);
311 AutomaticDependency::ParseFiles ( const Module
& module
)
314 GetModuleFiles ( module
, files
);
315 for ( size_t i
= 0; i
< files
.size (); i
++ )
316 ParseFile ( module
, *files
[i
] );
320 AutomaticDependency::ParseFile ( const Module
& module
,
323 RetrieveFromCacheOrParse ( module
,
329 AutomaticDependency::LocateIncludedFile ( const FileLocation
& directory
,
330 const string
& includedFilename
)
333 switch ( directory
.directory
)
335 case SourceDirectory
:
338 case IntermediateDirectory
:
339 path
= Environment::GetIntermediatePath ();
341 case OutputDirectory
:
342 path
= Environment::GetOutputPath ();
345 throw InvalidOperationException ( __FILE__
,
347 "Invalid directory %d.",
348 directory
.directory
);
350 if ( directory
.relative_path
.length () > 0 )
352 if ( path
.length () > 0 )
354 path
+= directory
.relative_path
;
357 string normalizedFilename
= NormalizeFilename ( path
+ sSep
+ includedFilename
);
358 FILE* f
= fopen ( normalizedFilename
.c_str (), "rb" );
368 AutomaticDependency::GetIncludeDirectories ( vector
<Include
*>& includes
,
369 const Module
& module
)
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
] );
379 AutomaticDependency::LocateIncludedFile ( SourceFile
* sourceFile
,
380 const Module
& module
,
381 const string
& includedFilename
,
382 bool searchCurrentDirectory
,
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 ( ¤tDirectory
);
391 GetIncludeDirectories ( includes
, module
);
393 for ( size_t j
= 0; j
< includes
.size (); j
++ )
395 Include
& include
= *includes
[j
];
396 if ( LocateIncludedFile ( *include
.directory
,
400 include
.directory
->directory
== sourceFile
->file
.file
.directory
&&
401 include
.directory
->relative_path
== sourceFile
->file
.file
.relative_path
)
406 resolvedFile
.file
.directory
= include
.directory
->directory
;
407 size_t index
= includedFilename
.find_last_of ( "/\\" );
408 if ( index
== string::npos
)
410 resolvedFile
.file
.name
= includedFilename
;
411 resolvedFile
.file
.relative_path
= include
.directory
->relative_path
;
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 );
427 AutomaticDependency::RetrieveFromCacheOrParse ( const Module
& module
,
429 SourceFile
* parentSourceFile
)
431 string filename
= file
.GetFullPath();
432 SourceFile
* sourceFile
= sourcefile_map
[filename
];
433 if ( sourceFile
== NULL
)
435 sourceFile
= new SourceFile ( this,
439 sourcefile_map
[filename
] = sourceFile
;
440 sourceFile
->Parse ();
442 else if ( parentSourceFile
!= NULL
)
443 sourceFile
->parents
.push_back ( parentSourceFile
);
448 AutomaticDependency::RetrieveFromCache ( const File
& file
)
450 string filename
= file
.GetFullPath();
451 return sourcefile_map
[filename
];
455 AutomaticDependency::CheckAutomaticDependencies ( bool verbose
)
458 for ( size_t mi
= 0; mi
< project
.modules
.size (); mi
++ )
460 Module
& module
= *project
.modules
[mi
];
461 CheckAutomaticDependencies ( module
, verbose
);
466 AutomaticDependency::GetModulesToCheck ( Module
& module
, vector
<const Module
*>& modules
)
468 modules
.push_back ( &module
);
469 for ( size_t i
= 0; i
< module
.non_if_data
.libraries
.size (); i
++ )
471 Library
& library
= *module
.non_if_data
.libraries
[i
];
472 if ( library
.importedModule
->type
!= ObjectLibrary
)
474 modules
.push_back ( library
.importedModule
);
477 /* FIXME: Collect libraries in IFs here */
481 AutomaticDependency::CheckAutomaticDependenciesForModule ( Module
& module
,
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
);
494 AutomaticDependency::CheckAutomaticDependencies ( const Module
& module
,
497 struct utimbuf timebuf
;
499 GetModuleFiles ( module
, files
);
500 for ( size_t fi
= 0; fi
< files
.size (); fi
++ )
502 File
& file
= *files
[fi
];
503 SourceFile
* sourceFile
= RetrieveFromCache ( file
);
504 if ( sourceFile
!= NULL
)
506 CheckAutomaticDependenciesForFile ( sourceFile
);
507 assert ( sourceFile
->youngestLastWriteTime
!= 0 );
508 if ( sourceFile
->youngestLastWriteTime
> sourceFile
->lastWriteTime
)
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 () );
518 timebuf
.actime
= sourceFile
->youngestLastWriteTime
;
519 timebuf
.modtime
= sourceFile
->youngestLastWriteTime
;
520 utime ( sourceFile
->file
.GetFullPath ().c_str (),
528 AutomaticDependency::CheckAutomaticDependenciesForFile ( SourceFile
* sourceFile
)
530 if ( sourceFile
->youngestLastWriteTime
> 0 )
533 if ( sourceFile
->files
.size () == 0 )
535 sourceFile
->youngestLastWriteTime
= sourceFile
->lastWriteTime
;
536 sourceFile
->youngestFile
= sourceFile
;
540 for ( size_t i
= 0; i
< sourceFile
->files
.size (); i
++ )
542 SourceFile
* childSourceFile
= sourceFile
->files
[i
];
544 CheckAutomaticDependenciesForFile ( childSourceFile
);
545 if ( ( childSourceFile
->youngestLastWriteTime
> sourceFile
->youngestLastWriteTime
) ||
546 ( childSourceFile
->lastWriteTime
> sourceFile
->youngestLastWriteTime
) )
548 if ( childSourceFile
->youngestLastWriteTime
> childSourceFile
->lastWriteTime
)
550 sourceFile
->youngestLastWriteTime
= childSourceFile
->youngestLastWriteTime
;
551 sourceFile
->youngestFile
= childSourceFile
->youngestFile
;
555 sourceFile
->youngestLastWriteTime
= childSourceFile
->lastWriteTime
;
556 sourceFile
->youngestFile
= childSourceFile
;