summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur LAURENT <[email protected]>2025-10-23 23:40:11 +0200
committerArthur LAURENT <[email protected]>2025-10-23 23:40:11 +0200
commitc1b73a9a9dc3eba4dc96efc9cab7314b5eca9b9b (patch)
tree4421086bfb6caeb01376de837185a1a4658da600
parent3a9b7a4db2c851415be0282a36e60deecd7f53f1 (diff)
feat(swift) implement swift => c++/obj interop
-rw-r--r--tests/projects/swift/cxx_interop/lib/fibonacci/fibonacci.swift7
-rw-r--r--tests/projects/swift/cxx_interop/src/fibonacci.cpp7
-rw-r--r--tests/projects/swift/cxx_interop/xmake.lua8
-rw-r--r--tests/projects/swift/cxx_interop_lib/lib/fibonacci/fibonacci.swift7
-rw-r--r--tests/projects/swift/cxx_interop_lib/src/fibonacci.cpp7
-rw-r--r--tests/projects/swift/cxx_interop_lib/xmake.lua12
-rw-r--r--xmake/core/tool/compiler.lua7
-rw-r--r--xmake/rules/swift/xmake.lua165
-rw-r--r--xmake/toolchains/xcode/load_watchos.lua1
9 files changed, 220 insertions, 1 deletions
diff --git a/tests/projects/swift/cxx_interop/lib/fibonacci/fibonacci.swift b/tests/projects/swift/cxx_interop/lib/fibonacci/fibonacci.swift
new file mode 100644
index 000000000..24149f613
--- /dev/null
+++ b/tests/projects/swift/cxx_interop/lib/fibonacci/fibonacci.swift
@@ -0,0 +1,7 @@
+public func fibonacciSwift(_ x: CInt) -> CInt {
+ print("x [swift]: \(x)")
+ if x <= 1 {
+ return 1
+ }
+ return fibonacciSwift(x - 1) + fibonacciSwift(x - 2)
+}
diff --git a/tests/projects/swift/cxx_interop/src/fibonacci.cpp b/tests/projects/swift/cxx_interop/src/fibonacci.cpp
new file mode 100644
index 000000000..af50b1171
--- /dev/null
+++ b/tests/projects/swift/cxx_interop/src/fibonacci.cpp
@@ -0,0 +1,7 @@
+#include <fibonacci-Swift.h>
+#include <iostream>
+
+int main(int argc, char ** argv) {
+ std::cout << SwiftFibonacci::fibonacciSwift(5) << std::endl;
+ return 0;
+}
diff --git a/tests/projects/swift/cxx_interop/xmake.lua b/tests/projects/swift/cxx_interop/xmake.lua
new file mode 100644
index 000000000..cd5ef0469
--- /dev/null
+++ b/tests/projects/swift/cxx_interop/xmake.lua
@@ -0,0 +1,8 @@
+target("cxx_interop")
+ set_kind("binary")
+ set_languages("cxx20")
+ add_files("lib/**.swift", {public = true})
+ add_files("src/**.cpp")
+ set_values("swift.modulename", "SwiftFibonacci")
+ set_values("swift.interop", "cxx")
+ set_values("swift.interop.headername", "fibonacci-Swift.h")
diff --git a/tests/projects/swift/cxx_interop_lib/lib/fibonacci/fibonacci.swift b/tests/projects/swift/cxx_interop_lib/lib/fibonacci/fibonacci.swift
new file mode 100644
index 000000000..24149f613
--- /dev/null
+++ b/tests/projects/swift/cxx_interop_lib/lib/fibonacci/fibonacci.swift
@@ -0,0 +1,7 @@
+public func fibonacciSwift(_ x: CInt) -> CInt {
+ print("x [swift]: \(x)")
+ if x <= 1 {
+ return 1
+ }
+ return fibonacciSwift(x - 1) + fibonacciSwift(x - 2)
+}
diff --git a/tests/projects/swift/cxx_interop_lib/src/fibonacci.cpp b/tests/projects/swift/cxx_interop_lib/src/fibonacci.cpp
new file mode 100644
index 000000000..af50b1171
--- /dev/null
+++ b/tests/projects/swift/cxx_interop_lib/src/fibonacci.cpp
@@ -0,0 +1,7 @@
+#include <fibonacci-Swift.h>
+#include <iostream>
+
+int main(int argc, char ** argv) {
+ std::cout << SwiftFibonacci::fibonacciSwift(5) << std::endl;
+ return 0;
+}
diff --git a/tests/projects/swift/cxx_interop_lib/xmake.lua b/tests/projects/swift/cxx_interop_lib/xmake.lua
new file mode 100644
index 000000000..f1de85d13
--- /dev/null
+++ b/tests/projects/swift/cxx_interop_lib/xmake.lua
@@ -0,0 +1,12 @@
+target("fibonacci")
+ set_kind("$(kind)")
+ set_languages("cxx20")
+ add_files("lib/**.swift", {public = true})
+ set_values("swift.modulename", "SwiftFibonacci")
+ set_values("swift.interop", "cxx")
+
+target("cxx_interop_lib")
+ set_kind("binary")
+ set_languages("cxx20")
+ add_files("src/**.cpp")
+ add_deps("fibonacci")
diff --git a/xmake/core/tool/compiler.lua b/xmake/core/tool/compiler.lua
index ff8ce6a5d..5c974edb3 100644
--- a/xmake/core/tool/compiler.lua
+++ b/xmake/core/tool/compiler.lua
@@ -363,5 +363,12 @@ function compiler:compflags(opt)
return self:_preprocess_flags(flags)
end
+-- get the tool language flags
+--
+-- @return flags list
+function compiler:languageflags(lang)
+ return self:_tool():nf_language(lang)
+end
+
-- return module
return compiler
diff --git a/xmake/rules/swift/xmake.lua b/xmake/rules/swift/xmake.lua
index 42611b79c..9e3167292 100644
--- a/xmake/rules/swift/xmake.lua
+++ b/xmake/rules/swift/xmake.lua
@@ -18,6 +18,165 @@
-- @file xmake.lua
--
+rule("swift.interop", function()
+ set_sourcekinds("sc")
+ add_orders("swift.interop", "swift.build")
+ add_orders("swift.interop", "c++")
+ on_config(function(target)
+
+ import("core.base.json")
+
+ if not target:values("swift.interop") then
+ return
+ end
+
+ local sourcebatches = target:sourcebatches()
+ local sourcebatch = sourcebatches and sourcebatches["swift.build"]
+ local sourcefiles = sourcebatch and sourcebatch.sourcefiles
+ local enabled = false
+ for _, sourcefile in ipairs(sourcefiles) do
+ local fileconfig = target:fileconfig(sourcefile)
+ if fileconfig and fileconfig.public then
+ enabled = true
+ break
+ end
+ end
+ if not enabled then
+ return
+ end
+
+ local sc = target:tool("sc")
+ assert(sc, "No swift compiler found!")
+ local outdata, errdata = os.iorunv(sc, {"-print-target-info"})
+ assert(outdata, errdata)
+
+ local target_info = json.decode(outdata)
+ assert(target_info and target_info.paths and target_info.paths.runtimeResourcePath, "Failed to get swift resource path")
+ target:add("includedirs", target_info.runtimeResourcePath, {public = true})
+
+ local outdir = target:autogendir("swift")
+ target:add("includedirs", outdir, {public = true})
+
+ local mode = (type(target:values("swift.interop")) == "string") and target:values("swift.interop") or "objc"
+ if mode == "cxx" then
+ target:add("scflags", "-cxx-interoperability-mode=default")
+ end
+
+ local headername = (target:values("swift.interop.headername") or (target:name() .. "-Swift")) .. ".h"
+ local header = path.join(outdir, headername)
+ target:add("headerfiles", header)
+
+ target:data_set("swift.interop", mode)
+ target:data_set("swift.interop.outdir", outdir)
+ end)
+
+ on_prepare(function(target, jobgraph, opt)
+
+ if not target:data("swift.interop") then
+ return
+ end
+
+ import("utils.progress")
+ import("core.base.option")
+ import("core.language.language")
+ import("core.project.depend")
+
+ local function _get_target_cppversion()
+ local compinst = target:compiler("cxx")
+ local languages = target:get("languages")
+ for _, language in ipairs(languages) do
+ if language:startswith("c++")
+ or language:startswith("cxx")
+ or language:startswith("gnu++")
+ or language:startswith("gnuxx") then
+ local v = compinst:languageflags(language)
+ if v then return v end
+ end
+ end
+ end
+
+ local mode = target:data("swift.interop")
+ local sc = target:tool("sc")
+ assert(sc, "No swift compiler found!")
+ local outdir = target:data("swift.interop.outdir")
+ if not os.isdir(outdir) then os.mkdir(outdir) end
+ assert(outdir)
+
+ local sourcebatches = target:sourcebatches()
+ local sourcebatch = sourcebatches and sourcebatches["swift.build"]
+ local sourcefiles = sourcebatch and sourcebatch.sourcefiles
+ local public_sourcefiles
+ for _, sourcefile in ipairs(sourcefiles) do
+ local fileconfig = target:fileconfig(sourcefile)
+ if fileconfig and fileconfig.public then
+ public_sourcefiles = public_sourcefiles or {}
+ table.insert(public_sourcefiles, sourcefile)
+ end
+ end
+
+ if public_sourcefiles then
+ local modulename = target:values("swift.modulename") or target:name()
+ local headername = (target:values("swift.interop.headername") or (target:name() .. "-Swift")) .. ".h"
+ local header = path.join(outdir, headername)
+
+ depend.on_changed(function()
+ jobgraph:add(target:fullname() .. "/gen_swift_header", function(index, total, opt)
+ local stdflag
+ if mode == "cxx" then
+ local cxxstandard = _get_target_cppversion()
+ if cxxstandard then
+ stdflag = {"-Xcc", cxxstandard}
+ end
+ end
+
+ if opt.progress then
+ progress.show(
+ opt.progress,
+ "${clear}${color.build.target}<%s> generating.swift.header %s",
+ target:fullname(),
+ headername
+ )
+ end
+
+ local compinst = import("core.tool.compiler").load("sc")
+ local _scflags = compinst:compflags({target = target, sourcekind = "sc"})
+ local scflags
+ if target:has_tool("sc", "swift_frontend") then
+ for _, scflag in ipairs(_scflags) do
+ scflags = scflags or {}
+ if not os.isfile(scflag) then
+ table.insert(scflags, scflag)
+ end
+ end
+ else
+ scflags = _scflags
+ end
+
+
+ local emit_flag = mode == "cxx" and "-emit-clang-header-path" or "-emit-objc-header-path"
+
+ local flags = table.join({
+ "-frontend",
+ "-typecheck",
+ emit_flag,
+ header,
+ },
+ scflags or {},
+ stdflag or {},
+ public_sourcefiles)
+ if option.get("verbose") then print(os.args(table.join(sc, flags))) end
+
+ local outdata, errdata = os.iorunv(sc, flags)
+ assert(outdata, errdata)
+ end)
+ end, {
+ files = public_sourcefiles,
+ changed = target:is_rebuilt() or not os.isfile(header),
+ })
+ end
+ end, { jobgraph = true })
+end)
+
-- define rule: swift.build
rule("swift.build")
set_sourcekinds("sc")
@@ -27,10 +186,11 @@ rule("swift.build")
target:add("scflags", "-parse-as-library")
end
+ local modulename = target:values("swift.modulename") or target:name()
+ target:add("scflags", "-module-name", modulename, {force = true})
-- we use swift-frontend to support multiple modules
-- @see https://github.com/xmake-io/xmake/issues/3916
if target:has_tool("sc", "swift_frontend") then
- target:add("scflags", "-module-name", target:name(), {force = true})
local sourcebatch = target:sourcebatches()["swift.build"]
if sourcebatch then
for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
@@ -43,6 +203,9 @@ rule("swift.build")
-- define rule: swift
rule("swift")
+ -- add interop rules
+ add_deps("swift.interop")
+
-- add build rules
add_deps("swift.build")
diff --git a/xmake/toolchains/xcode/load_watchos.lua b/xmake/toolchains/xcode/load_watchos.lua
index e3deebb8a..99fc12313 100644
--- a/xmake/toolchains/xcode/load_watchos.lua
+++ b/xmake/toolchains/xcode/load_watchos.lua
@@ -49,6 +49,7 @@ function main(toolchain)
toolchain:add("scflags", format("-target %s-apple-ios%s", arch, target_minver) , "-sdk " .. xcode_sysroot)
toolchain:add("scshflags", format("-target %s-apple-ios%s", arch, target_minver) , "-sdk " .. xcode_sysroot)
toolchain:add("scldflags", format("-target %s-apple-ios%s", arch, target_minver) , "-sdk " .. xcode_sysroot)
+ toolchain:add("scarflags", format("-target %s-apple-ios%s", arch, target_minver) , "-sdk " .. xcode_sysroot)
toolchain:add("scshflags", "-emit-library")
toolchain:add("scarflags", "-emit-library", "-static")