/* This program cleans build.exe trees. */ #ifndef BUILD_STABILIZE_EXPORTS #include "windows.h" #define NUMBER_OF(x) (sizeof(x)/sizeof((x)[0])) #include #include #include // error messages const static wchar_t errorNT[] = L"This program requires Windows NT.\n"; #define NumberOf(x) (sizeof(x)/sizeof((x)[0])) void Log(const char* s, const wchar_t* q) { printf(s, q); } void Log(const wchar_t* s) { printf("%ls\n", s); } void Fail(const wchar_t* s) { Log(s); exit(EXIT_FAILURE); } void LogRecurse(const wchar_t* s) { printf("Recurse %ls\n", s); } void LogDelete(const wchar_t* s) { printf("Delete %ls\n", s); } bool IsDotOrDotDot(const wchar_t* s) { return ( s[0] == '.' && ( (s[1] == 0) || (s[1] == '.' && s[2] == 0) ) ); } #endif /* BUILD_STABILIZE_EXPORTS */ #ifdef BUILD_STABILIZE_EXPORTS #define LogRecurse(x) /* nothing */ #define LogDelete(x) /* nothing */ #define wchar_t char #define WIN32_FIND_DATAW WIN32_FIND_DATAA #define FindFirstFileW FindFirstFileA #define wcscpy strcpy #define DeleteFileW DeleteFileA #define RemoveDirectoryW RemoveDirectoryA #define wcslen strlen #define FindNextFileW FindNextFileA #endif /* BUILD_STABILIZE_EXPORTS */ void DeleteDirectory(wchar_t* directory, int length, WIN32_FIND_DATAW* wfd) { directory[length] = 0; LogRecurse(directory); directory[length] = '\\'; directory[length+1] = '*'; directory[length+2] = 0; HANDLE hFind = FindFirstFileW(directory, wfd); if (hFind != INVALID_HANDLE_VALUE) { __try { do { if (IsDotOrDotDot(wfd->cFileName)) { continue; } DWORD dwFileAttributes = wfd->dwFileAttributes; directory[length] = '\\'; wcscpy(directory + length + 1, wfd->cFileName); if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { DeleteDirectory(directory, length + 1 + wcslen(wfd->cFileName), wfd); } else { if (dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) { //if (!SetFileAttributesW(directory, dwFileAttributes & ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) { // ... } } if (DeleteFileW(directory)) { LogDelete(directory); } else { // ... } } } while (FindNextFileW(hFind, wfd)); } __finally { FindClose(hFind); } } directory[length] = 0; RemoveDirectoryW(directory); LogDelete(directory); } #ifndef BUILD_STABILIZE_EXPORTS void CleanDirectory( const wchar_t* obj, wchar_t* directory, int length, WIN32_FIND_DATAW* wfd ) { directory[length] = 0; LogRecurse(directory); // clean build[d,fre,chk].[log,wrn,err] builds const static wchar_t dfrechk[][4] = { L"", L"d", L"fre", L"chk" }; const static wchar_t logwrnerr[][4] = { L"log", L"wrn", L"err" }; for (int b = 0 ; b < NUMBER_OF(dfrechk); b++) { for (int g = 0 ; g < NUMBER_OF(logwrnerr) ; ++g) { swprintf(directory + length, L"\\build%s.%s", dfrechk[b], logwrnerr[g]); if (DeleteFileW(directory)) { LogDelete(directory); } else { // ... } } } // Dangerous to clean files out of source directory, but: // FUTURE clean *.plg (VC Project Log?) files // FUTURE clean *.rsp files that sometimes appear in source dir? // FUTURE clean MSG*.bin files that sometimes appear in source dir? // FUTURE clean RC* files that sometimes appear in source dir? directory[length] = '\\'; wcscpy(directory + length + 1, obj); HANDLE hFind = FindFirstFileW(directory, wfd); if (hFind != INVALID_HANDLE_VALUE) { __try { do { directory[length] = '\\'; wcscpy(directory + length + 1, wfd->cFileName); DeleteDirectory(directory, length + 1 + wcslen(wfd->cFileName), wfd); } while (FindNextFileW(hFind, wfd)); } __finally { FindClose(hFind); } } directory[length] = '\\'; directory[length+1] = '*'; directory[length+2] = 0; hFind = FindFirstFileW(directory, wfd); if (hFind != INVALID_HANDLE_VALUE) { __try { do { if (IsDotOrDotDot(wfd->cFileName)) { continue; } if (!(wfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { continue; } directory[length] = '\\'; wcscpy(directory + length + 1, wfd->cFileName); CleanDirectory(obj, directory, length + 1 + wcslen(wfd->cFileName), wfd); } while (FindNextFileW(hFind, wfd)); } __finally { FindClose(hFind); } } directory[length] = 0; } #define Lx(x) L ## x #define L(x) Lx(x) int Clean( int argc, wchar_t** argv ) { Log(L"Clean version " L(__TIME__) L" " L(__DATE__)); // are we running on NT? long version = GetVersion(); int build = ((version >> 16) & 0x7fff); int majorVersion = (version & 0xff); bool nt = !(version & 0x80000000); if (!nt) { Fail(errorNT); } // These two buffers are shared by the whole call tree. Be careful. WIN32_FIND_DATAW wfd; wchar_t currentDirectory[1U << 15]; if (argc != 2) { Log( "Usage: %ls [delete string].\n" " Typical deletion strings are obj, objd, and obj?.\n" " This will recursively delete directories matching\n" " the deletion string. It will also delete\n" " all files named build[d,fre,chk].[log,wrn,err].\n", argv[0] ? argv[0] : L"xxx.exe" ); return EXIT_FAILURE; } // I prefer GetCurrentDirectory, but other programs just print '.' // if (!GetCurrentDirectoryW(NUMBER_OF(currentDirectory), currentDirectory)) // { // Fail(); // } currentDirectory[0] = '.'; currentDirectory[1] = 0; CleanDirectory(argv[1], currentDirectory, wcslen(currentDirectory), &wfd); return EXIT_SUCCESS; } int __cdecl wmain( int argc, wchar_t** argv ) { return Clean(argc, argv); } #endif /* BUILD_STABILIZE_EXPORTS */