├── .gitignore ├── tests └── testnd.nim ├── nimdeps.nimble ├── LICENSE ├── README.md └── nimdeps.nim /.gitignore: -------------------------------------------------------------------------------- 1 | nimdeps 2 | nimcache 3 | *.exe 4 | *.swp 5 | test* 6 | -------------------------------------------------------------------------------- /tests/testnd.nim: -------------------------------------------------------------------------------- 1 | import nimdeps 2 | 3 | const FDIRS = @["testnd.nim"] 4 | setupDeps(FDIRS) 5 | -------------------------------------------------------------------------------- /nimdeps.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "genotrance" 5 | description = "Nim library to bundle dependency files into executable" 6 | license = "MIT" 7 | 8 | skipDirs = @["tests"] 9 | 10 | task docs, "Generate docs": 11 | exec "nim doc -o:build --project --index:on nimdeps.nim" 12 | if "--publish" in commandLineParams: 13 | exec "cd build && ghp-import --no-jekyll -fp ." 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ganesh Viswanathan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nimdeps is a [Nim](https://nim-lang.org/) package to bundle dependency files into the generated executable. 2 | 3 | It is very common to have external dependency files such as data files, graphics and other payloads as part of an application. Nimdeps makes it easy to carry all this payload within the application binary instead of having to build a separate installer. 4 | 5 | All dependencies get packaged into the application binary at compile time. All references to dependencies need to be relative to the application project directory at compile time. At runtime, all files are checked for existence and extracted if not already present or changed. Dependencies get extracted relative to the application location at runtime. 6 | 7 | __Installation__ 8 | 9 | Nimdeps can be installed via [Nimble](https://github.com/nim-lang/nimble): 10 | 11 | ``` 12 | > nimble install nimdeps 13 | ``` 14 | 15 | This will download and install nimdeps in the standard Nimble package location, typically ~/.nimble. Once installed, it can be imported into any Nim program. 16 | 17 | __Usage__ 18 | 19 | Module documentation can be found [here](http://genotrance.github.io/nimdeps/nimdeps.html). 20 | 21 | ```nim 22 | import nimdeps 23 | 24 | const FILES = @["data.dat", "icon.png"] 25 | setupDepFiles(FILES) 26 | 27 | setupDepDir("depDir1") 28 | 29 | setupDeps(@["data2.dat", "depDir2"]) 30 | ``` 31 | 32 | NOTE: Nimdeps should be invoked prior to loading any of the dependencies so that they are extracted prior to usage. At this time, DLL files are not supported if they are loaded using the `{.dynlib.}` pragma since Nim loads such libraries prior to nimdeps running. Likewise if they are linked with `-l` since the OS checks for them at load time. This can be avoided by manually loading the DLL using the `dynlib` module or lazily loaded using the [lazylib](https://github.com/genotrance/lazylib) package. 33 | 34 | __Feedback__ 35 | 36 | Nimdeps is a work in progress and any feedback or suggestions are welcome. It is hosted on [GitHub](https://github.com/genotrance/nimdeps) with an MIT license so issues, forks and PRs are most appreciated. 37 | -------------------------------------------------------------------------------- /nimdeps.nim: -------------------------------------------------------------------------------- 1 | import macros, os, strutils 2 | 3 | proc writeDepFile(constname, filename: string) = 4 | ## Write out bundled dependency files to filesystem - this is automatically 5 | ## called by setupDepFile() 6 | ## 7 | ## `constname` is name of the constant string that contains the file contents 8 | ## which is the relative path passed to setupDepFile() with all special 9 | ## characters replaced by _. 10 | ## 11 | ## `filename` is the path to write the file contents relative to the location 12 | ## of the executable. 13 | let 14 | fullpath = joinPath(getAppDir(), filename) 15 | pdir = parentDir(fullpath) 16 | 17 | if not dirExists(pdir): 18 | try: 19 | createDir(pdir) 20 | except OSError: 21 | echo "Failed to create directory: " & pdir 22 | quit(1) 23 | 24 | if not existsFile(fullpath) or getFileSize(fullpath) != constname.len: 25 | writeFile(fullpath, constname) 26 | 27 | while not existsFile(fullpath): 28 | sleep(10) 29 | 30 | macro setupDepFileImpl(filename: static[string]): untyped = 31 | result = newNimNode(nnkStmtList) 32 | var fullpath = joinPath(getProjectPath(), filename) 33 | if not fileExists(fullpath) and not dirExists(fullpath): 34 | echo "nimdeps: Failed to find " & fullpath 35 | quit(1) 36 | echo "Loading " & fullpath 37 | 38 | var 39 | vname = "V" & filename.multiReplace([ 40 | ("/", "_"), ("\\", "_"), (":", "_"), (".", "_")]).toUpperAscii() 41 | ivname = ident(vname) 42 | 43 | result.add(quote do: 44 | const `ivname` = staticRead `fullpath` 45 | ) 46 | 47 | result.add(quote do: 48 | writeDepFile(`ivname`, `filename`) 49 | ) 50 | 51 | macro setupDepFile*(filename: static[string]): untyped = 52 | ## Setup dependency file to be bundled into executable 53 | ## 54 | ## `filename` is a file to bundle during compile time and write back at 55 | ## runtime. Path should be relative to project directory during compile time 56 | ## and will be relative to executable location at runtime. 57 | return quote do: 58 | setupDepFileImpl(`filename`) 59 | 60 | macro setupDepFiles*(filenames: static[seq[string]]): untyped = 61 | ## Setup list of dependency files to be bundled into executable 62 | ## 63 | ## `filenames` is an array of files to bundle during compile time and write 64 | ## back at runtime. 65 | result = newNimNode(nnkStmtList) 66 | for filename in filenames: 67 | result.add(quote do: 68 | setupDepFileImpl(`filename`) 69 | ) 70 | 71 | template setupDepDir*(dir: untyped): untyped = 72 | ## Setup directory of dependency files to be bundled into executable 73 | ## 74 | ## `dir` is a directory to bundle during compile time and write back at 75 | ## runtime. Path should be relative to project directory during compile time 76 | ## and will be relative to executable location at runtime. 77 | setupDepFiles(block: 78 | var 79 | fls: seq[string] = @[] 80 | fullpath = joinPath(getProjectPath(), dir) 81 | 82 | for f in walkDirRec(fullpath): 83 | fls.add(f.replace(getProjectPath(), "")) 84 | 85 | fls 86 | ) 87 | 88 | macro setupDepDirs*(dirs: static[seq[string]]): untyped = 89 | ## Setup directories of dependency files to be bundled into executable 90 | ## 91 | ## `dirs` is an array of directories to bundle during compile time and write 92 | ## back at runtime. 93 | result = newNimNode(nnkStmtList) 94 | for dir in dirs: 95 | result.add(quote do: 96 | setupDepDir(`dir`) 97 | ) 98 | 99 | macro setupDeps*(fdirs: static[seq[string]]): untyped = 100 | ## Setup list of directories and files to be bundled into executable 101 | ## 102 | ## `fdirs` is an array of directories and files to bundle during compile time 103 | ## and write back at runtime. 104 | result = newNimNode(nnkStmtList) 105 | for fdir in fdirs: 106 | let fullpath = joinPath(getProjectPath(), fdir) 107 | if dirExists(fullpath): 108 | result.add(quote do: 109 | setupDepDir(`fdir`) 110 | ) 111 | else: 112 | result.add(quote do: 113 | setupDepFileImpl(`fdir`) 114 | ) 115 | --------------------------------------------------------------------------------