logos-messaging-nim/waku.nimble

447 lines
18 KiB
Nim

#!fmt: off
import os
mode = ScriptMode.Verbose
### Package
version = "0.36.0"
author = "Status Research & Development GmbH"
description = "Waku, Private P2P Messaging for Resource-Restricted Devices"
license = "MIT or Apache License 2.0"
#bin = @["build/waku"]
### Dependencies
requires "nim >= 2.2.4"
# Pure Nim packages - pinned to exact commits via Nimble
requires "https://github.com/status-im/nim-chronicles#54f5b726025e8c7385e3a6529d3aa27454c6e6ff"
requires "https://github.com/status-im/nim-chronos#0646c444fce7c7ed08ef6f2c9a7abfd172ffe655"
requires "https://github.com/status-im/nim-confutils#e214b3992a31acece6a9aada7d0a1ad37c928f3b"
requires "https://github.com/status-im/nim-dnsdisc#203abd2b3e758e0ea3ae325769b20a7e1bcd1010"
requires "https://github.com/status-im/nim-faststreams#c3ac3f639ed1d62f59d3077d376a29c63ac9750c"
requires "https://github.com/status-im/nim-http-utils#79cbab1460f4c0cdde2084589d017c43a3d7b4f1"
requires "https://github.com/status-im/nim-json-rpc#9665c265035f49f5ff94bbffdeadde68e19d6221"
requires "https://github.com/status-im/nim-json-serialization#b65fd6a7e64c864dabe40e7dfd6c7d07db0014ac"
requires "https://github.com/status-im/nim-serialization#6f525d5447d97256750ca7856faead03e562ed20"
requires "https://github.com/status-im/nim-stew#e5740014961438610d336cd81706582dbf2c96f0"
requires "https://github.com/status-im/nim-stint#470b7892561b5179ab20bd389a69217d6213fe58"
requires "https://github.com/status-im/nim-metrics#ecf64c6078d1276d3b7d9b3d931fbdb70004db11"
requires "https://github.com/status-im/nim-presto#92b1c7ff141e6920e1f8a98a14c35c1fa098e3be"
requires "https://github.com/status-im/nim-taskpools#9e8ccc754631ac55ac2fd495e167e74e86293edb"
requires "https://github.com/status-im/nim-testutils#94d68e796c045d5b37cabc6be32d7bfa168f8857"
requires "https://github.com/status-im/nim-unittest2#8b51e99b4a57fcfb31689230e75595f024543024"
requires "https://github.com/status-im/nim-websock#ebe308a79a7b440a11dfbe74f352be86a3883508"
requires "https://github.com/status-im/nim-zlib#daa8723fd32299d4ca621c837430c29a5a11e19a"
requires "https://github.com/status-im/nim-toml-serialization#f3480570566b00a9f7ed1abf1f9731fb856ae1f2"
requires "https://github.com/status-im/nim-minilru#0c4b2bce959591f0a862e9b541ba43c6d0cf3476"
requires "https://github.com/nitely/nim-regex#4593305ed1e49731fc75af1dc572dd2559aad19c"
requires "https://github.com/nitely/nim-unicodedb#66f2458710dc641dd4640368f9483c8a0ec70561"
requires "https://github.com/arnetheduck/nim-results#df8113dda4c2d74d460a8fa98252b0b771bf1f27"
requires "https://github.com/cheatfate/nimcrypto#721fb99ee099b632eb86dfad1f0d96ee87583774"
requires "https://github.com/nim-lang/db_connector#74aef399e5c232f95c9fc5c987cebac846f09d62"
requires "https://github.com/ba0f3/dnsclient.nim#23214235d4784d24aceed99bbfe153379ea557c8"
# Complex packages with native dependencies - pinned via Nimble
# These have C/Rust code but Nimble can still fetch them
requires "https://github.com/status-im/nim-eth#d9135e6c3c5d6d819afdfb566aa8d958756b73a8"
requires "https://github.com/vacp2p/nim-libp2p#e82080f7b1aa61c6d35fa5311b873f41eff4bb52"
requires "https://github.com/status-im/nim-web3#81ee8ce479d86acb73be7c4f365328e238d9b4a3"
# nim-libbacktrace removed - optional dependency with build issues in Nimble
# If needed, enable with USE_LIBBACKTRACE=1 and install manually
requires "https://github.com/status-im/nim-bearssl#11e798b62b8e6beabe958e048e9e24c7e0f9ee63"
requires "https://github.com/status-im/nim-secp256k1#9dd3df62124aae79d564da636bb22627c53c7676"
requires "https://github.com/status-im/nim-nat-traversal#860e18c37667b5dd005b94c63264560c35d88004"
requires "https://github.com/arnetheduck/nim-sqlite3-abi#bdf01cf4236fb40788f0733466cdf6708783cbac"
### Helper functions
proc buildModule(filePath, params = "", lang = "c"): bool =
if not dirExists "build":
mkDir "build"
# allow something like "nim nimbus --verbosity:0 --hints:off nimbus.nims"
var extra_params = params
for i in 2 ..< paramCount() - 1:
extra_params &= " " & paramStr(i)
if not fileExists(filePath):
echo "File to build not found: " & filePath
return false
exec "nim " & lang & " --out:build/" & filepath & ".bin --mm:refc " & extra_params &
" " & filePath
# exec will raise exception if anything goes wrong
return true
proc buildBinary(name: string, srcDir = "./", params = "", lang = "c") =
if not dirExists "build":
mkDir "build"
# allow something like "nim nimbus --verbosity:0 --hints:off nimbus.nims"
var extra_params = params
for i in 2 ..< paramCount():
extra_params &= " " & paramStr(i)
exec "nim " & lang & " --out:build/" & name & " --mm:refc " & extra_params & " " &
srcDir & name & ".nim"
proc buildLibrary(lib_name: string, srcDir = "./", params = "", `type` = "static") =
if not dirExists "build":
mkDir "build"
# allow something like "nim nimbus --verbosity:0 --hints:off nimbus.nims"
var extra_params = params
for i in 2 ..< (paramCount() - 1):
extra_params &= " " & paramStr(i)
if `type` == "static":
exec "nim c" & " --out:build/" & lib_name &
" --threads:on --app:staticlib --opt:size --noMain --mm:refc --header -d:metrics --nimMainPrefix:libwaku --skipParentCfg:on -d:discv5_protocol_id=d5waku " &
extra_params & " " & srcDir & "libwaku.nim"
else:
exec "nim c" & " --out:build/" & lib_name &
" --threads:on --app:lib --opt:size --noMain --mm:refc --header -d:metrics --nimMainPrefix:libwaku --skipParentCfg:off -d:discv5_protocol_id=d5waku " &
extra_params & " " & srcDir & "libwaku.nim"
proc buildMobileAndroid(srcDir = ".", params = "") =
let cpu = getEnv("CPU")
let abiDir = getEnv("ABIDIR")
let outDir = "build/android/" & abiDir
if not dirExists outDir:
mkDir outDir
var extra_params = params
for i in 2 ..< paramCount():
extra_params &= " " & paramStr(i)
exec "nim c" & " --out:" & outDir &
"/libwaku.so --threads:on --app:lib --opt:size --noMain --mm:refc -d:chronicles_sinks=textlines[dynamic] --header --passL:-L" &
outdir & " --passL:-lrln --passL:-llog --cpu:" & cpu & " --os:android -d:androidNDK " &
extra_params & " " & srcDir & "/libwaku.nim"
proc test(name: string, params = "-d:chronicles_log_level=DEBUG", lang = "c") =
# XXX: When running `> NIM_PARAMS="-d:chronicles_log_level=INFO" make test2`
# I expect compiler flag to be overridden, however it stays with whatever is
# specified here.
buildBinary name, "tests/", params
exec "build/" & name
### Waku common tasks
task testcommon, "Build & run common tests":
test "all_tests_common", "-d:chronicles_log_level=WARN -d:chronosStrictException"
### Waku tasks
task wakunode2, "Build Waku v2 cli node":
let name = "wakunode2"
buildBinary name, "apps/wakunode2/", " -d:chronicles_log_level='TRACE' "
task benchmarks, "Some benchmarks":
let name = "benchmarks"
buildBinary name, "apps/benchmarks/", "-p:../.."
task wakucanary, "Build waku-canary tool":
let name = "wakucanary"
buildBinary name, "apps/wakucanary/"
task networkmonitor, "Build network monitor tool":
let name = "networkmonitor"
buildBinary name, "apps/networkmonitor/"
task rln_db_inspector, "Build the rln db inspector":
let name = "rln_db_inspector"
buildBinary name, "tools/rln_db_inspector/"
task test, "Build & run Waku tests":
test "all_tests_waku"
task testwakunode2, "Build & run wakunode2 app tests":
test "all_tests_wakunode2"
task example2, "Build Waku examples":
buildBinary "waku_example", "examples/"
buildBinary "publisher", "examples/"
buildBinary "subscriber", "examples/"
buildBinary "filter_subscriber", "examples/"
buildBinary "lightpush_publisher", "examples/"
task chat2, "Build example Waku chat usage":
# NOTE For debugging, set debug level. For chat usage we want minimal log
# output to STDOUT. Can be fixed by redirecting logs to file (e.g.)
#buildBinary name, "examples/", "-d:chronicles_log_level=WARN"
let name = "chat2"
buildBinary name,
"apps/chat2/",
"-d:chronicles_sinks=textlines[file] -d:ssl -d:chronicles_log_level='TRACE' "
task chat2mix, "Build example Waku chat mix usage":
# NOTE For debugging, set debug level. For chat usage we want minimal log
# output to STDOUT. Can be fixed by redirecting logs to file (e.g.)
#buildBinary name, "examples/", "-d:chronicles_log_level=WARN"
let name = "chat2mix"
buildBinary name,
"apps/chat2mix/",
"-d:chronicles_sinks=textlines[file] -d:ssl -d:chronicles_log_level='TRACE' "
task chat2bridge, "Build chat2bridge":
let name = "chat2bridge"
buildBinary name, "apps/chat2bridge/"
task liteprotocoltester, "Build liteprotocoltester":
let name = "liteprotocoltester"
buildBinary name, "apps/liteprotocoltester/"
task lightpushwithmix, "Build lightpushwithmix":
let name = "lightpush_publisher_mix"
buildBinary name, "examples/lightpush_mix/"
task buildone, "Build custom target":
let filepath = paramStr(paramCount())
discard buildModule filepath
task buildTest, "Test custom target":
let filepath = paramStr(paramCount())
discard buildModule(filepath)
import std/strutils
task execTest, "Run test":
# Expects to be parameterized with test case name in quotes
# preceded with the nim source file name and path
# If no test case name is given still it requires empty quotes `""`
let filepath = paramStr(paramCount() - 1)
var testSuite = paramStr(paramCount()).strip(chars = {'\"'})
if testSuite != "":
testSuite = " \"" & testSuite & "\""
exec "build/" & filepath & ".bin " & testSuite
### C Bindings
let chroniclesParams =
"-d:chronicles_line_numbers " & "-d:chronicles_runtime_filtering=on " &
"""-d:chronicles_sinks="textlines,json" """ &
"-d:chronicles_default_output_device=Dynamic " &
"""-d:chronicles_disabled_topics="eth,dnsdisc.client" """ & "--warning:Deprecated:off " &
"--warning:UnusedImport:on " & "-d:chronicles_log_level=TRACE"
task libwakuStatic, "Build the cbindings waku node library":
let lib_name = paramStr(paramCount())
buildLibrary lib_name, "library/", chroniclesParams, "static"
task libwakuDynamic, "Build the cbindings waku node library":
let lib_name = paramStr(paramCount())
buildLibrary lib_name, "library/", chroniclesParams, "dynamic"
### Mobile Android
task libWakuAndroid, "Build the mobile bindings for Android":
let srcDir = "./library"
let extraParams = "-d:chronicles_log_level=ERROR"
buildMobileAndroid srcDir, extraParams
### Mobile iOS
import std/sequtils
# Helper to find Nimble package path
proc findNimblePkg(pkgName: string): string =
let nimbleDir = getEnv("NIMBLE_DIR", getEnv("HOME") & "/.nimble")
let pkgsDir = nimbleDir & "/pkgs2"
if dirExists(pkgsDir):
for kind, path in walkDir(pkgsDir):
if kind == pcDir and path.contains(pkgName):
return path
# Fallback to pkgs (older nimble)
let pkgsDirOld = nimbleDir & "/pkgs"
if dirExists(pkgsDirOld):
for kind, path in walkDir(pkgsDirOld):
if kind == pcDir and path.contains(pkgName):
return path
return ""
proc buildMobileIOS(srcDir = ".", params = "") =
echo "Building iOS libwaku library"
let iosArch = getEnv("IOS_ARCH")
let iosSdk = getEnv("IOS_SDK")
let sdkPath = getEnv("IOS_SDK_PATH")
if sdkPath.len == 0:
quit "Error: IOS_SDK_PATH not set. Set it to the path of the iOS SDK"
# Find Nimble package paths for native dependencies
let bearSslPkg = findNimblePkg("nim-bearssl")
let secp256k1Pkg = findNimblePkg("nim-secp256k1")
let natTraversalPkg = findNimblePkg("nim-nat-traversal")
let nimLibPath = gorge("nim --version 2>/dev/null | head -1").split(" ")[0] & "/lib"
if bearSslPkg.len == 0:
quit "Error: nim-bearssl package not found. Run 'nimble install' first."
if secp256k1Pkg.len == 0:
quit "Error: nim-secp256k1 package not found. Run 'nimble install' first."
if natTraversalPkg.len == 0:
quit "Error: nim-nat-traversal package not found. Run 'nimble install' first."
# Use SDK name in path to differentiate device vs simulator
let outDir = "build/ios/" & iosSdk & "-" & iosArch
if not dirExists outDir:
mkDir outDir
var extra_params = params
for i in 2 ..< paramCount():
extra_params &= " " & paramStr(i)
let cpu = if iosArch == "arm64": "arm64" else: "amd64"
# The output static library
let nimcacheDir = outDir & "/nimcache"
let objDir = outDir & "/obj"
let vendorObjDir = outDir & "/vendor_obj"
let aFile = outDir & "/libwaku.a"
if not dirExists objDir:
mkDir objDir
if not dirExists vendorObjDir:
mkDir vendorObjDir
let clangBase = "clang -arch " & iosArch & " -isysroot " & sdkPath &
" -mios-version-min=18.0 -fembed-bitcode -fPIC -O2"
# Generate C sources from Nim (no linking)
exec "nim c" &
" --nimcache:" & nimcacheDir &
" --os:ios --cpu:" & cpu &
" --compileOnly:on" &
" --noMain --mm:refc" &
" --threads:on --opt:size --header" &
" -d:metrics -d:discv5_protocol_id=d5waku" &
" --nimMainPrefix:libwaku --skipParentCfg:on" &
" --cc:clang" &
" " & extra_params &
" " & srcDir & "/libwaku.nim"
# Compile vendor C libraries for iOS
# --- BearSSL ---
echo "Compiling BearSSL for iOS..."
let bearSslSrcDir = bearSslPkg & "/bearssl/csources/src"
let bearSslIncDir = bearSslPkg & "/bearssl/csources/inc"
for path in walkDirRec(bearSslSrcDir):
if path.endsWith(".c"):
let relPath = path.replace(bearSslSrcDir & "/", "").replace("/", "_")
let baseName = relPath.changeFileExt("o")
let oFile = vendorObjDir / ("bearssl_" & baseName)
if not fileExists(oFile):
exec clangBase & " -I" & bearSslIncDir & " -I" & bearSslSrcDir & " -c " & path & " -o " & oFile
# --- secp256k1 ---
echo "Compiling secp256k1 for iOS..."
let secp256k1Dir = secp256k1Pkg & "/vendor/secp256k1"
let secp256k1Flags = " -I" & secp256k1Dir & "/include" &
" -I" & secp256k1Dir & "/src" &
" -I" & secp256k1Dir &
" -DENABLE_MODULE_RECOVERY=1" &
" -DENABLE_MODULE_ECDH=1" &
" -DECMULT_WINDOW_SIZE=15" &
" -DECMULT_GEN_PREC_BITS=4"
# Main secp256k1 source
let secp256k1Obj = vendorObjDir / "secp256k1.o"
if not fileExists(secp256k1Obj):
exec clangBase & secp256k1Flags & " -c " & secp256k1Dir & "/src/secp256k1.c -o " & secp256k1Obj
# Precomputed tables (required for ecmult operations)
let secp256k1PreEcmultObj = vendorObjDir / "secp256k1_precomputed_ecmult.o"
if not fileExists(secp256k1PreEcmultObj):
exec clangBase & secp256k1Flags & " -c " & secp256k1Dir & "/src/precomputed_ecmult.c -o " & secp256k1PreEcmultObj
let secp256k1PreEcmultGenObj = vendorObjDir / "secp256k1_precomputed_ecmult_gen.o"
if not fileExists(secp256k1PreEcmultGenObj):
exec clangBase & secp256k1Flags & " -c " & secp256k1Dir & "/src/precomputed_ecmult_gen.c -o " & secp256k1PreEcmultGenObj
# --- miniupnpc ---
echo "Compiling miniupnpc for iOS..."
let miniupnpcSrcDir = natTraversalPkg & "/vendor/miniupnp/miniupnpc/src"
let miniupnpcIncDir = natTraversalPkg & "/vendor/miniupnp/miniupnpc/include"
let miniupnpcBuildDir = natTraversalPkg & "/vendor/miniupnp/miniupnpc/build"
let miniupnpcFiles = @[
"addr_is_reserved.c", "connecthostport.c", "igd_desc_parse.c",
"minisoap.c", "minissdpc.c", "miniupnpc.c", "miniwget.c",
"minixml.c", "portlistingparse.c", "receivedata.c", "upnpcommands.c",
"upnpdev.c", "upnperrors.c", "upnpreplyparse.c"
]
for fileName in miniupnpcFiles:
let srcPath = miniupnpcSrcDir / fileName
let oFile = vendorObjDir / ("miniupnpc_" & fileName.changeFileExt("o"))
if fileExists(srcPath) and not fileExists(oFile):
exec clangBase &
" -I" & miniupnpcIncDir &
" -I" & miniupnpcSrcDir &
" -I" & miniupnpcBuildDir &
" -DMINIUPNPC_SET_SOCKET_TIMEOUT" &
" -D_BSD_SOURCE -D_DEFAULT_SOURCE" &
" -c " & srcPath & " -o " & oFile
# --- libnatpmp ---
echo "Compiling libnatpmp for iOS..."
let natpmpSrcDir = natTraversalPkg & "/vendor/libnatpmp-upstream"
# Only compile natpmp.c - getgateway.c uses net/route.h which is not available on iOS
let natpmpObj = vendorObjDir / "natpmp_natpmp.o"
if not fileExists(natpmpObj):
exec clangBase &
" -I" & natpmpSrcDir &
" -DENABLE_STRNATPMPERR" &
" -c " & natpmpSrcDir & "/natpmp.c -o " & natpmpObj
# Use iOS-specific stub for getgateway
let getgatewayStubSrc = "./library/ios_natpmp_stubs.c"
let getgatewayStubObj = vendorObjDir / "natpmp_getgateway_stub.o"
if fileExists(getgatewayStubSrc) and not fileExists(getgatewayStubObj):
exec clangBase & " -c " & getgatewayStubSrc & " -o " & getgatewayStubObj
# --- BearSSL stubs (for tools functions not in main library) ---
echo "Compiling BearSSL stubs for iOS..."
let bearSslStubsSrc = "./library/ios_bearssl_stubs.c"
let bearSslStubsObj = vendorObjDir / "bearssl_stubs.o"
if fileExists(bearSslStubsSrc) and not fileExists(bearSslStubsObj):
exec clangBase & " -c " & bearSslStubsSrc & " -o " & bearSslStubsObj
# Compile all Nim-generated C files to object files
echo "Compiling Nim-generated C files for iOS..."
var cFiles: seq[string] = @[]
for kind, path in walkDir(nimcacheDir):
if kind == pcFile and path.endsWith(".c"):
cFiles.add(path)
# Get Nim lib path
let nimPath = gorge("dirname $(which nim)").strip & "/../lib"
for cFile in cFiles:
let baseName = extractFilename(cFile).changeFileExt("o")
let oFile = objDir / baseName
exec clangBase &
" -DENABLE_STRNATPMPERR" &
" -I" & nimPath & "/" &
" -I" & bearSslPkg & "/bearssl/csources/inc/" &
" -I" & bearSslPkg & "/bearssl/csources/tools/" &
" -I" & bearSslPkg & "/bearssl/abi/" &
" -I" & secp256k1Pkg & "/vendor/secp256k1/include/" &
" -I" & natTraversalPkg & "/vendor/miniupnp/miniupnpc/include/" &
" -I" & natTraversalPkg & "/vendor/libnatpmp-upstream/" &
" -I" & nimcacheDir &
" -c " & cFile &
" -o " & oFile
# Create static library from all object files
echo "Creating static library..."
var objFiles: seq[string] = @[]
for kind, path in walkDir(objDir):
if kind == pcFile and path.endsWith(".o"):
objFiles.add(path)
for kind, path in walkDir(vendorObjDir):
if kind == pcFile and path.endsWith(".o"):
objFiles.add(path)
exec "libtool -static -o " & aFile & " " & objFiles.join(" ")
echo "✔ iOS library created: " & aFile
task libWakuIOS, "Build the mobile bindings for iOS":
let srcDir = "./library"
let extraParams = "-d:chronicles_log_level=ERROR"
buildMobileIOS srcDir, extraParams