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 FILE *pAltFile
= stdout
;
54 CStringArray includeDirectories
;
56 // turn a file, relative path or other into an absolute path
57 // This function copied from MFC 1.52
58 BOOL PASCAL
_AfxFullPath(LPSTR lpszPathOut
, LPCSTR lpszFileIn
)
59 // lpszPathOut = buffer of _MAX_PATH
60 // lpszFileIn = file, relative path or absolute path
61 // (both in ANSI character set)
64 if (OpenFile(lpszFileIn
, &of
, OF_PARSE
) != HFILE_ERROR
)
66 // of.szPathName is in the OEM character set
67 OemToAnsi(of
.szPathName
, lpszPathOut
);
68 AnsiUpper(lpszPathOut
); // paths in upper case just to be sure
73 TRACE1("Warning: could not parse the path %Fs\n", lpszFileIn
);
74 lstrcpy(lpszPathOut
, lpszFileIn
); // take it literally
75 AnsiUpper(lpszPathOut
); // paths in upper case just to be sure
80 void AddIncludeDirectory( char *pString
){
82 int len
= s
.GetLength();
83 if(len
> 0 && s
[len
- 1] != '\\' ){
86 includeDirectories
.Add(s
);
89 BOOL
FileExists( const char *pString
){
93 result
= _stat( pString
, &buf
);
97 void FixPathName(CString
& str
) {
98 str
.MakeUpper(); // all upper case
100 // now switch all forward slashes to back slashes
102 while ((index
= str
.Find('/')) != -1) {
103 str
.SetAt(index
, '\\');
107 void FATName(CString
& csLONGName
)
109 // Only relevant for 16 bits.
111 // Convert filename to FAT (8.3) equivalent.
114 if(GetShortPathName(csLONGName
, aBuffer
, 2048)) {
115 csLONGName
= aBuffer
;
125 CPtrArray m_includes
; // pointers to CFileRecords in fileMap
129 static CMapStringToPtr fileMap
; // contains all allocated CFileRecords
130 static CStringArray orderedFileNames
;
131 static CMapStringToPtr includeMap
; // pointer to CFileRecords in fileMap
132 static CMapStringToPtr noDependMap
;
134 CFileRecord( const char *shortName
, const char* pFullName
, BOOL bSystem
, BOOL bSource
):
135 m_shortName( shortName
),
139 m_bSource( bSource
),
142 m_pathName
= pFullName
;
143 FixPathName(m_pathName
); // all upper case for consistency
144 ASSERT(FindFileRecord(m_pathName
) == NULL
); // make sure it's not already in the map
145 fileMap
[m_pathName
] = this; // add this to the filemap, using the appropriate name as the key
147 orderedFileNames
.Add(m_pathName
); // remember the order we saw source files in
152 // open the file and grab all the includes.
159 char *a
= new char[2048];
161 char srcPath
[_MAX_PATH
];
163 // construct the full path
164 if (!_AfxFullPath(srcPath
, m_pathName
)) {
165 strcpy(srcPath
, m_pathName
);
168 // strip off the source filename to end up with just the path
169 LPSTR pTemp
= strrchr(srcPath
, '\\');
174 f
= fopen(m_pathName
, "r");
175 if(f
!= NULL
&& f
!= (FILE *)-1) {
176 setvbuf(f
, NULL
, _IOFBF
, 32768); // use a large file buffer
177 while(fgets(a
, 2047, f
)) {
178 // if the string "//{{NO_DEPENDENCIES}}" is at the start of one of the
179 // first 10 lines of a file, don't build dependencies on it or any
180 // of the files it includes
182 static char* pDependStr
= "//{{NO_DEPENDENCIES}}";
183 if (strncmp(a
, pDependStr
, strlen(pDependStr
)) == 0) {
184 noDependMap
[m_pathName
] = 0; // add it to the noDependMap
185 break; // no need for further processing of this file
189 // have to handle a variety of legal syntaxes that we find in our source files:
193 // if the first non-whitespace char is a '#', consider this line
195 pTemp
+= strspn(pTemp
, " \t"); // skip whitespace
197 ++pTemp
; // skip the '#'
198 pTemp
+= strspn(pTemp
, " \t"); // skip more whitespace
199 if( !strncmp(pTemp
, "include", 7) ){
200 pTemp
+= 7; // skip the "include"
201 pTemp
+= strspn(pTemp
, " \t"); // skip more whitespace
202 bSystem
= (*pTemp
== '<'); // mark if it's a system include or not
203 // forget system files -- we just have to search all the paths
204 // every time and never find them! This change alone speeds a full
205 // depend run on my system from 5 minutes to 3:15
206 // if (bSystem || (*pTemp == '"')) {
208 LPSTR pStart
= pTemp
+ 1; // mark the start of the string
209 pTemp
= pStart
+ strcspn(pStart
, ">\" "); // find the end of the string
210 *pTemp
= 0; // terminate the string
212 // construct the full pathname from the path part of the
213 // source file and the name listed here
216 CFileRecord
*pAddMe
= AddFile( pStart
, fullName
, bSystem
);
218 m_includes
.Add(pAddMe
);
229 void PrintIncludes(){
231 while( i
< m_includes
.GetSize() ){
232 CFileRecord
*pRec
= (CFileRecord
*) m_includes
[i
];
234 // Don't write out files that don't exist or are not in the namespace
235 // of the programs using it (netscape_AppletMozillaContext.h doesn't
236 // mix well with 16 bits).
237 // Also don't write out files that are in the noDependMap
239 if( !pRec
->m_bVisited
&& pRec
->m_pathName
.GetLength() != 0 && !noDependMap
.Lookup(pRec
->m_pathName
, lookupJunk
)) {
241 // not supposed to have a file in the list that doesn't exist
242 ASSERT(FileExists(pRec
->m_pathName
));
245 csOutput
= pRec
->m_pathName
;
248 fprintf(pAltFile
, "\\\n %s ", (const char *) csOutput
);
250 // mark this one as done so we don't do it more than once
251 pRec
->m_bVisited
= TRUE
;
253 pRec
->PrintIncludes();
265 // clear all the m_bVisisted flags so we can use it to keep track
266 // of whether we've already output this file as a dependency
267 next
= fileMap
.GetStartPosition();
269 fileMap
.GetNextAssoc( next
, name
, *(void**)&pRec
);
270 pRec
->m_bVisited
= FALSE
;
273 char fname
[_MAX_FNAME
];
275 if (pRec
->m_pathName
.GetLength() != 0) {
277 fprintf(pAltFile
, "\n\n\n%s:\t", m_pathName
);
281 csOutput
= m_pathName
;
284 _splitpath( csOutput
, NULL
, NULL
, fname
, NULL
);
286 fprintf(pAltFile
, "\n\n\n$(OUTDIR)\\%s.obj: %s ", fname
, (const char*) csOutput
);
288 m_bVisited
= TRUE
; // mark it as done so we won't do it again
294 static CString
NormalizeFileName( const char* pName
){
295 return CString(pName
);
298 static CFileRecord
* FindFileRecord( const char *pName
){
299 CFileRecord
* pRec
= NULL
;
302 fileMap
.Lookup(name
, (void*&)pRec
);
306 static CFileRecord
* AddFile( const char* pShortName
, const char* pFullName
, BOOL bSystem
= FALSE
,
307 BOOL bSource
= FALSE
){
309 char fullName
[_MAX_PATH
];
312 CString fixedShortName
;
315 // normalize the name
316 fixedShortName
= pShortName
;
317 FixPathName(fixedShortName
);
318 pShortName
= fixedShortName
;
320 // if it is source, we might be getting an obj file. If we do,
321 // convert it to a c or c++ file.
322 if( bSource
&& (strcmp(GetExt(pShortName
),".obj") == 0) ){
323 char path_buffer
[_MAX_PATH
];
324 char fname
[_MAX_FNAME
] = "";
327 _splitpath( pShortName
, NULL
, NULL
, fname
, NULL
);
328 if( FileExists( s
= CString(fname
) + ".cpp") ){
332 else if( FileExists( s
= CString(fname
) + ".c" ) ){
341 // if pFullName was not constructed, construct it here based on the current directory
343 _AfxFullPath(fullName
, pShortName
);
344 pFullName
= fullName
;
347 // first check to see if we already have this exact file
348 CFileRecord
*pRec
= FindFileRecord(pFullName
);
350 // if not found and not a source file check the header list --
351 // all files we've found in include directories are in the includeMap.
352 // we can save gobs of time by getting it from there
353 if (!pRec
&& !bSource
)
354 includeMap
.Lookup(fixedShortName
, (void*&)pRec
);
357 // not in one of our lists, start scrounging on disk
359 // check the fullname first
360 if (FileExists(pFullName
)) {
361 foundName
= pFullName
;
365 // if still not found, search the include paths
367 while( i
< includeDirectories
.GetSize() ){
368 if( FileExists( includeDirectories
[i
] + pShortName
) ){
369 foundName
= includeDirectories
[i
] + pShortName
;
382 // source files are not allowed to be missing
383 if (bSource
&& !pRec
&& !bFound
) {
384 fprintf(stderr
, "Source file: %s doesn't exist\n", pFullName
);
385 mainReturn
= -1; // exit with an error, don't write out the results
389 if (!pRec
&& !bFound
&& !bSystem
) {
390 fprintf(stderr
, "Header not found: %s (%s)\n", pShortName
, pFullName
);
394 // if none of the above logic found it already in the list,
395 // must be a new file, add it to the list
396 if (bFound
&& (pRec
== NULL
)) {
397 pRec
= new CFileRecord( pShortName
, foundName
, bSystem
, bSource
);
399 // if this one isn't a source file add it to the includeMap
400 // for performance reasons (so we can find it there next time rather
401 // than having to search the file system again)
403 includeMap
[pShortName
] = pRec
;
410 static void PrintDependancies(){
416 // use orderedFileNames to preserve order
417 for (int pos
= 0; pos
< orderedFileNames
.GetSize(); pos
++) {
418 pRec
= FindFileRecord(orderedFileNames
[pos
]);
419 if(pRec
&& pRec
->m_bSource
){
430 if( m_includes
.GetSize() != 0 ){
431 fprintf(pAltFile
, "\n\n\n%s: \\\n",m_pathName
);
433 while( i
< m_includes
.GetSize() ){
434 pRec
= (CFileRecord
*) m_includes
[i
];
435 fprintf(pAltFile
, "\t\t\t%s\t\\\n",pRec
->m_pathName
);
441 static void PrintDependancies2(){
447 next
= fileMap
.GetStartPosition();
449 fileMap
.GetNextAssoc( next
, name
, *(void**)&pRec
);
450 pRec
->PrintDepend2();
455 static void PrintTargets(const char *pMacroName
, const char *pDelimiter
){
461 BOOL bNeedDelimiter
= FALSE
;
462 fprintf(pAltFile
, "%s = ", pMacroName
);
464 // use orderedFileNames to preserve target order
465 for (int pos
= 0; pos
< orderedFileNames
.GetSize(); pos
++) {
466 pRec
= FindFileRecord(orderedFileNames
[pos
]);
469 if( pRec
&& pRec
->m_bSource
&& pRec
->m_pathName
.GetLength() != 0){
470 char fname
[_MAX_FNAME
];
473 csOutput
= pRec
->m_pathName
;
476 _splitpath( csOutput
, NULL
, NULL
, fname
, NULL
);
479 fprintf(pAltFile
, "%s\n", pDelimiter
);
480 bNeedDelimiter
= FALSE
;
483 fprintf(pAltFile
, " $(OUTDIR)\\%s.obj ", fname
);
484 bNeedDelimiter
= TRUE
;
487 fprintf(pAltFile
, "\n\n\n");
490 static CString
DirDefine( const char *pPath
){
491 char path_buffer
[_MAX_PATH
];
492 char dir
[_MAX_DIR
] = "";
493 char dir2
[_MAX_DIR
] = "";
494 char fname
[_MAX_FNAME
] = "";
495 char ext
[_MAX_EXT
] = "";
498 _splitpath( pPath
, 0, dir
, 0, ext
);
502 while( dir
&& !bDone
){
503 // remove the trailing slash
504 dir
[ strlen(dir
)-1] = 0;
505 _splitpath( dir
, 0, dir2
, fname
, 0 );
506 if( strcmp( fname
, "SRC" ) == 0 ){
513 s
= CString(fname
) + "_" + (ext
+1);
518 static void PrintSources(){
520 CString dirName
, newDirName
;
522 for( i
=0; i
< orderedFileNames
.GetSize(); i
++ ){
523 newDirName
= DirDefine( orderedFileNames
[i
] );
524 if( newDirName
!= dirName
){
525 fprintf( pAltFile
, "\n\n\nFILES_%s= $(FILES_%s) \\",
526 (const char*)newDirName
, (const char*)newDirName
);
527 dirName
= newDirName
;
529 fprintf( pAltFile
, "\n\t%s^", (const char*)orderedFileNames
[i
] );
533 static CString
SourceDirName( const char *pPath
, BOOL bFileName
){
534 char path_buffer
[_MAX_PATH
];
535 char drive
[_MAX_DRIVE
] = "";
536 char dir
[_MAX_DIR
] = "";
537 char fname
[_MAX_FNAME
] = "";
538 char ext
[_MAX_EXT
] = "";
541 _splitpath( pPath
, drive
, dir
, fname
, ext
);
543 s
= CString(drive
) + dir
;
545 s
+= CString("FNAME") + ext
;
548 // remove the trailing slash
549 s
= s
.Left( s
.GetLength() - 1 );
555 static CString
GetExt( const char *pPath
){
556 char ext
[_MAX_EXT
] = "";
558 _splitpath( pPath
, 0,0,0, ext
);
560 CString s
= CString(ext
);
565 static void PrintBuildRules(){
569 CMapStringToPtr dirList
;
571 for( i
=0; i
< orderedFileNames
.GetSize(); i
++ ){
572 dirList
[ SourceDirName(orderedFileNames
[i
], TRUE
) ]= 0;
579 next
= dirList
.GetStartPosition();
581 dirList
.GetNextAssoc( next
, name
, pVal
);
582 CString dirDefine
= DirDefine( name
);
583 CString ext
= GetExt( name
);
584 name
= SourceDirName( name
, FALSE
);
585 CString response
= dirDefine
.Left(8);
588 "\n\n\n{%s}%s{$(OUTDIR)}.obj:\n"
589 "\t@rem <<$(OUTDIR)\\%s.cl\n"
593 "\t$(CPP) @$(OUTDIR)\\%s.cl %%s\n",
596 (const char*)response
,
597 (const char*)dirDefine
,
598 (const char*)response
603 "\t@rem <<$(OUTDIR)\\%s.cl\n"
609 "\t$(CPP) @$(OUTDIR)\\%s.cl\n"
611 (const char*)dirDefine
,
612 (const char*)response
,
613 (const char*)dirDefine
,
614 (const char*)dirDefine
,
615 (const char*)response
620 // Loop through one more time and build the final batch build
624 "\n\n\nBATCH_BUILD_OBJECTS:\t\t\\\n");
626 next
= dirList
.GetStartPosition();
628 dirList
.GetNextAssoc( next
, name
, pVal
);
629 CString dirDefine
= DirDefine( name
);
632 "\tBATCH_%s\t\t\\\n", dirDefine
);
640 static void ProcessFiles(){
646 // search all the files for headers, adding each one to the list when found
647 // rather than do it recursively, it simple marks each one it's done
648 // and starts over, stopping only when all are marked as done
650 next
= fileMap
.GetStartPosition();
652 fileMap
.GetNextAssoc( next
, name
, *(void**)&pRec
);
653 if( pRec
->m_bVisited
== FALSE
&& pRec
->m_bSystem
== FALSE
){
654 // mark this file as already done so we don't read it again
655 // to find its headers
656 pRec
->m_bVisited
= TRUE
;
658 // Start searching from the beginning again
659 // because ProcessFile may have added new files
660 // and changed the GetNextAssoc order
661 next
= fileMap
.GetStartPosition();
670 CMapStringToPtr
CFileRecord::fileMap
; // contains all allocated CFileRecords
671 CStringArray
CFileRecord::orderedFileNames
;
672 CMapStringToPtr
CFileRecord::includeMap
; // pointers to CFileRecords in fileMap
673 CMapStringToPtr
CFileRecord::noDependMap
; // no data, just an index
675 int main( int argc
, char** argv
){
678 static int iRecursion
= 0; // Track levels of recursion.
679 static CString outputFileName
;
685 if( argv
[i
][0] == '-' || argv
[i
][0] == '/' ){
686 switch( argv
[i
][1] ){
690 if( argv
[i
][2] != 0 ){
691 pStr
= &(argv
[i
][2]);
697 if( pStr
== 0 || *pStr
== '-' || *pStr
== '/' ){
701 AddIncludeDirectory( pStr
);
707 if( argv
[i
][2] != 0 ){
708 pStr
= &(argv
[i
][2]);
714 if( pStr
== 0 || *pStr
== '-' || *pStr
== '/'){
720 if( f
.Open( pStr
, CFile::modeRead
) ){
721 while(f
.ReadString(s
)){
725 CFileRecord::AddFile( s
, NULL
, FALSE
, TRUE
);
731 fprintf(stderr
,"makedep: file not found: %s", pStr
);
739 if( argv
[i
][2] != 0 ){
740 pStr
= &(argv
[i
][2]);
746 if( pStr
== 0 || *pStr
== '-' || *pStr
== '/'){
752 outputFileName
= pStr
;
753 if(!(pAltFile
= fopen(pStr
, "w+"))) {
754 fprintf(stderr
, "makedep: file not found: %s", pStr
);
761 if( argv
[i
][2] == '6') {
777 fprintf(stderr
, "usage: makedep -I <dirname> -F <filelist> <filename>\n"
778 " -I <dirname> Directory name, can be repeated\n"
779 " -F <filelist> List of files to scan, one per line\n"
780 " -O <outputFile> File to write output, default stdout\n");
784 else if( argv
[i
][0] == '@' ){
785 // file contains our commands.
789 char **apNewArgv
= new char*[5000];
790 memset(apNewArgv
, 0, sizeof(apNewArgv
));
792 // First one is always the name of the exe.
793 apNewArgv
[0] = argv
[0];
796 const char *pTraverse
;
797 const char *pBeginArg
;
798 if( f
.Open( &argv
[i
][1], CFile::modeRead
) ){
799 while( iNewArgc
< 5000 && f
.ReadString(s
) ) {
800 // Scan the string for args, and do the right thing.
801 pTraverse
= (const char *)s
;
802 while(iNewArgc
< 5000 && *pTraverse
) {
803 if(isspace(*pTraverse
)) {
808 // Extract to next space.
809 pBeginArg
= pTraverse
;
813 while(*pTraverse
&& !isspace(*pTraverse
));
814 apNewArgv
[iNewArgc
] = new char[pTraverse
- pBeginArg
+ 1];
815 memset(apNewArgv
[iNewArgc
], 0, pTraverse
- pBeginArg
+ 1);
816 strncpy(apNewArgv
[iNewArgc
], pBeginArg
, pTraverse
- pBeginArg
);
823 // Recurse if needed.
825 main(iNewArgc
, apNewArgv
);
828 // Free off the argvs (but not the very first one which we didn't allocate).
829 while(iNewArgc
> 1) {
831 delete [] apNewArgv
[iNewArgc
];
836 CFileRecord::AddFile( argv
[i
], NULL
, FALSE
, TRUE
);
841 // Only of the very bottom level of recursion do we do this.
842 if(iRecursion
== 1) {
844 // only write the results out if no errors encountered
845 if (mainReturn
== 0) {
846 CFileRecord::ProcessFiles();
848 CFileRecord::PrintTargets("OBJ_FILES", "\\");
850 CFileRecord::PrintTargets("LINK_OBJS", "+\\");
853 CFileRecord::PrintTargets("LINK_OBJS", "^");
855 CFileRecord::PrintSources();
856 CFileRecord::PrintBuildRules();
858 CFileRecord::PrintDependancies();
861 if(pAltFile
!= stdout
) {
863 if (mainReturn
!= 0) {
864 remove(outputFileName
); // kill output file if returning an error
870 if (iRecursion
== 0 )
872 // last time through -- clean up allocated CFileRecords!
877 next
= CFileRecord::fileMap
.GetStartPosition();
879 CFileRecord::fileMap
.GetNextAssoc( next
, name
, *(void**)&pFRec
);