aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGanesh Viswanathan <dev@genotrance.com>2020-06-16 17:41:18 -0500
committerGanesh Viswanathan <dev@genotrance.com>2020-06-16 17:41:18 -0500
commitcebf6073424a50781ecce268eeb8d63f4aa8f0c0 (patch)
treedf63048800546c79806fbfa2d731bf80eb897a0d
parent7a2aff2ed32a858df0c6dd5b477498d35c4aab11 (diff)
downloadnimterop-cebf6073424a50781ecce268eeb8d63f4aa8f0c0.tar.gz
nimterop-cebf6073424a50781ecce268eeb8d63f4aa8f0c0.zip
libdir support
-rw-r--r--nimterop/build.nim97
-rw-r--r--nimterop/cimport.nim13
-rw-r--r--nimterop/conan.nim2
-rw-r--r--nimterop/jbb.nim16
-rw-r--r--nimterop/nimconf.nim61
5 files changed, 120 insertions, 69 deletions
diff --git a/nimterop/build.nim b/nimterop/build.nim
index eda27e7..6cb497d 100644
--- a/nimterop/build.nim
+++ b/nimterop/build.nim
@@ -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`
)
diff --git a/nimterop/cimport.nim b/nimterop/cimport.nim
index 0559db7..c81c35b 100644
--- a/nimterop/cimport.nim
+++ b/nimterop/cimport.nim
@@ -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()
diff --git a/nimterop/conan.nim b/nimterop/conan.nim
index bfe2e94..f8740d0 100644
--- a/nimterop/conan.nim
+++ b/nimterop/conan.nim
@@ -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
diff --git a/nimterop/jbb.nim b/nimterop/jbb.nim
index 9f0d173..3e7e623 100644
--- a/nimterop/jbb.nim
+++ b/nimterop/jbb.nim
@@ -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)
diff --git a/nimterop/nimconf.nim b/nimterop/nimconf.nim
index 33215ef..98fc8fe 100644
--- a/nimterop/nimconf.nim
+++ b/nimterop/nimconf.nim
@@ -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) \ No newline at end of file
+ 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