2 * Copyright (C) 2005 Casper S. Hornstrup
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.
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.
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.
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
30 SourceFile::SourceFile ( AutomaticDependency
* automaticDependency
,
32 const string
& filename
,
34 bool isNonAutomaticDependency
)
35 : automaticDependency ( automaticDependency
),
37 filename ( filename
),
38 isNonAutomaticDependency ( isNonAutomaticDependency
),
39 youngestLastWriteTime ( 0 )
42 parents
.push_back ( parent
);
43 GetDirectoryAndFilenameParts ();
47 SourceFile::GetDirectoryAndFilenameParts ()
49 size_t index
= filename
.find_last_of ( cSep
);
50 if ( index
!= string::npos
)
52 directoryPart
= filename
.substr ( 0, index
);
53 filenamePart
= filename
.substr ( index
+ 1, filename
.length () - index
);
58 filenamePart
= filename
;
75 FILE* f
= fopen ( filename
.c_str (), "rb" );
77 throw FileNotFoundException ( filename
);
79 if ( fstat ( fileno ( f
), &statbuf
) != 0 )
82 throw AccessDeniedException ( filename
);
84 lastWriteTime
= statbuf
.st_mtime
;
86 unsigned long len
= (unsigned long) filelen ( f
);
87 if ( len
> MAX_BYTES_TO_READ
)
88 len
= MAX_BYTES_TO_READ
;
90 fread ( &buf
[0], 1, len
, f
);
97 SourceFile::SkipWhitespace ()
99 while ( p
< end
&& isspace ( *p
))
104 SourceFile::ReadInclude ( string
& filename
,
105 bool& searchCurrentDirectory
,
110 if ( ( *p
== '#') && ( end
- p
> 13 ) )
112 bool include
= false;
117 if ( strncmp ( p
, "include ", 8 ) == 0 )
123 if ( strncmp ( p
, "include_next ", 13 ) == 0 )
135 register char ch
= *p
;
136 if ( ch
== '<' || ch
== '"' )
138 searchCurrentDirectory
= (ch
== '"');
140 filename
.resize ( MAX_PATH
);
143 while ( p
< end
&& ch
!= '>' && ch
!= '"' && ch
!= '\n' )
150 filename
.resize ( i
);
160 searchCurrentDirectory
= false;
166 SourceFile::IsParentOf ( const SourceFile
* parent
,
167 const SourceFile
* child
)
170 for ( i
= 0; i
< child
->parents
.size (); i
++ )
172 if ( child
->parents
[i
] != NULL
)
174 if ( child
->parents
[i
] == parent
)
178 for ( i
= 0; i
< child
->parents
.size (); i
++ )
180 if ( child
->parents
[i
] != NULL
)
182 if ( IsParentOf ( parent
,
183 child
->parents
[i
] ) )
191 SourceFile::IsIncludedFrom ( const string
& normalizedFilename
)
193 if ( normalizedFilename
== filename
)
196 SourceFile
* sourceFile
= automaticDependency
->RetrieveFromCache ( normalizedFilename
);
197 if ( sourceFile
== NULL
)
200 return IsParentOf ( sourceFile
,
205 SourceFile::GetParentSourceFile ()
207 if ( isNonAutomaticDependency
)
213 SourceFile::CanProcessFile ( const string
& extension
)
215 if ( extension
== ".h" || extension
== ".H" )
217 if ( extension
== ".c" || extension
== ".C" )
219 if ( extension
== ".cpp" || extension
== ".CPP" )
221 if ( extension
== ".rc" || extension
== ".RC" )
223 if ( extension
== ".s" || extension
== ".S" )
229 SourceFile::ParseFile ( const string
& normalizedFilename
)
231 string extension
= GetExtension ( normalizedFilename
);
232 if ( CanProcessFile ( extension
) )
234 if ( IsIncludedFrom ( normalizedFilename
) )
237 SourceFile
* sourceFile
= automaticDependency
->RetrieveFromCacheOrParse ( module
,
239 GetParentSourceFile () );
251 string
includedFilename ( "" );
253 bool searchCurrentDirectory
;
255 while ( ReadInclude ( includedFilename
,
256 searchCurrentDirectory
,
259 string
resolvedFilename ( "" );
260 bool locatedFile
= automaticDependency
->LocateIncludedFile ( this,
263 searchCurrentDirectory
,
268 SourceFile
* sourceFile
= ParseFile ( resolvedFilename
);
269 if ( sourceFile
!= NULL
)
270 files
.push_back ( sourceFile
);
279 SourceFile::Location () const
282 const char* end_of_line
= strchr ( buf
.c_str (), '\n' );
283 while ( end_of_line
&& end_of_line
< p
)
286 end_of_line
= strchr ( end_of_line
+ 1, '\n' );
288 return ssprintf ( "%s(%i)",
294 AutomaticDependency::AutomaticDependency ( const Project
& project
)
295 : project ( project
)
299 AutomaticDependency::~AutomaticDependency ()
301 std::map
<std::string
, SourceFile
*>::iterator theIterator
;
302 for ( theIterator
= sourcefile_map
.begin (); theIterator
!= sourcefile_map
.end (); theIterator
++ )
303 delete theIterator
->second
;
307 AutomaticDependency::ParseFiles ()
309 for ( size_t i
= 0; i
< project
.modules
.size (); i
++ )
310 ParseFiles ( *project
.modules
[i
] );
314 AutomaticDependency::GetModuleFiles ( const Module
& module
,
315 vector
<File
*>& files
) const
317 for ( size_t i
= 0; i
< module
.non_if_data
.files
.size (); i
++ )
318 files
.push_back ( module
.non_if_data
.files
[i
] );
320 /* FIXME: Collect files in IFs here */
322 if ( module
.pch
!= NULL
)
323 files
.push_back ( &module
.pch
->file
);
327 AutomaticDependency::ParseFiles ( const Module
& module
)
330 GetModuleFiles ( module
, files
);
331 for ( size_t i
= 0; i
< files
.size (); i
++ )
332 ParseFile ( module
, *files
[i
] );
336 AutomaticDependency::ParseFile ( const Module
& module
,
339 string normalizedFilename
= NormalizeFilename ( file
.name
);
340 RetrieveFromCacheOrParse ( module
,
346 AutomaticDependency::LocateIncludedFile ( const string
& directory
,
347 const string
& includedFilename
,
348 string
& resolvedFilename
)
350 string normalizedFilename
= NormalizeFilename ( directory
+ sSep
+ includedFilename
);
351 FILE* f
= fopen ( normalizedFilename
.c_str (), "rb" );
355 resolvedFilename
= normalizedFilename
;
358 resolvedFilename
= "";
363 AutomaticDependency::GetFilename ( const string
& filename
)
365 size_t index
= filename
.find_last_of ( cSep
);
366 if (index
== string::npos
)
369 return filename
.substr ( index
+ 1,
370 filename
.length () - index
- 1);
374 AutomaticDependency::GetIncludeDirectories ( vector
<Include
*>& includes
,
375 const Module
& module
,
376 Include
& currentDirectory
,
377 bool searchCurrentDirectory
)
380 if ( searchCurrentDirectory
)
381 includes
.push_back( ¤tDirectory
);
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
] );
389 AutomaticDependency::LocateIncludedFile ( SourceFile
* sourceFile
,
390 const Module
& module
,
391 const string
& includedFilename
,
392 bool searchCurrentDirectory
,
394 string
& resolvedFilename
)
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
++ )
401 Include
& include
= *includes
[j
];
402 if ( LocateIncludedFile ( include
.directory
,
406 if ( includeNext
&& stricmp ( resolvedFilename
.c_str (),
407 sourceFile
->filename
.c_str () ) == 0 )
412 resolvedFilename
= "";
417 AutomaticDependency::RetrieveFromCacheOrParse ( const Module
& module
,
418 const string
& filename
,
419 SourceFile
* parentSourceFile
)
421 SourceFile
* sourceFile
= sourcefile_map
[filename
];
422 if ( sourceFile
== NULL
)
424 sourceFile
= new SourceFile ( this,
429 sourcefile_map
[filename
] = sourceFile
;
430 sourceFile
->Parse ();
432 else if ( parentSourceFile
!= NULL
)
433 sourceFile
->parents
.push_back ( parentSourceFile
);
438 AutomaticDependency::RetrieveFromCache ( const string
& filename
)
440 return sourcefile_map
[filename
];
444 AutomaticDependency::CheckAutomaticDependencies ( bool verbose
)
447 for ( size_t mi
= 0; mi
< project
.modules
.size (); mi
++ )
449 Module
& module
= *project
.modules
[mi
];
450 CheckAutomaticDependencies ( module
, verbose
);
455 AutomaticDependency::GetModulesToCheck ( Module
& module
, vector
<const Module
*>& modules
)
457 modules
.push_back ( &module
);
458 for ( size_t i
= 0; i
< module
.non_if_data
.libraries
.size (); i
++ )
460 Library
& library
= *module
.non_if_data
.libraries
[i
];
461 if ( library
.importedModule
->type
!= ObjectLibrary
)
463 modules
.push_back ( library
.importedModule
);
466 /* FIXME: Collect libraries in IFs here */
470 AutomaticDependency::CheckAutomaticDependenciesForModule ( Module
& module
,
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
);
483 AutomaticDependency::CheckAutomaticDependencies ( const Module
& module
,
486 struct utimbuf timebuf
;
488 GetModuleFiles ( module
, files
);
489 for ( size_t fi
= 0; fi
< files
.size (); fi
++ )
491 File
& file
= *files
[fi
];
492 string normalizedFilename
= NormalizeFilename ( file
.name
);
494 SourceFile
* sourceFile
= RetrieveFromCache ( normalizedFilename
);
495 if ( sourceFile
!= NULL
)
497 CheckAutomaticDependenciesForFile ( sourceFile
);
498 assert ( sourceFile
->youngestLastWriteTime
!= 0 );
499 if ( sourceFile
->youngestLastWriteTime
> sourceFile
->lastWriteTime
)
503 printf ( "Marking %s for rebuild due to younger file %s\n",
504 sourceFile
->filename
.c_str (),
505 sourceFile
->youngestFile
->filename
.c_str () );
507 timebuf
.actime
= sourceFile
->youngestLastWriteTime
;
508 timebuf
.modtime
= sourceFile
->youngestLastWriteTime
;
509 utime ( sourceFile
->filename
.c_str (),
517 AutomaticDependency::CheckAutomaticDependenciesForFile ( SourceFile
* sourceFile
)
519 if ( sourceFile
->youngestLastWriteTime
> 0 )
522 if ( sourceFile
->files
.size () == 0 )
524 sourceFile
->youngestLastWriteTime
= sourceFile
->lastWriteTime
;
525 sourceFile
->youngestFile
= sourceFile
;
529 for ( size_t i
= 0; i
< sourceFile
->files
.size (); i
++ )
531 SourceFile
* childSourceFile
= sourceFile
->files
[i
];
533 CheckAutomaticDependenciesForFile ( childSourceFile
);
534 if ( ( childSourceFile
->youngestLastWriteTime
> sourceFile
->youngestLastWriteTime
) ||
535 ( childSourceFile
->lastWriteTime
> sourceFile
->youngestLastWriteTime
) )
537 if ( childSourceFile
->youngestLastWriteTime
> childSourceFile
->lastWriteTime
)
539 sourceFile
->youngestLastWriteTime
= childSourceFile
->youngestLastWriteTime
;
540 sourceFile
->youngestFile
= childSourceFile
->youngestFile
;
544 sourceFile
->youngestLastWriteTime
= childSourceFile
->lastWriteTime
;
545 sourceFile
->youngestFile
= childSourceFile
;