--!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 gnu2mslib.lua -- -- imports import("core.base.option") import("core.base.hashset") import("core.project.config") import("core.tool.toolchain") import("lib.detect.find_tool") -- get .def file path from gnulib function _get_defpath_from_gnulib(gnulib, msvc) -- check assert(os.isfile(gnulib), "%s not found!", gnulib) -- get dumpbin.exe local dumpbin = assert(find_tool("dumpbin", {envs = msvc:runenvs()}), "gnu2mslib(): dumpbin.exe not found!") -- get symbols local symbols_exported = hashset.new() local symbols = try {function() return os.iorunv(dumpbin.program, {"/linkermember", gnulib}) end} if symbols then local symbols_count local symbols_index = 0 for _, line in ipairs(symbols:split('\n', {plain = true})) do if symbols_count == nil then symbols_count = line:match("(%d+) public symbols") if symbols_count then symbols_count = tonumber(symbols_count) end else local symbol = line:match("%s+[%dABCDEF]+%s+(.+)") if symbol then if not symbol:startswith("__") and not symbol:startswith("?") then symbols_exported:insert(symbol) end symbols_index = symbols_index + 1 if symbols_index >= symbols_count then break end end end end end -- generate .def file if symbols_exported:size() > 0 then local defpath = os.tmpfile() .. ".def" local file = io.open(defpath, "w") file:print("EXPORTS") for _, symbol in ipairs(symbols_exported) do file:print("%s", symbol) end file:close() return defpath end end -- convert mingw/gnu xxx.dll.a to msvc xxx.lib -- -- gnu2mslib(mslib, gnulib_or_defpath, {arch = "x64", dllname = "foo.dll"} -- -- @see https://github.com/xmake-io/xmake/issues/1181 -- function main(mslib, gnulib_or_defpath, opt) -- check opt = opt or {} assert(is_host("windows"), "we can only run gnu2mslib() on windows!") assert(mslib and gnulib_or_defpath, "invalid input parameters, usage: gnu2mslib(mslib, gnulib_or_defpath, {arch = \"x64\", dllname = \"foo.dll\"})") -- get msvc toolchain local msvc = toolchain.load("msvc", {plat = opt.plat, arch = opt.arch}) if not msvc:check() then raise("we can not get msvc toolchain!") end -- get lib.exe local libtool = assert(find_tool("lib", {envs = msvc:runenvs()}), "gnu2mslib(): lib.exe not found!") -- get dll name local dllname = opt.dllname or path.basename(mslib) .. ".dll" -- get def file path local defpath = gnulib_or_defpath:endswith(".def") and gnulib_or_defpath or _get_defpath_from_gnulib(gnulib_or_defpath, msvc) assert(defpath and os.isfile(defpath), "gnu2mslib(): convert failed, cannot get .def file!") -- generate mslib from gnulib os.vrunv(libtool.program, {"/machine:" .. opt.arch, "/def:" .. defpath, "/name:" .. path.filename(dllname), "/out:" .. mslib}, {envs = msvc:runenvs()}) -- remove temporary .def file if not gnulib_or_defpath:endswith(".def") then os.rm(defpath) end end