libdir support
This commit is contained in:
parent
2b3b5e2bec
commit
9d8fee19c0
5 changed files with 120 additions and 69 deletions
|
|
@ -158,8 +158,10 @@ proc mkDir*(dir: string) =
|
||||||
flag = when not defined(Windows): "-p" else: ""
|
flag = when not defined(Windows): "-p" else: ""
|
||||||
discard execAction(&"mkdir {flag} {dir.sanitizePath}", retry = 2)
|
discard execAction(&"mkdir {flag} {dir.sanitizePath}", retry = 2)
|
||||||
|
|
||||||
proc cpFile*(source, dest: string, move = false) =
|
proc cpFile*(source, dest: string, psymlink = false, move = false) =
|
||||||
## Copy a file from `source` to `dest` at compile time
|
## Copy a file from `source` to `dest` at compile time
|
||||||
|
##
|
||||||
|
## `psymlink = true` preserves symlinks instead of dereferencing on posix
|
||||||
let
|
let
|
||||||
source = source.replace("/", $DirSep)
|
source = source.replace("/", $DirSep)
|
||||||
dest = dest.replace("/", $DirSep)
|
dest = dest.replace("/", $DirSep)
|
||||||
|
|
@ -173,7 +175,10 @@ proc cpFile*(source, dest: string, move = false) =
|
||||||
if move:
|
if move:
|
||||||
"mv -f"
|
"mv -f"
|
||||||
else:
|
else:
|
||||||
"cp -f"
|
if psymlink:
|
||||||
|
"cp -fd"
|
||||||
|
else:
|
||||||
|
"cp -f"
|
||||||
|
|
||||||
discard execAction(&"{cmd} {source.sanitizePath} {dest.sanitizePath}", retry = 2)
|
discard execAction(&"{cmd} {source.sanitizePath} {dest.sanitizePath}", retry = 2)
|
||||||
|
|
||||||
|
|
@ -233,6 +238,20 @@ proc mvTree*(source, dest: string) =
|
||||||
## Move contents of source dir to the destination, not the directory itself
|
## Move contents of source dir to the destination, not the directory itself
|
||||||
cpTree(source, dest, move = true)
|
cpTree(source, dest, move = true)
|
||||||
|
|
||||||
|
proc getFileDate*(fullpath: string): string =
|
||||||
|
## Get file date for `fullpath`
|
||||||
|
var
|
||||||
|
ret = 0
|
||||||
|
cmd =
|
||||||
|
when defined(Windows):
|
||||||
|
&"cmd /c for %a in ({fullpath.sanitizePath}) do echo %~ta"
|
||||||
|
elif defined(Linux):
|
||||||
|
&"stat -c %y {fullpath.sanitizePath}"
|
||||||
|
elif defined(OSX) or defined(FreeBSD):
|
||||||
|
&"stat -f %m {fullpath.sanitizePath}"
|
||||||
|
|
||||||
|
(result, ret) = execAction(cmd)
|
||||||
|
|
||||||
proc getProjectCacheDir*(name: string, forceClean = true): string =
|
proc getProjectCacheDir*(name: string, forceClean = true): string =
|
||||||
## Get a cache directory where all nimterop artifacts can be stored
|
## Get a cache directory where all nimterop artifacts can be stored
|
||||||
##
|
##
|
||||||
|
|
@ -1083,7 +1102,8 @@ macro isDefined*(def: untyped): untyped =
|
||||||
|
|
||||||
macro getHeader*(
|
macro getHeader*(
|
||||||
header: static[string], giturl: static[string] = "", dlurl: static[string] = "",
|
header: static[string], giturl: static[string] = "", dlurl: static[string] = "",
|
||||||
conanuri: static[string] = "", jbburi: static[string] = "", outdir: static[string] = "",
|
conanuri: static[string] = "", jbburi: static[string] = "",
|
||||||
|
outdir: static[string] = "", libdir: static[string] = "",
|
||||||
conFlags: static[string] = "", cmakeFlags: static[string] = "", makeFlags: static[string] = "",
|
conFlags: static[string] = "", cmakeFlags: static[string] = "", makeFlags: static[string] = "",
|
||||||
altNames: static[string] = "", buildTypes: static[openArray[BuildType]] = [btCmake, btAutoconf]): untyped =
|
altNames: static[string] = "", buildTypes: static[openArray[BuildType]] = [btCmake, btAutoconf]): untyped =
|
||||||
## Get the path to a header file for wrapping with
|
## Get the path to a header file for wrapping with
|
||||||
|
|
@ -1125,7 +1145,14 @@ macro getHeader*(
|
||||||
##
|
##
|
||||||
## The header path is stored in `const xxxPath` and can be used in a `cImport()` call
|
## The header path is stored in `const xxxPath` and can be used in a `cImport()` call
|
||||||
## in the calling wrapper. The dynamic library path is stored in `const xxxLPath` and can
|
## in the calling wrapper. The dynamic library path is stored in `const xxxLPath` and can
|
||||||
## be used for the `dynlib` parameter (within quotes) or with `{.passL.}`.
|
## be used for the `dynlib` parameter (within quotes) or with `{.passL.}`. Any dependency
|
||||||
|
## libraries downloaded by `Conan` or `JBB` are returned in `const xxxLDeps` as a seq[string].
|
||||||
|
##
|
||||||
|
## `libdir` can be used to instruct `getHeader()` to copy shared libraries and their
|
||||||
|
## dependencies to that directory. This prevents any runtime failures if `outdir` gets
|
||||||
|
## removed or its contents changed. By default, `libdir` is set to the output directory
|
||||||
|
## where the program binary will be created. The values of `xxxLPath` and `xxxLDeps` will
|
||||||
|
## reflect this new location.
|
||||||
##
|
##
|
||||||
## `-d:xxxStatic` can be specified to statically link with the library instead. This
|
## `-d:xxxStatic` can be specified to statically link with the library instead. This
|
||||||
## will automatically add a `{.passL.}` call to the static library for convenience. Note
|
## will automatically add a `{.passL.}` call to the static library for convenience. Note
|
||||||
|
|
@ -1214,6 +1241,8 @@ macro getHeader*(
|
||||||
""
|
""
|
||||||
mode = getCompilerMode(header)
|
mode = getCompilerMode(header)
|
||||||
|
|
||||||
|
libdir = if libdir.len != 0: libdir else: getOutDir()
|
||||||
|
|
||||||
# Use alternate library names if specified for regex search
|
# Use alternate library names if specified for regex search
|
||||||
if altNames.len != 0:
|
if altNames.len != 0:
|
||||||
lre = lre % ("(" & altNames.replace(",", "|") & ")")
|
lre = lre % ("(" & altNames.replace(",", "|") & ")")
|
||||||
|
|
@ -1272,9 +1301,9 @@ macro getHeader*(
|
||||||
static:
|
static:
|
||||||
`preBuild`(`outdir`, prePath)
|
`preBuild`(`outdir`, prePath)
|
||||||
|
|
||||||
const
|
let
|
||||||
# Library binary path - build if not standard
|
# Library binary path - build if not standard / conan / jbb
|
||||||
`lpath`* =
|
lpath {.compiletime.} =
|
||||||
when useStd:
|
when useStd:
|
||||||
stdLPath
|
stdLPath
|
||||||
elif `nameConan` or `nameJBB`:
|
elif `nameConan` or `nameJBB`:
|
||||||
|
|
@ -1283,7 +1312,7 @@ macro getHeader*(
|
||||||
buildLibrary(`lname`, `outdir`, `conFlags`, `cmakeFlags`, `makeFlags`, `buildTypes`)
|
buildLibrary(`lname`, `outdir`, `conFlags`, `cmakeFlags`, `makeFlags`, `buildTypes`)
|
||||||
|
|
||||||
# Library dependecy paths
|
# Library dependecy paths
|
||||||
`ldeps`*: seq[string] =
|
ldeps {.compiletime.}: seq[string] =
|
||||||
when `nameConan`:
|
when `nameConan`:
|
||||||
getConanLDeps(`outdir`)
|
getConanLDeps(`outdir`)
|
||||||
elif `nameJBB`:
|
elif `nameJBB`:
|
||||||
|
|
@ -1291,6 +1320,7 @@ macro getHeader*(
|
||||||
else:
|
else:
|
||||||
@[]
|
@[]
|
||||||
|
|
||||||
|
const
|
||||||
# Header path - search again in case header is generated in build
|
# Header path - search again in case header is generated in build
|
||||||
`path`* =
|
`path`* =
|
||||||
if prePath.len != 0:
|
if prePath.len != 0:
|
||||||
|
|
@ -1301,12 +1331,57 @@ macro getHeader*(
|
||||||
static:
|
static:
|
||||||
doAssert `path`.len != 0, "\nHeader " & `header` & " not found - " &
|
doAssert `path`.len != 0, "\nHeader " & `header` & " not found - " &
|
||||||
"missing/empty outdir or -d:$1Std -d:$1Git -d:$1DL -d:$1Conan or -d:$1JBB not specified" % `name`
|
"missing/empty outdir or -d:$1Std -d:$1Git -d:$1DL -d:$1Conan or -d:$1JBB not specified" % `name`
|
||||||
doAssert `lpath`.len != 0, "\nLibrary " & `lname` & " not found"
|
doAssert lpath.len != 0, "\nLibrary " & `lname` & " not found"
|
||||||
echo "# Including library " & `lpath`
|
|
||||||
|
proc extractFilenameStatic(str: string): string {.compiletime.} =
|
||||||
|
var
|
||||||
|
pos = -1
|
||||||
|
for i in countdown(str.len - 1, 0):
|
||||||
|
if str[i] == '/' or str[i] == '\\':
|
||||||
|
pos = i + 1
|
||||||
|
break
|
||||||
|
result = str[pos .. ^1]
|
||||||
|
|
||||||
|
proc joinPathStatic(str1, str2: string): string {.compiletime.} =
|
||||||
|
let
|
||||||
|
sep = when defined(Windows): "\\" else: "/"
|
||||||
|
result = str1 & sep & str2
|
||||||
|
|
||||||
# Automatically link with static library and dependencies
|
|
||||||
when `nameStatic`:
|
when `nameStatic`:
|
||||||
|
const
|
||||||
|
`lpath`* = lpath
|
||||||
|
`ldeps`* = ldeps
|
||||||
|
|
||||||
|
# Automatically link with static library and dependencies
|
||||||
{.passL: `lpath`.}
|
{.passL: `lpath`.}
|
||||||
if `ldeps`.len != 0:
|
if `ldeps`.len != 0:
|
||||||
{.passL: `ldeps`.join(" ").}
|
{.passL: `ldeps`.join(" ").}
|
||||||
|
|
||||||
|
static:
|
||||||
|
echo "# Including library " & lpath
|
||||||
|
if `ldeps`.len != 0:
|
||||||
|
echo "# Including dependencies " & `ldeps`.join(" ")
|
||||||
|
else:
|
||||||
|
const
|
||||||
|
`lpath`* = joinPathStatic(`libdir`, lpath.extractFilenameStatic())
|
||||||
|
`ldeps`* = block:
|
||||||
|
var
|
||||||
|
ldeps = ldeps
|
||||||
|
for i in 0 ..< ldeps.len:
|
||||||
|
let
|
||||||
|
lname = ldeps[i].extractFilenameStatic()
|
||||||
|
ldeptgt = joinPathStatic(`libdir`, lname)
|
||||||
|
if not fileExists(ldeptgt) or getFileDate(ldeps[i]) > getFileDate(ldeptgt):
|
||||||
|
echo "# Copying " & lname & " to " & `libdir`
|
||||||
|
cpFile(ldeps[i], ldeptgt, psymlink = true)
|
||||||
|
ldeps[i] = ldeptgt
|
||||||
|
ldeps
|
||||||
|
|
||||||
|
static:
|
||||||
|
# Copy shared libraries and dependencies to `libdir`
|
||||||
|
if not fileExists(`lpath`) or getFileDate(lpath) > getFileDate(`lpath`):
|
||||||
|
echo "# Copying " & `lpath`.extractFilenameStatic() & " to " & `libdir`
|
||||||
|
cpFile(lpath, `lpath`)
|
||||||
|
|
||||||
|
echo "# Including library " & `lpath`
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -70,19 +70,6 @@ proc walkDirImpl(indir, inext: string, file=true): seq[string] =
|
||||||
if ret == 0:
|
if ret == 0:
|
||||||
result = output.splitLines()
|
result = output.splitLines()
|
||||||
|
|
||||||
proc getFileDate(fullpath: string): string =
|
|
||||||
var
|
|
||||||
ret = 0
|
|
||||||
cmd =
|
|
||||||
when defined(Windows):
|
|
||||||
&"cmd /c for %a in ({fullpath.sanitizePath}) do echo %~ta"
|
|
||||||
elif defined(Linux):
|
|
||||||
&"stat -c %y {fullpath.sanitizePath}"
|
|
||||||
elif defined(OSX) or defined(FreeBSD):
|
|
||||||
&"stat -f %m {fullpath.sanitizePath}"
|
|
||||||
|
|
||||||
(result, ret) = execAction(cmd)
|
|
||||||
|
|
||||||
proc getCacheValue(fullpath: string): string =
|
proc getCacheValue(fullpath: string): string =
|
||||||
if not gStateCT.nocache:
|
if not gStateCT.nocache:
|
||||||
result = fullpath.getFileDate()
|
result = fullpath.getFileDate()
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ proc parseConanManifest(pkg: ConanPackage, outdir: string) =
|
||||||
if line.startsWith("lib/"):
|
if line.startsWith("lib/"):
|
||||||
if line.endsWith(".a") or line.endsWith(".lib"):
|
if line.endsWith(".a") or line.endsWith(".lib"):
|
||||||
pkg.staticLibs.add line
|
pkg.staticLibs.add line
|
||||||
elif line.endsWith(".so"):
|
elif line.endsWith(".so") or line.endsWith(".dylib"):
|
||||||
pkg.sharedLibs.add line
|
pkg.sharedLibs.add line
|
||||||
elif line.startsWith("bin/") and line.endsWith("dll"):
|
elif line.startsWith("bin/") and line.endsWith("dll"):
|
||||||
pkg.sharedLibs.add line
|
pkg.sharedLibs.add line
|
||||||
|
|
|
||||||
|
|
@ -94,18 +94,11 @@ proc parseJBBArtifacts(pkg: JBBPackage, outdir: string) =
|
||||||
break
|
break
|
||||||
|
|
||||||
proc findJBBLibs(pkg: JBBPackage, outdir: string) =
|
proc findJBBLibs(pkg: JBBPackage, outdir: string) =
|
||||||
pkg.sharedLibs = findFiles("lib[\\\\/].*\\.(so|dylib)", outdir)
|
pkg.sharedLibs = findFiles("(bin|lib)[\\\\/].*\\.(so|dll|dynlib)[0-9.]*", outdir)
|
||||||
pkg.sharedLibs.add findFiles("bin[\\\\/].*\\.(dll)", outdir)
|
|
||||||
for i in 0 ..< pkg.sharedLibs.len:
|
|
||||||
if pkg.sharedLibs[i].isAbsolute:
|
|
||||||
pkg.sharedLibs[i] = pkg.sharedLibs[i][outdir.len+1 .. ^1]
|
|
||||||
|
|
||||||
for lib in findFiles("lib[\\\\/].*\\.(a|lib)$", outdir):
|
for lib in findFiles("lib[\\\\/].*\\.(a|lib)$", outdir):
|
||||||
if not lib.endsWith(".dll.a"):
|
if not lib.endsWith(".dll.a"):
|
||||||
if lib.isAbsolute:
|
pkg.staticLibs.add lib
|
||||||
pkg.staticLibs.add lib[outdir.len+1 .. ^1]
|
|
||||||
else:
|
|
||||||
pkg.staticLibs.add lib
|
|
||||||
|
|
||||||
proc getJBBRepo*(pkg: JBBPackage, outdir: string) =
|
proc getJBBRepo*(pkg: JBBPackage, outdir: string) =
|
||||||
## Clone JBB package repo and checkout version tag if version is
|
## Clone JBB package repo and checkout version tag if version is
|
||||||
|
|
@ -174,11 +167,10 @@ proc downloadJBB*(pkg: JBBPackage, outdir: string, clean = true) =
|
||||||
&" v{pkg.version}"
|
&" v{pkg.version}"
|
||||||
else:
|
else:
|
||||||
""
|
""
|
||||||
path = outdir / "downloads" / pkg.name
|
path = outdir / pkg.name
|
||||||
echo &"# Downloading {pkg.name}{vstr} from BinaryBuilder.org"
|
echo &"# Downloading {pkg.name}{vstr} from BinaryBuilder.org"
|
||||||
downloadUrl(pkg.url, path, quiet = true)
|
downloadUrl(pkg.url, path, quiet = true)
|
||||||
pkg.findJBBLibs(path)
|
pkg.findJBBLibs(path)
|
||||||
mvTree(path, outdir)
|
|
||||||
|
|
||||||
pkg.dlJBBRequires(outdir)
|
pkg.dlJBBRequires(outdir)
|
||||||
|
|
||||||
|
|
@ -204,7 +196,7 @@ proc getJBBLDeps*(pkg: JBBPackage, outdir: string, shared: bool, main = true): s
|
||||||
|
|
||||||
if not main:
|
if not main:
|
||||||
for lib in libs:
|
for lib in libs:
|
||||||
result.add outdir / lib
|
result.add lib
|
||||||
|
|
||||||
for cpkg in pkg.requires:
|
for cpkg in pkg.requires:
|
||||||
result.add cpkg.getJBBLDeps(outdir, shared, main = false)
|
result.add cpkg.getJBBLDeps(outdir, shared, main = false)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type
|
||||||
paths*: OrderedSet[string]
|
paths*: OrderedSet[string]
|
||||||
nimblePaths*: OrderedSet[string]
|
nimblePaths*: OrderedSet[string]
|
||||||
nimcacheDir*: string
|
nimcacheDir*: string
|
||||||
|
outDir*: string
|
||||||
|
|
||||||
proc getJson(projectDir: string): JsonNode =
|
proc getJson(projectDir: string): JsonNode =
|
||||||
# Get `nim dump` json value for `projectDir`
|
# Get `nim dump` json value for `projectDir`
|
||||||
|
|
@ -67,35 +68,6 @@ proc stripName(path, projectName: string): string =
|
||||||
else:
|
else:
|
||||||
result = path
|
result = path
|
||||||
|
|
||||||
proc getNimcacheDir*(projectDir = ""): string =
|
|
||||||
## Get nimcache directory for current compilation or specified `projectDir`
|
|
||||||
when nimvm:
|
|
||||||
when (NimMajor, NimMinor, NimPatch) >= (1, 2, 0):
|
|
||||||
# Get value at compile time from `std/compilesettings`
|
|
||||||
result = stripName(
|
|
||||||
querySetting(SingleValueSetting.nimcacheDir),
|
|
||||||
querySetting(SingleValueSetting.projectName)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
# Not Nim v1.2.0+ or runtime
|
|
||||||
if result.len == 0:
|
|
||||||
let
|
|
||||||
# Get project directory for < v1.2.0 at compile time
|
|
||||||
projectDir = if projectDir.len != 0: projectDir else: getProjectDir()
|
|
||||||
|
|
||||||
# Use `nim dump` to figure out nimcache for `projectDir`
|
|
||||||
let
|
|
||||||
dumpJson = getJson(projectDir)
|
|
||||||
|
|
||||||
if dumpJson != nil and dumpJson.hasKey("nimcache"):
|
|
||||||
result = stripName(dumpJson["nimcache"].getStr(), "dummy")
|
|
||||||
|
|
||||||
# Set to OS defaults if not detectable
|
|
||||||
if result.len == 0:
|
|
||||||
result = getOsCacheDir()
|
|
||||||
|
|
||||||
proc jsonToSeq(node: JsonNode, key: string): seq[string] =
|
proc jsonToSeq(node: JsonNode, key: string): seq[string] =
|
||||||
# Convert JsonArray to seq[string] for specified `key`
|
# Convert JsonArray to seq[string] for specified `key`
|
||||||
if node.hasKey(key):
|
if node.hasKey(key):
|
||||||
|
|
@ -126,7 +98,11 @@ proc getNimConfig*(projectDir = ""): Config =
|
||||||
libPath = getCurrentCompilerExe().parentDir().parentDir() / "lib"
|
libPath = getCurrentCompilerExe().parentDir().parentDir() / "lib"
|
||||||
lazyPaths = querySettingSeq(MultipleValueSetting.lazyPaths)
|
lazyPaths = querySettingSeq(MultipleValueSetting.lazyPaths)
|
||||||
searchPaths = querySettingSeq(MultipleValueSetting.searchPaths)
|
searchPaths = querySettingSeq(MultipleValueSetting.searchPaths)
|
||||||
result.nimcacheDir = querySetting(SingleValueSetting.nimcacheDir)
|
result.nimcacheDir = stripName(
|
||||||
|
querySetting(SingleValueSetting.nimcacheDir),
|
||||||
|
querySetting(SingleValueSetting.projectName)
|
||||||
|
)
|
||||||
|
result.outDir = querySetting(SingleValueSetting.outDir)
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
@ -150,6 +126,11 @@ proc getNimConfig*(projectDir = ""): Config =
|
||||||
# Usually `libPath` is last entry in `searchPaths`
|
# Usually `libPath` is last entry in `searchPaths`
|
||||||
libPath = searchPaths[^1]
|
libPath = searchPaths[^1]
|
||||||
|
|
||||||
|
if dumpJson.hasKey("nimcache"):
|
||||||
|
result.nimcacheDir = stripName(dumpJson["nimcache"].getStr(), "dummy")
|
||||||
|
if dumpJson.hasKey("outdir"):
|
||||||
|
result.outDir = dumpJson["outdir"].getStr()
|
||||||
|
|
||||||
# Parse version
|
# Parse version
|
||||||
if version.len != 0:
|
if version.len != 0:
|
||||||
let
|
let
|
||||||
|
|
@ -188,7 +169,11 @@ proc getNimConfig*(projectDir = ""): Config =
|
||||||
if not skip:
|
if not skip:
|
||||||
result.paths.incl path
|
result.paths.incl path
|
||||||
|
|
||||||
result.nimcacheDir = getNimcacheDir(projectDir)
|
if result.nimcacheDir.len == 0:
|
||||||
|
result.nimcacheDir = getOsCacheDir()
|
||||||
|
|
||||||
|
if result.outDir.len == 0:
|
||||||
|
result.outDir = projectDir
|
||||||
|
|
||||||
proc getNimConfigFlags(cfg: Config): string =
|
proc getNimConfigFlags(cfg: Config): string =
|
||||||
# Convert configuration into Nim flags for cfg file or command line
|
# Convert configuration into Nim flags for cfg file or command line
|
||||||
|
|
@ -227,4 +212,16 @@ proc writeNimConfig*(cfgFile: string, projectDir = "") =
|
||||||
let
|
let
|
||||||
cfg = getNimConfig(projectDir)
|
cfg = getNimConfig(projectDir)
|
||||||
cfgOut = getNimConfigFlags(cfg)
|
cfgOut = getNimConfigFlags(cfg)
|
||||||
writeFile(cfgFile, cfgOut)
|
writeFile(cfgFile, cfgOut)
|
||||||
|
|
||||||
|
proc getNimcacheDir*(projectDir = ""): string =
|
||||||
|
## Get nimcache directory for current compilation or specified `projectDir`
|
||||||
|
let
|
||||||
|
cfg = getNimConfig(projectDir)
|
||||||
|
result = cfg.nimcacheDir
|
||||||
|
|
||||||
|
proc getOutDir*(projectDir = ""): string =
|
||||||
|
## Get output directory for current compilation or specified `projectDir`
|
||||||
|
let
|
||||||
|
cfg = getNimConfig(projectDir)
|
||||||
|
result = cfg.outDir
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue