diff --git a/home/private_dot_config/nim/README.md b/home/private_dot_config/nim/README.md new file mode 100644 index 0000000..b16a621 --- /dev/null +++ b/home/private_dot_config/nim/README.md @@ -0,0 +1,4 @@ +# Nim Configuration + + +`config.nims` sourced from -> https://github.com/kaushalmodi/nim_config diff --git a/home/private_dot_config/nim/config.nims b/home/private_dot_config/nim/config.nims new file mode 100644 index 0000000..808f743 --- /dev/null +++ b/home/private_dot_config/nim/config.nims @@ -0,0 +1,476 @@ + + +from macros import error +from strutils import `%`, endsWith, strip, replace +from sequtils import filterIt, concat + +const + nimVersion = (major: NimMajor, minor: NimMinor, patch: NimPatch) + +when nimVersion <= (0, 19, 9): + from ospaths import `/`, splitPath, splitFile + when not defined(projectDir): + let projectDir = getCurrentDir + echo "\nprojectDir() is not defined, fallback to getCurrentDir(), you must run it from the project directory (needs Nim devel).\n" +else: + when nimVersion < (1, 2, 0): + from os import `/`, splitPath, splitFile + import oswalkdir # This was deprecated in Nim 1.2.0; just use "import os" instead + else: + when nimVersion < (1, 3, 0): + from os import `/`, splitPath, splitFile, walkDirRec, pcFile, pcDir + else: + # nim devel + import std/[os] + +when nimVersion >= (1, 7, 3): + import std/[assertions] # for doAssert + +# Switches +hint("Processing", false) # Do not print the "Hint: .. [Processing]" messages when compiling + +# Use Dragonbox by default - https://github.com/nim-lang/Nim/commit/25efb5386293540b0542833625d3fb6e22f3cfbc +switch("define", "nimFpRoundtrips") + +when nimVersion >= (0, 20, 0): + when defined(strictMode): + switch("styleCheck", "error") + else: + switch("styleCheck", "hint") + +## Constants +const + doOptimize = true + stripSwitches = @["--strip-all", "--remove-section=.comment", "--remove-section=.note.gnu.gold-version", "--remove-section=.note", "--remove-section=.note.gnu.build-id", "--remove-section=.note.ABI-tag"] + # upxSwitches = @["--best"] # fast + upxSwitches = @["--ultra-brute"] # slower + checksumsSwitches = @["--tag"] + gpgSignSwitches = @["--clear-sign", "--armor", "--detach-sign", "--digest-algo sha512"] + gpgEncryptSwitches = @["--armor", "--symmetric", "--s2k-digest-algo sha512", "--cipher-algo AES256", "-z 9"] # 9=Max, 0=Disabled + +proc getGitRootMaybe(): string = + ## Try to get the path to the current git root directory. + ## Return ``projectDir()`` if a ``.git`` directory is not found. + const + maxAttempts = 10 # arbitrarily picked + var + path = projectDir() # projectDir() needs nim 0.20.0 (or nim devel as of Tue Oct 16 08:41:09 EDT 2018) + attempt = 0 + while (attempt < maxAttempts) and (not dirExists(path / ".git")): + path = path / "../" + attempt += 1 + if dirExists(path / ".git"): + result = path + else: + result = projectDir() + +## Lets +let + root = getGitRootMaybe() + (_, pkgName) = root.splitPath() + srcFile = root / "src" / (pkgName & ".nim") + # pcre + pcreVersion = getEnv("PCREVER", "8.42") + pcreSourceDir = "pcre-" & pcreVersion + pcreArchiveFile = pcreSourceDir & ".tar.bz2" + pcreDownloadLink = "https://downloads.sourceforge.net/pcre/" & pcreArchiveFile + pcreInstallDir = (root / "pcre/") & pcreVersion + # http://www.linuxfromscratch.org/blfs/view/8.1/general/pcre.html + pcreConfigureCmd = ["./configure", "--prefix=" & pcreInstallDir, "--enable-pcre16", "--enable-pcre32", "--disable-shared"] + pcreLibDir = pcreInstallDir / "lib" + pcreLibFile = pcreLibDir / "libpcre.a" + # libressl + libreSslVersion = getEnv("LIBRESSLVER", "2.8.1") + libreSslSourceDir = "libressl-" & libreSslVersion + libreSslArchiveFile = libreSslSourceDir & ".tar.gz" + libreSslDownloadLink = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/" & libreSslArchiveFile + libreSslInstallDir = (root / "libressl/") & libreSslVersion + libreSslConfigureCmd = ["./configure", "--disable-shared", "--prefix=" & libreSslInstallDir] + libreSslLibDir = libreSslInstallDir / "lib" + libreSslLibFile = libreSslLibDir / "libssl.a" + libreCryptoLibFile = libreSslLibDir / "libcrypto.a" + libreSslIncludeDir = libreSslInstallDir / "include/openssl" + # openssl + openSslSeedConfigOsCompiler = "linux-x86_64" + openSslVersion = getEnv("OPENSSLVER", "1.1.1") + openSslSourceDir = "openssl-" & openSslVersion + openSslArchiveFile = openSslSourceDir & ".tar.gz" + openSslDownloadLink = "https://www.openssl.org/source/" & openSslArchiveFile + openSslInstallDir = (root / "openssl/") & openSslVersion + # "no-async" is needed for openssl to compile using musl + # - https://gitter.im/nim-lang/Nim?at=5bbf75c3ae7be940163cc198 + # - https://www.openwall.com/lists/musl/2016/02/04/5 + # -DOPENSSL_NO_SECURE_MEMORY is needed to make openssl compile using musl. + # - https://github.com/openssl/openssl/issues/7207#issuecomment-420814524 + openSslConfigureCmd = ["./Configure", openSslSeedConfigOsCompiler, "no-shared", "no-zlib", "no-async", "-fPIC", "-DOPENSSL_NO_SECURE_MEMORY", "--prefix=" & openSslInstallDir] + openSslLibDir = openSslInstallDir / "lib" + openSslLibFile = openSslLibDir / "libssl.a" + openCryptoLibFile = openSslLibDir / "libcrypto.a" + openSslIncludeDir = openSslInstallDir / "include/openssl" + # Custom Header file to force to link to GLibC 2.5, for old Linux (x86_64). + glibc25DownloadLink = "https://raw.githubusercontent.com/wheybags/glibc_version_header/master/version_headers/x64/force_link_glibc_2.5.h" + + +## Helper Procs +# https://github.com/kaushalmodi/elnim +proc dollar[T](s: T): string = + result = $s +proc mapconcat[T](s: openArray[T]; sep = " "; op: proc(x: T): string = dollar): string = + ## Concatenate elements of ``s`` after applying ``op`` to each element. + ## Separate each element using ``sep``. + for i, x in s: + result.add(op(x)) + if i < s.len-1: + result.add(sep) + +proc parseArgs(): tuple[switches: seq[string], nonSwitches: seq[string]] = + ## Parse the args and return its components as + ## ``(switches, nonSwitches)``. + let + numParams = paramCount() # count starts at 0 + # So "nim musl foo.nim" will have a count of 2. + # param 0 will always be "nim" + doAssert numParams >= 1 + # param 1 will always be the task name like "musl". + let + subCmd = paramStr(1) + + if numParams < 2: + error("The '$1' sub-command needs at least one non-switch argument" % [subCmd]) + + for i in 2 .. numParams: + if paramStr(i)[0] == '-': # -d:foo or --define:foo + result.switches.add(paramStr(i)) + else: + result.nonSwitches.add(paramStr(i)) + +proc runUtil(f, util: string; args: seq[string]) = + ## Run ``util`` executable with ``args`` on ``f`` file. + doAssert findExe(util) != "", + "'$1' executable was not found" % [util] + let + cmd = concat(@[util], args, @[f]).mapconcat() + echo "Running '$1' .." % [cmd] + exec cmd + +template preBuild(targetPlusSwitches: string) = + assert targetPlusSwitches.len > 0, "Build arguments must not be empty" + when defined(libressl) and defined(openssl): + error("Define only 'libressl' or 'openssl', not both.") + let (switches, nimFiles) = parseArgs() + assert nimFiles.len > 0, """ + This nim sub-command accepts at least one Nim file name + Examples: nim FILE.nim + nim FILE1.nim FILE2.nim + nim -d:pcre FILE.nim + """ + var allBuildCmds {.inject.} = newSeqOfCap[tuple[nimArgs, binFile: string]](nimFiles.len) + for f in nimFiles: + let + extraSwitches = switches.mapconcat() + (dirName, baseName, tmpVar) = splitFile(f) + binFile = dirName / baseName # Save the binary in the same dir as the nim file + nimArgsArray = when doOptimize: + [targetPlusSwitches, "-d:musl", "-d:release", "--opt:size", "--passL:-s", "--listFullPaths:off", "--excessiveStackTrace:off", extraSwitches, " --out:" & binFile, f] + else: + [targetPlusSwitches, "-d:musl", extraSwitches, " --out:" & binFile, f] + nimArgs = nimArgsArray.mapconcat() + discard tmpVar # Avoid the "declared but not used" warning. Workaround for https://github.com/nim-lang/Nim/issues/12094 + allBuildCmds.add((nimArgs: nimArgs, binFile: binFile)) + + +## Tasks +task installPcre, "Install PCRE using musl-gcc": + if not fileExists(pcreLibFile): + if not dirExists(pcreSourceDir): + if not fileExists(pcreArchiveFile): + exec("curl -LO " & pcreDownloadLink) + exec("tar xf " & pcreArchiveFile) + else: + echo "PCRE lib source dir " & pcreSourceDir & " already exists" + withDir pcreSourceDir: + putEnv("CC", "musl-gcc -static") + exec(pcreConfigureCmd.mapconcat()) + exec("make -j8") + exec("make install") + else: + echo pcreLibFile & " already exists" + setCommand("nop") + +task installLibreSsl, "Install LIBRESSL using musl-gcc": + if (not fileExists(libreSslLibFile)) or (not fileExists(libreCryptoLibFile)): + if not dirExists(libreSslSourceDir): + if not fileExists(libreSslArchiveFile): + exec("curl -LO " & libreSslDownloadLink) + exec("tar xf " & libreSslArchiveFile) + else: + echo "LibreSSL lib source dir " & libreSslSourceDir & " already exists" + withDir libreSslSourceDir: + # -idirafter /usr/include/ # Needed for linux/sysctl.h + # -idirafter /usr/include/x86_64-linux-gnu/ # Needed for Travis/Ubuntu build to pass, for asm/types.h + putEnv("CC", "musl-gcc -static -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/") + putEnv("C_INCLUDE_PATH", libreSslIncludeDir) + exec(libreSslConfigureCmd.mapconcat()) + exec("make -j8 -C crypto") # build just the "crypto" component + exec("make -j8 -C ssl") # build just the "ssl" component + exec("make -C crypto install") + exec("make -C ssl install") + else: + echo libreSslLibFile & " already exists" + setCommand("nop") + +task installOpenSsl, "Install OPENSSL using musl-gcc": + if (not fileExists(openSslLibFile)) or (not fileExists(openCryptoLibFile)): + if not dirExists(openSslSourceDir): + if not fileExists(openSslArchiveFile): + exec("curl -LO " & openSslDownloadLink) + exec("tar xf " & openSslArchiveFile) + else: + echo "OpenSSL lib source dir " & openSslSourceDir & " already exists" + withDir openSslSourceDir: + # https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html + # -idirafter /usr/include/ # Needed for Travis/Ubuntu build to pass, for linux/version.h, etc. + # -idirafter /usr/include/x86_64-linux-gnu/ # Needed for Travis/Ubuntu build to pass, for asm/types.h + putEnv("CC", "musl-gcc -static -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/") + putEnv("C_INCLUDE_PATH", openSslIncludeDir) + exec(openSslConfigureCmd.mapconcat()) + echo "The insecure switch -DOPENSSL_NO_SECURE_MEMORY is needed so that OpenSSL can be compiled using MUSL." + exec("make -j8 depend") + exec("make -j8") + exec("make install_sw") + else: + echo openSslLibFile & " already exists" + setCommand("nop") + +task strip, "Optimize the binary size using 'strip' utility": + ## Usage: nim strip .. + let + (_, binFiles) = parseArgs() + for f in binFiles: + f.runUtil("strip", stripSwitches) + setCommand("nop") + +task upx, "Optimize the binary size using 'upx' utility": + ## Usage: nim upx .. + let + (_, binFiles) = parseArgs() + for f in binFiles: + f.runUtil("upx", upxSwitches) + setCommand("nop") + +task checksums, "Generate checksums of the binary using 'sha1sum' and 'md5sum'": + ## Usage: nim checksums .. + let (_, binFiles) = parseArgs() + for f in binFiles: + f.runUtil("md5sum", checksumsSwitches) + f.runUtil("sha1sum", checksumsSwitches) + setCommand("nop") + +task sign, "Sign the binary using 'gpg' (armored, ascii)": + ## Usage: nim sign .. + let (_, binFiles) = parseArgs() + for f in binFiles: + f.runUtil("gpg", gpgSignSwitches) + setCommand("nop") + +task encrypt, "Encrypt the binary using 'gpg' (compressed, symmetric, ascii)": + ## Usage: nim encrypt .. + # Decrypt is just double click or 'gpg --decrypt' (Asks Password). + let (_, binFiles) = parseArgs() + for f in binFiles: + f.runUtil("gpg", gpgEncryptSwitches) + setCommand("nop") + +task musl, "Build an optimized static binary using musl": + ## Usage: nim musl [-d:pcre] [-d:libressl|-d:openssl] .. + preBuild("c") + for cmd in allBuildCmds: + # Build binary + echo "\nRunning 'nim " & cmd.nimArgs & "' .." + selfExec cmd.nimArgs + when doOptimize: + cmd.binFile.runUtil("strip", stripSwitches) + cmd.binFile.runUtil("upx", upxSwitches) + echo "Built: " & cmd.binFile + +task glibc25, "Build C, dynamically linked to GLibC 2.5 (x86_64)": + ## Usage: nim glibc25 file.nim + # See https://github.com/wheybags/glibc_version_header/pull/21. + let + header = getCurrentDir() / "force_link_glibc_2.5.h" + optns = ["-ffast-math", "-flto", "-include" & header] # Don't use -march here + if not fileExists(header): + exec("curl -LO " & glibc25DownloadLink) + var passCSwitches: string + for o in optns: + passCSwitches.add(" --passC:" & o) + preBuild("c -d:ssl" & passCSwitches) + for cmd in allBuildCmds: + echo "\nRunning 'nim " & cmd.nimArgs + # preBuild auto-adds "-d:musl", so remove that. + # FIXME: Make preBuild not always add that switch. -- Thu Jun 13 12:13:17 EDT 2019 - kmodi + selfExec cmd.nimArgs.replace("-d:musl", "") + when doOptimize: + cmd.binFile.runUtil("strip", stripSwitches) + # Version check -- Changes from GLIBC_2.15 to GLIBC_2.5 + cmd.binFile.runUtil("ldd", @["-v"]) + +task js2asm, "Build JS, print Assembly from that JS (performance debug)": + ## Usage: nim js2asm .. + # This debugs performance of JavaScript, the less ASM the better JS. + # This ASM is NOT usable as proper ASM, just for Debug performance. + preBuild("js") + for cmd in allBuildCmds: + echo "\nRunning 'nim " & cmd.nimArgs + selfExec cmd.nimArgs + cmd.binFile.runUtil("node", @["--print_code"]) + +task c2asm, "Build C, print Assembly from that C (performance debug)": + ## Usage: nim c2asm .. + # This debugs performance of Nim, the less ASM the better your Nim. + const + optns = [ # This cleans up the produced ASM as much as possible. + "-ffast-math", "-march=native", "-fno-math-errno", "-fno-exceptions", + "-fno-asynchronous-unwind-tables", "-fno-inline-functions", "-std=c11", + "-fno-inline-functions-called-once", "-fno-inline-small-functions", + "-xc", "-s", "-S", "-O3", "-masm=intel", "-o-"] + var passCSwitches: string + for o in optns: + passCSwitches.add(" --passC:" & o) + preBuild("compileToC --compileOnly:on -d:danger -d:noSignalHandler" & passCSwitches) + for cmd in allBuildCmds: + echo "\nRunning 'nim " & cmd.nimArgs + selfExec cmd.nimArgs + let cSource = nimcacheDir() / cmd.binFile & ".nim.c" + cSource.runUtil("gcc", @optns) + +task fmt, "Run nimpretty on all git-managed .nim files in the current repo": + ## Usage: nim fmt + for file in walkDirRec(root, {pcFile, pcDir}): + if file.splitFile().ext == ".nim": + let + # https://github.com/nim-lang/Nim/issues/6262#issuecomment-454983572 + # https://stackoverflow.com/a/2406813/1219634 + fileIsGitManaged = gorgeEx("cd $1 && git ls-files --error-unmatch $2" % [getCurrentDir(), file]).exitCode == 0 + # ^^^^^-- That "cd" is required. + if fileIsGitManaged: + let + cmd = "nimpretty $1" % [file] + echo "Running $1 .." % [cmd] + exec(cmd) + setCommand("nop") + +task rmfiles, "Recursively remove all files with the specific extension(s) from the current directory": + ## Usage: nim rmfiles pyc c o + for extToDelete in parseArgs().nonSwitches: # Invalid Patterns: "", " ", "\t" + assert extToDelete.strip.len > 0, "Specified extension must not be whitespace or empty string" + assert extToDelete[0] != '.', "Do not prefix the extensions with dot" + for file in walkDirRec(getCurrentDir(), {pcFile, pcDir}): + if file.splitFile().ext == "." & extToDelete: + # echo "file to delete: ", file + rmFile(file) + setCommand("nop") + +task test, "Run tests via 'nim doc' (runnableExamples) and tests in tests/ dir": + let + testDir = root / "tests" + selfExec("doc " & srcFile) + if dirExists(testDir): + let + testFiles = listFiles(testDir).filterIt(it.len >= 5 and it.endsWith(".nim")) + for t in testFiles: + selfExec "c -r " & t + +when nimVersion >= (1, 3, 5): + task docs, "Deploy doc html + search index to public/ directory": + let + deployDir = root / "public" + selfExec("doc --project --outdir:$1 $2" % [deployDir, srcFile]) # generates search index and theindex.html too. + withDir deployDir: + # Move PKGNAME.html to index.html so that we can access the + # documentation on a clean URL like https://foo.bar/ instead of + # https://foo.bar/PKGNAME.html. + mvFile(pkgName & ".html", "index.html") + # As we renamed the file, we need to rename that in hyperlinks + # and the search index too. + for file in walkDirRec(".", {pcFile}): + exec(r"sed -i -r 's|$1\.html|index.html|g' $2" % [pkgName, file]) +else: + task docs, "Deploy doc html + search index to public/ directory": + let + deployDir = root / "public" + docOutBaseName = "index" + deployHtmlFile = deployDir / (docOutBaseName & ".html") + genDocCmd = "nim doc --out:$1 --index:on $2" % [deployHtmlFile, srcFile] + genTheIndexCmd = "nim buildIndex -o:$1/theindex.html $1" % [deployDir] + deployJsFile = deployDir / "dochack.js" + docHackJsSource = "https://nim-lang.github.io/Nim/dochack.js" # devel docs dochack.js + mkDir(deployDir) + exec(genDocCmd) + exec(genTheIndexCmd) + if not fileExists(deployJsFile): + withDir deployDir: + exec("curl -LO " & docHackJsSource) + +# https://www.reddit.com/r/nim/comments/byzq7d/go_run_for_nim/ +task runc, "Run equivalent of 'nim c -r ..'": + switch("run") + switch("verbosity", "0") + hint("Processing", false) + setCommand("c") + +task runcpp, "Run equivalent of 'nim cpp -r ..'": + switch("run") + switch("verbosity", "0") + hint("Processing", false) + setCommand("cpp") + +## Define Switch Parsing +# -d:musl +when defined(musl): + var + muslGccPath: string + echo " [-d:musl] Building a static binary using musl .." + muslGccPath = findExe("musl-gcc") + if muslGccPath == "": + error("'musl-gcc' binary was not found in PATH.") + switch("passL", "-static") + switch("gcc.exe", muslGccPath) + switch("gcc.linkerexe", muslGccPath) + # -d:pcre + when defined(pcre): + let + pcreIncludeDir = pcreInstallDir / "include" + if not fileExists(pcreLibFile): + selfExec "installPcre" # Install PCRE in current dir if pcreLibFile is not found + switch("passC", "-I" & pcreIncludeDir) # So that pcre.h is found when running the musl task + switch("define", "usePcreHeader") + switch("passL", pcreLibFile) + # -d:libressl or -d:openssl + when defined(libressl) or defined(openssl): + switch("define", "ssl") # Pass -d:ssl to nim + when defined(libressl): + let + sslLibFile = libreSslLibFile + cryptoLibFile = libreCryptoLibFile + sslIncludeDir = libreSslIncludeDir + sslLibDir = libreSslLibDir + when defined(openssl): + let + sslLibFile = openSslLibFile + cryptoLibFile = openCryptoLibFile + sslIncludeDir = openSslIncludeDir + sslLibDir = openSslLibDir + + if (not fileExists(sslLibFile)) or (not fileExists(cryptoLibFile)): + # Install SSL in current dir if sslLibFile or cryptoLibFile is not found + when defined(libressl): + selfExec "installLibreSsl" + when defined(openssl): + selfExec "installOpenSsl" + switch("passC", "-I" & sslIncludeDir) # So that ssl.h is found when running the musl task + switch("passL", "-L" & sslLibDir) + switch("passL", "-lssl") + switch("passL", "-lcrypto") # This *has* to come *after* -lssl + switch("dynlibOverride", "libssl") + switch("dynlibOverride", "libcrypto")