In Nim I can write the following code to import an external module:
import myFancyPantsModule
...
# And here I'd use the fancyPants proc
This works fine as long as I have the module, but for people who might download the code and not have the module installed compilation will fail with a not very user friendly message:
$ nim c fancyProgram.nim
fancyProgram.nim(1, 7) Error: cannot open 'myFancyPantsModule'
Is there any way I can wrap around the import
so that I can catch it similar to an exception and execute an alternative branch of code similar to the when
statement? I was hoping to find some importable
-like macro or something which I could use like:
when importable(myFancyPantsModule):
# And here I'd use the fancyPants proc
else:
quit("Oh, sorry, go to https://github.com/nim-lang/nimble and install " &
" the myFancyPantsModule using the nimble package manager")
In fact, rather than a simple error message I would like to make some modules optional, so that compilation still proceeds ahead, maybe with reduced functionality. Is this possible?
SOLUTION EDIT: Based on the answer here is my version how to solve the issue, first you need a moduleChecker
binary with the following source:
import os, osproc
let tmpFile = getTempDir() / "dynamicModuleChecker.nim"
proc checkModule(module: string) =
except:
echo "Cannot write ", tmpFile, " to check the availability of modules"
quit(1)
writeFile(tmpFile, "import " & module & "\n")
finally: removeFile(tmpFile)
except:
echo("Cannot run \"nimrod check\" to check the availability of modules")
quit(1)
if execCmdEx("nim check " & tmpFile).exitCode != 0:
echo("Cannot import module " & module & ".")
quit(1)
else:
echo "OK"
if ParamCount() < 1:
quit("Pass as first parameter the module to check")
else:
checkModule(ParamStr(1))
Then, having this command available the following macro can be used:
import macros
macro safeImport(module, message: string): stmt =
if "OK" == gorge("./moduleChecker " & module.strVal):
result = newNimNode(nnkStmtList).add(
newNimNode(nnkImportStmt).add(
newIdentNode(module.strVal)))
else:
error("\nModule " & module.strVal &
" not available.\n" & message.strVal)
safeImport("genieos",
"Please install \"http://gradha.github.io/genieos/\"")
It is too unfortunate that a separate process has to be spawned, not only for the external compilation but also another one to generate the temporary file to check, as there is no staticWrite
in the current version to generate files at compile time.
As far as I know, there is no (easy) way to do this. What you can do is to use a separate configuration/check stage in your build. E.g.:
import macros, os, osproc
proc checkModule(module, howtomessage: string) =
except:
echo("Cannot write .conftest.nim to check the availability of modules")
quit(1)
writeFile(".conftest.nim", "import " & module & "\n")
except: nil
removeFile(".conftest.nim")
except:
echo("Cannot run \"nimrod check\" to check the availability of modules")
quit(1)
if execCmdEx("nimrod check .conftest.nim").exitCode != 0:
echo("Cannot import module " & module & ".")
echo(howtomessage)
quit(1)
checkModule "foobar", "Please install it using the Babel package manager"
Then run something like:
nimrod cc --run configure.nim && nimrod cc main.nim
That's assuming the code above is stored in a file called configure.nim
and the nimrod executable is in your path (otherwise, you'll have to specify the nimrod path in configure.nim
also).