Question

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.

Was it helpful?

Solution

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).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top