I know that it is possible to add additional compile and link flags using {.passc: "-Isome/path".} or {.passl: "-Lsomelib".}. However these flags are passed to each single file that is compiled.
I would like to add some compiler flags to an additional specific c/c++ file only that is compiled using the pragma {.compile: "additionalfile.cpp".}. These flags should not be passed when any other file is compiled, e.g. the flag "-std=c++11" (which is my primary use case right now) should only be passed when the additional c++ file is compiled, but not to any other file. Note that I use the C compiler and not the C++ compiler to compile all other files (for other reasons), so only that single specific file should be compiled with the C++ compiler and the corresponding flags.
Is this possible?
when defined(gcc):
const cc = "gcc"
elif defined(clang):
const cc = "clang"
staticExec cc & "-c additionalfile.cpp --flags"
There is still no way to pass C/C++ compiler flags to only c/c++ files added by compile pragma. Related RFC: https://github.com/nim-lang/RFCs/issues/137
I found workaround that create static link library from the nim file that only has passC and compile pragma so that passC affect to only c/c++ files specified in that nim file.
Because of this bug, this code works only in devel Nim. https://github.com/nim-lang/Nim/issues/12745
For example:
test.h
int getX();
test.c
int getX() {
return MY_X;
}
testc.nim
# Pass C compiler options only to test.c
{.passC: "-D MY_X=123".}
{.compile: "test.c".}
testmod.nim
import os, strformat, std/compilesettings
proc getX*(): int {.header: "test.h".}
template linkModule(modulePath, nimCmd = "c", nimOptions: static[string] = "") =
static:
doAssert nimCmd == "c" or nimCmd == "cpp"
block:
const
modName = splitPath(modulePath).tail
libpath {.inject.} = querySetting(SingleValueSetting.nimcacheDir) / modName & ".staticlib"
modulePath2 {.inject.} = modulePath.quoteShell
nimCmd2 {.inject.} = nimCmd
nimOptions2 {.inject.} = nimOptions.string
{.passL: libpath.}
# `--os:any --gc:arc -d:useMalloc` is used to avoid compiling and linking stdlib_io.nim for faster build.
# It doesn't affect this module.
const cmd = fmt"{getCurrentCompilerExe()} {nimCmd2} --app:staticlib --os:any --gc:arc -d:useMalloc --out:{libpath.quoteShell} {nimOptions2} {modulePath2}"
static: echo cmd
const ret = gorgeEx(cmd)
static: echo ret.output
when ret.exitCode != 0:
{.error: "Abort due to compile error".}
linkModule "testc.nim"
sample.nim
import testmod
echo getX()
You can probably do something like this
# ccode_wrapper.nim
const Ccode = staticRead"ccode.c"
{.localPassC:"-DMY_CUSTOM_FLAG".}
{.emit: Ccode.}
i.e. replace the direct C file compilation by staticRead + emit + localPassC.
It is simpler than my code, but there is a problem in your code when you want to link multiple C files. If 2 C files have same name static variables outside of function, it would generate redefinition error. In C++, redefinition error will be also generated when 2 C++ files have same name variables or functions in anonymous namespace. You can avoid such error by creating 1 nim file per 1 C/C++ files, but I don't think that is not good idea when you want to use many C/C++ files.
And when you got compile error from C/C++, line number in the error doesn't match line number in C/C++ files. I think you can fix it by emitting #line N before C code.
My code might looks like complicated but linkModule template can be placed in other module and be used in elsewhere without copy&pasting it.
For example: test1.c
static int x = 1;
int getX1() {
return x;
}
test2.c
static int x = 2;
int getX2() {
return x;
}
testc.nim
const
Ccode1 = staticRead"test1.c"
Ccode2 = staticRead"test2.c"
{.emit: Ccode1.}
{.emit: Ccode2.}
sample.nim
import testc
proc getX1(): cint {.importc.}
proc getX2(): cint {.importc.}
echo getX1(), getX2()