diff options
Diffstat (limited to 'tools/trimcc.nim')
-rw-r--r--[-rwxr-xr-x] | tools/trimcc.nim | 203 |
1 files changed, 174 insertions, 29 deletions
diff --git a/tools/trimcc.nim b/tools/trimcc.nim index 966f406d4..41795e1aa 100755..100644 --- a/tools/trimcc.nim +++ b/tools/trimcc.nim @@ -1,29 +1,174 @@ -# Trim C compiler installation to a minimum - -import strutils, os - -proc newName(f: string): string = - var (dir, name, ext) = splitFile(f) - return dir / "trim_" & name & ext - -proc walker(dir: string) = - for kind, path in walkDir(dir): - case kind - of pcFile: - moveFile(newName(path), path) - # test if installation still works: - if execShellCmd(r"nimrod c --force_build tests\tlastmod") == 0: - echo "Optional: ", path - removeFile(newName(path)) - else: - echo "Required: ", path - # copy back: - moveFile(path, newName(path)) - of pcDirectory: - walker(path) - else: nil - -if paramCount() == 1: - walker(paramStr(1)) -else: - echo "Usage: trimcc c_compiler_directory" +# Trim C compiler installation to a minimum + +import strutils, os, pegs, strtabs, math, times + +const + Essential = """gcc.exe g++.exe gdb.exe ld.exe as.exe c++.exe cpp.exe cc1.exe +crtbegin.o crtend.o crt2.o dllcrt2.o libgcc_s_dw2-1.dll libgcc_s_sjlj-1.dll +libgcc_s_seh-1.dll libexpat-1.dll libwinpthread-1.dll aio.h dlfcn.h fcntl.h +fenv.h fmtmsg.h fnmatch.h ftw.h errno.h glob.h gtmath.h if.h in.h ipc.h +langinfo.h locale.h math.h mman.h netdb.h nl_types.h poll.h pthread.h pwd.h +sched.h select.h semaphore.h signal.h socket.h spawn.h stat.h statvfs.h stdio.h +stdlib.h string.h strings.h tcp.h time.h types.h ucontext.h uio.h utsname.h +unistd.h wait.h varargs.h windows.h zlib.h +""".split + +proc includes(headerpath, headerfile: string, whitelist: StringTableRef) = + whitelist[headerfile] = "processed" + for line in lines(headerpath): + if line =~ peg"""s <- ws '#include' ws ('"' / '<') {[^">]+} ('"' / '>') ws + comment <- '/*' @ '*/' / '//' .* + ws <- (comment / \s+)* """: + let m = matches[0].extractFilename + if whitelist.getOrDefault(m) != "processed": + whitelist[m] = "found" + +proc processIncludes(dir: string, whitelist: StringTableRef) = + for kind, path in walkDir(dir): + case kind + of pcFile: + let name = extractFilename(path) + if ('.' notin name and "include" in path) or ("c++" in path): + let n = whitelist.getOrDefault(name) + if n != "processed": whitelist[name] = "found" + if name.endsWith(".h"): + let n = whitelist.getOrDefault(name) + if n == "found": includes(path, name, whitelist) + of pcDir: processIncludes(path, whitelist) + else: discard + +proc gatherFiles(dir: string, whitelist: StringTableRef, result: var seq[string]) = + for kind, path in walkDir(dir): + case kind + of pcFile: + let name = extractFilename(path) + if not whitelist.hasKey(name): + result.add(path) + of pcDir: + gatherFiles(path, whitelist, result) + else: + discard + +proc gatherEmptyFolders(dir: string, whitelist: StringTableRef, result: var seq[string]) = + var empty = true + for kind, path in walkDir(dir): + case kind + of pcFile: + empty = false + of pcDir: + let (none, name) = splitPath(path) + if not whitelist.hasKey(name): + gatherEmptyFolders(path, whitelist, result) + empty = false + else: + discard + if empty: + result.add(dir) + +proc newName(f: string): string = + let (dir, name, ext) = splitFile(f) + return dir / "trim_" & name & ext + +proc ccStillWorks(): bool = + const + c1 = r"nim c --verbosity:0 --force_build koch" + c2 = r"nim c --verbosity:0 --force_build --threads:on --out:tempOne.exe tools/trimcc" + c3 = r"nim c --verbosity:0 --force_build --threads:on --out:tempTwo.exe tools/fakeDeps" + c4 = r".\koch.exe" + c5 = r".\tempOne.exe" + c6 = r".\tempTwo.exe" + result = execShellCmd(c1) == 0 and execShellCmd(c2) == 0 and + execShellCmd(c3) == 0 and execShellCmd(c4) == 0 and + execShellCmd(c5) == 0 and execShellCmd(c6) == 0 + +proc trialDeletion(files: seq[string], a, b: int, whitelist: StringTableRef): bool = + result = true + var single = (a == min(b, files.high)) + for path in files[a .. min(b, files.high)]: + try: + moveFile(dest=newName(path), source=path) + except OSError: + return false + + # Test if compilation still works, even with the moved files. + if ccStillWorks(): + for path in files[a .. min(b, files.high)]: + try: + removeFile(newName(path)) + echo "Optional: ", path + except OSError: + echo "Warning, couldn't move ", path + moveFile(dest=path, source=newName(path)) + return false + else: + for path in files[a .. min(b, files.high)]: + echo "Required: ", path + moveFile(dest=path, source=newName(path)) + if single: + whitelist[path] = "found" + result = false + +proc main(dir: string) = + # Construct a whitelist of files to not remove + var whitelist = newStringTable(modeCaseInsensitive) + for e in Essential: + whitelist[e] = "found" + while true: + let oldLen = whitelist.len + processIncludes(dir, whitelist) + if oldLen == whitelist.len: + break + + # Remove batches of files + var nearlyDone = false + while true: + # Gather files to test + var allFiles = newSeq[string]() + gatherFiles(dir, whitelist, allFiles) + + # Determine the initial size of groups to check + var + maxBucketSize = len(allFiles) + bucketSize = 1 + + # Loop through the list of files, deleting batches + var i = 0 + while i < allFiles.len: + var success = trialDeletion(allFiles, i, i+bucketSize-1, whitelist) + inc i, bucketSize + + # If we aren't on the last pass, adjust the batch size based on success + if not nearlyDone: + if success: + bucketSize = min(bucketSize * 2, maxBucketSize) + else: + bucketSize = max(bucketSize div 2, 1) + echo "Bucket size is now ", bucketSize + + # After looping through all the files, check if we need to break. + if nearlyDone: + break + if bucketSize == 1: + nearlyDone = true + + while true: + var + emptyFolders = newSeq[string]() + changed = false + + gatherEmptyFolders(dir, whitelist, emptyFolders) + for path in emptyFolders: + removeDir(path) + if not ccStillWorks(): + createDir(path) + whitelist[path] = "found" + else: + changed = true + if not changed: + break + +if paramCount() == 1: + doAssert ccStillWorks() + main(paramStr(1)) +else: + quit "Usage: trimcc c_compiler_directory", QuitSuccess |