libdir support

This commit is contained in:
Ganesh Viswanathan 2020-06-16 17:41:18 -05:00
commit 9d8fee19c0
5 changed files with 120 additions and 69 deletions

View file

@ -158,8 +158,10 @@ proc mkDir*(dir: string) =
flag = when not defined(Windows): "-p" else: ""
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
##
## `psymlink = true` preserves symlinks instead of dereferencing on posix
let
source = source.replace("/", $DirSep)
dest = dest.replace("/", $DirSep)
@ -173,7 +175,10 @@ proc cpFile*(source, dest: string, move = false) =
if move:
"mv -f"
else:
"cp -f"
if psymlink:
"cp -fd"
else:
"cp -f"
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
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 =
## Get a cache directory where all nimterop artifacts can be stored
##
@ -1083,7 +1102,8 @@ macro isDefined*(def: untyped): untyped =
macro getHeader*(
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] = "",
altNames: static[string] = "", buildTypes: static[openArray[BuildType]] = [btCmake, btAutoconf]): untyped =
## 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
## 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
## will automatically add a `{.passL.}` call to the static library for convenience. Note
@ -1214,6 +1241,8 @@ macro getHeader*(
""
mode = getCompilerMode(header)
libdir = if libdir.len != 0: libdir else: getOutDir()
# Use alternate library names if specified for regex search
if altNames.len != 0:
lre = lre % ("(" & altNames.replace(",", "|") & ")")
@ -1272,9 +1301,9 @@ macro getHeader*(
static:
`preBuild`(`outdir`, prePath)
const
# Library binary path - build if not standard
`lpath`* =
let
# Library binary path - build if not standard / conan / jbb
lpath {.compiletime.} =
when useStd:
stdLPath
elif `nameConan` or `nameJBB`:
@ -1283,7 +1312,7 @@ macro getHeader*(
buildLibrary(`lname`, `outdir`, `conFlags`, `cmakeFlags`, `makeFlags`, `buildTypes`)
# Library dependecy paths
`ldeps`*: seq[string] =
ldeps {.compiletime.}: seq[string] =
when `nameConan`:
getConanLDeps(`outdir`)
elif `nameJBB`:
@ -1291,6 +1320,7 @@ macro getHeader*(
else:
@[]
const
# Header path - search again in case header is generated in build
`path`* =
if prePath.len != 0:
@ -1301,12 +1331,57 @@ macro getHeader*(
static:
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`
doAssert `lpath`.len != 0, "\nLibrary " & `lname` & " not found"
echo "# Including library " & `lpath`
doAssert lpath.len != 0, "\nLibrary " & `lname` & " not found"
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`:
const
`lpath`* = lpath
`ldeps`* = ldeps
# Automatically link with static library and dependencies
{.passL: `lpath`.}
if `ldeps`.len != 0:
{.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`
)

View file

@ -70,19 +70,6 @@ proc walkDirImpl(indir, inext: string, file=true): seq[string] =
if ret == 0:
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 =
if not gStateCT.nocache:
result = fullpath.getFileDate()

View file

@ -278,7 +278,7 @@ proc parseConanManifest(pkg: ConanPackage, outdir: string) =
if line.startsWith("lib/"):
if line.endsWith(".a") or line.endsWith(".lib"):
pkg.staticLibs.add line
elif line.endsWith(".so"):
elif line.endsWith(".so") or line.endsWith(".dylib"):
pkg.sharedLibs.add line
elif line.startsWith("bin/") and line.endsWith("dll"):
pkg.sharedLibs.add line

View file

@ -94,18 +94,11 @@ proc parseJBBArtifacts(pkg: JBBPackage, outdir: string) =
break
proc findJBBLibs(pkg: JBBPackage, outdir: string) =
pkg.sharedLibs = findFiles("lib[\\\\/].*\\.(so|dylib)", 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]
pkg.sharedLibs = findFiles("(bin|lib)[\\\\/].*\\.(so|dll|dynlib)[0-9.]*", outdir)
for lib in findFiles("lib[\\\\/].*\\.(a|lib)$", outdir):
if not lib.endsWith(".dll.a"):
if lib.isAbsolute:
pkg.staticLibs.add lib[outdir.len+1 .. ^1]
else:
pkg.staticLibs.add lib
pkg.staticLibs.add lib
proc getJBBRepo*(pkg: JBBPackage, outdir: string) =
## 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}"
else:
""
path = outdir / "downloads" / pkg.name
path = outdir / pkg.name
echo &"# Downloading {pkg.name}{vstr} from BinaryBuilder.org"
downloadUrl(pkg.url, path, quiet = true)
pkg.findJBBLibs(path)
mvTree(path, outdir)
pkg.dlJBBRequires(outdir)
@ -204,7 +196,7 @@ proc getJBBLDeps*(pkg: JBBPackage, outdir: string, shared: bool, main = true): s
if not main:
for lib in libs:
result.add outdir / lib
result.add lib
for cpkg in pkg.requires:
result.add cpkg.getJBBLDeps(outdir, shared, main = false)

View file

@ -16,6 +16,7 @@ type
paths*: OrderedSet[string]
nimblePaths*: OrderedSet[string]
nimcacheDir*: string
outDir*: string
proc getJson(projectDir: string): JsonNode =
# Get `nim dump` json value for `projectDir`
@ -67,35 +68,6 @@ proc stripName(path, projectName: string): string =
else:
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] =
# Convert JsonArray to seq[string] for specified `key`
if node.hasKey(key):
@ -126,7 +98,11 @@ proc getNimConfig*(projectDir = ""): Config =
libPath = getCurrentCompilerExe().parentDir().parentDir() / "lib"
lazyPaths = querySettingSeq(MultipleValueSetting.lazyPaths)
searchPaths = querySettingSeq(MultipleValueSetting.searchPaths)
result.nimcacheDir = querySetting(SingleValueSetting.nimcacheDir)
result.nimcacheDir = stripName(
querySetting(SingleValueSetting.nimcacheDir),
querySetting(SingleValueSetting.projectName)
)
result.outDir = querySetting(SingleValueSetting.outDir)
else:
discard
@ -150,6 +126,11 @@ proc getNimConfig*(projectDir = ""): Config =
# Usually `libPath` is last entry in `searchPaths`
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
if version.len != 0:
let
@ -188,7 +169,11 @@ proc getNimConfig*(projectDir = ""): Config =
if not skip:
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 =
# Convert configuration into Nim flags for cfg file or command line
@ -227,4 +212,16 @@ proc writeNimConfig*(cfgFile: string, projectDir = "") =
let
cfg = getNimConfig(projectDir)
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