/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Dependency building hack // #include #include #include #include #include #include int mainReturn = 0; BOOL b16 = FALSE; BOOL bSimple = FALSE; // freopen won't work on stdout in win16 FILE *pAltFile = stdout; CStringArray includeDirectories; // turn a file, relative path or other into an absolute path // This function copied from MFC 1.52 BOOL PASCAL _AfxFullPath(LPSTR lpszPathOut, LPCSTR lpszFileIn) // lpszPathOut = buffer of _MAX_PATH // lpszFileIn = file, relative path or absolute path // (both in ANSI character set) { OFSTRUCT of; if (OpenFile(lpszFileIn, &of, OF_PARSE) != HFILE_ERROR) { // of.szPathName is in the OEM character set OemToAnsi(of.szPathName, lpszPathOut); AnsiUpper(lpszPathOut); // paths in upper case just to be sure return TRUE; } else { TRACE1("Warning: could not parse the path %Fs\n", lpszFileIn); lstrcpy(lpszPathOut, lpszFileIn); // take it literally AnsiUpper(lpszPathOut); // paths in upper case just to be sure return FALSE; } } void AddIncludeDirectory( char *pString ){ CString s = pString; int len = s.GetLength(); if(len > 0 && s[len - 1] != '\\' ){ s += "\\"; } includeDirectories.Add(s); } BOOL FileExists( const char *pString ){ struct _stat buf; int result; result = _stat( pString, &buf ); return (result == 0); } void FixPathName(CString& str) { str.MakeUpper(); // all upper case // now switch all forward slashes to back slashes int index; while ((index = str.Find('/')) != -1) { str.SetAt(index, '\\'); } } void FATName(CString& csLONGName) { // Only relevant for 16 bits. if(b16) { // Convert filename to FAT (8.3) equivalent. char aBuffer[2048]; if(GetShortPathName(csLONGName, aBuffer, 2048)) { csLONGName = aBuffer; } } } class CFileRecord { public: CString m_shortName; CString m_pathName; CPtrArray m_includes; // pointers to CFileRecords in fileMap BOOL m_bVisited; BOOL m_bSystem; BOOL m_bSource; static CMapStringToPtr fileMap; // contains all allocated CFileRecords static CStringArray orderedFileNames; static CMapStringToPtr includeMap; // pointer to CFileRecords in fileMap static CMapStringToPtr noDependMap; CFileRecord( const char *shortName, const char* pFullName, BOOL bSystem, BOOL bSource): m_shortName( shortName ), m_pathName(), m_includes(), m_bVisited(FALSE), m_bSource( bSource ), m_bSystem(bSystem){ m_pathName = pFullName; FixPathName(m_pathName); // all upper case for consistency ASSERT(FindFileRecord(m_pathName) == NULL); // make sure it's not already in the map fileMap[m_pathName] = this; // add this to the filemap, using the appropriate name as the key if (bSource) { orderedFileNames.Add(m_pathName); // remember the order we saw source files in } } // // open the file and grab all the includes. // void ProcessFile(){ FILE *f; CString fullName; BOOL bSystem; DWORD lineCntr = 0; char *a = new char[2048]; memset(a, 0, 2048); char srcPath[_MAX_PATH]; // construct the full path if (!_AfxFullPath(srcPath, m_pathName)) { strcpy(srcPath, m_pathName); } // strip off the source filename to end up with just the path LPSTR pTemp = strrchr(srcPath, '\\'); if (pTemp) { *(pTemp + 1) = 0; } f = fopen(m_pathName, "r"); if(f != NULL && f != (FILE *)-1) { setvbuf(f, NULL, _IOFBF, 32768); // use a large file buffer while(fgets(a, 2047, f)) { // if the string "//{{NO_DEPENDENCIES}}" is at the start of one of the // first 10 lines of a file, don't build dependencies on it or any // of the files it includes if (lineCntr < 10) { static char* pDependStr = "//{{NO_DEPENDENCIES}}"; if (strncmp(a, pDependStr, strlen(pDependStr)) == 0) { noDependMap[m_pathName] = 0; // add it to the noDependMap break; // no need for further processing of this file } } ++lineCntr; // have to handle a variety of legal syntaxes that we find in our source files: // #include // # include // #include // if the first non-whitespace char is a '#', consider this line pTemp = a; pTemp += strspn(pTemp, " \t"); // skip whitespace if (*pTemp == '#') { ++pTemp; // skip the '#' pTemp += strspn(pTemp, " \t"); // skip more whitespace if( !strncmp(pTemp, "include", 7) ){ pTemp += 7; // skip the "include" pTemp += strspn(pTemp, " \t"); // skip more whitespace bSystem = (*pTemp == '<'); // mark if it's a system include or not // forget system files -- we just have to search all the paths // every time and never find them! This change alone speeds a full // depend run on my system from 5 minutes to 3:15 // if (bSystem || (*pTemp == '"')) { if (*pTemp == '"') { LPSTR pStart = pTemp + 1; // mark the start of the string pTemp = pStart + strcspn(pStart, ">\" "); // find the end of the string *pTemp = 0; // terminate the string // construct the full pathname from the path part of the // source file and the name listed here fullName = srcPath; fullName += pStart; CFileRecord *pAddMe = AddFile( pStart, fullName, bSystem ); if (pAddMe) { m_includes.Add(pAddMe); } } } } } fclose(f); } delete [] a; } void PrintIncludes(){ int i = 0; while( i < m_includes.GetSize() ){ CFileRecord *pRec = (CFileRecord*) m_includes[i]; // Don't write out files that don't exist or are not in the namespace // of the programs using it (netscape_AppletMozillaContext.h doesn't // mix well with 16 bits). // Also don't write out files that are in the noDependMap void* lookupJunk; if( !pRec->m_bVisited && pRec->m_pathName.GetLength() != 0 && !noDependMap.Lookup(pRec->m_pathName, lookupJunk)) { // not supposed to have a file in the list that doesn't exist ASSERT(FileExists(pRec->m_pathName)); CString csOutput; csOutput = pRec->m_pathName; FATName(csOutput); fprintf(pAltFile, "\\\n %s ", (const char *) csOutput ); // mark this one as done so we don't do it more than once pRec->m_bVisited = TRUE; pRec->PrintIncludes(); } i++; } } void PrintDepend(){ CFileRecord *pRec; BOOL bFound; POSITION next; CString name; // clear all the m_bVisisted flags so we can use it to keep track // of whether we've already output this file as a dependency next = fileMap.GetStartPosition(); while( next ){ fileMap.GetNextAssoc( next, name, *(void**)&pRec ); pRec->m_bVisited = FALSE; } char fname[_MAX_FNAME]; if (pRec->m_pathName.GetLength() != 0) { if( bSimple ){ fprintf(pAltFile, "\n\n\n%s:\t", m_pathName ); } else { CString csOutput; csOutput = m_pathName; FATName(csOutput); _splitpath( csOutput, NULL, NULL, fname, NULL ); fprintf(pAltFile, "\n\n\n$(OUTDIR)\\%s.obj: %s ", fname, (const char*) csOutput ); } m_bVisited = TRUE; // mark it as done so we won't do it again PrintIncludes(); } } static CString NormalizeFileName( const char* pName ){ return CString(pName); } static CFileRecord* FindFileRecord( const char *pName ){ CFileRecord* pRec = NULL; CString name(pName); FixPathName(name); fileMap.Lookup(name, (void*&)pRec); return(pRec); } public: static CFileRecord* AddFile( const char* pShortName, const char* pFullName, BOOL bSystem = FALSE, BOOL bSource = FALSE ){ char fullName[_MAX_PATH]; BOOL bFound = FALSE; CString foundName; CString fixedShortName; CString s; // normalize the name fixedShortName = pShortName; FixPathName(fixedShortName); pShortName = fixedShortName; // if it is source, we might be getting an obj file. If we do, // convert it to a c or c++ file. if( bSource && (strcmp(GetExt(pShortName),".obj") == 0) ){ char path_buffer[_MAX_PATH]; char fname[_MAX_FNAME] = ""; CString s; _splitpath( pShortName, NULL, NULL, fname, NULL ); if( FileExists( s = CString(fname) + ".cpp") ){ pShortName = s; pFullName = s; } else if( FileExists( s = CString(fname) + ".c" ) ){ pShortName = s; pFullName = s; } else { return 0; } } // if pFullName was not constructed, construct it here based on the current directory if (!pFullName) { _AfxFullPath(fullName, pShortName); pFullName = fullName; } // first check to see if we already have this exact file CFileRecord *pRec = FindFileRecord(pFullName); // if not found and not a source file check the header list -- // all files we've found in include directories are in the includeMap. // we can save gobs of time by getting it from there if (!pRec && !bSource) includeMap.Lookup(fixedShortName, (void*&)pRec); if (!pRec) { // not in one of our lists, start scrounging on disk // check the fullname first if (FileExists(pFullName)) { foundName = pFullName; bFound = TRUE; } else { // if still not found, search the include paths int i = 0; while( i < includeDirectories.GetSize() ){ if( FileExists( includeDirectories[i] + pShortName ) ){ foundName = includeDirectories[i] + pShortName; bFound = TRUE; break; } i++; } } } else { // we found it bFound = TRUE; } // source files are not allowed to be missing if (bSource && !pRec && !bFound) { fprintf(stderr, "Source file: %s doesn't exist\n", pFullName); mainReturn = -1; // exit with an error, don't write out the results } #ifdef _DEBUG if (!pRec && !bFound && !bSystem) { fprintf(stderr, "Header not found: %s (%s)\n", pShortName, pFullName); } #endif // if none of the above logic found it already in the list, // must be a new file, add it to the list if (bFound && (pRec == NULL)) { pRec = new CFileRecord( pShortName, foundName, bSystem, bSource); // if this one isn't a source file add it to the includeMap // for performance reasons (so we can find it there next time rather // than having to search the file system again) if (!bSource) { includeMap[pShortName] = pRec; } } return pRec; } static void PrintDependancies(){ CFileRecord *pRec; BOOL bFound; POSITION next; CString name; // use orderedFileNames to preserve order for (int pos = 0; pos < orderedFileNames.GetSize(); pos++) { pRec = FindFileRecord(orderedFileNames[pos]); if(pRec && pRec->m_bSource ){ pRec->PrintDepend(); } } } void PrintDepend2(){ CFileRecord *pRec; int i; if( m_includes.GetSize() != 0 ){ fprintf(pAltFile, "\n\n\n%s: \\\n",m_pathName ); i = 0; while( i < m_includes.GetSize() ){ pRec = (CFileRecord*) m_includes[i]; fprintf(pAltFile, "\t\t\t%s\t\\\n",pRec->m_pathName ); i++; } } } static void PrintDependancies2(){ CFileRecord *pRec; BOOL bFound; POSITION next; CString name; next = fileMap.GetStartPosition(); while( next ){ fileMap.GetNextAssoc( next, name, *(void**)&pRec ); pRec->PrintDepend2(); } } static void PrintTargets(const char *pMacroName, const char *pDelimiter){ CFileRecord *pRec; BOOL bFound; POSITION next; CString name; BOOL bNeedDelimiter = FALSE; fprintf(pAltFile, "%s = ", pMacroName); // use orderedFileNames to preserve target order for (int pos = 0; pos < orderedFileNames.GetSize(); pos++) { pRec = FindFileRecord(orderedFileNames[pos]); ASSERT(pRec); if( pRec && pRec->m_bSource && pRec->m_pathName.GetLength() != 0){ char fname[_MAX_FNAME]; CString csOutput; csOutput = pRec->m_pathName; FATName(csOutput); _splitpath( csOutput, NULL, NULL, fname, NULL ); if(bNeedDelimiter) { fprintf(pAltFile, "%s\n", pDelimiter); bNeedDelimiter = FALSE; } fprintf(pAltFile, " $(OUTDIR)\\%s.obj ", fname ); bNeedDelimiter = TRUE; } } fprintf(pAltFile, "\n\n\n"); } static CString DirDefine( const char *pPath ){ char path_buffer[_MAX_PATH]; char dir[_MAX_DIR] = ""; char dir2[_MAX_DIR] = ""; char fname[_MAX_FNAME] = ""; char ext[_MAX_EXT] = ""; CString s; _splitpath( pPath, 0, dir, 0, ext ); BOOL bDone = FALSE; while( dir && !bDone){ // remove the trailing slash dir[ strlen(dir)-1] = 0; _splitpath( dir, 0, dir2, fname, 0 ); if( strcmp( fname, "SRC" ) == 0 ){ strcpy( dir, dir2 ); } else { bDone = TRUE; } } s = CString(fname) + "_" + (ext+1); return s; } static void PrintSources(){ int i; CString dirName, newDirName; for( i=0; i< orderedFileNames.GetSize(); i++ ){ newDirName= DirDefine( orderedFileNames[i] ); if( newDirName != dirName ){ fprintf( pAltFile, "\n\n\nFILES_%s= $(FILES_%s) \\", (const char*)newDirName, (const char*)newDirName ); dirName = newDirName; } fprintf( pAltFile, "\n\t%s^", (const char*)orderedFileNames[i] ); } } static CString SourceDirName( const char *pPath, BOOL bFileName){ char path_buffer[_MAX_PATH]; char drive[_MAX_DRIVE] = ""; char dir[_MAX_DIR] = ""; char fname[_MAX_FNAME] = ""; char ext[_MAX_EXT] = ""; CString s; _splitpath( pPath, drive, dir, fname, ext ); s = CString(drive) + dir; if( bFileName ){ s += CString("FNAME") + ext; } else { // remove the trailing slash s = s.Left( s.GetLength() - 1 ); } return s; } static CString GetExt( const char *pPath){ char ext[_MAX_EXT] = ""; _splitpath( pPath, 0,0,0, ext ); CString s = CString(ext); s.MakeLower(); return s; } static void PrintBuildRules(){ int i; CString dirName; CMapStringToPtr dirList; for( i=0; i< orderedFileNames.GetSize(); i++ ){ dirList[ SourceDirName(orderedFileNames[i], TRUE) ]= 0; } POSITION next; CString name; void *pVal; next = dirList.GetStartPosition(); while( next ){ dirList.GetNextAssoc( next, name, pVal); CString dirDefine = DirDefine( name ); CString ext = GetExt( name ); name = SourceDirName( name, FALSE ); CString response = dirDefine.Left(8); fprintf( pAltFile, "\n\n\n{%s}%s{$(OUTDIR)}.obj:\n" "\t@rem <<$(OUTDIR)\\%s.cl\n" "\t$(CFILEFLAGS)\n" "\t$(CFLAGS_%s)\n" "<m_bVisited == FALSE && pRec->m_bSystem == FALSE ){ // mark this file as already done so we don't read it again // to find its headers pRec->m_bVisited = TRUE; pRec->ProcessFile(); // Start searching from the beginning again // because ProcessFile may have added new files // and changed the GetNextAssoc order next = fileMap.GetStartPosition(); } } } }; CMapStringToPtr CFileRecord::fileMap; // contains all allocated CFileRecords CStringArray CFileRecord::orderedFileNames; CMapStringToPtr CFileRecord::includeMap; // pointers to CFileRecords in fileMap CMapStringToPtr CFileRecord::noDependMap; // no data, just an index int main( int argc, char** argv ){ int i = 1; char *pStr; static int iRecursion = 0; // Track levels of recursion. static CString outputFileName; // Entering. iRecursion++; while( i < argc ){ if( argv[i][0] == '-' || argv[i][0] == '/' ){ switch( argv[i][1] ){ case 'i': case 'I': if( argv[i][2] != 0 ){ pStr = &(argv[i][2]); } else { i++; pStr = argv[i]; } if( pStr == 0 || *pStr == '-' || *pStr == '/' ){ goto usage; } else { AddIncludeDirectory( pStr ); } break; case 'f': case 'F': if( argv[i][2] != 0 ){ pStr = &(argv[i][2]); } else { i++; pStr = argv[i]; } if( pStr == 0 || *pStr == '-' || *pStr == '/'){ goto usage; } else { CStdioFile f; CString s; if( f.Open( pStr, CFile::modeRead ) ){ while(f.ReadString(s)){ s.TrimLeft(); s.TrimRight(); if( s.GetLength() ){ CFileRecord::AddFile( s, NULL, FALSE, TRUE ); } } f.Close(); } else { fprintf(stderr,"makedep: file not found: %s", pStr ); exit(-1); } } break; case 'o': case 'O': if( argv[i][2] != 0 ){ pStr = &(argv[i][2]); } else { i++; pStr = argv[i]; } if( pStr == 0 || *pStr == '-' || *pStr == '/'){ goto usage; } else { CStdioFile f; CString s; outputFileName = pStr; if(!(pAltFile = fopen(pStr, "w+"))) { fprintf(stderr, "makedep: file not found: %s", pStr ); exit(-1); } } break; case '1': if( argv[i][2] == '6') { b16 = TRUE; } break; case 's': case 'S': bSimple = TRUE; break; case 'h': case 'H': case '?': usage: fprintf(stderr, "usage: makedep -I -F \n" " -I Directory name, can be repeated\n" " -F List of files to scan, one per line\n" " -O File to write output, default stdout\n"); exit(-1); } } else if( argv[i][0] == '@' ){ // file contains our commands. CStdioFile f; CString s; int iNewArgc = 0; char **apNewArgv = new char*[5000]; memset(apNewArgv, 0, sizeof(apNewArgv)); // First one is always the name of the exe. apNewArgv[0] = argv[0]; iNewArgc++; const char *pTraverse; const char *pBeginArg; if( f.Open( &argv[i][1], CFile::modeRead ) ){ while( iNewArgc < 5000 && f.ReadString(s) ) { // Scan the string for args, and do the right thing. pTraverse = (const char *)s; while(iNewArgc < 5000 && *pTraverse) { if(isspace(*pTraverse)) { pTraverse++; continue; } // Extract to next space. pBeginArg = pTraverse; do { pTraverse++; } while(*pTraverse && !isspace(*pTraverse)); apNewArgv[iNewArgc] = new char[pTraverse - pBeginArg + 1]; memset(apNewArgv[iNewArgc], 0, pTraverse - pBeginArg + 1); strncpy(apNewArgv[iNewArgc], pBeginArg, pTraverse - pBeginArg); iNewArgc++; } } f.Close(); } // Recurse if needed. if(iNewArgc > 1) { main(iNewArgc, apNewArgv); } // Free off the argvs (but not the very first one which we didn't allocate). while(iNewArgc > 1) { iNewArgc--; delete [] apNewArgv[iNewArgc]; } delete [] apNewArgv; } else { CFileRecord::AddFile( argv[i], NULL, FALSE, TRUE ); } i++; } // Only of the very bottom level of recursion do we do this. if(iRecursion == 1) { // only write the results out if no errors encountered if (mainReturn == 0) { CFileRecord::ProcessFiles(); if( !bSimple ){ CFileRecord::PrintTargets("OBJ_FILES", "\\"); if(b16) { CFileRecord::PrintTargets("LINK_OBJS", "+\\"); } else { CFileRecord::PrintTargets("LINK_OBJS", "^"); } CFileRecord::PrintSources(); CFileRecord::PrintBuildRules(); } CFileRecord::PrintDependancies(); } if(pAltFile != stdout) { fclose(pAltFile); if (mainReturn != 0) { remove(outputFileName); // kill output file if returning an error } } } iRecursion--; if (iRecursion == 0 ) { // last time through -- clean up allocated CFileRecords! CFileRecord *pFRec; CString name; POSITION next; next = CFileRecord::fileMap.GetStartPosition(); while( next ){ CFileRecord::fileMap.GetNextAssoc( next, name, *(void**)&pFRec ); delete pFRec; } } return mainReturn; }