1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
* License, v. 2.0. If a copy of the MPL was not distributed with this
4
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
// Dependency building hack
20
FILE *pAltFile = stdout;
22
CStringArray includeDirectories;
24
// turn a file, relative path or other into an absolute path
25
// This function copied from MFC 1.52
26
BOOL PASCAL _AfxFullPath(LPSTR lpszPathOut, LPCSTR lpszFileIn)
27
// lpszPathOut = buffer of _MAX_PATH
28
// lpszFileIn = file, relative path or absolute path
29
// (both in ANSI character set)
32
if (OpenFile(lpszFileIn, &of, OF_PARSE) != HFILE_ERROR)
34
// of.szPathName is in the OEM character set
35
OemToAnsi(of.szPathName, lpszPathOut);
36
AnsiUpper(lpszPathOut); // paths in upper case just to be sure
41
TRACE1("Warning: could not parse the path %Fs\n", lpszFileIn);
42
lstrcpy(lpszPathOut, lpszFileIn); // take it literally
43
AnsiUpper(lpszPathOut); // paths in upper case just to be sure
48
void AddIncludeDirectory( char *pString ){
50
int len = s.GetLength();
51
if(len > 0 && s[len - 1] != '\\' ){
54
includeDirectories.Add(s);
57
BOOL FileExists( const char *pString ){
61
result = _stat( pString, &buf );
65
void FixPathName(CString& str) {
66
str.MakeUpper(); // all upper case
68
// now switch all forward slashes to back slashes
70
while ((index = str.Find('/')) != -1) {
71
str.SetAt(index, '\\');
75
void FATName(CString& csLONGName)
77
// Only relevant for 16 bits.
79
// Convert filename to FAT (8.3) equivalent.
82
if(GetShortPathName(csLONGName, aBuffer, 2048)) {
93
CPtrArray m_includes; // pointers to CFileRecords in fileMap
97
static CMapStringToPtr fileMap; // contains all allocated CFileRecords
98
static CStringArray orderedFileNames;
99
static CMapStringToPtr includeMap; // pointer to CFileRecords in fileMap
100
static CMapStringToPtr noDependMap;
102
CFileRecord( const char *shortName, const char* pFullName, BOOL bSystem, BOOL bSource):
103
m_shortName( shortName ),
107
m_bSource( bSource ),
110
m_pathName = pFullName;
111
FixPathName(m_pathName); // all upper case for consistency
112
ASSERT(FindFileRecord(m_pathName) == NULL); // make sure it's not already in the map
113
fileMap[m_pathName] = this; // add this to the filemap, using the appropriate name as the key
115
orderedFileNames.Add(m_pathName); // remember the order we saw source files in
120
// open the file and grab all the includes.
127
char *a = new char[2048];
129
char srcPath[_MAX_PATH];
131
// construct the full path
132
if (!_AfxFullPath(srcPath, m_pathName)) {
133
strcpy(srcPath, m_pathName);
136
// strip off the source filename to end up with just the path
137
LPSTR pTemp = strrchr(srcPath, '\\');
142
f = fopen(m_pathName, "r");
143
if(f != NULL && f != (FILE *)-1) {
144
setvbuf(f, NULL, _IOFBF, 32768); // use a large file buffer
145
while(fgets(a, 2047, f)) {
146
// if the string "//{{NO_DEPENDENCIES}}" is at the start of one of the
147
// first 10 lines of a file, don't build dependencies on it or any
148
// of the files it includes
150
static char* pDependStr = "//{{NO_DEPENDENCIES}}";
151
if (strncmp(a, pDependStr, strlen(pDependStr)) == 0) {
152
noDependMap[m_pathName] = 0; // add it to the noDependMap
153
break; // no need for further processing of this file
157
// have to handle a variety of legal syntaxes that we find in our source files:
161
// if the first non-whitespace char is a '#', consider this line
163
pTemp += strspn(pTemp, " \t"); // skip whitespace
165
++pTemp; // skip the '#'
166
pTemp += strspn(pTemp, " \t"); // skip more whitespace
167
if( !strncmp(pTemp, "include", 7) ){
168
pTemp += 7; // skip the "include"
169
pTemp += strspn(pTemp, " \t"); // skip more whitespace
170
bSystem = (*pTemp == '<'); // mark if it's a system include or not
171
// forget system files -- we just have to search all the paths
172
// every time and never find them! This change alone speeds a full
173
// depend run on my system from 5 minutes to 3:15
174
// if (bSystem || (*pTemp == '"')) {
176
LPSTR pStart = pTemp + 1; // mark the start of the string
177
pTemp = pStart + strcspn(pStart, ">\" "); // find the end of the string
178
*pTemp = 0; // terminate the string
180
// construct the full pathname from the path part of the
181
// source file and the name listed here
184
CFileRecord *pAddMe = AddFile( pStart, fullName, bSystem );
186
m_includes.Add(pAddMe);
197
void PrintIncludes(){
199
while( i < m_includes.GetSize() ){
200
CFileRecord *pRec = (CFileRecord*) m_includes[i];
202
// Don't write out files that don't exist or are not in the namespace
203
// of the programs using it (netscape_AppletMozillaContext.h doesn't
204
// mix well with 16 bits).
205
// Also don't write out files that are in the noDependMap
207
if( !pRec->m_bVisited && pRec->m_pathName.GetLength() != 0 && !noDependMap.Lookup(pRec->m_pathName, lookupJunk)) {
209
// not supposed to have a file in the list that doesn't exist
210
ASSERT(FileExists(pRec->m_pathName));
213
csOutput = pRec->m_pathName;
216
fprintf(pAltFile, "\\\n %s ", (const char *) csOutput );
218
// mark this one as done so we don't do it more than once
219
pRec->m_bVisited = TRUE;
221
pRec->PrintIncludes();
233
// clear all the m_bVisisted flags so we can use it to keep track
234
// of whether we've already output this file as a dependency
235
next = fileMap.GetStartPosition();
237
fileMap.GetNextAssoc( next, name, *(void**)&pRec );
238
pRec->m_bVisited = FALSE;
241
char fname[_MAX_FNAME];
243
if (pRec->m_pathName.GetLength() != 0) {
245
fprintf(pAltFile, "\n\n\n%s:\t", m_pathName );
249
csOutput = m_pathName;
252
_splitpath( csOutput, NULL, NULL, fname, NULL );
254
fprintf(pAltFile, "\n\n\n$(OUTDIR)\\%s.obj: %s ", fname, (const char*) csOutput );
256
m_bVisited = TRUE; // mark it as done so we won't do it again
262
static CString NormalizeFileName( const char* pName ){
263
return CString(pName);
266
static CFileRecord* FindFileRecord( const char *pName ){
267
CFileRecord* pRec = NULL;
270
fileMap.Lookup(name, (void*&)pRec);
274
static CFileRecord* AddFile( const char* pShortName, const char* pFullName, BOOL bSystem = FALSE,
275
BOOL bSource = FALSE ){
277
char fullName[_MAX_PATH];
280
CString fixedShortName;
283
// normalize the name
284
fixedShortName = pShortName;
285
FixPathName(fixedShortName);
286
pShortName = fixedShortName;
288
// if it is source, we might be getting an obj file. If we do,
289
// convert it to a c or c++ file.
290
if( bSource && (strcmp(GetExt(pShortName),".obj") == 0) ){
291
char path_buffer[_MAX_PATH];
292
char fname[_MAX_FNAME] = "";
295
_splitpath( pShortName, NULL, NULL, fname, NULL );
296
if( FileExists( s = CString(fname) + ".cpp") ){
300
else if( FileExists( s = CString(fname) + ".c" ) ){
309
// if pFullName was not constructed, construct it here based on the current directory
311
_AfxFullPath(fullName, pShortName);
312
pFullName = fullName;
315
// first check to see if we already have this exact file
316
CFileRecord *pRec = FindFileRecord(pFullName);
318
// if not found and not a source file check the header list --
319
// all files we've found in include directories are in the includeMap.
320
// we can save gobs of time by getting it from there
321
if (!pRec && !bSource)
322
includeMap.Lookup(fixedShortName, (void*&)pRec);
325
// not in one of our lists, start scrounging on disk
327
// check the fullname first
328
if (FileExists(pFullName)) {
329
foundName = pFullName;
333
// if still not found, search the include paths
335
while( i < includeDirectories.GetSize() ){
336
if( FileExists( includeDirectories[i] + pShortName ) ){
337
foundName = includeDirectories[i] + pShortName;
350
// source files are not allowed to be missing
351
if (bSource && !pRec && !bFound) {
352
fprintf(stderr, "Source file: %s doesn't exist\n", pFullName);
353
mainReturn = -1; // exit with an error, don't write out the results
357
if (!pRec && !bFound && !bSystem) {
358
fprintf(stderr, "Header not found: %s (%s)\n", pShortName, pFullName);
362
// if none of the above logic found it already in the list,
363
// must be a new file, add it to the list
364
if (bFound && (pRec == NULL)) {
365
pRec = new CFileRecord( pShortName, foundName, bSystem, bSource);
367
// if this one isn't a source file add it to the includeMap
368
// for performance reasons (so we can find it there next time rather
369
// than having to search the file system again)
371
includeMap[pShortName] = pRec;
378
static void PrintDependancies(){
384
// use orderedFileNames to preserve order
385
for (int pos = 0; pos < orderedFileNames.GetSize(); pos++) {
386
pRec = FindFileRecord(orderedFileNames[pos]);
387
if(pRec && pRec->m_bSource ){
398
if( m_includes.GetSize() != 0 ){
399
fprintf(pAltFile, "\n\n\n%s: \\\n",m_pathName );
401
while( i < m_includes.GetSize() ){
402
pRec = (CFileRecord*) m_includes[i];
403
fprintf(pAltFile, "\t\t\t%s\t\\\n",pRec->m_pathName );
409
static void PrintDependancies2(){
415
next = fileMap.GetStartPosition();
417
fileMap.GetNextAssoc( next, name, *(void**)&pRec );
418
pRec->PrintDepend2();
423
static void PrintTargets(const char *pMacroName, const char *pDelimiter){
429
BOOL bNeedDelimiter = FALSE;
430
fprintf(pAltFile, "%s = ", pMacroName);
432
// use orderedFileNames to preserve target order
433
for (int pos = 0; pos < orderedFileNames.GetSize(); pos++) {
434
pRec = FindFileRecord(orderedFileNames[pos]);
437
if( pRec && pRec->m_bSource && pRec->m_pathName.GetLength() != 0){
438
char fname[_MAX_FNAME];
441
csOutput = pRec->m_pathName;
444
_splitpath( csOutput, NULL, NULL, fname, NULL );
447
fprintf(pAltFile, "%s\n", pDelimiter);
448
bNeedDelimiter = FALSE;
451
fprintf(pAltFile, " $(OUTDIR)\\%s.obj ", fname );
452
bNeedDelimiter = TRUE;
455
fprintf(pAltFile, "\n\n\n");
458
static CString DirDefine( const char *pPath ){
459
char path_buffer[_MAX_PATH];
460
char dir[_MAX_DIR] = "";
461
char dir2[_MAX_DIR] = "";
462
char fname[_MAX_FNAME] = "";
463
char ext[_MAX_EXT] = "";
466
_splitpath( pPath, 0, dir, 0, ext );
470
while( dir && !bDone){
471
// remove the trailing slash
472
dir[ strlen(dir)-1] = 0;
473
_splitpath( dir, 0, dir2, fname, 0 );
474
if( strcmp( fname, "SRC" ) == 0 ){
481
s = CString(fname) + "_" + (ext+1);
486
static void PrintSources(){
488
CString dirName, newDirName;
490
for( i=0; i< orderedFileNames.GetSize(); i++ ){
491
newDirName= DirDefine( orderedFileNames[i] );
492
if( newDirName != dirName ){
493
fprintf( pAltFile, "\n\n\nFILES_%s= $(FILES_%s) \\",
494
(const char*)newDirName, (const char*)newDirName );
495
dirName = newDirName;
497
fprintf( pAltFile, "\n\t%s^", (const char*)orderedFileNames[i] );
501
static CString SourceDirName( const char *pPath, BOOL bFileName){
502
char path_buffer[_MAX_PATH];
503
char drive[_MAX_DRIVE] = "";
504
char dir[_MAX_DIR] = "";
505
char fname[_MAX_FNAME] = "";
506
char ext[_MAX_EXT] = "";
509
_splitpath( pPath, drive, dir, fname, ext );
511
s = CString(drive) + dir;
513
s += CString("FNAME") + ext;
516
// remove the trailing slash
517
s = s.Left( s.GetLength() - 1 );
523
static CString GetExt( const char *pPath){
524
char ext[_MAX_EXT] = "";
526
_splitpath( pPath, 0,0,0, ext );
528
CString s = CString(ext);
533
static void PrintBuildRules(){
537
CMapStringToPtr dirList;
539
for( i=0; i< orderedFileNames.GetSize(); i++ ){
540
dirList[ SourceDirName(orderedFileNames[i], TRUE) ]= 0;
547
next = dirList.GetStartPosition();
549
dirList.GetNextAssoc( next, name, pVal);
550
CString dirDefine = DirDefine( name );
551
CString ext = GetExt( name );
552
name = SourceDirName( name, FALSE );
553
CString response = dirDefine.Left(8);
556
"\n\n\n{%s}%s{$(OUTDIR)}.obj:\n"
557
"\t@rem <<$(OUTDIR)\\%s.cl\n"
561
"\t$(CPP) @$(OUTDIR)\\%s.cl %%s\n",
564
(const char*)response,
565
(const char*)dirDefine,
566
(const char*)response
571
"\t@rem <<$(OUTDIR)\\%s.cl\n"
577
"\t$(CPP) @$(OUTDIR)\\%s.cl\n"
579
(const char*)dirDefine,
580
(const char*)response,
581
(const char*)dirDefine,
582
(const char*)dirDefine,
583
(const char*)response
588
// Loop through one more time and build the final batch build
592
"\n\n\nBATCH_BUILD_OBJECTS:\t\t\\\n");
594
next = dirList.GetStartPosition();
596
dirList.GetNextAssoc( next, name, pVal);
597
CString dirDefine = DirDefine( name );
600
"\tBATCH_%s\t\t\\\n", dirDefine );
608
static void ProcessFiles(){
614
// search all the files for headers, adding each one to the list when found
615
// rather than do it recursively, it simple marks each one it's done
616
// and starts over, stopping only when all are marked as done
618
next = fileMap.GetStartPosition();
620
fileMap.GetNextAssoc( next, name, *(void**)&pRec );
621
if( pRec->m_bVisited == FALSE && pRec->m_bSystem == FALSE ){
622
// mark this file as already done so we don't read it again
623
// to find its headers
624
pRec->m_bVisited = TRUE;
626
// Start searching from the beginning again
627
// because ProcessFile may have added new files
628
// and changed the GetNextAssoc order
629
next = fileMap.GetStartPosition();
638
CMapStringToPtr CFileRecord::fileMap; // contains all allocated CFileRecords
639
CStringArray CFileRecord::orderedFileNames;
640
CMapStringToPtr CFileRecord::includeMap; // pointers to CFileRecords in fileMap
641
CMapStringToPtr CFileRecord::noDependMap; // no data, just an index
643
int main( int argc, char** argv ){
646
static int iRecursion = 0; // Track levels of recursion.
647
static CString outputFileName;
653
if( argv[i][0] == '-' || argv[i][0] == '/' ){
654
switch( argv[i][1] ){
658
if( argv[i][2] != 0 ){
659
pStr = &(argv[i][2]);
665
if( pStr == 0 || *pStr == '-' || *pStr == '/' ){
669
AddIncludeDirectory( pStr );
675
if( argv[i][2] != 0 ){
676
pStr = &(argv[i][2]);
682
if( pStr == 0 || *pStr == '-' || *pStr == '/'){
688
if( f.Open( pStr, CFile::modeRead ) ){
689
while(f.ReadString(s)){
693
CFileRecord::AddFile( s, NULL, FALSE, TRUE );
699
fprintf(stderr,"makedep: file not found: %s", pStr );
707
if( argv[i][2] != 0 ){
708
pStr = &(argv[i][2]);
714
if( pStr == 0 || *pStr == '-' || *pStr == '/'){
720
outputFileName = pStr;
721
if(!(pAltFile = fopen(pStr, "w+"))) {
722
fprintf(stderr, "makedep: file not found: %s", pStr );
729
if( argv[i][2] == '6') {
745
fprintf(stderr, "usage: makedep -I <dirname> -F <filelist> <filename>\n"
746
" -I <dirname> Directory name, can be repeated\n"
747
" -F <filelist> List of files to scan, one per line\n"
748
" -O <outputFile> File to write output, default stdout\n");
752
else if( argv[i][0] == '@' ){
753
// file contains our commands.
757
char **apNewArgv = new char*[5000];
758
memset(apNewArgv, 0, sizeof(apNewArgv));
760
// First one is always the name of the exe.
761
apNewArgv[0] = argv[0];
764
const char *pTraverse;
765
const char *pBeginArg;
766
if( f.Open( &argv[i][1], CFile::modeRead ) ){
767
while( iNewArgc < 5000 && f.ReadString(s) ) {
768
// Scan the string for args, and do the right thing.
769
pTraverse = (const char *)s;
770
while(iNewArgc < 5000 && *pTraverse) {
771
if(isspace(*pTraverse)) {
776
// Extract to next space.
777
pBeginArg = pTraverse;
781
while(*pTraverse && !isspace(*pTraverse));
782
apNewArgv[iNewArgc] = new char[pTraverse - pBeginArg + 1];
783
memset(apNewArgv[iNewArgc], 0, pTraverse - pBeginArg + 1);
784
strncpy(apNewArgv[iNewArgc], pBeginArg, pTraverse - pBeginArg);
791
// Recurse if needed.
793
main(iNewArgc, apNewArgv);
796
// Free off the argvs (but not the very first one which we didn't allocate).
797
while(iNewArgc > 1) {
799
delete [] apNewArgv[iNewArgc];
804
CFileRecord::AddFile( argv[i], NULL, FALSE, TRUE );
809
// Only of the very bottom level of recursion do we do this.
810
if(iRecursion == 1) {
812
// only write the results out if no errors encountered
813
if (mainReturn == 0) {
814
CFileRecord::ProcessFiles();
816
CFileRecord::PrintTargets("OBJ_FILES", "\\");
818
CFileRecord::PrintTargets("LINK_OBJS", "+\\");
821
CFileRecord::PrintTargets("LINK_OBJS", "^");
823
CFileRecord::PrintSources();
824
CFileRecord::PrintBuildRules();
826
CFileRecord::PrintDependancies();
829
if(pAltFile != stdout) {
831
if (mainReturn != 0) {
832
remove(outputFileName); // kill output file if returning an error
838
if (iRecursion == 0 )
840
// last time through -- clean up allocated CFileRecords!
845
next = CFileRecord::fileMap.GetStartPosition();
847
CFileRecord::fileMap.GetNextAssoc( next, name, *(void**)&pFRec );