1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 // Dependency building hack
52 // freopen won't work on stdout in win16
53 FILE *pAltFile
= stdout
;
55 CStringArray includeDirectories
;
57 // turn a file, relative path or other into an absolute path
58 // This function copied from MFC 1.52
59 BOOL PASCAL
_AfxFullPath(LPSTR lpszPathOut
, LPCSTR lpszFileIn
)
60 // lpszPathOut = buffer of _MAX_PATH
61 // lpszFileIn = file, relative path or absolute path
62 // (both in ANSI character set)
65 if (OpenFile(lpszFileIn
, &of
, OF_PARSE
) != HFILE_ERROR
)
67 // of.szPathName is in the OEM character set
68 OemToAnsi(of
.szPathName
, lpszPathOut
);
69 AnsiUpper(lpszPathOut
); // paths in upper case just to be sure
74 TRACE1("Warning: could not parse the path %Fs\n", lpszFileIn
);
75 lstrcpy(lpszPathOut
, lpszFileIn
); // take it literally
76 AnsiUpper(lpszPathOut
); // paths in upper case just to be sure
81 void AddIncludeDirectory( char *pString
){
83 int len
= s
.GetLength();
84 if(len
> 0 && s
[len
- 1] != '\\' ){
87 includeDirectories
.Add(s
);
90 BOOL
FileExists( const char *pString
){
94 result
= _stat( pString
, &buf
);
98 void FixPathName(CString
& str
) {
99 str
.MakeUpper(); // all upper case
101 // now switch all forward slashes to back slashes
103 while ((index
= str
.Find('/')) != -1) {
104 str
.SetAt(index
, '\\');
108 void FATName(CString
& csLONGName
)
110 // Only relevant for 16 bits.
112 // Convert filename to FAT (8.3) equivalent.
115 if(GetShortPathName(csLONGName
, aBuffer
, 2048)) {
116 csLONGName
= aBuffer
;
126 CPtrArray m_includes
; // pointers to CFileRecords in fileMap
130 static CMapStringToPtr fileMap
; // contains all allocated CFileRecords
131 static CStringArray orderedFileNames
;
132 static CMapStringToPtr includeMap
; // pointer to CFileRecords in fileMap
133 static CMapStringToPtr noDependMap
;
135 CFileRecord( const char *shortName
, const char* pFullName
, BOOL bSystem
, BOOL bSource
):
136 m_shortName( shortName
),
140 m_bSource( bSource
),
143 m_pathName
= pFullName
;
144 FixPathName(m_pathName
); // all upper case for consistency
145 ASSERT(FindFileRecord(m_pathName
) == NULL
); // make sure it's not already in the map
146 fileMap
[m_pathName
] = this; // add this to the filemap, using the appropriate name as the key
148 orderedFileNames
.Add(m_pathName
); // remember the order we saw source files in
153 // open the file and grab all the includes.
160 char *a
= new char[2048];
162 char srcPath
[_MAX_PATH
];
164 // construct the full path
165 if (!_AfxFullPath(srcPath
, m_pathName
)) {
166 strcpy(srcPath
, m_pathName
);
169 // strip off the source filename to end up with just the path
170 LPSTR pTemp
= strrchr(srcPath
, '\\');
175 f
= fopen(m_pathName
, "r");
176 if(f
!= NULL
&& f
!= (FILE *)-1) {
177 setvbuf(f
, NULL
, _IOFBF
, 32768); // use a large file buffer
178 while(fgets(a
, 2047, f
)) {
179 // if the string "//{{NO_DEPENDENCIES}}" is at the start of one of the
180 // first 10 lines of a file, don't build dependencies on it or any
181 // of the files it includes
183 static char* pDependStr
= "//{{NO_DEPENDENCIES}}";
184 if (strncmp(a
, pDependStr
, strlen(pDependStr
)) == 0) {
185 noDependMap
[m_pathName
] = 0; // add it to the noDependMap
186 break; // no need for further processing of this file
190 // have to handle a variety of legal syntaxes that we find in our source files:
194 // if the first non-whitespace char is a '#', consider this line
196 pTemp
+= strspn(pTemp
, " \t"); // skip whitespace
198 ++pTemp
; // skip the '#'
199 pTemp
+= strspn(pTemp
, " \t"); // skip more whitespace
200 if( !strncmp(pTemp
, "include", 7) ){
201 pTemp
+= 7; // skip the "include"
202 pTemp
+= strspn(pTemp
, " \t"); // skip more whitespace
203 bSystem
= (*pTemp
== '<'); // mark if it's a system include or not
204 // forget system files -- we just have to search all the paths
205 // every time and never find them! This change alone speeds a full
206 // depend run on my system from 5 minutes to 3:15
207 // if (bSystem || (*pTemp == '"')) {
209 LPSTR pStart
= pTemp
+ 1; // mark the start of the string
210 pTemp
= pStart
+ strcspn(pStart
, ">\" "); // find the end of the string
211 *pTemp
= 0; // terminate the string
213 // construct the full pathname from the path part of the
214 // source file and the name listed here
217 CFileRecord
*pAddMe
= AddFile( pStart
, fullName
, bSystem
);
219 m_includes
.Add(pAddMe
);
230 void PrintIncludes(){
232 while( i
< m_includes
.GetSize() ){
233 CFileRecord
*pRec
= (CFileRecord
*) m_includes
[i
];
235 // Don't write out files that don't exist or are not in the namespace
236 // of the programs using it (netscape_AppletMozillaContext.h doesn't
237 // mix well with 16 bits).
238 // Also don't write out files that are in the noDependMap
240 if( !pRec
->m_bVisited
&& pRec
->m_pathName
.GetLength() != 0 && !noDependMap
.Lookup(pRec
->m_pathName
, lookupJunk
)) {
242 // not supposed to have a file in the list that doesn't exist
243 ASSERT(FileExists(pRec
->m_pathName
));
246 csOutput
= pRec
->m_pathName
;
249 fprintf(pAltFile
, "\\\n %s ", (const char *) csOutput
);
251 // mark this one as done so we don't do it more than once
252 pRec
->m_bVisited
= TRUE
;
254 pRec
->PrintIncludes();
266 // clear all the m_bVisisted flags so we can use it to keep track
267 // of whether we've already output this file as a dependency
268 next
= fileMap
.GetStartPosition();
270 fileMap
.GetNextAssoc( next
, name
, *(void**)&pRec
);
271 pRec
->m_bVisited
= FALSE
;
274 char fname
[_MAX_FNAME
];
276 if (pRec
->m_pathName
.GetLength() != 0) {
278 fprintf(pAltFile
, "\n\n\n%s:\t", m_pathName
);
282 csOutput
= m_pathName
;
285 _splitpath( csOutput
, NULL
, NULL
, fname
, NULL
);
287 fprintf(pAltFile
, "\n\n\n$(OUTDIR)\\%s.obj: %s ", fname
, (const char*) csOutput
);
289 m_bVisited
= TRUE
; // mark it as done so we won't do it again
295 static CString
NormalizeFileName( const char* pName
){
296 return CString(pName
);
299 static CFileRecord
* FindFileRecord( const char *pName
){
300 CFileRecord
* pRec
= NULL
;
303 fileMap
.Lookup(name
, (void*&)pRec
);
307 static CFileRecord
* AddFile( const char* pShortName
, const char* pFullName
, BOOL bSystem
= FALSE
,
308 BOOL bSource
= FALSE
){
310 char fullName
[_MAX_PATH
];
313 CString fixedShortName
;
316 // normalize the name
317 fixedShortName
= pShortName
;
318 FixPathName(fixedShortName
);
319 pShortName
= fixedShortName
;
321 // if it is source, we might be getting an obj file. If we do,
322 // convert it to a c or c++ file.
323 if( bSource
&& (strcmp(GetExt(pShortName
),".obj") == 0) ){
324 char path_buffer
[_MAX_PATH
];
325 char fname
[_MAX_FNAME
] = "";
328 _splitpath( pShortName
, NULL
, NULL
, fname
, NULL
);
329 if( FileExists( s
= CString(fname
) + ".cpp") ){
333 else if( FileExists( s
= CString(fname
) + ".c" ) ){
342 // if pFullName was not constructed, construct it here based on the current directory
344 _AfxFullPath(fullName
, pShortName
);
345 pFullName
= fullName
;
348 // first check to see if we already have this exact file
349 CFileRecord
*pRec
= FindFileRecord(pFullName
);
351 // if not found and not a source file check the header list --
352 // all files we've found in include directories are in the includeMap.
353 // we can save gobs of time by getting it from there
354 if (!pRec
&& !bSource
)
355 includeMap
.Lookup(fixedShortName
, (void*&)pRec
);
358 // not in one of our lists, start scrounging on disk
360 // check the fullname first
361 if (FileExists(pFullName
)) {
362 foundName
= pFullName
;
366 // if still not found, search the include paths
368 while( i
< includeDirectories
.GetSize() ){
369 if( FileExists( includeDirectories
[i
] + pShortName
) ){
370 foundName
= includeDirectories
[i
] + pShortName
;
383 // source files are not allowed to be missing
384 if (bSource
&& !pRec
&& !bFound
) {
385 fprintf(stderr
, "Source file: %s doesn't exist\n", pFullName
);
386 mainReturn
= -1; // exit with an error, don't write out the results
390 if (!pRec
&& !bFound
&& !bSystem
) {
391 fprintf(stderr
, "Header not found: %s (%s)\n", pShortName
, pFullName
);
395 // if none of the above logic found it already in the list,
396 // must be a new file, add it to the list
397 if (bFound
&& (pRec
== NULL
)) {
398 pRec
= new CFileRecord( pShortName
, foundName
, bSystem
, bSource
);
400 // if this one isn't a source file add it to the includeMap
401 // for performance reasons (so we can find it there next time rather
402 // than having to search the file system again)
404 includeMap
[pShortName
] = pRec
;
411 static void PrintDependancies(){
417 // use orderedFileNames to preserve order
418 for (int pos
= 0; pos
< orderedFileNames
.GetSize(); pos
++) {
419 pRec
= FindFileRecord(orderedFileNames
[pos
]);
420 if(pRec
&& pRec
->m_bSource
){
431 if( m_includes
.GetSize() != 0 ){
432 fprintf(pAltFile
, "\n\n\n%s: \\\n",m_pathName
);
434 while( i
< m_includes
.GetSize() ){
435 pRec
= (CFileRecord
*) m_includes
[i
];
436 fprintf(pAltFile
, "\t\t\t%s\t\\\n",pRec
->m_pathName
);
442 static void PrintDependancies2(){
448 next
= fileMap
.GetStartPosition();
450 fileMap
.GetNextAssoc( next
, name
, *(void**)&pRec
);
451 pRec
->PrintDepend2();
456 static void PrintTargets(const char *pMacroName
, const char *pDelimiter
){
462 BOOL bNeedDelimiter
= FALSE
;
463 fprintf(pAltFile
, "%s = ", pMacroName
);
465 // use orderedFileNames to preserve target order
466 for (int pos
= 0; pos
< orderedFileNames
.GetSize(); pos
++) {
467 pRec
= FindFileRecord(orderedFileNames
[pos
]);
470 if( pRec
&& pRec
->m_bSource
&& pRec
->m_pathName
.GetLength() != 0){
471 char fname
[_MAX_FNAME
];
474 csOutput
= pRec
->m_pathName
;
477 _splitpath( csOutput
, NULL
, NULL
, fname
, NULL
);
480 fprintf(pAltFile
, "%s\n", pDelimiter
);
481 bNeedDelimiter
= FALSE
;
484 fprintf(pAltFile
, " $(OUTDIR)\\%s.obj ", fname
);
485 bNeedDelimiter
= TRUE
;
488 fprintf(pAltFile
, "\n\n\n");
491 static CString
DirDefine( const char *pPath
){
492 char path_buffer
[_MAX_PATH
];
493 char dir
[_MAX_DIR
] = "";
494 char dir2
[_MAX_DIR
] = "";
495 char fname
[_MAX_FNAME
] = "";
496 char ext
[_MAX_EXT
] = "";
499 _splitpath( pPath
, 0, dir
, 0, ext
);
503 while( dir
&& !bDone
){
504 // remove the trailing slash
505 dir
[ strlen(dir
)-1] = 0;
506 _splitpath( dir
, 0, dir2
, fname
, 0 );
507 if( strcmp( fname
, "SRC" ) == 0 ){
514 s
= CString(fname
) + "_" + (ext
+1);
519 static void PrintSources(){
521 CString dirName
, newDirName
;
523 for( i
=0; i
< orderedFileNames
.GetSize(); i
++ ){
524 newDirName
= DirDefine( orderedFileNames
[i
] );
525 if( newDirName
!= dirName
){
526 fprintf( pAltFile
, "\n\n\nFILES_%s= $(FILES_%s) \\",
527 (const char*)newDirName
, (const char*)newDirName
);
528 dirName
= newDirName
;
530 fprintf( pAltFile
, "\n\t%s^", (const char*)orderedFileNames
[i
] );
534 static CString
SourceDirName( const char *pPath
, BOOL bFileName
){
535 char path_buffer
[_MAX_PATH
];
536 char drive
[_MAX_DRIVE
] = "";
537 char dir
[_MAX_DIR
] = "";
538 char fname
[_MAX_FNAME
] = "";
539 char ext
[_MAX_EXT
] = "";
542 _splitpath( pPath
, drive
, dir
, fname
, ext
);
544 s
= CString(drive
) + dir
;
546 s
+= CString("FNAME") + ext
;
549 // remove the trailing slash
550 s
= s
.Left( s
.GetLength() - 1 );
556 static CString
GetExt( const char *pPath
){
557 char ext
[_MAX_EXT
] = "";
559 _splitpath( pPath
, 0,0,0, ext
);
561 CString s
= CString(ext
);
566 static void PrintBuildRules(){
570 CMapStringToPtr dirList
;
572 for( i
=0; i
< orderedFileNames
.GetSize(); i
++ ){
573 dirList
[ SourceDirName(orderedFileNames
[i
], TRUE
) ]= 0;
580 next
= dirList
.GetStartPosition();
582 dirList
.GetNextAssoc( next
, name
, pVal
);
583 CString dirDefine
= DirDefine( name
);
584 CString ext
= GetExt( name
);
585 name
= SourceDirName( name
, FALSE
);
586 CString response
= dirDefine
.Left(8);
589 "\n\n\n{%s}%s{$(OUTDIR)}.obj:\n"
590 "\t@rem <<$(OUTDIR)\\%s.cl\n"
594 "\t$(CPP) @$(OUTDIR)\\%s.cl %%s\n",
597 (const char*)response
,
598 (const char*)dirDefine
,
599 (const char*)response
604 "\t@rem <<$(OUTDIR)\\%s.cl\n"
610 "\t$(CPP) @$(OUTDIR)\\%s.cl\n"
612 (const char*)dirDefine
,
613 (const char*)response
,
614 (const char*)dirDefine
,
615 (const char*)dirDefine
,
616 (const char*)response
621 // Loop through one more time and build the final batch build
625 "\n\n\nBATCH_BUILD_OBJECTS:\t\t\\\n");
627 next
= dirList
.GetStartPosition();
629 dirList
.GetNextAssoc( next
, name
, pVal
);
630 CString dirDefine
= DirDefine( name
);
633 "\tBATCH_%s\t\t\\\n", dirDefine
);
641 static void ProcessFiles(){
647 // search all the files for headers, adding each one to the list when found
648 // rather than do it recursively, it simple marks each one it's done
649 // and starts over, stopping only when all are marked as done
651 next
= fileMap
.GetStartPosition();
653 fileMap
.GetNextAssoc( next
, name
, *(void**)&pRec
);
654 if( pRec
->m_bVisited
== FALSE
&& pRec
->m_bSystem
== FALSE
){
655 // mark this file as already done so we don't read it again
656 // to find its headers
657 pRec
->m_bVisited
= TRUE
;
659 // Start searching from the beginning again
660 // because ProcessFile may have added new files
661 // and changed the GetNextAssoc order
662 next
= fileMap
.GetStartPosition();
671 CMapStringToPtr
CFileRecord::fileMap
; // contains all allocated CFileRecords
672 CStringArray
CFileRecord::orderedFileNames
;
673 CMapStringToPtr
CFileRecord::includeMap
; // pointers to CFileRecords in fileMap
674 CMapStringToPtr
CFileRecord::noDependMap
; // no data, just an index
676 int main( int argc
, char** argv
){
679 static int iRecursion
= 0; // Track levels of recursion.
680 static CString outputFileName
;
686 if( argv
[i
][0] == '-' || argv
[i
][0] == '/' ){
687 switch( argv
[i
][1] ){
691 if( argv
[i
][2] != 0 ){
692 pStr
= &(argv
[i
][2]);
698 if( pStr
== 0 || *pStr
== '-' || *pStr
== '/' ){
702 AddIncludeDirectory( pStr
);
708 if( argv
[i
][2] != 0 ){
709 pStr
= &(argv
[i
][2]);
715 if( pStr
== 0 || *pStr
== '-' || *pStr
== '/'){
721 if( f
.Open( pStr
, CFile::modeRead
) ){
722 while(f
.ReadString(s
)){
726 CFileRecord::AddFile( s
, NULL
, FALSE
, TRUE
);
732 fprintf(stderr
,"makedep: file not found: %s", pStr
);
740 if( argv
[i
][2] != 0 ){
741 pStr
= &(argv
[i
][2]);
747 if( pStr
== 0 || *pStr
== '-' || *pStr
== '/'){
753 outputFileName
= pStr
;
754 if(!(pAltFile
= fopen(pStr
, "w+"))) {
755 fprintf(stderr
, "makedep: file not found: %s", pStr
);
762 if( argv
[i
][2] == '6') {
778 fprintf(stderr
, "usage: makedep -I <dirname> -F <filelist> <filename>\n"
779 " -I <dirname> Directory name, can be repeated\n"
780 " -F <filelist> List of files to scan, one per line\n"
781 " -O <outputFile> File to write output, default stdout\n");
785 else if( argv
[i
][0] == '@' ){
786 // file contains our commands.
790 char **apNewArgv
= new char*[5000];
791 memset(apNewArgv
, 0, sizeof(apNewArgv
));
793 // First one is always the name of the exe.
794 apNewArgv
[0] = argv
[0];
797 const char *pTraverse
;
798 const char *pBeginArg
;
799 if( f
.Open( &argv
[i
][1], CFile::modeRead
) ){
800 while( iNewArgc
< 5000 && f
.ReadString(s
) ) {
801 // Scan the string for args, and do the right thing.
802 pTraverse
= (const char *)s
;
803 while(iNewArgc
< 5000 && *pTraverse
) {
804 if(isspace(*pTraverse
)) {
809 // Extract to next space.
810 pBeginArg
= pTraverse
;
814 while(*pTraverse
&& !isspace(*pTraverse
));
815 apNewArgv
[iNewArgc
] = new char[pTraverse
- pBeginArg
+ 1];
816 memset(apNewArgv
[iNewArgc
], 0, pTraverse
- pBeginArg
+ 1);
817 strncpy(apNewArgv
[iNewArgc
], pBeginArg
, pTraverse
- pBeginArg
);
824 // Recurse if needed.
826 main(iNewArgc
, apNewArgv
);
829 // Free off the argvs (but not the very first one which we didn't allocate).
830 while(iNewArgc
> 1) {
832 delete [] apNewArgv
[iNewArgc
];
837 CFileRecord::AddFile( argv
[i
], NULL
, FALSE
, TRUE
);
842 // Only of the very bottom level of recursion do we do this.
843 if(iRecursion
== 1) {
845 // only write the results out if no errors encountered
846 if (mainReturn
== 0) {
847 CFileRecord::ProcessFiles();
849 CFileRecord::PrintTargets("OBJ_FILES", "\\");
851 CFileRecord::PrintTargets("LINK_OBJS", "+\\");
854 CFileRecord::PrintTargets("LINK_OBJS", "^");
856 CFileRecord::PrintSources();
857 CFileRecord::PrintBuildRules();
859 CFileRecord::PrintDependancies();
862 if(pAltFile
!= stdout
) {
864 if (mainReturn
!= 0) {
865 remove(outputFileName
); // kill output file if returning an error
871 if (iRecursion
== 0 )
873 // last time through -- clean up allocated CFileRecords!
878 next
= CFileRecord::fileMap
.GetStartPosition();
880 CFileRecord::fileMap
.GetNextAssoc( next
, name
, *(void**)&pFRec
);