--!A cross-platform build utility based on Lua -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- -- Copyright (C) 2015-present, Xmake Open Source Community. -- -- @author ruki -- @file xmake.lua -- rule("swift.interop") set_sourcekinds("sc") add_orders("swift.interop", "swift.build") add_orders("swift.interop", "c++.build") on_config(function(target) import("core.base.json") if not target:values("swift.interop") then target:rule_enable("swift.interop", false) return end local sourcebatches = target:sourcebatches() local sourcebatch = sourcebatches and sourcebatches["swift.interop"] local sourcefiles = sourcebatch and sourcebatch.sourcefiles sourcefiles = sourcefiles or {} 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 target:rule_enable("swift.interop", false) 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.paths.runtimeResourcePath, {public = true}) local outdir = target:autogendir("swift.interop") target:add("includedirs", outdir, {public = true}) local mode = (type(target:values("swift.interop")) == "string") and target:values("swift.interop") or "objc" 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_files(function(target, jobgraph, sourcebatch, opt) import("utils.progress") import("core.base.option") import("core.tool.compiler") import("core.project.depend") local function _get_target_cpp_langflags() local cpp_langflags = _g.cpp_langflags if cpp_langflags == nil then local languages = target:get("languages") for _, language in ipairs(languages) do local language = language if language:startswith("c++") or language:startswith("cxx") or language:startswith("gnu++") or language:startswith("gnuxx") then -- c++26 currently prevent compilation of swift modules language = language:gsub("26", "23"):gsub("latest", "23") cpp_langflags = compiler.map_flags("cxx", "language", language, {target = target}) _g.cpp_langflags = cpp_langflags or false end end end return cpp_langflags or nil end local mode = target:data("swift.interop") local sc = target:compiler("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 sourcefiles = 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) jobgraph:add(target:fullname() .. "/gen_swift_header", function(index, total, opt) depend.on_changed(function() local stdflag if mode == "cxx" then local cpp_langflags = _get_target_cpp_langflags() if cpp_langflags then for _, flag in ipairs(cpp_langflags) do stdflag = stdflag or {} table.join2(stdflag, {"-Xcc", flag}) end end end if opt.progress then progress.show(opt.progress, "${clear}generating.swift.header %s", headername) end local _scflags = sc: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:program(), flags))) end os.tryrm(header) local outdata, errdata = os.iorunv(sc:program(), flags) assert(outdata, errdata) end, { files = public_sourcefiles, changed = target:is_rebuilt() or not os.isfile(header), }) end) end end, { jobgraph = true }) -- define rule: swift.build rule("swift.build") set_sourcekinds("sc") on_build_files("private.action.build.object", {jobgraph = true, batch = true}) on_config(function (target) import("config")(target) end) -- define rule: swift rule("swift") -- add build rules add_deps("swift.interop") -- add build rules add_deps("swift.build") -- inherit links and linkdirs of all dependent targets by default add_deps("utils.inherit.links") -- support `add_files("src/*.o")` to merge object files to target add_deps("utils.merge.object") -- add linker rules add_deps("linker")