diff options
| author | ruki <[email protected]> | 2026-01-31 08:38:32 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-01-31 08:38:32 +0800 |
| commit | 09a670d2fef977f1494135b4146631dbf81a4446 (patch) | |
| tree | 1abc2bd2d2ee867d55a6c377c8e0f755d0b719d7 | |
| parent | 51a68ee33918b95811420beb6a56673a83abe6cc (diff) | |
| parent | 62b7806dd006540fda8c8c85d173973ae2479fbf (diff) | |
Merge pull request #7272 from luadebug/nimlib
enhance Nim support for shared libraries and rpath handling
| -rw-r--r-- | tests/projects/nim/link_library/headers/test_header.h | 10 | ||||
| -rw-r--r-- | tests/projects/nim/link_library/inc/test.h | 11 | ||||
| -rw-r--r-- | tests/projects/nim/link_library/maindll.nim | 16 | ||||
| -rw-r--r-- | tests/projects/nim/link_library/mainlib.nim | 30 | ||||
| -rw-r--r-- | tests/projects/nim/link_library/shared.nim | 29 | ||||
| -rw-r--r-- | tests/projects/nim/link_library/static.nim | 26 | ||||
| -rw-r--r-- | tests/projects/nim/link_library/xmake.lua | 47 | ||||
| -rw-r--r-- | xmake/languages/nim/xmake.lua | 13 | ||||
| -rw-r--r-- | xmake/modules/core/tools/nim.lua | 111 |
9 files changed, 287 insertions, 6 deletions
diff --git a/tests/projects/nim/link_library/headers/test_header.h b/tests/projects/nim/link_library/headers/test_header.h new file mode 100644 index 000000000..4b71d3173 --- /dev/null +++ b/tests/projects/nim/link_library/headers/test_header.h @@ -0,0 +1,10 @@ +#ifndef TEST_HEADER_H +#define TEST_HEADER_H + +#define TEST_HEADER_VAL 123 + +static int test_add_five(int x) { + return x + 5; +} + +#endif diff --git a/tests/projects/nim/link_library/inc/test.h b/tests/projects/nim/link_library/inc/test.h new file mode 100644 index 000000000..f7d68cac8 --- /dev/null +++ b/tests/projects/nim/link_library/inc/test.h @@ -0,0 +1,11 @@ + +#ifndef TEST_H +#define TEST_H + +#ifdef TEST_STATIC + #define TEST_MSG "Hello from Static Lib!" +#else + #define TEST_MSG "Hello from Shared Lib!" +#endif + +#endif diff --git a/tests/projects/nim/link_library/maindll.nim b/tests/projects/nim/link_library/maindll.nim new file mode 100644 index 000000000..b13fe1caf --- /dev/null +++ b/tests/projects/nim/link_library/maindll.nim @@ -0,0 +1,16 @@ +import shared + +echo "Calling shared lib mulTwo(10): ", mulTwo(10) +echo "Calling shared lib countWords('hello, world, hello'): ", countWords("hello, world, hello") +echo "Calling shared lib getMsg('test'): ", getMsg() + +{.emit: """ +#include <test_header.h> +""".} + +var testHeaderVal {.importc: "TEST_HEADER_VAL", nodecl.}: cint +echo "TEST_HEADER_VAL: ", testHeaderVal + +proc test_add_five(x: cint): cint {.importc: "test_add_five", nodecl.} +echo "test_add_five(80): ", test_add_five(80) + diff --git a/tests/projects/nim/link_library/mainlib.nim b/tests/projects/nim/link_library/mainlib.nim new file mode 100644 index 000000000..1a1fae06c --- /dev/null +++ b/tests/projects/nim/link_library/mainlib.nim @@ -0,0 +1,30 @@ +import static + +{.emit: """ +#include <zlib.h> +#define STB_IMAGE_IMPLEMENTATION +#include <stb_image.h> +""".} + +proc zlibVersion(): cstring {.importc: "zlibVersion", nodecl.} +proc stbi_set_flip_vertically_on_load(flag_true_if_should_flip: cint) {.importc: "stbi_set_flip_vertically_on_load", nodecl.} + +echo "Zlib Version: ", zlibVersion() + +stbi_set_flip_vertically_on_load(1) +echo "STB Image: Flip vertically on load set to 1" + +echo "Calling static lib addTwo(10): ", addTwo(10) +echo "Calling static lib getAlphabet(): ", getAlphabet() +echo "Calling static lib getMsg('test'): ", getMsg() + +{.emit: """ +#include <test_header.h> +""".} + +var testHeaderVal {.importc: "TEST_HEADER_VAL", nodecl.}: cint +echo "TEST_HEADER_VAL: ", testHeaderVal + +proc test_add_five(x: cint): cint {.importc: "test_add_five", nodecl.} +echo "test_add_five(55): ", test_add_five(55) + diff --git a/tests/projects/nim/link_library/shared.nim b/tests/projects/nim/link_library/shared.nim new file mode 100644 index 000000000..58dacc6df --- /dev/null +++ b/tests/projects/nim/link_library/shared.nim @@ -0,0 +1,29 @@ +proc mulTwo*(x: int): int = + return x * 2 + +import tables, strutils + +proc countWords*(input: string): string = + var wordFrequencies = initCountTable[string]() + for word in input.split(", "): + wordFrequencies.inc(word) + return "The most frequent word is '" & $wordFrequencies.largest & "'" + +{.emit: """ +#include "test.h" +""".} + +proc getMsg*(): cstring {.exportc, dynlib.} = + var msg: cstring + {.emit: "`msg` = TEST_MSG;".} + return msg + +{.emit: """ +#include <test_header.h> +""".} + +var testHeaderVal {.importc: "TEST_HEADER_VAL", nodecl.}: cint +echo "TEST_HEADER_VAL: ", testHeaderVal + +proc test_add_five(x: cint): cint {.importc: "test_add_five", nodecl.} +echo "test_add_five(10): ", test_add_five(10) diff --git a/tests/projects/nim/link_library/static.nim b/tests/projects/nim/link_library/static.nim new file mode 100644 index 000000000..6808d5895 --- /dev/null +++ b/tests/projects/nim/link_library/static.nim @@ -0,0 +1,26 @@ +proc addTwo*(x: int): int = + return x + 2 + +proc getAlphabet*(): string = + for letter in 'a'..'z': + result.add(letter) + +{.emit: """ +#define TEST_STATIC +#include "test.h" +""".} + +proc getMsg*(): cstring {.exportc, dynlib.} = + var msg: cstring + {.emit: "`msg` = TEST_MSG;".} + return msg + +{.emit: """ +#include <test_header.h> +""".} + +var testHeaderVal {.importc: "TEST_HEADER_VAL", nodecl.}: cint +echo "TEST_HEADER_VAL: ", testHeaderVal + +proc test_add_five(x: cint): cint {.importc: "test_add_five", nodecl.} +echo "test_add_five(60): ", test_add_five(60) diff --git a/tests/projects/nim/link_library/xmake.lua b/tests/projects/nim/link_library/xmake.lua new file mode 100644 index 000000000..ba56fcbf2 --- /dev/null +++ b/tests/projects/nim/link_library/xmake.lua @@ -0,0 +1,47 @@ +set_project("link_libs") +add_rules("mode.debug", "mode.release") + +add_requires("zlib", {system = false, configs = {shared = true}}) +add_requires("stb", {system = false}) + +target("headers") + set_kind("headeronly") + add_headerfiles("headers/*.h") + add_includedirs("headers", {public = true}) + +target("executablestatic") + set_kind("binary") + add_files("mainlib.nim") + add_deps("staticlib") + add_packages("zlib", "stb", {public = true}) + if is_plat("linux") then + add_syslinks("pthread", "m") + end + +target("executableshared") + set_kind("binary") + add_files("maindll.nim") + add_deps("sharedlib") + if is_plat("linux") then + add_syslinks("pthread", "m") + end + +target("staticlib") + set_kind("static") + add_files("static.nim") + add_includedirs("inc", {public = true}) + add_headerfiles("inc/*.h") + if is_plat("linux") then + add_syslinks("pthread", "m") + end + add_deps("headers") + +target("sharedlib") + set_kind("shared") + add_files("shared.nim") + add_includedirs("inc", {public = true}) + add_headerfiles("inc/*.h") + if is_plat("linux") then + add_syslinks("pthread", "m") + end + add_deps("headers") diff --git a/xmake/languages/nim/xmake.lua b/xmake/languages/nim/xmake.lua index 65ad6429b..458cd1d33 100644 --- a/xmake/languages/nim/xmake.lua +++ b/xmake/languages/nim/xmake.lua @@ -39,10 +39,13 @@ language("nim") , "target.optimize:check" , "target.vectorexts:check" , "target.includedirs" + , "target.sysincludedirs" , "toolchain.includedirs" } , binary = { "config.linkdirs" + , "target.includedirs" + , "target.sysincludedirs" , "target.linkdirs" , "target.rpathdirs" , "target.strip" @@ -52,9 +55,14 @@ language("nim") , "config.links" , "target.links" , "toolchain.links" + , "config.syslinks" + , "target.syslinks" + , "toolchain.syslinks" } , shared = { "config.linkdirs" + , "target.includedirs" + , "target.sysincludedirs" , "target.linkdirs" , "target.strip" , "target.symbols" @@ -62,10 +70,15 @@ language("nim") , "config.links" , "target.links" , "toolchain.links" + , "config.syslinks" + , "target.syslinks" + , "toolchain.syslinks" } , static = { "target.strip" , "target.symbols" + , "target.includedirs" + , "target.sysincludedirs" } } diff --git a/xmake/modules/core/tools/nim.lua b/xmake/modules/core/tools/nim.lua index 94cc0645c..415d102f2 100644 --- a/xmake/modules/core/tools/nim.lua +++ b/xmake/modules/core/tools/nim.lua @@ -34,6 +34,23 @@ function init(self) -- init shflags self:set("ncshflags", "--app:lib", "--noMain") + + -- init arch flags + local arch = self:arch() + if arch then + if self:is_arch("x86", "i386") then + self:add("ncflags", "--cpu:i386", "--define:bit32") + if self:is_plat("linux", "macosx", "bsd", "mingw") then + self:add("ncflags", '--passC:"-m32"', '--passL:"-m32"') + end + elseif self:is_arch("x64", "x86_64") then + self:add("ncflags", "--cpu:amd64", "--define:bit64") + elseif self:is_arch("arm64.*") then + self:add("ncflags", "--cpu:arm64", "--define:bit64") + elseif self:is_arch("arm.*") then + self:add("ncflags", "--cpu:arm", "--define:bit32") + end + end end -- make the warning flag @@ -92,31 +109,113 @@ end function nf_strip(self, level) if self:is_plat("linux", "macosx", "bsd") then if level == "debug" or level == "all" then - return "--passL:-s" + return '--passL:"-s"' end end end -- make the includedir flag function nf_includedir(self, dir) - return {"--passC:-I" .. path.translate(dir)} + return {string.format('--passC:"-I%s"', path.translate(dir))} +end + +-- make the sysincludedir flag +function nf_sysincludedir(self, dir) + return nf_includedir(self, dir) end -- make the link flag function nf_link(self, lib) if self:is_plat("windows") then - return "--passL:" .. lib .. ".lib" + return string.format('--passL:"%s.lib"', lib) + else + return string.format('--passL:"-l%s"', lib) + end +end + +-- make the syslink flag +function nf_syslink(self, lib) + if self:is_plat("windows") then + return string.format('--passL:"%s.lib"', lib) else - return "--passL:-l" .. lib + if lib == "pthread" then + return {"--threads:on", string.format('--passL:"-l%s"', lib), '--dynlibOverride:"pthread"'} + else + return string.format('--passL:"-l%s"', lib) + end end end -- make the linkdir flag function nf_linkdir(self, dir) if self:is_plat("windows") then - return {"--passL:-libpath:" .. path.translate(dir)} + return {string.format('--passL:"-libpath:%s"', path.translate(dir))} else - return {"--passL:-L" .. path.translate(dir)} + return {string.format('--passL:"-L%s"', path.translate(dir))} + end +end + +-- make the rpathdir flag +function nf_rpathdir(self, dir, opt) + if self:is_plat("windows") then + return + end + opt = opt or {} + local extra = opt.extra + if extra and extra.installonly then + return + end + dir = path.translate(dir) + + -- Use --passL:"-Wl,-rpath=<dir>" to pass rpath to the linker + -- We use standard -Wl,-rpath for gcc/clang on linux/macosx/bsd without check mainly. + if self:is_plat("macosx", "iphoneos") then + dir = dir:gsub("([@$][%w_]+)", function (name) + if name == "$ORIGIN" then + return "@loader_path" + end + return name + end) + local rpath = string.format("-Wl,-rpath,%s", dir) + return {string.format('--passL:"%s"', rpath)} + elseif self:is_plat("linux", "bsd", "android") then + dir = dir:gsub("([@$][%w_]+)", function (name) + if name == "@loader_path" or name == "@executable_path" then + return "\\$ORIGIN" + elseif name == "$ORIGIN" then + return "\\$ORIGIN" + end + return name + end) + local rpath = string.format("-Wl,-rpath=%s", dir) + local flags = {string.format('--passL:"%s"', rpath)} + if extra then + if extra.runpath == false and self:has_flags(string.format('--passL:"%s,--disable-new-dtags"', rpath), "ldflags") then + flags[1] = string.format('--passL:"%s,--disable-new-dtags"', rpath) + elseif extra.runpath == true and self:has_flags(string.format('--passL:"%s,--enable-new-dtags"', rpath), "ldflags") then + flags[1] = string.format('--passL:"%s,--enable-new-dtags"', rpath) + end + end + return flags + end + + -- fallback + if self:has_flags(string.format('--passL:"-Wl,-rpath=%s"', dir), "ldflags") then + local flags = {string.format('--passL:"-Wl,-rpath=%s"', (dir:gsub("@[%w_]+", function (name) + local maps = { ["@loader_path"] = "$ORIGIN", ["@executable_path"] = "$ORIGIN" } + return maps[name] + end)))} + -- add_rpathdirs("...", {runpath = false}) + if extra then + if extra.runpath == false and self:has_flags(string.format('--passL:"-Wl,-rpath=%s,--disable-new-dtags"', dir), "ldflags") then + flags[1] = string.format('--passL:"-Wl,-rpath=%s,--disable-new-dtags"', dir) + elseif extra.runpath == true and self:has_flags(string.format('--passL:"-Wl,-rpath=%s,--enable-new-dtags"', dir), "ldflags") then + flags[1] = string.format('--passL:"-Wl,-rpath=%s,--enable-new-dtags"', dir) + end + end + return flags + elseif self:has_flags('--passL:"-Xlinker" --passL:"-rpath" --passL:"-Xlinker" ' .. string.format('--passL:"%s"', dir), "ldflags") then + return {'--passL:"-Xlinker"', '--passL:"-rpath"', '--passL:"-Xlinker"', string.format('--passL:"%s"', (dir:gsub("%$ORIGIN", "@loader_path")))} end end |
