Skip to content

Commit 737ea40

Browse files
authored
Merge pull request #44 from Clonkk/devel
Devel
2 parents 477983a + 5bd7dc5 commit 737ea40

File tree

7 files changed

+124
-18
lines changed

7 files changed

+124
-18
lines changed

changelog.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
Changelog for Nimjl. Date in format YYYY_MM_DD
22

3+
Release v0.7.4 - 2022_06_08
4+
===========================
5+
* Improve Pkg template to handle version, url etc; parameters :
6+
* See ex11
7+
```nim
8+
Julia.init(1):
9+
Pkg:
10+
add(name="Polynomials", version="3.0.0")
11+
add(name="LinearAlgebra")
12+
add("DSP")
13+
Embed:
14+
file("myfile.jl")
15+
```
16+
17+
Release v0.7.3 - 2022_01_04
18+
===========================
19+
* Bugfix related to JULIA_PATH not being defined
20+
21+
Release v0.7.2 - 2022_01_04
22+
===========================
23+
* Add nthreads argument to Julia.init() to start the Julia VM on multiple threads
24+
25+
Release v0.7.1 - 2022_01_04
26+
===========================
27+
* Normalize path during compilation
28+
* Various docs improvements
29+
330
Release v0.7.0 - 2022_01_04
431
===========================
532
* Add Julia.useModule alias for jlUseModule

examples/ex11_external_deps.nim

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import nimjl
2+
3+
## See https://pkgdocs.julialang.org/dev/api/#Pkg.add for more info
4+
Julia.init(1):
5+
Pkg:
6+
add(name="Polynomials", version="3.0.0")
7+
add(name="LinearAlgebra")
8+
add("DSP")
9+
add(name="Wavelets", version="0.9.4")

nimjl/cores.nim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ proc jlExceptionHandler*() =
1212
## Convert a Julia exception to Nim exception
1313
if not isNil(excpt):
1414
let msg = $(jl_exception_message())
15+
echo "->", msg, "<-"
1516
raise newException(JlError, msg)
1617
else:
1718
discard
@@ -21,6 +22,11 @@ proc jlEval*(code: string): JlValue =
2122
result = jl_eval_string(code)
2223
jlExceptionHandler()
2324

25+
proc jlTopLevelEval*(x: JlValue) : JlValue =
26+
## Only use it if you know what you're doing
27+
result = jl_toplevel_eval(JlMain, x)
28+
jlExceptionHandler()
29+
2430
proc jlInclude*(filename: string) =
2531
## Include Julia file
2632
let tmp = jlEval(&"include(\"{filename}\")")
@@ -132,3 +138,4 @@ proc jlEmbedFile*(filename: static[string]) =
132138
## Embed specific Julia file
133139
const jlContent = staticRead(getProjectPath() / filename)
134140
staticContents[filename] = jlContent
141+

nimjl/glucose.nim

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This file is named glucose because it gives you sugar ;)
22
# It contains most syntactic sugar to ease using Julia inside Nim
3-
import std/os
3+
import std/[os, strutils, strformat]
44
import ./types
55
import ./cores
66
import ./functions
@@ -13,16 +13,63 @@ type Julia* = object
1313
proc init*(jl: type Julia, nthreads: int = 1) =
1414
jlVmInit(nthreads)
1515

16+
# This should only be used to generate Expr() in order to use named argument in Pkg interface
17+
proc fmtJlExpr(val: string): string =
18+
result = ""
19+
if val[0] == ':' or val.startsWith("Expr") or val.startsWith("QuoteNode"):
20+
result.add(val)
21+
else:
22+
result.addQuoted(val)
23+
24+
proc jlExpr(head: string, vals: varargs[string]): string =
25+
result = "Expr("
26+
result &= fmtJlExpr(head)
27+
28+
for val in vals:
29+
if not val.isEmptyorWhitespace():
30+
result &= ", "
31+
result &= fmtJlExpr(val)
32+
result &= ")"
33+
34+
type
35+
JlPkgSpec = object
36+
name, url, path, subdir, rev, version, mode, level: string
37+
JlPkgs = seq[JlPkgSpec]
38+
39+
# Workaround because named parameters do not work inside closure for proc defined in template
40+
# TODO : Should string be static ?
41+
proc addImpl(pkgs: var JlPkgs, name: static string, url: static string = "", path: static string = "", subdir: static string = "", rev: static string = "", version: static string = "", mode: static string = "", level: static string = "") =
42+
if not jlVmIsInit():
43+
pkgs.add(JlPkgSpec(name: name, url: url, path: path, subdir: subdir, rev: rev, version: version, mode: mode, level: level))
44+
45+
template add*(name: static string, url: static string = "", path: static string = "", subdir: static string = "", rev: static string = "", version: static string = "", mode: static string = "", level: static string = "") =
46+
## Nim native way of calling Julia Pkg.add during Julia.init()
47+
##
48+
## See https://pkgdocs.julialang.org/dev/api/#Pkg.add for more info
49+
## Pkg.add("Example") # Add a package from registry
50+
## Pkg.add("Example"; preserve=Pkg.PRESERVE_ALL) # Add the `Example` package and preserve existing dependencies
51+
## Pkg.add(name="Example", version="0.3") # Specify version; latest release in the 0.3 series
52+
## Pkg.add(name="Example", version="0.3.1") # Specify version; exact release
53+
## Pkg.add(url="https://github.com/JuliaLang/Example.jl", rev="master") # From url to remote gitrepo
54+
## Pkg.add(url="/remote/mycompany/juliapackages/OurPackage") # From path to local gitrepo
55+
## Pkg.add(url="https://github.com/Company/MonoRepo", subdir="juliapkgs/Package.jl)") # With subdir
56+
when declared(jl_pkg_private_scope):
57+
addImpl(jl_pkg_private_scope, name, url, path, subdir, rev, version, mode, level)
58+
else:
59+
{.error: "Pkg: add() can only be called during Julia.init() scope"}
60+
1661
template init*(jl: type Julia, nthreads: int, body: untyped) =
1762
## Init Julia VM
18-
var packages: seq[string]
19-
template Pkg(innerbody: untyped) =
20-
## Pkg installation API
21-
proc add(pkgname: string) =
22-
packages.add pkgname
23-
innerbody
24-
25-
template Embed(innerbody: untyped) =
63+
var packages :JlPkgs
64+
template Pkg(innerbody: untyped) {.used.} =
65+
block:
66+
# Technically accessible but since the type are not exported, what are you going to do with it ?
67+
# It's good enough : the API is simple and close to Julia native for people not to get confused
68+
var jl_pkg_private_scope {.inject.}: JlPkgs
69+
innerbody
70+
packages = jl_pkg_private_scope
71+
72+
template Embed(innerbody: untyped) {.used.} =
2673
## Emded Julia file explicitly of from a directory
2774
template file(filename: static[string]) =
2875
## Embed file
@@ -47,10 +94,19 @@ template init*(jl: type Julia, nthreads: int, body: untyped) =
4794
jl_init()
4895
# Module installation
4996
Julia.useModule("Pkg")
50-
let pkg = Julia.getModule("Pkg")
51-
for pkgname in packages:
52-
discard jlCall(pkg, "add", pkgname)
53-
jlUsing(pkgname)
97+
for pkgspec in packages:
98+
var exprs: seq[string] = @[jlExpr(":.", ":Pkg", "QuoteNode(:add)")]
99+
for key, field in pkgspec.fieldPairs():
100+
let fname = ":" & key
101+
if not isEmptyOrWhitespace(field):
102+
exprs.add jlExpr(":kw", fname, field)
103+
let strexpr = jlExpr(":call", exprs)
104+
var jlexpr = jlEval(strexpr)
105+
# Will crash if version are invalid
106+
discard jlTopLevelEval(jlexpr)
107+
# TODO : handle precompilation ?
108+
# Julia.precompile()
109+
jlUsing(pkgspec.name)
54110

55111
# Eval Julia code embedded
56112
loadJlRessources()

nimjl/private/jlcores.nim

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@ type jl_func *{.importc: "jl_function_t", pure, final.} = object
1111
type jl_module *{.importc: "jl_module_t", pure, final.} = object
1212
type jl_datatype*{.importc: "jl_datatype_t", pure, final.} = object
1313
type jl_sym*{.importc: "jl_sym_t", pure, final.} = object
14+
type jl_expr*{.importc: "jl_expr_t", pure, final.} = object
15+
# head: ptr jl_sym
16+
# args: ptr jl_array
1417
{.pop.}
1518

1619
{.push nodecl, header: JuliaHeader, dynlib: JuliaLibName.}
1720
proc jl_symbol*(symname: cstring): ptr jl_sym {.importc: "jl_symbol".}
1821

1922
proc jl_eval_string*(code: cstring): ptr jl_value {.importc: "jl_eval_string".}
2023

24+
proc jl_toplevel_eval*(m: ptr jl_module, expr: ptr jl_value): ptr jl_value {.importc: "jl_toplevel_eval".}
25+
2126
# Error handler
2227
proc jl_exception_occurred*(): ptr jl_value {.importc: "jl_exception_occurred".}
2328

nimjl/types.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type
88
JlArray*[T] = ptr jl_array
99
JlSym* = ptr jl_sym
1010
JlDataType* = ptr jl_datatype
11+
JlExpr* = ptr jl_expr
1112

1213
type
1314
JlError* = object of IOError

readme.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Mostly quality-of-life improvements, especially when handling arrays.
6161
* Linux / WSL supports only
6262
* Windows dynamic library linking is different than Linux.
6363
* If you need Windows support, consider opening an issue or a PR :).
64+
* Otherwise, just use WSL
6465

6566
# Examples
6667

@@ -77,19 +78,19 @@ echo res # 2.0
7778
7879
```
7980

80-
## New in version 0.7.0
81+
## New in version 0.7.4
8182

82-
It is now possible to embed Julia files inside a Nim compiled binary to easily distribute Julia code. To make distribution possible, an API to call ``Pkg.add("...")`` has also been added.
83+
It is now possible to embed Julia files inside a Nim compiled binary to easily distribute Julia code. To make distribution possible, an API to call ``Pkg.add("...")`` has also been added **with version number easy to specify**.
8384

8485
```nim
8586
import nimjl
8687
8788
Julia.init:
8889
Pkg:
90+
add(name="Polynomials", version="3.0.0")
91+
add(name="LinearAlgebra")
8992
add("DSP")
90-
add("Wavelets")
91-
add("LinearAlgebra")
92-
add("Statistics")
93+
add(name="Wavelets", version="0.9.4")
9394
9495
Embed:
9596
# embed all files with '*.jl' extension in folder ``JuliaToolBox/``

0 commit comments

Comments
 (0)