diff options
| author | Ângelo Andrade Cirino <[email protected]> | 2022-01-10 10:23:17 -0300 |
|---|---|---|
| committer | Ângelo Andrade Cirino <[email protected]> | 2022-01-10 10:23:17 -0300 |
| commit | ed0c0c5e0249aa966ea7061b30710abdd0b09d87 (patch) | |
| tree | 2a51c869a3aea1a093ed569e749bdf0465634a6c /xmake/modules | |
| parent | 885d00da8caf74aeed758d030b9a1b50d9078e1f (diff) | |
| parent | 2431dd7e6142ff98b55741cfd4de4d2e69f4f8b5 (diff) | |
Merged from upstream/master
Diffstat (limited to 'xmake/modules')
159 files changed, 5795 insertions, 951 deletions
diff --git a/xmake/modules/cli/amalgamate.lua b/xmake/modules/cli/amalgamate.lua new file mode 100644 index 000000000..a9835eda6 --- /dev/null +++ b/xmake/modules/cli/amalgamate.lua @@ -0,0 +1,108 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file amalgamate.lua +-- + +-- imports +import("core.base.option") +import("core.project.config") +import("core.project.task") +import("core.project.project") + +-- the options +local options = +{ + {'u', "uniqueid", "kv", nil, "Set the unique id." }, + {'o', "outputdir", "kv", nil, "Set the output directory."}, + {nil, "target", "v", nil, "The target name." } +} + +-- generate code +function _generate_amalgamate_code(target, opt) + + -- only for library/binary + if not target:is_library() and not target:is_binary() then + return + end + + -- generate source code + local outputdir = opt.outputdir + local uniqueid = opt.uniqueid + for _, sourcebatch in pairs(target:sourcebatches()) do + local sourcekind = sourcebatch.sourcekind + if sourcekind == "cc" or sourcekind == "cxx" then + local outputpath = path.join(outputdir, target:name() .. (sourcekind == "cxx" and ".cpp" or ".c")) + local outputfile = io.open(outputpath, "w") + for _, sourcefile in ipairs(sourcebatch.sourcefiles) do + if uniqueid then + outputfile:print("#define %s %s", uniqueid, "unity_" .. hash.uuid():split("-", {plain = true})[1]) + end + outputfile:write(io.readfile(sourcefile)) + if uniqueid then + outputfile:print("#undef %s", uniqueid) + end + end + outputfile:close() + cprint("${bright}%s generated!", outputpath) + end + end + + -- generate header file + local srcheaders = target:headerfiles(includedir) + if srcheaders and #srcheaders > 0 then + local outputpath = path.join(outputdir, target:name() .. ".h") + local outputfile = io.open(outputpath, "w") + for _, srcheader in ipairs(srcheaders) do + if uniqueid then + outputfile:print("#define %s %s", uniqueid, "unity_" .. hash.uuid():split("-", {plain = true})[1]) + end + outputfile:write(io.readfile(srcheader)) + if uniqueid then + outputfile:print("#undef %s", uniqueid) + end + end + outputfile:close() + cprint("${bright}%s generated!", outputpath) + end +end + +-- generate amalgamate code +-- +-- https://github.com/xmake-io/xmake/issues/1438 +-- +function main(...) + + -- parse arguments + local argv = table.pack(...) + local args = option.parse(argv, options, "Generate amalgamate code.", + "", + "Usage: xmake l cli.amalgamate [options]") + + -- config first + task.run("config") + + -- generate amalgamate code + args.outputdir = args.outputdir or config.buildir() + if args.target then + _generate_amalgamate_code(args.target, args) + else + for _, target in ipairs(project.ordertargets()) do + _generate_amalgamate_code(target, args) + end + end +end diff --git a/xmake/modules/core/project/depend.lua b/xmake/modules/core/project/depend.lua index cb25db359..89a517c07 100644 --- a/xmake/modules/core/project/depend.lua +++ b/xmake/modules/core/project/depend.lua @@ -78,8 +78,8 @@ end function is_changed(dependinfo, opt) -- empty depend info? always be changed - local files = dependinfo.files or {} - local values = dependinfo.values or {} + local files = table.wrap(dependinfo.files) + local values = table.wrap(dependinfo.values) if #files == 0 and #values == 0 then return true end @@ -102,7 +102,7 @@ function is_changed(dependinfo, opt) -- check the dependent values are changed? local depvalues = values - local optvalues = opt.values or {} + local optvalues = table.wrap(opt.values) if #depvalues ~= #optvalues then return true end @@ -124,8 +124,11 @@ function is_changed(dependinfo, opt) end -- check the dependent files list are changed? - local optfiles = opt.files - if optfiles then + if opt.files then + local optfiles = table.wrap(opt.files) + if #files ~= #optfiles then + return true + end for idx, file in ipairs(files) do if file ~= optfiles[idx] then return true @@ -178,7 +181,7 @@ function on_changed(callback, opt) -- need build this object? -- @note we use mtime(dependfile) instead of mtime(objectfile) to ensure the object file is is fully compiled. -- @see https://github.com/xmake-io/xmake/issues/748 - if not is_changed(dependinfo, {lastmtime = opt.lastmtime or os.mtime(dependfile), values = opt.values}) then + if not is_changed(dependinfo, {lastmtime = opt.lastmtime or os.mtime(dependfile), values = opt.values, files = opt.files}) then return end diff --git a/xmake/modules/core/tools/ar.lua b/xmake/modules/core/tools/ar.lua index dc858a19e..0496238b4 100644 --- a/xmake/modules/core/tools/ar.lua +++ b/xmake/modules/core/tools/ar.lua @@ -20,7 +20,7 @@ -- imports import("core.tool.compiler") -import("private.utils.progress") +import("utils.progress") -- init it function init(self) @@ -30,42 +30,26 @@ end -- make the strip flag function strip(self, level) - - -- the maps local maps = { debug = "-S" , all = "-s" } - - -- make it return maps[level] end -- make the link arguments list function linkargv(self, objectfiles, targetkind, targetfile, flags, opt) - - -- check - assert(targetkind == "static") - - -- init arguments opt = opt or {} local argv = table.join(flags, targetfile, objectfiles) if is_host("windows") and not opt.rawargs then argv = winos.cmdargv(argv, {escape = true}) end - - -- make it return self:program(), argv end -- link the library file function link(self, objectfiles, targetkind, targetfile, flags) - - -- check - assert(targetkind == "static", "the target kind: %s is not support for ar", targetkind) - - -- ensure the target directory os.mkdir(path.directory(targetfile)) -- @note remove the previous archived file first to force recreating a new file diff --git a/xmake/modules/core/tools/armar.lua b/xmake/modules/core/tools/armar.lua new file mode 100644 index 000000000..66740c11a --- /dev/null +++ b/xmake/modules/core/tools/armar.lua @@ -0,0 +1,26 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file armar.lua +-- + +inherit("ar") + +function init(self) + _super.init(self) +end + diff --git a/xmake/modules/core/tools/armasm.lua b/xmake/modules/core/tools/armasm.lua new file mode 100644 index 000000000..3ae578c55 --- /dev/null +++ b/xmake/modules/core/tools/armasm.lua @@ -0,0 +1,179 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file armasm.lua +-- + +-- imports +import("core.base.option") +import("core.base.global") +import("core.language.language") +import("utils.progress") + +-- init it +function init(self) +end + +-- make the symbol flag +function nf_symbol(self, level) + -- only for source kind + local kind = self:kind() + if language.sourcekinds()[kind] then + local maps = _g.symbol_maps + if not maps then + maps = + { + debug = "-g" + } + _g.symbol_maps = maps + end + return maps[level .. '_' .. kind] or maps[level] + end +end + +-- make the optimize flag +function nf_optimize(self, level) + local maps = + { + none = "-O0" + , fast = "-O1" + , faster = "-O2" + , fastest = "-O3" + , smallest = "-Os" + , aggressive = "-Ofast" + } + return maps[level] +end + +-- make the language flag +function nf_language(self, stdname) + + -- the stdc maps + if _g.cmaps == nil then + _g.cmaps = + { + ansi = "-c89" + , c89 = "-c89" + , gnu89 = "-c89" + , c99 = "-c99" + , gnu99 = "-c99" + , c11 = "-c11" + , gnu11 = "-c11" + , clatest = {"-c11", "-c99", "-c89"} + , gnulatest = {"-c11", "-c99", "-c89"} + } + end + local maps = _g.cmaps + local result = maps[stdname] + if type(result) == "table" then + for _, v in ipairs(result) do + if self:has_flags(v, "cxflags") then + result = v + maps[stdname] = result + return result + end + end + else + return result + end +end + +-- make runtime flag +function nf_runtime(self, runtime) + if runtime == "microlib" then + return {"--pd", "__MICROLIB SETA 1"} + end +end + +-- make the define flag +function nf_define(self, macro) + return {"--pd", macro .. " SETA 1"} +end + +-- make the includedir flag +function nf_includedir(self, dir) + return {"-I" .. dir} +end + +-- make the sysincludedir flag +function nf_sysincludedir(self, dir) + return nf_includedir(self, dir) +end + +-- make the compile arguments list +function compargv(self, sourcefile, objectfile, flags) + return self:program(), table.join(flags, "-o", objectfile, sourcefile) +end + +-- compile the source file +function compile(self, sourcefile, objectfile, dependinfo, flags) + + -- ensure the object directory + os.mkdir(path.directory(objectfile)) + + -- compile it + try + { + function () + local outdata, errdata = os.iorunv(compargv(self, sourcefile, objectfile, flags)) + return (outdata or "") .. (errdata or "") + end, + catch + { + function (errors) + + -- try removing the old object file for forcing to rebuild this source file + os.tryrm(objectfile) + + -- find the start line of error + local lines = tostring(errors):split("\n") + local start = 0 + for index, line in ipairs(lines) do + if line:find("error:", 1, true) or line:find("错误:", 1, true) then + start = index + break + end + end + + -- get 16 lines of errors + if start > 0 or not option.get("verbose") then + if start == 0 then start = 1 end + errors = table.concat(table.slice(lines, start, start + ((#lines - start > 16) and 16 or (#lines - start))), "\n") + end + + -- raise compiling errors + raise(errors) + end + }, + finally + { + function (ok, warnings) + + -- print some warnings + if warnings and #warnings > 0 and (option.get("verbose") or option.get("warning") or global.get("build_warning")) then + if progress.showing_without_scroll() then + print("") + end + cprint("${color.warning}%s", table.concat(table.slice(warnings:split('\n'), 1, 8), '\n')) + end + end + } + } +end + + + diff --git a/xmake/modules/core/tools/armcc.lua b/xmake/modules/core/tools/armcc.lua new file mode 100644 index 000000000..403e143f0 --- /dev/null +++ b/xmake/modules/core/tools/armcc.lua @@ -0,0 +1,183 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file armcc.lua +-- + +-- imports +import("core.base.option") +import("core.base.global") +import("core.language.language") +import("utils.progress") + +-- init it +function init(self) +end + +-- make the symbol flag +function nf_symbol(self, level) + -- only for source kind + local kind = self:kind() + if language.sourcekinds()[kind] then + local maps = _g.symbol_maps + if not maps then + maps = + { + debug = "-g" + } + _g.symbol_maps = maps + end + return maps[level .. '_' .. kind] or maps[level] + end +end + +-- make runtime flag +function nf_runtime(self, runtime) + if runtime == "microlib" then + return "-D__MICROLIB" + end +end + +-- make the optimize flag +function nf_optimize(self, level) + local maps = + { + none = "-O0" + , fast = "-O1" + , faster = "-O2" + , fastest = "-O3" + , smallest = "-Os" + , aggressive = "-Ofast" + } + return maps[level] +end + +-- make the language flag +function nf_language(self, stdname) + + -- the stdc maps + if _g.cmaps == nil then + _g.cmaps = + { + ansi = "-c89" + , c89 = "-c89" + , gnu89 = "-c89" + , c99 = "-c99" + , gnu99 = "-c99" + , c11 = "-c11" + , gnu11 = "-c11" + , clatest = {"-c11", "-c99", "-c89"} + , gnulatest = {"-c11", "-c99", "-c89"} + } + end + local maps = _g.cmaps + local result = maps[stdname] + if type(result) == "table" then + for _, v in ipairs(result) do + if self:has_flags(v, "cxflags") then + result = v + maps[stdname] = result + return result + end + end + else + return result + end +end + +-- make the define flag +function nf_define(self, macro) + return "-D" .. macro +end + +-- make the undefine flag +function nf_undefine(self, macro) + return "-U" .. macro +end + +-- make the includedir flag +function nf_includedir(self, dir) + return {"-I" .. dir} +end + +-- make the sysincludedir flag +function nf_sysincludedir(self, dir) + return nf_includedir(self, dir) +end + +-- make the compile arguments list +function compargv(self, sourcefile, objectfile, flags) + return self:program(), table.join("-c", flags, "-o", objectfile, sourcefile) +end + +-- compile the source file +function compile(self, sourcefile, objectfile, dependinfo, flags) + + -- ensure the object directory + os.mkdir(path.directory(objectfile)) + + -- compile it + try + { + function () + local outdata, errdata = os.iorunv(compargv(self, sourcefile, objectfile, flags)) + return (outdata or "") .. (errdata or "") + end, + catch + { + function (errors) + + -- try removing the old object file for forcing to rebuild this source file + os.tryrm(objectfile) + + -- find the start line of error + local lines = tostring(errors):split("\n") + local start = 0 + for index, line in ipairs(lines) do + if line:find("error:", 1, true) or line:find("错误:", 1, true) then + start = index + break + end + end + + -- get 16 lines of errors + if start > 0 or not option.get("verbose") then + if start == 0 then start = 1 end + errors = table.concat(table.slice(lines, start, start + ((#lines - start > 16) and 16 or (#lines - start))), "\n") + end + + -- raise compiling errors + raise(errors) + end + }, + finally + { + function (ok, warnings) + + -- print some warnings + if warnings and #warnings > 0 and (option.get("verbose") or option.get("warning") or global.get("build_warning")) then + if progress.showing_without_scroll() then + print("") + end + cprint("${color.warning}%s", table.concat(table.slice(warnings:split('\n'), 1, 8), '\n')) + end + end + } + } +end + + diff --git a/xmake/modules/core/tools/armclang.lua b/xmake/modules/core/tools/armclang.lua new file mode 100644 index 000000000..dcec82015 --- /dev/null +++ b/xmake/modules/core/tools/armclang.lua @@ -0,0 +1,32 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file armclang.lua +-- + +inherit("gcc") + +function init(self) + _super.init(self) +end + +-- make runtime flag +function nf_runtime(self, runtime) + if runtime == "microlib" then + return "-D__MICROLIB" + end +end diff --git a/xmake/modules/core/tools/armlink.lua b/xmake/modules/core/tools/armlink.lua new file mode 100644 index 000000000..e25a81967 --- /dev/null +++ b/xmake/modules/core/tools/armlink.lua @@ -0,0 +1,61 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file armlink.lua +-- + +-- imports +import("core.base.option") +import("core.base.global") +import("utils.progress") + +function init(self) +end + +-- make the link flag +function nf_link(self, lib) + return "lib" .. lib .. ".a" +end + +-- make the syslink flag +function nf_syslink(self, lib) + return nf_link(self, lib) +end + +-- make the linkdir flag +function nf_linkdir(self, dir) + return {"--userlibpath", dir} +end + +-- make runtime flag +function nf_runtime(self, runtime) + if runtime == "microlib" then + return "--library_type=microlib" + end +end + +-- make the link arguments list +function linkargv(self, objectfiles, targetkind, targetfile, flags) + return self:program(), table.join("-o", targetfile, objectfiles, flags) +end + +-- link the target file +function link(self, objectfiles, targetkind, targetfile, flags) + os.mkdir(path.directory(targetfile)) + os.runv(linkargv(self, objectfiles, targetkind, targetfile, flags)) +end + diff --git a/xmake/modules/core/tools/circle.lua b/xmake/modules/core/tools/circle.lua new file mode 100644 index 000000000..fcf567549 --- /dev/null +++ b/xmake/modules/core/tools/circle.lua @@ -0,0 +1,34 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file circle.lua +-- + +inherit("gcc") + +function init(self) + _super.init(self) +end + +function nf_strip(self, level) + local maps = + { + debug = "-Wl,-S" + , all = "-Wl,-s" + } + return maps[level] +end diff --git a/xmake/modules/core/tools/cl.lua b/xmake/modules/core/tools/cl.lua index d95d58e5d..b76061cd7 100644 --- a/xmake/modules/core/tools/cl.lua +++ b/xmake/modules/core/tools/cl.lua @@ -26,7 +26,7 @@ import("core.project.project") import("core.language.language") import("private.tools.vstool") import("private.tools.cl.parse_include") -import("private.utils.progress") +import("utils.progress") -- init it function init(self) @@ -34,12 +34,6 @@ function init(self) -- init cxflags self:set("cxflags", "-nologo") - -- we need show full file path to goto error position if xmake is called in vstudio - -- https://github.com/xmake-io/xmake/issues/1049 - if os.getenv("XMAKE_IN_VSTUDIO") then - self:add("cxflags", "-FC") - end - -- init flags map self:set("mapflags", { @@ -62,6 +56,8 @@ function init(self) , ["-W2"] = "-W2" , ["-W3"] = "-W3" , ["-Werror"] = "-WX" + , ["-Wswitch"] = "-we4062" + , ["-Wswitch-enum"] = "-we4061" , ["%-Wno%-error=.*"] = "" , ["%-fno%-.*"] = "" @@ -116,7 +112,7 @@ function nf_symbols(self, levels, target) end -- check and add symbol output file - local pdbflags = "-Fd" .. path.join(symboldir, "compile." .. path.filename(symbolfile)) + local pdbflags = "-Fd" .. (target:is_static() and symbolfile or path.join(symboldir, "compile." .. path.filename(symbolfile))) if self:has_flags({"-FS", "-Fd" .. os.nuldev() .. ".pdb"}, "cxflags", { flagskey = "-FS -Fd" }) then pdbflags = {"-FS", pdbflags} end @@ -197,12 +193,14 @@ function nf_language(self, stdname) _g.cmaps = { -- stdc - c99 = "-TP" -- compile as c++ files because older msvc only support c89 - , gnu99 = "-TP" - , c11 = {"-std:c11", "-TP"} - , gnu11 = {"-std:c11", "-TP"} - , c17 = {"-std:c17", "-TP"} - , gnu17 = {"-std:c17", "-TP"} + c99 = "-TP" -- compile as c++ files because older msvc only support c89 + , gnu99 = "-TP" + , c11 = {"-std:c11", "-TP"} + , gnu11 = {"-std:c11", "-TP"} + , c17 = {"-std:c17", "-TP"} + , gnu17 = {"-std:c17", "-TP"} + , clatest = {"-std:c17", "-std:c11"} + , gnulatest = {"-std:c17", "-std:c11"} } end @@ -222,6 +220,8 @@ function nf_language(self, stdname) , gnuxx20 = {"-std:c++20", "-std:c++latest"} , cxx2a = {"-std:c++20", "-std:c++latest"} , gnuxx2a = {"-std:c++20", "-std:c++latest"} + , cxxlatest = "-std:c++latest" + , gnuxxlatest = "-std:c++latest" } local cxxmaps2 = {} for k, v in pairs(_g.cxxmaps) do @@ -237,13 +237,17 @@ function nf_language(self, stdname) end -- map it - local flags = maps[stdname] - if flags then - for _, flag in ipairs(table.wrap(flags)) do - if self:has_flags(flag, "cxflags") then - return flag + local result = maps[stdname] + if type(result) == "table" then + for _, v in ipairs(result) do + if self:has_flags(v, "cxflags") then + result = v + maps[stdname] = result + return result end end + else + return result end end @@ -266,13 +270,17 @@ end function nf_sysincludedir(self, dir) local has_external_includedir = _g._HAS_EXTERNAL_INCLUDEDIR if has_external_includedir == nil then - if self:has_flags({"-experimental:external", "-external:W0", "-external:I" .. os.args(path.translate(dir))}, "cxflags", {flagskey = "cl_external_includedir"}) then - has_external_includedir = true + if self:has_flags({"-external:W0", "-external:I" .. os.args(path.translate(dir))}, "cxflags", {flagskey = "cl_external_includedir"}) then + has_external_includedir = 2 -- full support + elseif self:has_flags({"-experimental:external", "-external:W0", "-external:I" .. os.args(path.translate(dir))}, "cxflags", {flagskey = "cl_external_includedir_experimental"}) then + has_external_includedir = 1 -- experimental support end - has_external_includedir = has_external_includedir or false + has_external_includedir = has_external_includedir or 0 _g._HAS_EXTERNAL_INCLUDEDIR = has_external_includedir end - if has_external_includedir then + if has_external_includedir >= 2 then + return {"-external:W0", "-external:I" .. path.translate(dir)} + elseif has_external_includedir >= 1 then return {"-experimental:external", "-external:W0", "-external:I" .. path.translate(dir)} else return nf_includedir(self, dir) @@ -371,6 +379,15 @@ function _has_source_dependencies(self) return has_source_dependencies end +function _is_in_vstudio() + local is_in_vstudio = _g._IS_IN_VSTUDIO + if is_in_vstudio == nil then + is_in_vstudio = os.getenv("XMAKE_IN_VSTUDIO") or false + _g._IS_IN_VSTUDIO = is_in_vstudio + end + return is_in_vstudio +end + -- make the compile arguments list function compargv(self, sourcefile, objectfile, flags, opt) @@ -403,6 +420,7 @@ function compile(self, sourcefile, objectfile, dependinfo, flags, opt) -- generate includes file local compflags = flags + if dependinfo then if _has_source_dependencies(self) then depfile = os.tmpfile() @@ -412,6 +430,16 @@ function compile(self, sourcefile, objectfile, dependinfo, flags, opt) end end + -- we need show full file path to goto error position if xmake is called in vstudio + -- https://github.com/xmake-io/xmake/issues/1049 + if _is_in_vstudio() then + if compflags == flags then + compflags = table.join(flags, "-FC") + else + table.join2(compflags, "-FC") + end + end + -- use vstool to compile and enable vs_unicode_output @see https://github.com/xmake-io/xmake/issues/528 local program, argv = compargv(self, sourcefile, objectfile, compflags, opt) return vstool.iorunv(program, argv, {envs = self:runenvs()}) @@ -444,7 +472,10 @@ function compile(self, sourcefile, objectfile, dependinfo, flags, opt) end end end - os.raise(results) + if not option.get("verbose") then + results = results .. "\n ${yellow}> in ${bright}" .. sourcefile + end + raise(results) end }, finally @@ -466,7 +497,10 @@ function compile(self, sourcefile, objectfile, dependinfo, flags, opt) end end if #lines > 0 then - local warnings = table.concat(table.slice(lines, 1, (#lines > 8 and 8 or #lines)), "\r\n") + if not option.get("diagnosis") then + lines = table.slice(lines, 1, (#lines > 16 and 16 or #lines)) + end + local warnings = table.concat(lines, "\r\n") if progress.showing_without_scroll() then print("") end @@ -488,4 +522,3 @@ function compile(self, sourcefile, objectfile, dependinfo, flags, opt) end end end - diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua index a0d39f3b4..e5734a953 100644 --- a/xmake/modules/core/tools/clang.lua +++ b/xmake/modules/core/tools/clang.lua @@ -124,3 +124,16 @@ function nf_warning(self, level) } return maps[level] end + +-- make the symbol flag +function nf_symbol(self, level) + local kind = self:kind() + if kind == "ld" or kind == "sh" then + -- clang/windows need add `-g` to linker to generate pdb symbol file + if self:plat() == "windows" and level == "debug" then + return "-g" + end + else + return _super.nf_symbol(self, level) + end +end diff --git a/xmake/modules/core/tools/clang_cl.lua b/xmake/modules/core/tools/clang_cl.lua index 4e0046fe8..9c4236343 100644 --- a/xmake/modules/core/tools/clang_cl.lua +++ b/xmake/modules/core/tools/clang_cl.lua @@ -117,6 +117,10 @@ function nf_language(self, stdname) , gnu99 = "-Xclang -std=gnu99" , c11 = "-Xclang -std=c11" , gnu11 = "-Xclang -std=gnu11" + , c17 = "-Xclang -std=c17" + , gnu17 = "-Xclang -std=gnu17" + , clatest = "-Xclang -std=c17" + , gnulatest = "-Xclang -std=gnu17" } end @@ -134,10 +138,12 @@ function nf_language(self, stdname) , gnuxx17 = "-Xclang -std=gnu++17" , cxx1z = "-Xclang -std=c++1z" , gnuxx1z = "-Xclang -std=gnu++1z" - , cxx20 = "-Xclang -std=c++2a" - , gnuxx20 = "-Xclang -std=gnu++2a" + , cxx20 = "-Xclang -std=c++20" + , gnuxx20 = "-Xclang -std=gnu++20" , cxx2a = "-Xclang -std=c++2a" , gnuxx2a = "-Xclang -std=gnu++2a" + , cxxlatest = "-Xclang -std=c++latest" + , gnuxxlatest = "-Xclang -std=gnu++latest" } local cxxmaps2 = {} for k, v in pairs(_g.cxxmaps) do diff --git a/xmake/modules/core/tools/dmd.lua b/xmake/modules/core/tools/dmd.lua index 2a9d860e1..3f29da28f 100644 --- a/xmake/modules/core/tools/dmd.lua +++ b/xmake/modules/core/tools/dmd.lua @@ -146,11 +146,7 @@ end -- link the target file function link(self, objectfiles, targetkind, targetfile, flags) - - -- ensure the target directory os.mkdir(path.directory(targetfile)) - - -- link it os.runv(linkargv(self, objectfiles, targetkind, targetfile, flags)) end @@ -161,11 +157,7 @@ end -- compile the source file function compile(self, sourcefile, objectfile, dependinfo, flags) - - -- ensure the object directory os.mkdir(path.directory(objectfile)) - - -- compile it os.runv(compargv(self, sourcefile, objectfile, flags)) end diff --git a/xmake/modules/core/tools/fpc.lua b/xmake/modules/core/tools/fpc.lua new file mode 100644 index 000000000..98b746c8a --- /dev/null +++ b/xmake/modules/core/tools/fpc.lua @@ -0,0 +1,110 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file fpc.lua +-- + +-- imports +import("core.base.option") +import("core.project.config") +import("core.project.project") + +-- init it +function init(self) + if not is_plat("windows", "mingw") then + self:add("shared.pcflags", "-Cg") + end +end + +-- make the optimize flag +function nf_optimize(self, level) + local maps = + { + none = "-O-" + , fast = "-O1" + , fastest = "-O3" + , smallest = "-O2" + , aggressive = "-O4" + } + return maps[level] +end + +-- make the strip flag +function nf_strip(self, level) + if level == "all" then + return "-Xs" + end +end + +-- make the symbol flag +function nf_symbol(self, level) + if level == "debug" and self:kind() == "pc" then + if self:plat() == "windows" then + return {"-gw3", "-WN"} + else + return "-gw3" + end + end +end + +-- make the link flag +function nf_link(self, lib) + return "-k-l" .. lib +end + +-- make the syslink flag +function nf_syslink(self, lib) + return nf_link(self, lib) +end + +-- make the linkdir flag +function nf_linkdir(self, dir) + return {"-k-L" .. dir} +end + +-- make the rpathdir flag +function nf_rpathdir(self, dir) + dir = path.translate(dir) + if self:has_flags("-k-rpath=" .. dir, "ldflags") then + return {"-k-rpath=" .. (dir:gsub("@[%w_]+", function (name) + local maps = {["@loader_path"] = "$ORIGIN", ["@executable_path"] = "$ORIGIN"} + return maps[name] + end))} + end +end + +-- make the framework flag +function nf_framework(self, framework) + return {"-k-framework", framework} +end + +-- make the frameworkdir flag +function nf_frameworkdir(self, frameworkdir) + return {"-k-F", path.translate(frameworkdir)} +end + +-- make the build arguments list +function buildargv(self, sourcefiles, targetkind, targetfile, flags) + return self:program(), table.join(flags, "-o" .. targetfile, sourcefiles) +end + +-- build the target file +function build(self, sourcefiles, targetkind, targetfile, flags) + os.mkdir(path.directory(targetfile)) + os.runv(buildargv(self, sourcefiles, targetkind, targetfile, flags)) +end + diff --git a/xmake/modules/core/tools/gcc.lua b/xmake/modules/core/tools/gcc.lua index 89b732ec9..40d3b8a4e 100644 --- a/xmake/modules/core/tools/gcc.lua +++ b/xmake/modules/core/tools/gcc.lua @@ -27,7 +27,7 @@ import("core.project.config") import("core.project.project") import("core.language.language") import("private.tools.ccache") -import("private.utils.progress") +import("utils.progress") -- init it function init(self) @@ -42,7 +42,11 @@ function init(self) self:set("shflags", "-shared") -- add -fPIC for shared - if not is_plat("windows", "mingw") then + -- + -- we need check it for clang/gcc with window target + -- @see https://github.com/xmake-io/xmake/issues/1392 + -- + if not is_plat("windows", "mingw") and self:has_flags("-fPIC", "cxflags") then self:add("shflags", "-fPIC") self:add("shared.cxflags", "-fPIC") end @@ -72,13 +76,13 @@ function init(self) end -- make the strip flag -function nf_strip(self, level) +function nf_strip(self, level, target) local maps = { debug = "-Wl,-S" , all = "-s" } - if is_plat("macosx") or is_plat("iphoneos") then + if target:is_plat("macosx") or target:is_plat("iphoneos") then maps.all = "-Wl,-x" end return maps[level] @@ -113,8 +117,8 @@ function nf_warning(self, level) , less = "-Wall" , more = "-Wall" , all = "-Wall" - , allextra = "-Wall -Wextra" - , everything = "-Wall -Wextra -Weffc++" + , allextra = {"-Wall", "-Wextra"} + , everything = self:kind() == "cxx" and {"-Wall", "-Wextra", "-Weffc++"} or {"-Wall", "-Wextra"} , error = "-Werror" } return maps[level] @@ -180,6 +184,8 @@ function nf_language(self, stdname) , gnu11 = "-std=gnu11" , c17 = "-std=c17" , gnu17 = "-std=gnu17" + , clatest = {"-std=c17", "-std=c11", "-std=c99", "-std=c89", "-ansi"} + , gnulatest = {"-std=gnu17", "-std=gnu11", "-std=gnu99", "-std=gnu89", "-ansi"} } end @@ -197,10 +203,12 @@ function nf_language(self, stdname) , gnuxx17 = "-std=gnu++17" , cxx1z = "-std=c++1z" , gnuxx1z = "-std=gnu++1z" - , cxx20 = "-std=c++2a" - , gnuxx20 = "-std=gnu++2a" + , cxx20 = {"-std=c++20", "-std=c++2a"} + , gnuxx20 = {"-std=gnu++20", "-std=c++2a"} , cxx2a = "-std=c++2a" , gnuxx2a = "-std=gnu++2a" + , cxxlatest = {"-std=c++20", "-std=c++2a", "-std=c++17", "-std=c++14", "-std=c++11", "-std=c++1z", "-std=c++98"} + , gnuxxlatest = {"-std=gnu++20", "-std=gnu++2a", "-std=gnu++17", "-std=gnu++14", "-std=gnu++11", "-std=c++1z", "-std=gnu++98"} } local cxxmaps2 = {} for k, v in pairs(_g.cxxmaps) do @@ -216,9 +224,18 @@ function nf_language(self, stdname) elseif self:kind() == "sc" then maps = {} end - - -- make it - return maps[stdname] + local result = maps[stdname] + if type(result) == "table" then + for _, v in ipairs(result) do + if self:has_flags(v, "cxflags") then + result = v + maps[stdname] = result + return result + end + end + else + return result + end end -- make the define flag @@ -276,7 +293,7 @@ end -- make the frameworkdir flag function nf_frameworkdir(self, frameworkdir) - return {"-F", path.translate(frameworkdir)} + return {"-F" .. path.translate(frameworkdir)} end -- make the c precompiled header flag @@ -334,7 +351,7 @@ function linkargv(self, objectfiles, targetkind, targetfile, flags, opt) -- add `-Wl,--out-implib,outputdir/libxxx.a` for xxx.dll on mingw/gcc if targetkind == "shared" and is_plat("mingw") then - table.insert(flags_extra, "-Wl,--out-implib," .. path.join(path.directory(targetfile), path.basename(targetfile) .. ".lib")) + table.insert(flags_extra, "-Wl,--out-implib," .. path.join(path.directory(targetfile), path.basename(targetfile) .. ".dll.a")) end -- init arguments @@ -408,7 +425,6 @@ end -- make the compile arguments list function compargv(self, sourcefile, objectfile, flags) - -- precompiled header? local extension = path.extension(sourcefile) if (extension:startswith(".h") or extension == ".inl") then @@ -478,7 +494,12 @@ function compile(self, sourcefile, objectfile, dependinfo, flags) end -- raise compiling errors - raise(#lines > 0 and table.concat(lines, "\n") or "") + local results = #lines > 0 and table.concat(lines, "\n") or "" + if not option.get("verbose") then + results = results .. "\n ${yellow}> in ${bright}" .. sourcefile + end + raise(results) + end }, finally @@ -488,7 +509,10 @@ function compile(self, sourcefile, objectfile, dependinfo, flags) if ok and errdata and #errdata > 0 and (option.get("diagnosis") or option.get("warning") or global.get("build_warning")) then local lines = errdata:split('\n', {plain = true}) if #lines > 0 then - local warnings = table.concat(table.slice(lines, 1, (#lines > 8 and 8 or #lines)), "\n") + if not option.get("diagnosis") then + lines = table.slice(lines, 1, (#lines > 16 and 16 or #lines)) + end + local warnings = table.concat(lines, "\n") if progress.showing_without_scroll() then print("") end diff --git a/xmake/modules/core/tools/go.lua b/xmake/modules/core/tools/go.lua index fbd046cf0..2b2b76623 100644 --- a/xmake/modules/core/tools/go.lua +++ b/xmake/modules/core/tools/go.lua @@ -25,56 +25,34 @@ import("core.project.project") -- init it function init(self) - - -- init arflags self:set("gcarflags", "grc") - - -- init the file formats - self:set("formats", { static = "$(name).a" }) end -- make the optimize flag function nf_optimize(self, level) - - -- the maps - local maps = - { + local maps = { none = "-N" } - - -- make it return maps[level] end -- make the symbol flag function nf_symbol(self, level, target, mapkind) - - -- only for compiler if mapkind ~= "object" then return end - - -- the maps - local maps = - { + local maps = { debug = "-E" } - - -- make it return maps[level] end -- make the strip flag function nf_strip(self, level) - - -- the maps - local maps = - { + local maps = { debug = "-s" , all = "-s" } - - -- make it return maps[level] end @@ -95,8 +73,6 @@ end -- make the link arguments list function linkargv(self, objectfiles, targetkind, targetfile, flags) - - -- make it if targetkind == "static" then return self:program(), table.join("tool", "pack", flags, targetfile, objectfiles) else @@ -106,11 +82,7 @@ end -- link the target file function link(self, objectfiles, targetkind, targetfile, flags) - - -- ensure the target directory os.mkdir(path.directory(targetfile)) - - -- link it local program, argv = linkargv(self, objectfiles, targetkind, targetfile, flags) os.runv(program, argv, {envs = self:runenvs()}) end @@ -122,11 +94,7 @@ end -- compile the source file function compile(self, sourcefiles, objectfile, dependinfo, flags) - - -- ensure the object directory os.mkdir(path.directory(objectfile)) - - -- compile it local program, argv = compargv(self, sourcefiles, objectfile, flags) os.runv(program, argv, {envs = self:runenvs()}) end diff --git a/xmake/modules/core/tools/ml.lua b/xmake/modules/core/tools/ml.lua index 83da11fb6..59749974c 100644 --- a/xmake/modules/core/tools/ml.lua +++ b/xmake/modules/core/tools/ml.lua @@ -20,6 +20,7 @@ -- imports import("private.tools.vstool") +import("core.base.hashset") -- init it -- @@ -28,11 +29,7 @@ import("private.tools.vstool") function init(self) -- init asflags - if self:program():find("64") then - self:set("asflags", "-nologo") - else - self:set("asflags", "-nologo", "-Gd") - end + self:set("asflags", "-nologo") -- init flags map self:set("mapflags", @@ -57,10 +54,25 @@ function init(self) }) end +-- make the symbol flags +function nf_symbols(self, levels, target) + local flags = nil + local values = hashset.from(levels) + if values:has("debug") then + flags = {} + if values:has("edit") then + table.insert(flags, "-ZI") + elseif values:has("embed") then + table.insert(flags, "-Z7") + else + table.insert(flags, "-Zi") + end + end + return flags +end + -- make the warning flag function nf_warning(self, level) - - -- the maps local maps = { none = "-w" @@ -70,8 +82,6 @@ function nf_warning(self, level) , everything = "-W3" , error = "-WX" } - - -- make it return maps[level] end @@ -97,6 +107,21 @@ end -- make the compile arguments list function compargv(self, sourcefile, objectfile, flags) + -- we need to set the default -Gd option for the x86 architecture, + -- if the other calling convention flags are not set + -- + -- we can't directly remove -Gd. This is not only for backward compatibility, + -- but also to simplify mixed compilation with c programs. + -- + -- although this may affect some performance, + -- it only takes effect under x86 asm, so there will be no major performance issues. + -- + -- @see https://github.com/xmake-io/xmake/issues/1779 + -- + if not self:program():find("64", 1, true) and + not table.contains(flags, "-Gc", "/Gc", "-GZ", "/GZ") then + table.insert(flags, "-Gd") + end return self:program(), table.join("-c", flags, "-Fo" .. objectfile, sourcefile) end diff --git a/xmake/modules/core/tools/nim.lua b/xmake/modules/core/tools/nim.lua new file mode 100644 index 000000000..0eece46cf --- /dev/null +++ b/xmake/modules/core/tools/nim.lua @@ -0,0 +1,161 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file nim.lua +-- + +-- imports +import("core.base.option") +import("core.project.config") +import("core.project.project") + +-- init it +-- +-- @see https://nim-lang.org/docs/nimc.html +function init(self) + + -- init arflags + self:set("ncarflags", "--app:staticlib", "--noMain") + + -- init shflags + self:set("ncshflags", "--app:lib", "--noMain") +end + +-- make the warning flag +function nf_warning(self, level) + local maps = + { + none = "--warning:X:off" + , less = "--warning:X:on" + , more = "--warning:X:on" + , all = "--warning:X:on" + , allextra = "--warning:X:on" + , everything = "--warning:X:on" + , error = "--warningAsError:X:on" + } + return maps[level] +end + +-- make the define flag +function nf_define(self, macro) + return "--define:" .. macro +end + +-- make the undefine flag +function nf_undefine(self, macro) + return "--undef:" .. macro +end + +-- make the optimize flag +function nf_optimize(self, level) + local maps = + { + none = "--opt:none" + , fast = "-d:release" + , faster = "-d:release" + , fastest = "-d:release" + , smallest = {"-d:release", "--opt:size"} + , aggressive = "-d:danger" + } + return maps[level] +end + +-- make the symbol flag +function nf_symbol(self, level) + local maps = + { + debug = "--debugger:native" + } + return maps[level] +end + +-- make the strip flag +function nf_strip(self, level, target) + if target:is_plat("linux", "macosx", "bsd") then + if level == "debug" or level == "all" then + return "--passL:-s" + end + end +end + +-- make the includedir flag +function nf_includedir(self, dir) + return {"--passC:-I" .. path.translate(dir)} +end + +-- make the link flag +function nf_link(self, lib, target) + if target:is_plat("windows") then + return "--passL:" .. lib .. ".lib" + else + return "--passL:-l" .. lib + end +end + +-- make the linkdir flag +function nf_linkdir(self, dir, target) + if target:is_plat("windows") then + return {"--passL:-libpath:" .. path.translate(dir)} + else + return {"--passL:-L" .. path.translate(dir)} + end +end + +-- make the build arguments list +function buildargv(self, sourcefiles, targetkind, targetfile, flags) + local flags_extra = {} + if targetkind == "static" then + -- fix multiple definition of `NimMain', it is only workaround solution + -- we need to wait for this problem to be resolved + -- + -- @see https://github.com/nim-lang/Nim/issues/15955 + local uniquekey = hash.uuid(targetfile):split("-", {plain = true})[1] + table.insert(flags_extra, "--passC:-DNimMain=NimMain_" .. uniquekey) + table.insert(flags_extra, "--passC:-DNimMainInner=NimMainInner_" .. uniquekey) + table.insert(flags_extra, "--passC:-DNimMainModule=NimMainModule_" .. uniquekey) + table.insert(flags_extra, "--passC:-DPreMain=PreMain_" .. uniquekey) + table.insert(flags_extra, "--passC:-DPreMainInner=PreMainInner_" .. uniquekey) + end + if targetkind ~= "static" and is_plat("windows") then + -- fix link flags for windows + -- @see https://github.com/nim-lang/Nim/issues/19033 + local flags_new = {} + local flags_link = {} + for _, flag in ipairs(flags) do + if flag:find("passL:", 1, true) then + table.insert(flags_link, flag) + else + table.insert(flags_new, flag) + end + end + if #flags_link > 0 then + table.insert(flags_new, "--passL:-link") + table.join2(flags_new, flags_link) + end + flags = flags_new + end + return self:program(), table.join("c", flags, flags_extra, "-o:" .. targetfile, sourcefiles) +end + +-- build the target file +function build(self, sourcefiles, targetkind, targetfile, flags) + os.mkdir(path.directory(targetfile)) + local program, argv = buildargv(self, sourcefiles, targetkind, targetfile, flags) + os.runv(program, argv, {envs = self:runenvs()}) +end + + diff --git a/xmake/modules/core/tools/nvcc.lua b/xmake/modules/core/tools/nvcc.lua index 784b606c5..2aad54096 100644 --- a/xmake/modules/core/tools/nvcc.lua +++ b/xmake/modules/core/tools/nvcc.lua @@ -26,7 +26,7 @@ import("core.project.project") import("core.platform.platform") import("core.language.language") import("private.tools.ccache") -import("private.utils.progress") +import("utils.progress") -- init it function init(self) @@ -59,7 +59,7 @@ function nf_symbol(self, level, target) -- debug? generate *.pdb file local flags = nil if level == "debug" then - flags = "-g -lineinfo" + flags = {"-g", "-lineinfo"} if is_plat("windows") then local host_flags = nil local symbolfile = nil @@ -82,11 +82,10 @@ function nf_symbol(self, level, target) else host_flags = "-Zi" end - flags = flags .. ' -Xcompiler "' .. host_flags .. '"' + table.insert(flags, "-Xcompiler") + table.insert(flags, host_flags) end end - - -- none return flags end @@ -178,6 +177,8 @@ function nf_language(self, stdname) cxx03 = "--std c++03" , cxx11 = "--std c++11" , cxx14 = "--std c++14" + , cxx17 = "--std c++17" + , cxxlatest = {"--std c++17", "--std c++14", "--std c++11", "--std c++03"} } local cxxmaps2 = {} for k, v in pairs(_g.cxxmaps) do @@ -185,7 +186,18 @@ function nf_language(self, stdname) end table.join2(_g.cxxmaps, cxxmaps2) end - return _g.cxxmaps[stdname] + local maps = _g.cxxmaps + local result = maps[stdname] + if type(result) == "table" then + for _, v in ipairs(result) do + if self:has_flags(v, "cxflags") then + result = v + maps[stdname] = result + break + end + end + end + return result end -- make the define flag @@ -200,7 +212,7 @@ end -- make the includedir flag function nf_includedir(self, dir) - return {"-I", dir} + return {"-I" .. path.translate(dir)} end -- make the sysincludedir flag @@ -220,7 +232,7 @@ end -- make the linkdir flag function nf_linkdir(self, dir) - return {"-L", dir} + return {"-L" .. path.translate(dir)} end -- make the rpathdir flag @@ -258,9 +270,9 @@ function linkargv(self, objectfiles, targetkind, targetfile, flags) end -- add `-Wl,--out-implib,outputdir/libxxx.a` for xxx.dll on mingw/gcc - if targetkind == "shared" and config.plat() == "mingw" then + if targetkind == "shared" and is_plat("mingw") then table.insert(flags_extra, "-Xlinker") - table.insert(flags_extra, "-Wl,--out-implib," .. path.join(path.directory(targetfile), path.basename(targetfile) .. ".lib")) + table.insert(flags_extra, "-Wl,--out-implib," .. path.join(path.directory(targetfile), path.basename(targetfile) .. ".dll.a")) end -- make link args diff --git a/xmake/modules/core/tools/rc.lua b/xmake/modules/core/tools/rc.lua index 208161b67..6796c0fe0 100644 --- a/xmake/modules/core/tools/rc.lua +++ b/xmake/modules/core/tools/rc.lua @@ -25,7 +25,7 @@ import("private.tools.vstool") -- init it function init(self) - if winos.version():gt("winxp") then + if self:has_flags("-nologo", "mrcflags") then -- fix vs2008 on xp, e.g. fatal error RC1106: invalid option: -ologo self:set("mrcflags", "-nologo") end diff --git a/xmake/modules/core/tools/rustc.lua b/xmake/modules/core/tools/rustc.lua index ade2daea4..ab41620cd 100644 --- a/xmake/modules/core/tools/rustc.lua +++ b/xmake/modules/core/tools/rustc.lua @@ -25,24 +25,10 @@ import("core.project.project") -- init it function init(self) - - -- init arflags - self:set("rcarflags", "--crate-type=lib") - - -- init shflags - self:set("rcshflags", "--crate-type=dylib") - - -- init ldflags - self:set("rcldflags", "--crate-type=bin") - - -- init the file formats - self:set("formats", { static = "lib$(name).rlib" }) end -- make the optimize flag function nf_optimize(self, level) - - -- the maps local maps = { none = "-C opt-level=0" @@ -52,21 +38,15 @@ function nf_optimize(self, level) , smallest = "-C opt-level=s" , aggressive = "-C opt-level=z" } - - -- make it return maps[level] end -- make the symbol flag function nf_symbol(self, level) - - -- the maps local maps = { debug = "-C debuginfo=2" } - - -- make it return maps[level] end @@ -75,18 +55,66 @@ function nf_linkdir(self, dir) return {"-L" .. dir} end +-- make the link flag +function nf_link(self, lib) + return "-l" .. lib +end + +-- make the syslink flag +function nf_syslink(self, lib) + return nf_link(self, lib) +end + +-- make the frameworkdir flag, crate module dependency directories +function nf_frameworkdir(self, frameworkdir) + return {"-L", "dependency=" .. frameworkdir} +end + +-- make the framework flag, crate module +function nf_framework(self, framework) + local basename = path.basename(framework) + local cratename = basename:match("lib(.-)%-.-") or basename:match("lib(.-)") + if cratename then + return {"--extern", cratename .. "=" .. framework} + end +end + +-- make the rpathdir flag +function nf_rpathdir(self, dir) + dir = path.translate(dir) + if self:has_flags({"-C", "link-arg=-Wl,-rpath=$ORIGIN"}, "ldflags") then + return {"-C", "link-arg=-Wl,-rpath=" .. (dir:gsub("@[%w_]+", function (name) + local maps = {["@loader_path"] = "$ORIGIN", ["@executable_path"] = "$ORIGIN"} + return maps[name] + end))} + elseif self:has_flags({"-C", "link-arg=-Xlinker", "-C", "link-arg=-rpath", "-C", "link-arg=-Xlinker", "-C", "link-arg=@loader_path"}, "ldflags") then + return {"-C", "link-arg=-Xlinker", + "-C", "link-arg=-rpath", + "-C", "link-arg=-Xlinker", + "-C", "link-arg=" .. (dir:gsub("%$ORIGIN", "@loader_path"))} + end +end + -- make the build arguments list function buildargv(self, sourcefiles, targetkind, targetfile, flags) - return self:program(), table.join(flags, "-o", targetfile, sourcefiles) + -- add rpath for dylib (macho), e.g. -install_name @rpath/file.dylib + local flags_extra = {} + if targetkind == "shared" and is_plat("macosx", "iphoneos", "watchos") then + table.insert(flags_extra, "-C") + table.insert(flags_extra, "link-arg=-Xlinker") + table.insert(flags_extra, "-C") + table.insert(flags_extra, "link-arg=-install_name") + table.insert(flags_extra, "-C") + table.insert(flags_extra, "link-arg=-Xlinker") + table.insert(flags_extra, "-C") + table.insert(flags_extra, "link-arg=@rpath/" .. path.filename(targetfile)) + end + return self:program(), table.join(flags, flags_extra, "-o", targetfile, sourcefiles) end -- build the target file function build(self, sourcefiles, targetkind, targetfile, flags) - - -- ensure the target directory os.mkdir(path.directory(targetfile)) - - -- build it os.runv(buildargv(self, sourcefiles, targetkind, targetfile, flags)) end @@ -97,11 +125,7 @@ end -- compile the source file function compile(self, sourcefiles, objectfile, dependinfo, flags) - - -- ensure the object directory os.mkdir(path.directory(objectfile)) - - -- compile it os.runv(compargv(self, sourcefiles, objectfile, flags)) end diff --git a/xmake/modules/core/tools/sdcc.lua b/xmake/modules/core/tools/sdcc.lua index 00e081384..4605f12f8 100644 --- a/xmake/modules/core/tools/sdcc.lua +++ b/xmake/modules/core/tools/sdcc.lua @@ -21,7 +21,7 @@ -- imports import("core.base.option") import("core.base.global") -import("private.utils.progress") +import("utils.progress") -- init it function init(self) @@ -68,23 +68,17 @@ end -- make the warning flag function nf_warning(self, level) - - -- the maps local maps = { none = "--less-pedantic" , less = "--less-pedantic" , error = "-Werror" } - - -- make it return maps[level] end -- make the optimize flag function nf_optimize(self, level) - - -- the maps local maps = { none = "" @@ -94,19 +88,14 @@ function nf_optimize(self, level) , smallest = "--opt-code-size" , aggressive = "--opt-code-speed" } - - -- make it return maps[level] end -- make the language flag function nf_language(self, stdname) - - -- the stdc maps if _g.cmaps == nil then _g.cmaps = { - -- stdc ansi = "--std-c89" , c89 = "--std-c89" , gnu89 = "--std-sdcc89" @@ -116,9 +105,23 @@ function nf_language(self, stdname) , gnu11 = "--std-sdcc11" , c20 = "--std-c2x" , gnu20 = "--std-sdcc2x" + , clatest = {"--std-c2x", "--std-c11", "--std-c99", "--std-c89"} + , gnulatest = {"--std-sdcc2x", "--std-sdcc11", "--std-sdcc99", "--std-sdcc89"} } end - return _g.cmaps[stdname] + local maps = _g.cmaps + local result = maps[stdname] + if type(result) == "table" then + for _, v in ipairs(result) do + if self:has_flags(v, "cxflags") then + result = v + maps[stdname] = result + return result + end + end + else + return result + end end -- make the define flag @@ -163,11 +166,7 @@ end -- link the target file function link(self, objectfiles, targetkind, targetfile, flags) - - -- ensure the target directory os.mkdir(path.directory(targetfile)) - - -- link it os.runv(linkargv(self, objectfiles, targetkind, targetfile, flags)) end diff --git a/xmake/modules/core/tools/zig.lua b/xmake/modules/core/tools/zig.lua index 2270e2a3e..f0d6c5d98 100644 --- a/xmake/modules/core/tools/zig.lua +++ b/xmake/modules/core/tools/zig.lua @@ -55,7 +55,6 @@ function nf_optimize(self, level) { none = "-O Debug" , fast = "-O ReleaseSafe" - , aggressive = "-O ReleaseFast" , fastest = "-O ReleaseFast" , smallest = "-O ReleaseSmall" , aggressive = "-O ReleaseFast" @@ -117,11 +116,7 @@ end -- link the target file function link(self, objectfiles, targetkind, targetfile, flags) - - -- ensure the target directory os.mkdir(path.directory(targetfile)) - - -- link it os.runv(linkargv(self, objectfiles, targetkind, targetfile, flags)) end @@ -132,11 +127,7 @@ end -- compile the source file function compile(self, sourcefile, objectfile, dependinfo, flags) - - -- ensure the object directory os.mkdir(path.directory(objectfile)) - - -- compile it os.runv(compargv(self, sourcefile, objectfile, flags)) end diff --git a/xmake/modules/detect/sdks/find_iccenv.lua b/xmake/modules/detect/sdks/find_iccenv.lua index 0e432a99c..84b40fade 100644 --- a/xmake/modules/detect/sdks/find_iccenv.lua +++ b/xmake/modules/detect/sdks/find_iccenv.lua @@ -102,6 +102,11 @@ function _find_intel_on_windows(opt) -- find iclvars_bat.bat local paths = {"$(env ICPP_COMPILER20)"} local iclvars_bat = find_file("bin/iclvars.bat", paths) + -- look for setvars.bat which is new in 2021 + if not iclvars_bat then + paths = {"$(env ICPP_COMPILER21)"} + iclvars_bat = find_file("../../../setvars.bat", paths) + end if iclvars_bat then -- load iclvars_bat @@ -123,6 +128,22 @@ function _find_intel_on_linux(opt) local sdkdir = path.directory(path.directory(icc)) return {sdkdir = sdkdir, bindir = path.directory(icc), path.join(sdkdir, "include"), libdir = path.join(sdkdir, "lib")} end + + -- find it from oneapi sdk directory + local oneapi_rootdirs = {"~/intel/oneapi/compiler", "/opt/intel/oneapi/compiler"} + local arch = os.arch() == "x86_64" and "intel64" or "ia32" + paths = {} + for _, rootdir in ipairs(oneapi_rootdirs) do + table.insert(paths, path.join(rootdir, "*", is_host("macosx") and "mac" or "linux", "bin", arch)) + end + if #paths > 0 then + local icc = find_file("icc", paths) + if icc then + local bindir = path.directory(icc) + local sdkdir = path.directory(path.directory(bindir)) + return {sdkdir = sdkdir, bindir = bindir, libdir = path.join(sdkdir, "compiler", "lib", arch)} + end + end end -- find intel c/c++ environment diff --git a/xmake/modules/detect/sdks/find_ifortenv.lua b/xmake/modules/detect/sdks/find_ifortenv.lua index f3aafad06..d851b3f6a 100644 --- a/xmake/modules/detect/sdks/find_ifortenv.lua +++ b/xmake/modules/detect/sdks/find_ifortenv.lua @@ -102,6 +102,12 @@ function _find_intel_on_windows(opt) -- find ifortvars_bat.bat local paths = {"$(env IFORT_COMPILER20)"} local ifortvars_bat = find_file("bin/ifortvars.bat", paths) + -- look for setvars.bat which is new in 2021 + if not ifortvars_bat then + paths = {"$(env IFORT_COMPILER21)"} + ifortvars_bat = find_file("../../../setvars.bat", paths) + end + if ifortvars_bat then -- load ifortvars_bat @@ -116,12 +122,28 @@ end -- find intel fortran envirnoment on linux function _find_intel_on_linux(opt) - -- attempt to find the sdk directory local paths = {"/opt/intel/bin", "/usr/local/bin", "/usr/bin"} local ifort = find_file("ifort", paths) if ifort then - local sdkdir = path.directory(path.directory(ifort)) - return {sdkdir = sdkdir, bindir = path.directory(ifort), path.join(sdkdir, "include"), libdir = path.join(sdkdir, "lib")} + local bindir = path.directory(ifort) + local sdkdir = path.directory(bindir) + return {sdkdir = sdkdir, bindir = bindir, libdir = path.join(sdkdir, "lib")} + end + + -- find it from oneapi sdk directory + local oneapi_rootdirs = {"~/intel/oneapi/compiler", "/opt/intel/oneapi/compiler"} + local arch = os.arch() == "x86_64" and "intel64" or "ia32" + paths = {} + for _, rootdir in ipairs(oneapi_rootdirs) do + table.insert(paths, path.join(rootdir, "*", is_host("macosx") and "mac" or "linux", "bin", arch)) + end + if #paths > 0 then + local ifort = find_file("ifort", paths) + if ifort then + local bindir = path.directory(ifort) + local sdkdir = path.directory(path.directory(bindir)) + return {sdkdir = sdkdir, bindir = bindir, libdir = path.join(sdkdir, "compiler", "lib", arch)} + end end end diff --git a/xmake/modules/detect/sdks/find_mdk.lua b/xmake/modules/detect/sdks/find_mdk.lua new file mode 100644 index 000000000..a2af360f1 --- /dev/null +++ b/xmake/modules/detect/sdks/find_mdk.lua @@ -0,0 +1,123 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_mdk.lua +-- + +-- imports +import("lib.detect.find_path") +import("core.base.option") +import("core.base.semver") +import("core.project.config") +import("core.cache.detectcache") + +-- find MDK directory +function _find_sdkdir(sdkdir) + local paths = { + "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Keil\\Products\\MDK;Path)" + } + if sdkdir then + table.insert(paths, 1, sdkdir) + end + local result = find_path("armcc", paths) or find_path("armclang", paths) + if not result then + -- find it from some logical drives paths + paths = {} + for _, logical_drive in ipairs(winos.logical_drives()) do + table.insert(paths, path.join(logical_drive, "Keil_v5", "ARM")) + end + result = find_path("armcc", paths) or find_path("armclang", paths) + end + return result +end + +-- find MDK toolchains +function _find_mdk(sdkdir) + + -- find mdk directory + sdkdir = _find_sdkdir(sdkdir) + if not sdkdir or not os.isdir(sdkdir) then + return nil + end + local result = {sdkdir = sdkdir} + + -- get sdk version + local sdkver = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Keil\\Products\\MDK;Version") + if sdkver then + sdkver = semver.match(sdkver, 1, "V%d+%.%d+") + if sdkver then + result.sdkver = sdkver:rawstr() + end + end + + -- armcc sdk directory + local sdkdir_armcc = path.join(sdkdir, "armcc") + if os.isdir(sdkdir_armcc) and os.isfile(path.join(sdkdir_armcc, "bin", "armcc.exe")) then + result.sdkdir_armcc = sdkdir_armcc + end + + -- armclang sdk directory + local sdkdir_armclang = path.join(sdkdir, "armclang") + if os.isdir(sdkdir_armclang) and os.isfile(path.join(sdkdir_armclang, "bin", "armclang.exe")) then + result.sdkdir_armclang = sdkdir_armclang + end + return result +end + +-- find MDK toolchains +-- +-- @param sdkdir the MDK directory +-- @param opt the argument options, e.g. {verbose = true, force = false} +-- +-- @return the MDK toolchains. e.g. {sdkver = ..., sdkdir, sdkdir_armcc, sdkdir_armclang} +-- +-- @code +-- +-- local toolchains = find_mdk("~/mdk") +-- +-- @endcode +-- +function main(sdkdir, opt) + + -- init arguments + opt = opt or {} + + -- attempt to load cache first + local key = "detect.sdks.find_mdk" + local cacheinfo = detectcache:get(key) or {} + if not opt.force and cacheinfo.mdk and cacheinfo.mdk.sdkdir and os.isdir(cacheinfo.mdk.sdkdir) then + return cacheinfo.mdk + end + + -- find mdk + local mdk = _find_mdk(sdkdir or config.get("sdk")) + if mdk then + if opt.verbose or option.get("verbose") then + cprint("checking for MDK directory ... ${color.success}%s", mdk.sdkdir) + end + else + if opt.verbose or option.get("verbose") then + cprint("checking for MDK directory ... ${color.nothing}${text.nothing}") + end + end + + -- save to cache + cacheinfo.mdk = mdk or false + detectcache:set(key, cacheinfo) + detectcache:save() + return mdk +end diff --git a/xmake/modules/detect/sdks/find_mingw.lua b/xmake/modules/detect/sdks/find_mingw.lua index 7be315a9c..e5276834b 100644 --- a/xmake/modules/detect/sdks/find_mingw.lua +++ b/xmake/modules/detect/sdks/find_mingw.lua @@ -94,6 +94,9 @@ function _find_mingw(sdkdir, bindir, cross) -- find cross toolchain local toolchain = find_cross_toolchain(sdkdir or bindir, {bindir = bindir, cross = cross}) + if not toolchain then -- fallback, e.g. gcc.exe without cross + toolchain = find_cross_toolchain(sdkdir or bindir, {bindir = bindir}) + end if toolchain then return {sdkdir = toolchain.sdkdir, bindir = toolchain.bindir, cross = toolchain.cross} end diff --git a/xmake/modules/detect/sdks/find_ndk.lua b/xmake/modules/detect/sdks/find_ndk.lua index bc07f6081..ac412bce0 100644 --- a/xmake/modules/detect/sdks/find_ndk.lua +++ b/xmake/modules/detect/sdks/find_ndk.lua @@ -50,10 +50,16 @@ function _find_ndkdir(sdkdir) if not sdkdir then sdkdir = os.getenv("ANDROID_NDK_HOME") or os.getenv("ANDROID_NDK_ROOT") if not sdkdir and config.get("android_sdk") then - sdkdir = path.join(config.get("android_sdk"), "ndk-bundle") + local ndkbundle = path.join(config.get("android_sdk"), "ndk-bundle") + if os.isdir(ndkbundle) then + sdkdir = ndkbundle + end end if not sdkdir and is_host("macosx") then - sdkdir = "~/Library/Android/sdk/ndk-bundle" + sdkdir = find_directory("NDK", "/Applications/AndroidNDK*.app/Contents") + if not sdkdir then + sdkdir = "~/Library/Android/sdk/ndk-bundle" + end end end @@ -188,8 +194,12 @@ function _find_ndk(sdkdir, arch, ndk_sdkver, ndk_toolchains_ver) local gcc_toolchain_subdir = gcc_toolchain_subdirs[arch] or "arm-linux-androideabi-*" -- find the binary directory - local bindir = find_directory("bin", path.join(sdkdir, "toolchains", "llvm", "prebuilt", "*")) -- larger than ndk r16 - if not bindir then + local llvm_toolchain + local prebuilt = (is_host("macosx") and "darwin" or os.host()) .. "-x86_64" + local bindir = find_directory("bin", path.join(sdkdir, "toolchains", "llvm", "prebuilt", prebuilt)) -- larger than ndk r16 + if bindir then + llvm_toolchain = path.directory(bindir) + else bindir = find_directory("bin", path.join(sdkdir, "toolchains", gcc_toolchain_subdir, "prebuilt", "*")) end if not bindir then @@ -231,6 +241,7 @@ function _find_ndk(sdkdir, arch, ndk_sdkver, ndk_toolchains_ver) bindir = bindir, cross = cross, sdkver = sdkver, + llvm_toolchain = llvm_toolchain, -- >= ndk r22 gcc_toolchain = gcc_toolchain, toolchains_ver = toolchains_ver, sysroot = sysroot} @@ -280,7 +291,6 @@ function main(sdkdir, opt) if opt.verbose or option.get("verbose") then cprint("checking for NDK directory ... ${color.success}%s", ndk.sdkdir) cprint("checking for SDK version of NDK ... ${color.success}%s", ndk.sdkver) - cprint("checking for toolchains version of NDK ... ${color.success}%s", ndk.toolchains_ver) end else diff --git a/xmake/modules/detect/sdks/find_qt.lua b/xmake/modules/detect/sdks/find_qt.lua index 9dbc7b386..202b52d88 100644 --- a/xmake/modules/detect/sdks/find_qt.lua +++ b/xmake/modules/detect/sdks/find_qt.lua @@ -35,7 +35,10 @@ function _find_sdkdir(sdkdir, sdkver) table.insert(subdirs, path.join(sdkver or "*", is_arch("x86_64") and "gcc_64" or "gcc_32", "bin")) table.insert(subdirs, path.join(sdkver or "*", is_arch("x86_64") and "clang_64" or "clang_32", "bin")) elseif is_plat("macosx") then + table.insert(subdirs, path.join(sdkver or "*", "macos", "bin")) -- for Qt 6.2 table.insert(subdirs, path.join(sdkver or "*", is_arch("x86_64") and "clang_64" or "clang_32", "bin")) + elseif is_plat("iphoneos") then + table.insert(subdirs, path.join(sdkver or "*", "ios", "bin")) elseif is_plat("windows") then local vs = config.get("vs") if vs then @@ -120,11 +123,7 @@ end -- find qmake function _find_qmake(sdkdir, sdkver) - - -- find qt directory sdkdir = _find_sdkdir(sdkdir, sdkver) - - -- get the bin directory local qmake = find_tool("qmake", {paths = sdkdir and path.join(sdkdir, "bin")}) if qmake then return qmake.program @@ -174,8 +173,27 @@ function _find_qt(sdkdir, sdkver) local libdir = qtenvs.QT_INSTALL_LIBS local pluginsdir = qtenvs.QT_INSTALL_PLUGINS local includedir = qtenvs.QT_INSTALL_HEADERS - local mkspecsdir = path.join(qtenvs.QT_INSTALL_ARCHDATA, "mkspecs") - return {sdkdir = sdkdir, bindir = bindir, libexecdir = libexecdir, libdir = libdir, includedir = includedir, qmldir = qmldir, pluginsdir = pluginsdir, mkspecsdir = mkspecsdir, sdkver = sdkver} + local mkspecsdir = qtenvs.QMAKE_MKSPECS or path.join(qtenvs.QT_INSTALL_ARCHDATA, "mkspecs") + -- for 6.2 + local bindir_host + if libexecdir and is_plat("android", "iphoneos") then + local rootdir = path.directory(path.directory(bindir)) + if is_host("macosx") then + bindir_host = path.join(rootdir, "macos", "bin") + else + -- TODO + end + end + local libexecdir_host + if libexecdir and is_plat("android", "iphoneos") then + local rootdir = path.directory(path.directory(libexecdir)) + if is_host("macosx") then + libexecdir_host = path.join(rootdir, "macos", "libexec") + else + -- TODO + end + end + return {sdkdir = sdkdir, bindir = bindir, bindir_host = bindir_host, libexecdir = libexecdir, libexecdir_host = libexecdir_host, libdir = libdir, includedir = includedir, qmldir = qmldir, pluginsdir = pluginsdir, mkspecsdir = mkspecsdir, sdkver = sdkver} end -- find qt sdk toolchains diff --git a/xmake/modules/detect/sdks/find_vstudio.lua b/xmake/modules/detect/sdks/find_vstudio.lua index 2934d23c8..0b3fb1361 100644 --- a/xmake/modules/detect/sdks/find_vstudio.lua +++ b/xmake/modules/detect/sdks/find_vstudio.lua @@ -19,6 +19,7 @@ -- -- imports +import("core.project.config") import("lib.detect.find_file") import("lib.detect.find_tool") @@ -34,9 +35,66 @@ local vcvars = {"path", "WindowsLibPath", "WindowsSDKVersion", "WindowsSdkBinPath", + "WindowsSdkVerBinPath", + "ExtensionSdkDir", "UniversalCRTSdkDir", "UCRTVersion", - "VCToolsVersion"} + "VCToolsVersion", + "VCIDEInstallDir", + "VCToolsInstallDir", + "VCToolsRedistDir", + "VisualStudioVersion", + "VSCMD_VER", + "VSCMD_ARG_app_plat", + "VSCMD_ARG_HOST_ARCH", + "VSCMD_ARG_TGT_ARCH"} + +-- init vsvers +local vsvers = +{ + ["17.0"] = "2022" +, ["16.0"] = "2019" +, ["15.0"] = "2017" +, ["14.0"] = "2015" +, ["12.0"] = "2013" +, ["11.0"] = "2012" +, ["10.0"] = "2010" +, ["9.0"] = "2008" +, ["8.0"] = "2005" +, ["7.1"] = "2003" +, ["7.0"] = "7.0" +, ["6.0"] = "6.0" +, ["5.0"] = "5.0" +, ["4.2"] = "4.2" +} + +-- init vsenvs +local vsenvs = +{ + ["17.0"] = "VS170COMNTOOLS" +, ["16.0"] = "VS160COMNTOOLS" +, ["15.0"] = "VS150COMNTOOLS" +, ["14.0"] = "VS140COMNTOOLS" +, ["12.0"] = "VS120COMNTOOLS" +, ["11.0"] = "VS110COMNTOOLS" +, ["10.0"] = "VS100COMNTOOLS" +, ["9.0"] = "VS90COMNTOOLS" +, ["8.0"] = "VS80COMNTOOLS" +, ["7.1"] = "VS71COMNTOOLS" +, ["7.0"] = "VS70COMNTOOLS" +, ["6.0"] = "VS60COMNTOOLS" +, ["5.0"] = "VS50COMNTOOLS" +, ["4.2"] = "VS42COMNTOOLS" +} + +-- get all known Visual Studio environment variables +function get_vcvars() + local realvcvars = vcvars + for _, v in pairs(vsenvs) do + table.insert(realvcvars, v) + end + return realvcvars +end -- load vcvarsall environment variables function _load_vcvarsall(vcvarsall, vsver, arch, opt) @@ -59,7 +117,7 @@ function _load_vcvarsall(vcvarsall, vsver, arch, opt) else file:print("call \"%s\" %s %s > nul", vcvarsall, arch, opt.sdkver and opt.sdkver or "") end - for idx, var in ipairs(vcvars) do + for idx, var in ipairs(get_vcvars()) do file:print("echo " .. var .. " = %%" .. var .. "%%") end file:close() @@ -148,41 +206,6 @@ function main(opt) return end - -- init vsvers - local vsvers = - { - ["17.0"] = "2022" - , ["16.0"] = "2019" - , ["15.0"] = "2017" - , ["14.0"] = "2015" - , ["12.0"] = "2013" - , ["11.0"] = "2012" - , ["10.0"] = "2010" - , ["9.0"] = "2008" - , ["8.0"] = "2005" - , ["7.1"] = "2003" - , ["7.0"] = "7.0" - , ["6.0"] = "6.0" - , ["5.0"] = "5.0" - , ["4.2"] = "4.2" - } - - -- init vsenvs - local vsenvs = - { - ["14.0"] = "VS140COMNTOOLS" - , ["12.0"] = "VS120COMNTOOLS" - , ["11.0"] = "VS110COMNTOOLS" - , ["10.0"] = "VS100COMNTOOLS" - , ["9.0"] = "VS90COMNTOOLS" - , ["8.0"] = "VS80COMNTOOLS" - , ["7.1"] = "VS71COMNTOOLS" - , ["7.0"] = "VS70COMNTOOLS" - , ["6.0"] = "VS60COMNTOOLS" - , ["5.0"] = "VS50COMNTOOLS" - , ["4.2"] = "VS42COMNTOOLS" - } - -- init options opt = opt or {} @@ -255,7 +278,8 @@ function main(opt) local vswhere_VCAuxiliaryBuildDir = nil if (tonumber(version) >= 15) and vswhere then local vswhere_vrange = format("%s,%s)", version, (version + 1)) - local result = os.iorunv(vswhere.program, {"-prerelease", "-property", "installationpath", "-version", vswhere_vrange}) + -- build tools: https://github.com/microsoft/vswhere/issues/22 @@ https://aka.ms/vs/workloads + local result = os.iorunv(vswhere.program, {"-products", "*", "-prerelease", "-property", "installationpath", "-version", vswhere_vrange}) if result then vswhere_VCAuxiliaryBuildDir = path.join(result:trim(), "VC", "Auxiliary", "Build") end @@ -282,7 +306,18 @@ function main(opt) if not vcvarsall then -- find vs from some logical drives paths paths = {} - for _, logical_drive in ipairs(winos.logical_drives()) do + local logical_drives = winos.logical_drives() + -- we attempt to find vs from wdk directory + -- wdk: E:\Program Files\Windows Kits\10 + -- vcvarsall: E:\Program Files\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build + local wdk = config.get("wdk") + if wdk and os.isdir(wdk) then + local p = wdk:find("Program Files") + if p then + table.insert(logical_drives, wdk:sub(1, p - 1)) + end + end + for _, logical_drive in ipairs(logical_drives) do if os.isdir(path.join(logical_drive, "Program Files (x86)")) then table.insert(paths, path.join(logical_drive, "Program Files (x86)", "Microsoft Visual Studio", vsvers[version], "*", "VC", "Auxiliary", "Build")) table.insert(paths, path.join(logical_drive, "Program Files (x86)", "Microsoft Visual Studio " .. version, "VC")) diff --git a/xmake/modules/detect/tools/circle/has_flags.lua b/xmake/modules/detect/tools/circle/has_flags.lua new file mode 100644 index 000000000..6464e10b6 --- /dev/null +++ b/xmake/modules/detect/tools/circle/has_flags.lua @@ -0,0 +1,23 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file has_flags.lua +-- + +-- imports +inherit("detect.tools.gcc.has_flags") + diff --git a/xmake/modules/detect/tools/cl/cfeatures.lua b/xmake/modules/detect/tools/cl/cfeatures.lua index 88c65559f..9b043f7e1 100644 --- a/xmake/modules/detect/tools/cl/cfeatures.lua +++ b/xmake/modules/detect/tools/cl/cfeatures.lua @@ -31,6 +31,11 @@ function main() local msvc_minver = "_MSC_VER >= 1200" local msvc_2005 = "_MSC_VER >= 1400" local msvc_2010 = "_MSC_VER >= 1600" + local msvc_2019 = "_MSC_VER >= 1920" + + -- set language standard supports + _set("c_std_89", msvc_2005) + _set("c_std_99", msvc_2019) -- set features _set("c_static_assert", msvc_2010) diff --git a/xmake/modules/detect/tools/cl/cxxfeatures.lua b/xmake/modules/detect/tools/cl/cxxfeatures.lua index 661af6640..c8b683e6f 100644 --- a/xmake/modules/detect/tools/cl/cxxfeatures.lua +++ b/xmake/modules/detect/tools/cl/cxxfeatures.lua @@ -32,6 +32,7 @@ end -- http://www.visualstudio.com/en-us/news/vs2015-preview-vs.aspx -- http://blogs.msdn.com/b/vcblog/archive/2015/04/29/c-11-14-17-features-in-vs-2015-rc.aspx -- http://blogs.msdn.com/b/vcblog/archive/2015/06/19/c-11-14-17-features-in-vs-2015-rtm.aspx +-- https://docs.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance?view=msvc-160 -- -- porting from Modules/Compiler/MSVC-CXX-FeatureTests.cmake -- @@ -49,6 +50,15 @@ function main() local msvc_2013 = "_MSC_VER >= 1800" local msvc_2015 = "_MSC_VER >= 1900" local msvc_2017 = "_MSC_VER >= 1910" + local msvc_2019 = "_MSC_VER >= 1920" + local msvc_2022 = "_MSC_VER >= 1930" + + -- set language standard supports + _set("cxx_std_98", msvc_2005) + _set("cxx_std_11", msvc_2015) + _set("cxx_std_14", msvc_2017) + _set("cxx_std_17", msvc_2019) + _set("cxx_std_20", msvc_2022) -- VS version 15 (not 2015) introduces support for aggregate initializers. _set("cxx_aggregate_default_initializers", "_MSC_FULL_VER >= 190024406") diff --git a/xmake/modules/detect/tools/cl/has_flags.lua b/xmake/modules/detect/tools/cl/has_flags.lua index a1cb9ace7..1c7f65325 100644 --- a/xmake/modules/detect/tools/cl/has_flags.lua +++ b/xmake/modules/detect/tools/cl/has_flags.lua @@ -20,6 +20,7 @@ -- imports import("core.cache.detectcache") +import("core.language.language") -- attempt to check it from the argument list function _check_from_arglist(flags, opt) @@ -55,12 +56,17 @@ function _check_from_arglist(flags, opt) return allflags[flags[1]:gsub("/", "-")] end +-- get extension +function _get_extension(opt) + return opt.flagkind == "cxxflags" and ".cpp" or (table.wrap(language.sourcekinds()[opt.toolkind or "cc"])[1] or ".c") +end + -- try running to check flags function _check_try_running(flags, opt) -- make an stub source file local tmpdir = path.join(os.tmpdir(), "detect") - local sourcefile = path.join(tmpdir, "cl_has_flags.c") + local sourcefile = path.join(tmpdir, "cl_has_flags" .. _get_extension(opt)) if not os.isfile(sourcefile) then io.writefile(sourcefile, "int main(int argc, char** argv)\n{return 0;}") end diff --git a/xmake/modules/detect/tools/clang/cfeatures.lua b/xmake/modules/detect/tools/clang/cfeatures.lua index 8cdfadfd0..e9a1687a6 100644 --- a/xmake/modules/detect/tools/clang/cfeatures.lua +++ b/xmake/modules/detect/tools/clang/cfeatures.lua @@ -33,11 +33,19 @@ function main() _g.features = cfeatures() -- init conditions + -- clang -std=c11 -dM -E - < /dev/null | grep __STDC_VERSION__ local clang_minver = "((__clang_major__ * 100) + __clang_minor__) >= 304" + local c17 = clang_minver .. " && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201710L" local c11 = clang_minver .. " && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L" local c99 = clang_minver .. " && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L" local c90 = clang_minver + -- set language standard supports + _set("c_std_89", c90) + _set("c_std_99", c99) + _set("c_std_11", c11) + _set("c_std_17", c17) + -- set features _set("c_static_assert", c11) _set("c_restrict", c99) diff --git a/xmake/modules/detect/tools/clang/cxxfeatures.lua b/xmake/modules/detect/tools/clang/cxxfeatures.lua index bbf3da8b8..1fd57c8d6 100644 --- a/xmake/modules/detect/tools/clang/cxxfeatures.lua +++ b/xmake/modules/detect/tools/clang/cxxfeatures.lua @@ -28,6 +28,8 @@ end -- get features -- +-- @see http://clang.llvm.org/cxx_status.html +-- -- porting from Modules/Compiler/Clang-CXX-FeatureTests.cmake -- function main() @@ -36,12 +38,22 @@ function main() _g.features = cxxfeatures() -- init conditions + -- clang -x c++ -std=c++20 -dM -E - < /dev/null | grep __cplusplus local clang_minver = "((__clang_major__ * 100) + __clang_minor__) >= 301" + local clang80_cxx20 = "((__clang_major__ * 100) + __clang_minor__) >= 800 && __cplusplus >= 202002L" + local clang50_cxx17 = "((__clang_major__ * 100) + __clang_minor__) >= 500 && __cplusplus >= 201703L" local clang34_cxx14 = "((__clang_major__ * 100) + __clang_minor__) >= 304 && __cplusplus > 201103L" local clang31_cxx11 = clang_minver .. " && __cplusplus >= 201103L" local clang29_cxx11 = clang_minver .. " && __cplusplus >= 201103L" local clang_cxx98 = clang_minver .. " && __cplusplus >= 199711L" + -- set language standard supports + _set("cxx_std_98", clang_cxx98) + _set("cxx_std_11", clang29_cxx11) + _set("cxx_std_14", clang34_cxx14) + _set("cxx_std_17", clang50_cxx17) + _set("cxx_std_20", clang80_cxx20) + -- set features for __has_feature() local features_of_has_feature = { diff --git a/xmake/modules/detect/tools/clang_cl/has_flags.lua b/xmake/modules/detect/tools/clang_cl/has_flags.lua index 27728697f..4917fb665 100644 --- a/xmake/modules/detect/tools/clang_cl/has_flags.lua +++ b/xmake/modules/detect/tools/clang_cl/has_flags.lua @@ -61,7 +61,7 @@ function _try_running(...) local argv = {...} local errors = nil - return try { function () os.runv(unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors end -- try running to check flags diff --git a/xmake/modules/detect/tools/dmd/has_flags.lua b/xmake/modules/detect/tools/dmd/has_flags.lua index 459db9228..235e99d4c 100644 --- a/xmake/modules/detect/tools/dmd/has_flags.lua +++ b/xmake/modules/detect/tools/dmd/has_flags.lua @@ -40,7 +40,7 @@ function _try_running(...) local argv = {...} local errors = nil - return try { function () os.runv(unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors end -- attempt to check it from the argument list diff --git a/xmake/modules/detect/tools/find_7z.lua b/xmake/modules/detect/tools/find_7z.lua index b441c2200..ce1a04f4a 100644 --- a/xmake/modules/detect/tools/find_7z.lua +++ b/xmake/modules/detect/tools/find_7z.lua @@ -55,6 +55,11 @@ function main(opt) program = find_program("7za", opt) end + -- find it from msys/mingw, it is only a shell script + if not program and is_subhost("msys") then + program = find_program("sh 7z", opt) + end + -- find program version local version = nil if program and opt and opt.version then diff --git a/xmake/modules/detect/tools/find_armar.lua b/xmake/modules/detect/tools/find_armar.lua new file mode 100644 index 000000000..725d6a988 --- /dev/null +++ b/xmake/modules/detect/tools/find_armar.lua @@ -0,0 +1,65 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_armar.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") +import("detect.sdks.find_mdk") + +-- find armar +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local armar = find_armar() +-- local armar, version = find_armar({program = "armar", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + opt.check = "-h" + + -- find program + local program = find_program(opt.program or "armar.exe", opt) + if not program then + local mdk = find_mdk() + if mdk then + if mdk.sdkdir_armcc then + program = find_program(path.join(mdk.sdkdir_armcc, "bin", "armar.exe"), opt) + end + if not program and mdk.sdkdir_armclang then + program = find_program(path.join(mdk.sdkdir_armclang, "bin", "armar.exe"), opt) + end + end + end + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_armasm.lua b/xmake/modules/detect/tools/find_armasm.lua new file mode 100644 index 000000000..280333195 --- /dev/null +++ b/xmake/modules/detect/tools/find_armasm.lua @@ -0,0 +1,65 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_armasm.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") +import("detect.sdks.find_mdk") + +-- find armasm +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local armasm = find_armasm() +-- local armasm, version = find_armasm({program = "armasm", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + opt.check = "-h" + + -- find program + local program = find_program(opt.program or "armasm.exe", opt) + if not program then + local mdk = find_mdk() + if mdk then + if mdk.sdkdir_armcc then + program = find_program(path.join(mdk.sdkdir_armcc, "bin", "armasm.exe"), opt) + end + if not program and mdk.sdkdir_armclang then + program = find_program(path.join(mdk.sdkdir_armclang, "bin", "armasm.exe"), opt) + end + end + end + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_armcc.lua b/xmake/modules/detect/tools/find_armcc.lua new file mode 100644 index 000000000..584e9b3a9 --- /dev/null +++ b/xmake/modules/detect/tools/find_armcc.lua @@ -0,0 +1,60 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_armcc.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") +import("detect.sdks.find_mdk") + +-- find armcc +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local armcc = find_armcc() +-- local armcc, version = find_armcc({program = "armcc", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + opt.check = "-h" + + -- find program + local program = find_program(opt.program or "armcc.exe", opt) + if not program then + local mdk = find_mdk() + if mdk and mdk.sdkdir_armcc then + program = find_program(path.join(mdk.sdkdir_armcc, "bin", "armcc.exe"), opt) + end + end + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_armclang.lua b/xmake/modules/detect/tools/find_armclang.lua new file mode 100644 index 000000000..ed6b0bbfe --- /dev/null +++ b/xmake/modules/detect/tools/find_armclang.lua @@ -0,0 +1,59 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_armclang.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") +import("detect.sdks.find_mdk") + +-- find armclang +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local armclang = find_armclang() +-- local armclang, version = find_armclang({program = "armclang", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "armclang.exe", opt) + if not program then + local mdk = find_mdk() + if mdk and mdk.sdkdir_armclang then + program = find_program(path.join(mdk.sdkdir_armclang, "bin", "armclang.exe"), opt) + end + end + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_armlink.lua b/xmake/modules/detect/tools/find_armlink.lua new file mode 100644 index 000000000..3ab383c86 --- /dev/null +++ b/xmake/modules/detect/tools/find_armlink.lua @@ -0,0 +1,65 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_armlink.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") +import("detect.sdks.find_mdk") + +-- find armlink +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local armlink = find_armlink() +-- local armlink, version = find_armlink({program = "armlink", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + opt.check = "-h" + + -- find program + local program = find_program(opt.program or "armlink.exe", opt) + if not program then + local mdk = find_mdk() + if mdk then + if mdk.sdkdir_armcc then + program = find_program(path.join(mdk.sdkdir_armcc, "bin", "armlink.exe"), opt) + end + if not program and mdk.sdkdir_armclang then + program = find_program(path.join(mdk.sdkdir_armclang, "bin", "armlink.exe"), opt) + end + end + end + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_cargo.lua b/xmake/modules/detect/tools/find_cargo.lua new file mode 100644 index 000000000..a67b29821 --- /dev/null +++ b/xmake/modules/detect/tools/find_cargo.lua @@ -0,0 +1,52 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_cargo.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find nim +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local nim = find_cargo() +-- local nim, version = find_cargo({program = "cargo", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "cargo", opt) + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_circle.lua b/xmake/modules/detect/tools/find_circle.lua new file mode 100644 index 000000000..ba2f805c2 --- /dev/null +++ b/xmake/modules/detect/tools/find_circle.lua @@ -0,0 +1,52 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_circle.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find circle +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local circle = find_circle() +-- local circle, version = find_circle({program = "circle", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "circle", opt) + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_cxxbridge.lua b/xmake/modules/detect/tools/find_cxxbridge.lua new file mode 100644 index 000000000..400573fa7 --- /dev/null +++ b/xmake/modules/detect/tools/find_cxxbridge.lua @@ -0,0 +1,52 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_cxxbridge.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find nim +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local nim = find_cxxbridge() +-- local nim, version = find_cxxbridge({program = "cxxbridge", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "cxxbridge", opt) + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_fpc.lua b/xmake/modules/detect/tools/find_fpc.lua new file mode 100644 index 000000000..8414acba8 --- /dev/null +++ b/xmake/modules/detect/tools/find_fpc.lua @@ -0,0 +1,52 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_fpc.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find fpc +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local fpc = find_fpc() +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + opt.check = opt.check or "-h" + + -- find program + local program = find_program(opt.program or "fpc", opt) + + -- find program version + local version = nil + if program and opt and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_glslangValidator.lua b/xmake/modules/detect/tools/find_glslangValidator.lua new file mode 100644 index 000000000..93bc94670 --- /dev/null +++ b/xmake/modules/detect/tools/find_glslangValidator.lua @@ -0,0 +1,52 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_glslangValidator.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find glslangValidator +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local glslangValidator = find_glslangValidator() +-- local glslangValidator, version = find_glslangValidator({program = "glslangValidator", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "glslangValidator", opt) + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_glslc.lua b/xmake/modules/detect/tools/find_glslc.lua new file mode 100644 index 000000000..ada89e672 --- /dev/null +++ b/xmake/modules/detect/tools/find_glslc.lua @@ -0,0 +1,61 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_glslc.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") +import("core.tool.toolchain") + +-- find glslc +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local glslc = find_glslc() +-- local glslc, version = find_glslc({program = "glslc", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "glslc", opt) + if not program and is_plat("android") then + local ndk = toolchain.load("ndk"):config("ndk") + if ndk then + local prebuilt = (is_host("macosx") and "darwin" or os.host()) .. "-x86_64" + opt.paths = path.join(ndk, "shader-tools", prebuilt) + program = find_program(opt.program or "glslc", opt) + end + end + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_ifort.lua b/xmake/modules/detect/tools/find_ifort.lua index ecdb94dfd..31f158996 100644 --- a/xmake/modules/detect/tools/find_ifort.lua +++ b/xmake/modules/detect/tools/find_ifort.lua @@ -36,7 +36,6 @@ import("lib.detect.find_programver") -- @endcode -- function main(opt) - opt = opt or {} if is_host("windows") then -- find program diff --git a/xmake/modules/detect/tools/find_llvm_as.lua b/xmake/modules/detect/tools/find_llvm_as.lua new file mode 100644 index 000000000..213f63abf --- /dev/null +++ b/xmake/modules/detect/tools/find_llvm_as.lua @@ -0,0 +1,57 @@ +--!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, TBOOX Open Source Group. +-- +-- @author xq114 +-- @file find_llvm_as.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find llvm-ar +-- +-- @param opt the argument options, e.g. {version = true, program = "c:\xxx\llvm-as.exe"} +-- +-- @return program, version +-- +-- @code +-- +-- local llvm_as = find_llvm_as() +-- local llvm_as, version = find_llvm_as({version = true}) +-- local llvm_as, version = find_llvm_as({version = true, program = "c:\xxx\llvm-as.exe"}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + opt.check = opt.check or "-version" + opt.command = opt.command or "-version" + + -- find program + local program = find_program(opt.program or "llvm-as", opt) + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + + -- ok? + return program, version +end diff --git a/xmake/modules/detect/tools/find_make.lua b/xmake/modules/detect/tools/find_make.lua new file mode 100644 index 000000000..eb2957d63 --- /dev/null +++ b/xmake/modules/detect/tools/find_make.lua @@ -0,0 +1,53 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_make.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find make +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local make = find_make() +-- +-- @endcode +-- +function main(opt) + + -- find program + opt = opt or {} + local program = find_program(opt.program or (is_host("bsd") and "gmake" or "make"), opt) + if not program and not opt.program and is_subhost("msys", "cygwin") then + program = find_program("mingw32-make.exe", opt) + end + + -- find program version + local version = nil + if program and opt and opt.version then + version = find_programver(program, opt) + end + return program, version +end + diff --git a/xmake/modules/detect/tools/find_metal.lua b/xmake/modules/detect/tools/find_metal.lua new file mode 100644 index 000000000..71a01728b --- /dev/null +++ b/xmake/modules/detect/tools/find_metal.lua @@ -0,0 +1,50 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_metal.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find metal +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local metal = find_metal() +-- local metal, version = find_metal({program = "xcrun -sdk macosx metal", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- find program + opt = opt or {} + local program = find_program(opt.program or "xcrun -sdk macosx metal", opt) + + -- find program version + local version = nil + if program and opt and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_metallib.lua b/xmake/modules/detect/tools/find_metallib.lua new file mode 100644 index 000000000..6989729f9 --- /dev/null +++ b/xmake/modules/detect/tools/find_metallib.lua @@ -0,0 +1,50 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_metallib.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find metallib +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local metallib = find_metallib() +-- local metallib, version = find_metallib({program = "xcrun -sdk macosx metallib", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- find program + opt = opt or {} + local program = find_program(opt.program or "xcrun -sdk macosx metallib", opt) + + -- find program version + local version = nil + if program and opt and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_nim.lua b/xmake/modules/detect/tools/find_nim.lua new file mode 100644 index 000000000..ae524ddbc --- /dev/null +++ b/xmake/modules/detect/tools/find_nim.lua @@ -0,0 +1,52 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_nim.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find nim +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local nim = find_nim() +-- local nim, version = find_nim({program = "nim", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "nim", opt) + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_nimble.lua b/xmake/modules/detect/tools/find_nimble.lua new file mode 100644 index 000000000..7d4364b64 --- /dev/null +++ b/xmake/modules/detect/tools/find_nimble.lua @@ -0,0 +1,52 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_nimble.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find nim +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local nim = find_nimble() +-- local nim, version = find_nimble({program = "nimble", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "nimble", opt) + + -- find program version + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_pkg_config.lua b/xmake/modules/detect/tools/find_pkg_config.lua index 00698d2cc..0dc83454a 100644 --- a/xmake/modules/detect/tools/find_pkg_config.lua +++ b/xmake/modules/detect/tools/find_pkg_config.lua @@ -48,7 +48,5 @@ function main(opt) if program and opt and opt.version then version = find_programver(program, opt) end - - -- ok? return program, version end diff --git a/xmake/modules/detect/tools/find_pkgconf.lua b/xmake/modules/detect/tools/find_pkgconf.lua new file mode 100644 index 000000000..563989268 --- /dev/null +++ b/xmake/modules/detect/tools/find_pkgconf.lua @@ -0,0 +1,52 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_pkgconf.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find pkgconf +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local pkgconf = find_pkgconf() +-- local pkgconf, version = find_pkgconf({version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "pkgconf", opt) + + -- find program version + local version = nil + if program and opt and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/find_swig.lua b/xmake/modules/detect/tools/find_swig.lua new file mode 100644 index 000000000..25220030e --- /dev/null +++ b/xmake/modules/detect/tools/find_swig.lua @@ -0,0 +1,53 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_swig.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find swig +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local swig = find_swig() +-- local swig, version = find_swig({program = "swig", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + opt.check = opt.check or "-version" + + -- find program + local program = find_program(opt.program or "swig", opt) + + -- find program version + local version = nil + if program and opt and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/modules/detect/tools/fpc/has_flags.lua b/xmake/modules/detect/tools/fpc/has_flags.lua new file mode 100644 index 000000000..109e1014b --- /dev/null +++ b/xmake/modules/detect/tools/fpc/has_flags.lua @@ -0,0 +1,98 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file has_flags.lua +-- + +-- imports +import("core.cache.detectcache") + +-- try running +function _try_running(...) + + local argv = {...} + local errors = nil + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors +end + +-- attempt to check it from the argument list +function _check_from_arglist(flags, opt) + + -- only one flag? + if #flags > 1 then + return + end + + -- make cache key + local key = "detect.tools.fpc.has_flags" + + -- make allflags key + local flagskey = opt.program .. "_" .. (opt.programver or "") + + -- get all allflags from argument list + local allflags = detectcache:get2(key, flagskey) + if not allflags then + + -- get argument list + allflags = {} + local arglist = os.iorunv(opt.program, {"-h"}) + if arglist then + for arg in arglist:gmatch("%s+(%-[%-%a%d]+)%s+") do + allflags[arg] = true + end + end + + -- save cache + detectcache:set2(key, flagskey, allflags) + detectcache:save() + end + return allflags[flags[1]] +end + +-- try running to check flags +function _check_try_running(flags, opt) + + -- make an stub source file + local sourcefile = path.join(os.tmpdir(), "detect", "fpc_has_flags.pas") + if not os.isfile(sourcefile) then + io.writefile(sourcefile, "program Hello;\nbegin\nend.") + end + + -- check it + local binaryfile = os.tmpfile() + local ok, errors = _try_running(opt.program, table.join(flags, "-o" .. binaryfile, sourcefile)) + os.tryrm(binaryfile) + return ok, errors +end + +-- has_flags(flags)? +-- +-- @param opt the argument options, e.g. {toolname = "", program = "", programver = "", toolkind = "[cc|cxx|ld|ar|sh|gc|rc|dc|mm|mxx]"} +-- +-- @return true or false +-- +function main(flags, opt) + + -- attempt to check it from the argument list + if _check_from_arglist(flags, opt) then + return true + end + + -- try running to check it + return _check_try_running(flags, opt) +end + diff --git a/xmake/modules/detect/tools/gcc/cfeatures.lua b/xmake/modules/detect/tools/gcc/cfeatures.lua index 664892720..b9ebaa036 100644 --- a/xmake/modules/detect/tools/gcc/cfeatures.lua +++ b/xmake/modules/detect/tools/gcc/cfeatures.lua @@ -29,10 +29,17 @@ function main() -- init conditions local gcc_minver = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 304" + local gcc10_c17 = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 1000 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201710L" local gcc46_c11 = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 406 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201000L" local gcc34_c99 = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 304 && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L" local gcc_c90 = gcc_minver + -- set language standard supports + _set("c_std_89", gcc46_c90) + _set("c_std_99", gcc34_c99) + _set("c_std_11", gcc46_c11) + _set("c_std_17", gcc46_c17) + -- set features _set("c_static_assert", gcc46_c11) -- GNU 4.7 correctly sets __STDC_VERSION__ to 201112L, but GNU 4.6 sets it to 201000L _set("c_restrict", gcc34_c99) diff --git a/xmake/modules/detect/tools/gcc/cxxfeatures.lua b/xmake/modules/detect/tools/gcc/cxxfeatures.lua index 4edb33a27..9d628522c 100644 --- a/xmake/modules/detect/tools/gcc/cxxfeatures.lua +++ b/xmake/modules/detect/tools/gcc/cxxfeatures.lua @@ -28,13 +28,17 @@ end -- -- http://gcc.gnu.org/projects/cxx0x.html -- http://gcc.gnu.org/projects/cxx1y.html +-- https://gcc.gnu.org/projects/cxx-status.html -- -- porting from Modules/Compiler/GNU-CXX-FeatureTests.cmake -- function main() -- init conditions + -- gcc -x c++ -std=c++20 -dM -E - < /dev/null | grep __cplusplus local gcc_minver = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 404" + local gcc90_cxx20 = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 900 && __cplusplus >= 202002L" + local gcc70_cxx17 = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 700 && __cplusplus >= 201703L" local gcc50_cxx14 = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 500 && __cplusplus >= 201402L" local gcc49_cxx14 = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 409 && __cplusplus > 201103L" local gcc481_cxx11 = "((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L" @@ -46,6 +50,13 @@ function main() local gcc44_cxx11 = "(__GNUC__ * 100 + __GNUC_MINOR__) >= 404 && " .. gcc_cxx0x_defined local gcc43_cxx11 = gcc_minver .. " && " .. gcc_cxx0x_defined + -- set language standard supports + _set("cxx_std_98", gcc_minver) + _set("cxx_std_11", gcc43_cxx11) + _set("cxx_std_14", gcc49_cxx14) + _set("cxx_std_17", gcc70_cxx17) + _set("cxx_std_20", gcc90_cxx20) + -- set features _set("cxx_variable_templates", gcc50_cxx14) _set("cxx_relaxed_constexpr", gcc50_cxx14) @@ -151,6 +162,60 @@ function main() _set("cxx_variadic_macros", gcc_minver .. " && " .. gcc_cxx0x_defined) _set("cxx_template_template_parameters", gcc_minver .. " && __cplusplus") + -- c++17 language features with predefined macros + -- https://en.cppreference.com/w/cpp/feature_test + local features_cxx17 = { + "__cpp_aggregate_bases", + "__cpp_aligned_new", + "__cpp_capture_star_this", + "__cpp_constexpr", + "__cpp_deduction_guides", + "__cpp_enumerator_attributes", + "__cpp_fold_expressions", + "__cpp_guaranteed_copy_elision", + "__cpp_hex_float", + "__cpp_if_constexpr", + "__cpp_inheriting_constructors", + "__cpp_inline_variables", + "__cpp_namespace_attributes", + "__cpp_noexcept_function_type", + "__cpp_nontype_template_args", + "__cpp_nontype_template_parameter_auto", + "__cpp_range_based_for", + "__cpp_static_assert", + "__cpp_structured_bindings", + "__cpp_template_template_args", + "__cpp_variadic_using"} + for _, feature in ipairs(features_cxx17) do + _set((feature:gsub("__cpp", "cxx")), "__cplusplus && defined(" .. feature .. ")") + end + + -- c++20 language features with predefined macros + -- https://en.cppreference.com/w/cpp/feature_test + local features_cxx20 = { + "__cpp_aggregate_paren_init", + "__cpp_char8_t", + "__cpp_concepts", + "__cpp_conditional_explicit", + "__cpp_consteval", + "__cpp_constexpr", + "__cpp_constexpr_dynamic_alloc", + "__cpp_constexpr_in_decltype", + "__cpp_constinit", + "__cpp_deduction_guides", + "__cpp_designated_initializers", + "__cpp_generic_lambdas", + "__cpp_impl_coroutine", + "__cpp_impl_destroying_delete", + "__cpp_impl_three_way_comparison", + "__cpp_init_captures", + "__cpp_modules", + "__cpp_nontype_template_args", + "__cpp_using_enum"} + for _, feature in ipairs(features_cxx20) do + _set((feature:gsub("__cpp", "cxx")), "__cplusplus && defined(" .. feature .. ")") + end + -- get features return _g.features end diff --git a/xmake/modules/detect/tools/gcc/has_flags.lua b/xmake/modules/detect/tools/gcc/has_flags.lua index 391ff2bfb..f7cf1d29e 100644 --- a/xmake/modules/detect/tools/gcc/has_flags.lua +++ b/xmake/modules/detect/tools/gcc/has_flags.lua @@ -44,47 +44,40 @@ end -- attempt to check it from the argument list function _check_from_arglist(flags, opt, islinker) - - -- only for compiler - if islinker or #flags > 1 then - return - end - - -- make cache key - local key = "detect.tools.gcc.has_flags" - - -- make flags key + local key = "detect.tools.gcc." .. (islinker and "has_ldflags" or "has_cflags") local flagskey = opt.program .. "_" .. (opt.programver or "") - - -- get all flags from argument list local allflags = detectcache:get2(key, flagskey) if not allflags then - - -- get argument list allflags = {} - local arglist = os.iorunv(opt.program, {"--help"}, {envs = opt.envs}) + local arglist = try {function () return os.iorunv(opt.program, {islinker and "-Wl,--help" or "--help"}, {envs = opt.envs}) end} if arglist then for arg in arglist:gmatch("%s+(%-[%-%a%d]+)%s+") do allflags[arg] = true end end - - -- save cache detectcache:set2(key, flagskey, allflags) detectcache:save() end - return allflags[flags[1]] + local flag = flags[1] + if islinker and flag then + if flag:startswith("-Wl,") then + flag = flag:match("-Wl,(.-),") or flag:sub(5) + end + end + return allflags[flag] +end + +-- get extension +function _get_extension(opt) + -- @note we need detect extension for ndk/clang++.exe: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated] + return (opt.program:endswith("++") or opt.flagkind == "cxxflags") and ".cpp" or (table.wrap(language.sourcekinds()[opt.toolkind or "cc"])[1] or ".c") end -- try running to check flags function _check_try_running(flags, opt, islinker) - -- get extension - -- @note we need detect extension for ndk/clang++.exe: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated] - local extension = opt.program:endswith("++") and ".cpp" or (table.wrap(language.sourcekinds()[opt.toolkind or "cc"])[1] or ".c") - -- make an stub source file - local sourcefile = path.join(os.tmpdir(), "detect", "gcc_has_flags" .. extension) + local sourcefile = path.join(os.tmpdir(), "detect", "gcc_has_flags" .. _get_extension(opt)) if not os.isfile(sourcefile) then io.writefile(sourcefile, "int main(int argc, char** argv)\n{return 0;}") end diff --git a/xmake/modules/detect/tools/go/has_flags.lua b/xmake/modules/detect/tools/go/has_flags.lua index 267d4de8a..e1acf3769 100644 --- a/xmake/modules/detect/tools/go/has_flags.lua +++ b/xmake/modules/detect/tools/go/has_flags.lua @@ -35,7 +35,7 @@ function _try_running(...) local argv = {...} local errors = nil - return try { function () os.runv(unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors end -- attempt to check it from the argument list diff --git a/xmake/modules/detect/tools/ml/has_flags.lua b/xmake/modules/detect/tools/ml/has_flags.lua index 53576fa22..0dbda2134 100644 --- a/xmake/modules/detect/tools/ml/has_flags.lua +++ b/xmake/modules/detect/tools/ml/has_flags.lua @@ -61,12 +61,21 @@ function _check_try_running(flags, opt) -- make an stub source file local sourcefile = path.join(os.tmpdir(), "detect", "ml_has_flags.asm") if not os.isfile(sourcefile) then - io.writefile(sourcefile, ".code\nend") + io.writefile(sourcefile, [[ +ifndef X64 +.686p +.model flat, C +endif +.code +end]]) end -- check it local errors = nil return try { function () + if opt.program:find("ml64", 1, true) then + table.insert(flags, "-DX64") + end local _, errs = os.iorunv(opt.program, table.join("-c", "-nologo", flags, "-Fo" .. os.nuldev(), sourcefile), {envs = opt.envs}) if errs and #errs:trim() > 0 then return false, errs diff --git a/xmake/modules/detect/tools/nim/has_flags.lua b/xmake/modules/detect/tools/nim/has_flags.lua new file mode 100644 index 000000000..5538257fb --- /dev/null +++ b/xmake/modules/detect/tools/nim/has_flags.lua @@ -0,0 +1,98 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file has_flags.lua +-- + +-- imports +import("core.cache.detectcache") + +-- try running +function _try_running(...) + + local argv = {...} + local errors = nil + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors +end + +-- attempt to check it from the argument list +function _check_from_arglist(flags, opt) + + -- only one flag? + if #flags > 1 then + return + end + + -- make cache key + local key = "detect.tools.nim.has_flags" + + -- make allflags key + local flagskey = opt.program .. "_" .. (opt.programver or "") + + -- get all allflags from argument list + local allflags = detectcache:get2(key, flagskey) + if not allflags then + + -- get argument list + allflags = {} + local arglist = os.iorunv(opt.program, {"--help"}) + if arglist then + for arg in arglist:gmatch("%s+(%-[%-%a%d]+)%s+") do + allflags[arg] = true + end + end + + -- save cache + detectcache:set2(key, flagskey, allflags) + detectcache:save() + end + return allflags[flags[1]] +end + +-- try running to check flags +function _check_try_running(flags, opt) + + -- make an stub source file + local sourcefile = path.join(os.tmpdir(), "detect", "nim_has_flags.nim") + if not os.isfile(sourcefile) then + io.writefile(sourcefile, "echo \"hello\"") + end + + -- check it + local cachedir = os.tmpfile() .. ".dir" + local ok, errors = _try_running(opt.program, table.join("c", "-c", flags, "--nimcache:" .. cachedir, sourcefile)) + os.tryrm(cachedir) + return ok, errors +end + +-- has_flags(flags)? +-- +-- @param opt the argument options, e.g. {toolname = "", program = "", programver = "", toolkind = "[cc|cxx|ld|ar|sh|gc|rc|dc|mm|mxx]"} +-- +-- @return true or false +-- +function main(flags, opt) + + -- attempt to check it from the argument list + if _check_from_arglist(flags, opt) then + return true + end + + -- try running to check it + return _check_try_running(flags, opt) +end + diff --git a/xmake/modules/detect/tools/rc/has_flags.lua b/xmake/modules/detect/tools/rc/has_flags.lua new file mode 100644 index 000000000..ac023c466 --- /dev/null +++ b/xmake/modules/detect/tools/rc/has_flags.lua @@ -0,0 +1,68 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file has_flags.lua +-- + +-- imports +import("core.cache.detectcache") +import("core.language.language") + +-- attempt to check it from the argument list +function _check_from_arglist(flags, opt) + + -- only one flag? + if #flags > 1 then + return + end + + -- make cache key + local key = "detect.tools.rc.has_flags" + + -- make allflags key + local flagskey = opt.program .. "_" .. (opt.programver or "") + + -- get all allflags from argument list + local allflags = detectcache:get2(key, flagskey) + if not allflags then + + -- get argument list + allflags = {} + local arglist = os.iorunv(opt.program, {"-?"}, {envs = opt.envs}) + if arglist then + for arg in arglist:gmatch("(/[%-%a%d]+)%s+") do + allflags[arg:gsub("/", "-")] = true + end + end + + -- save cache + detectcache:set2(key, flagskey, allflags) + detectcache:save() + end + return allflags[flags[1]:gsub("/", "-")] +end + +-- has_flags(flags)? +-- +-- @param opt the argument options, e.g. {toolname = "", program = "", programver = ""} +-- +-- @return true or false +-- +function main(flags, opt) + return _check_from_arglist(flags, opt) +end + diff --git a/xmake/modules/detect/tools/rustc/has_flags.lua b/xmake/modules/detect/tools/rustc/has_flags.lua index e7c55a180..e1696e454 100644 --- a/xmake/modules/detect/tools/rustc/has_flags.lua +++ b/xmake/modules/detect/tools/rustc/has_flags.lua @@ -21,12 +21,22 @@ -- imports import("core.cache.detectcache") +-- is linker? +function _islinker(flags, opt) + local flags_str = table.concat(flags, " ") + if flags_str:startswith("-C linkarg=") then + return true + end + local toolkind = opt.toolkind or "" + return toolkind == "ld" or toolkind == "sh" or toolkind:endswith("ld") or toolkind:endswith("sh") +end + -- try running function _try_running(...) local argv = {...} local errors = nil - return try { function () os.runv(unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors end -- attempt to check it from the argument list @@ -64,7 +74,7 @@ function _check_from_arglist(flags, opt) end -- try running to check flags -function _check_try_running(flags, opt) +function _check_try_running(flags, opt, islinker) -- make an stub source file local sourcefile = path.join(os.tmpdir(), "detect", "rustc_has_flags.rs") @@ -72,14 +82,15 @@ function _check_try_running(flags, opt) io.writefile(sourcefile, "fn main() {\n}") end - -- check it + -- check flags for linker + if islinker then + return _try_running(opt.program, table.join("--crate-type=bin", flags, "-o", os.tmpfile(), sourcefile), opt) + end + + -- check flags for compiler local objectfile = os.tmpfile() .. ".o" local ok, errors = _try_running(opt.program, table.join("--emit", "obj", flags, "-o", objectfile, sourcefile)) - - -- remove files os.tryrm(objectfile) - - -- ok? return ok, errors end @@ -91,12 +102,15 @@ end -- function main(flags, opt) + -- is linker? + local islinker = _islinker(flags, opt) + -- attempt to check it from the argument list if _check_from_arglist(flags, opt) then return true end -- try running to check it - return _check_try_running(flags, opt) + return _check_try_running(flags, opt, islinker) end diff --git a/xmake/modules/detect/tools/sdcc/has_flags.lua b/xmake/modules/detect/tools/sdcc/has_flags.lua index 1198b6f44..c7c440a8a 100644 --- a/xmake/modules/detect/tools/sdcc/has_flags.lua +++ b/xmake/modules/detect/tools/sdcc/has_flags.lua @@ -41,7 +41,7 @@ function _try_running(...) local argv = {...} local errors = nil - return try { function () os.runv(unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors end -- attempt to check it from the argument list diff --git a/xmake/modules/detect/tools/swiftc/has_flags.lua b/xmake/modules/detect/tools/swiftc/has_flags.lua index 541b897a9..fc4ff6540 100644 --- a/xmake/modules/detect/tools/swiftc/has_flags.lua +++ b/xmake/modules/detect/tools/swiftc/has_flags.lua @@ -26,7 +26,7 @@ function _try_running(...) local argv = {...} local errors = nil - return try { function () os.runv(unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors end -- attempt to check it from the argument list diff --git a/xmake/modules/detect/tools/zig/has_flags.lua b/xmake/modules/detect/tools/zig/has_flags.lua index b12f3defb..1dea91cfe 100644 --- a/xmake/modules/detect/tools/zig/has_flags.lua +++ b/xmake/modules/detect/tools/zig/has_flags.lua @@ -34,7 +34,7 @@ function _try_running(...) local argv = {...} local errors = nil - return try { function () os.runv(unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors + return try { function () os.runv(table.unpack(argv)); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors end -- attempt to check it from the argument list diff --git a/xmake/modules/devel/git/apply.lua b/xmake/modules/devel/git/apply.lua index 97923da02..ccfd5bf80 100644 --- a/xmake/modules/devel/git/apply.lua +++ b/xmake/modules/devel/git/apply.lua @@ -44,17 +44,6 @@ function main(patchfile, opt) opt = opt or {} local argv = {"apply", "--reject", "--ignore-whitespace", patchfile} - -- enter repository directory - local oldir = nil - if opt.repodir then - oldir = os.cd(opt.repodir) - end - -- apply it - os.vrunv(git.program, argv) - - -- leave repository directory - if oldir then - os.cd(oldir) - end + os.vrunv(git.program, argv, {curdir = opt.repodir}) end diff --git a/xmake/modules/devel/git/checkout.lua b/xmake/modules/devel/git/checkout.lua index 7a32aa3b1..c78d8f439 100644 --- a/xmake/modules/devel/git/checkout.lua +++ b/xmake/modules/devel/git/checkout.lua @@ -47,17 +47,6 @@ function main(commit, opt) -- init argv local argv = {"checkout", commit} - -- enter repository directory - local oldir = nil - if opt.repodir then - oldir = os.cd(opt.repodir) - end - -- checkout it - os.vrunv(git.program, argv) - - -- leave repository directory - if oldir then - os.cd(oldir) - end + os.vrunv(git.program, argv, {curdir = opt.repodir}) end diff --git a/xmake/modules/devel/git/clean.lua b/xmake/modules/devel/git/clean.lua index ae21c3d88..52fa81a0b 100644 --- a/xmake/modules/devel/git/clean.lua +++ b/xmake/modules/devel/git/clean.lua @@ -61,17 +61,6 @@ function main(opt) table.insert(argv, "-x") end - -- enter repository directory - local oldir = nil - if opt.repodir then - oldir = os.cd(opt.repodir) - end - -- clean it - os.vrunv(git.program, argv) - - -- leave repository directory - if oldir then - os.cd(oldir) - end + os.vrunv(git.program, argv, {curdir = opt.repodir}) end diff --git a/xmake/modules/devel/git/lastcommit.lua b/xmake/modules/devel/git/lastcommit.lua new file mode 100644 index 000000000..1ad6de1f4 --- /dev/null +++ b/xmake/modules/devel/git/lastcommit.lua @@ -0,0 +1,66 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file lastcommit.lua +-- + +-- imports +import("core.base.option") +import("lib.detect.find_tool") +import("net.proxy") + +-- get last commit in git repository +-- +-- @param opt the options, e.g. {repodir = ..} +-- +-- @return the last commit +-- +-- @code +-- +-- import("devel.git") +-- +-- local lastcommit = git.lastcommit({repodir = ..}) +-- +-- @endcode +-- +function main(opt) + + -- find git + local git = assert(find_tool("git"), "git not found!") + + -- init arguments + local argv = {"rev-parse", "HEAD"} + + -- trace + if option.get("verbose") then + print("%s %s", git.program, os.args(argv)) + end + + -- use proxy? + local envs + local proxy_conf = proxy.config(url) + if proxy_conf then + envs = {ALL_PROXY = proxy_conf} + end + + -- get last commit + local lastcommit = os.iorunv(git.program, argv, {envs = envs, curdir = opt.repodir}) + if lastcommit then + lastcommit = lastcommit:trim() + end + return lastcommit +end diff --git a/xmake/modules/devel/git/pull.lua b/xmake/modules/devel/git/pull.lua index 9970f8dbc..412e6e757 100644 --- a/xmake/modules/devel/git/pull.lua +++ b/xmake/modules/devel/git/pull.lua @@ -58,18 +58,12 @@ function main(opt) table.insert(argv, "--tags") end - -- enter repository directory - local oldir = nil - if opt.repodir then - oldir = os.cd(opt.repodir) - end - -- use proxy? local envs local proxy_conf = proxy.config() if proxy_conf then -- get proxy configuration from the current remote url - local remoteinfo = try { function() return os.iorunv(git.program, {"remote", "-v"}) end } + local remoteinfo = try { function() return os.iorunv(git.program, {"remote", "-v"}, {curdir = opt.repodir}) end } if remoteinfo then for _, line in ipairs(remoteinfo:split('\n', {plain = true})) do local splitinfo = line:split("%s+") @@ -86,10 +80,5 @@ function main(opt) end -- pull it - os.vrunv(git.program, argv, {envs = envs}) - - -- leave repository directory - if oldir then - os.cd(oldir) - end + os.vrunv(git.program, argv, {envs = envs, curdir = opt.repodir}) end diff --git a/xmake/modules/devel/git/reset.lua b/xmake/modules/devel/git/reset.lua index 28b984008..4ed798d25 100644 --- a/xmake/modules/devel/git/reset.lua +++ b/xmake/modules/devel/git/reset.lua @@ -71,17 +71,6 @@ function main(opt) table.insert(argv, opt.commit) end - -- enter repository directory - local oldir = nil - if opt.repodir then - oldir = os.cd(opt.repodir) - end - -- reset it - os.vrunv(git.program, argv) - - -- leave repository directory - if oldir then - os.cd(oldir) - end + os.vrunv(git.program, argv, {curdir = opt.repodir}) end diff --git a/xmake/modules/devel/git/submodule/update.lua b/xmake/modules/devel/git/submodule/update.lua index 5d9323a02..511684c1c 100644 --- a/xmake/modules/devel/git/submodule/update.lua +++ b/xmake/modules/devel/git/submodule/update.lua @@ -58,37 +58,26 @@ function main(opt) table.join2(argv, opt.paths) end - -- enter repository directory - local oldir = nil - if opt.repodir then - oldir = os.cd(opt.repodir) - end - -- enable long paths local longpaths_old local longpaths_changed = false if opt.longpaths then - local longpaths_old = try {function () return os.iorunv(git.program, {"config", "--get", "--global", "core.longpaths"}) end} + local longpaths_old = try {function () return os.iorunv(git.program, {"config", "--get", "--global", "core.longpaths"}, {curdir = opt.repodir}) end} if not longpaths_old or not longpaths_old:find("true") then - os.vrunv(git.program, {"config", "--global", "core.longpaths", "true"}) + os.vrunv(git.program, {"config", "--global", "core.longpaths", "true"}, {curdir = opt.repodir}) longpaths_changed = true end end -- submodule it - os.vrunv(git.program, argv) + os.vrunv(git.program, argv, {curdir = opt.repodir}) -- restore old long paths configuration if longpaths_changed then if longpaths_old and longpaths_old:find("false") then - os.vrunv(git.program, {"config", "--global", "core.longpaths", "false"}) + os.vrunv(git.program, {"config", "--global", "core.longpaths", "false"}, {curdir = opt.repodir}) else - os.vrunv(git.program, {"config", "--global", "--unset", "core.longpaths"}) + os.vrunv(git.program, {"config", "--global", "--unset", "core.longpaths"}, {curdir = opt.repodir}) end end - - -- leave repository directory - if oldir then - os.cd(oldir) - end end diff --git a/xmake/modules/lib/detect/check_cxsnippets.lua b/xmake/modules/lib/detect/check_cxsnippets.lua index aa728e27b..ea0e941d3 100644 --- a/xmake/modules/lib/detect/check_cxsnippets.lua +++ b/xmake/modules/lib/detect/check_cxsnippets.lua @@ -77,7 +77,11 @@ function _sourcecode(snippets, opt) -- add includes local sourcecode = "" - for _, include in ipairs(opt.includes) do + local includes = table.wrap(opt.includes) + if opt.tryrun and opt.output then + table.insert(includes, "stdio.h") + end + for _, include in ipairs(includes) do sourcecode = format("%s\n#include <%s>", sourcecode, include) end sourcecode = sourcecode .. "\n" @@ -88,11 +92,13 @@ function _sourcecode(snippets, opt) end sourcecode = sourcecode .. "\n" - -- add snippets - for _, snippet in pairs(snippets) do - sourcecode = sourcecode .. "\n" .. snippet + -- add snippets (build only) + if not opt.tryrun then + for _, snippet in pairs(snippets) do + sourcecode = sourcecode .. "\n" .. snippet + end + sourcecode = sourcecode .. "\n" end - sourcecode = sourcecode .. "\n" -- enter main function sourcecode = sourcecode .. "int main(int argc, char** argv)\n{\n" @@ -102,10 +108,19 @@ function _sourcecode(snippets, opt) sourcecode = format("%s\n %s;", sourcecode, _funccode(funcinfo)) end - -- leave main function - sourcecode = sourcecode .. "\n return 0;\n}\n" - - -- done + -- add snippets (tryrun) + if opt.tryrun then + for _, snippet in pairs(snippets) do + sourcecode = sourcecode .. "\n" .. snippet + end + if opt.output then + sourcecode = sourcecode .. "\nfflush(stdout);\n" + end + sourcecode = sourcecode .. "\n}\n" -- we need return exit code in snippet + else + -- leave main function + sourcecode = sourcecode .. "\n return 0;\n}\n" + end return sourcecode end @@ -116,7 +131,8 @@ end -- e.g. -- { verbose = false, target = [target|option], sourcekind = "[cc|cxx]" -- , types = {"wchar_t", "char*"}, includes = "stdio.h", funcs = {"sigsetjmp", "sigsetjmp((void*)0, 0)"} --- , configs = {defines = "xx", cxflags = ""}} +-- , configs = {defines = "xx", cxflags = ""} +-- , tryrun = true, output = true} -- -- funcs: -- sigsetjmp @@ -127,9 +143,9 @@ end -- @return true or false -- -- @code --- local ok = check_cxsnippets("void test() {}") --- local ok = check_cxsnippets({"void test(){}", "#define TEST 1"}, {types = "wchar_t", includes = "stdio.h"}) --- local ok = check_cxsnippets({snippet_name = "void test(){}", "#define TEST 1"}, {types = "wchar_t", includes = "stdio.h"}) +-- local ok, output_or_errors = check_cxsnippets("void test() {}") +-- local ok, output_or_errors = check_cxsnippets({"void test(){}", "#define TEST 1"}, {types = "wchar_t", includes = "stdio.h"}) +-- local ok, output_or_errors = check_cxsnippets({snippet_name = "void test(){}", "#define TEST 1"}, {types = "wchar_t", includes = "stdio.h"}) -- @endcode -- function main(snippets, opt) @@ -189,19 +205,30 @@ function main(snippets, opt) -- @note cannot cache result, all conditions will be changed -- attempt to compile it local errors = nil - local ok = try + local ok, output = try { function () if option.get("diagnosis") then cprint("${dim}> %s", compiler.compcmd(sourcefile, objectfile, opt)) end compiler.compile(sourcefile, objectfile, opt) - if #links > 0 then + if #links > 0 or opt.tryrun then if option.get("diagnosis") then cprint("${dim}> %s", linker.linkcmd("binary", {"cc", "cxx"}, objectfile, binaryfile, opt)) end linker.link("binary", {"cc", "cxx"}, objectfile, binaryfile, opt) end + if opt.tryrun then + if opt.output then + local output = os.iorun(binaryfile) + if output then + output = output:trim() + end + return true, output + else + os.vrun(binaryfile) + end + end return true end, catch { function (errs) errors = errs end } @@ -238,6 +265,6 @@ function main(snippets, opt) if errors and option.get("diagnosis") and #tostring(errors) > 0 then cprint("${color.warning}checkinfo:${clear dim} %s", errors) end - return ok + return ok, ok and output or errors end diff --git a/xmake/modules/lib/detect/features.lua b/xmake/modules/lib/detect/features.lua index 614d8c592..8b9bde45b 100644 --- a/xmake/modules/lib/detect/features.lua +++ b/xmake/modules/lib/detect/features.lua @@ -79,12 +79,7 @@ function main(name, opt) end _g._checking = nil - -- no features? result = result or {} - - -- save result to cache results[key] = result - - -- ok? return result end diff --git a/xmake/modules/lib/detect/find_package.lua b/xmake/modules/lib/detect/find_package.lua index 7b7ba7032..d3c9e57d1 100644 --- a/xmake/modules/lib/detect/find_package.lua +++ b/xmake/modules/lib/detect/find_package.lua @@ -24,6 +24,30 @@ import("core.project.config") import("core.cache.detectcache") import("package.manager.find_package") +-- concat packages +function _concat_packages(a, b) + local result = table.copy(a) + for k, v in pairs(b) do + local o = result[k] + if o ~= nil then + v = table.join(o, v) + end + result[k] = v + end + for k, v in pairs(result) do + if k == "links" then + if type(v) == "table" and #v > 1 then + -- we need ensure link orders when removing repeat values + v = table.reverse_unique(v) + end + else + v = table.unique(v) + end + result[k] = v + end + return result +end + -- find package using the package manager -- -- @param name the package name @@ -113,5 +137,10 @@ function main(name, opt) if not opt.version and result then result.version = nil end + + -- register concat + if result and type(result) == "table" then + debug.setmetatable(result, {__concat = _concat_packages}) + end return result and result or nil end diff --git a/xmake/modules/lib/detect/pkgconfig.lua b/xmake/modules/lib/detect/pkgconfig.lua index 9cd07a1e7..cf6b6d97d 100644 --- a/xmake/modules/lib/detect/pkgconfig.lua +++ b/xmake/modules/lib/detect/pkgconfig.lua @@ -24,7 +24,15 @@ import("core.project.target") import("core.project.config") import("lib.detect.find_file") import("lib.detect.find_library") -import("detect.tools.find_pkg_config") +import("lib.detect.find_tool") + +-- get pkgconfig +function _get_pkgconfig() + local pkgconfig = find_tool("pkg-config") or find_tool("pkgconf") + if pkgconfig then + return pkgconfig.program + end +end -- get version -- @@ -34,7 +42,7 @@ import("detect.tools.find_pkg_config") function version(name, opt) -- attempt to add search paths from pkg-config - local pkgconfig = find_pkg_config() + local pkgconfig = _get_pkgconfig() if not pkgconfig then return end @@ -46,7 +54,7 @@ function version(name, opt) local configdirs_old = os.getenv("PKG_CONFIG_PATH") local configdirs = table.wrap(opt.configdirs) if #configdirs > 0 then - os.setenv("PKG_CONFIG_PATH", unpack(configdirs)) + os.setenv("PKG_CONFIG_PATH", table.unpack(configdirs)) end -- get version @@ -73,7 +81,7 @@ end function variables(name, variables, opt) -- attempt to add search paths from pkg-config - local pkgconfig = find_pkg_config() + local pkgconfig = _get_pkgconfig() if not pkgconfig then return end @@ -85,7 +93,7 @@ function variables(name, variables, opt) local configdirs_old = os.getenv("PKG_CONFIG_PATH") local configdirs = table.wrap(opt.configdirs) if #configdirs > 0 then - os.setenv("PKG_CONFIG_PATH", unpack(configdirs)) + os.setenv("PKG_CONFIG_PATH", table.unpack(configdirs)) end -- get variable value @@ -125,7 +133,7 @@ end function libinfo(name, opt) -- attempt to add search paths from pkg-config - local pkgconfig = find_pkg_config() + local pkgconfig = _get_pkgconfig() if not pkgconfig then return end diff --git a/xmake/modules/net/ping.lua b/xmake/modules/net/ping.lua index 5f555cccd..93e9b4bec 100644 --- a/xmake/modules/net/ping.lua +++ b/xmake/modules/net/ping.lua @@ -88,7 +88,7 @@ function main(hosts, opt) end -- trace - vprint("pinging for the host(%s) ... %d ms", host, timeval) + vprint("pinging for the host(%s) ... %d ms", host, math.floor(timeval)) end end end, {total = #hosts}) diff --git a/xmake/modules/package/manager/apt/find_package.lua b/xmake/modules/package/manager/apt/find_package.lua index f27968c0e..e2617b4ec 100644 --- a/xmake/modules/package/manager/apt/find_package.lua +++ b/xmake/modules/package/manager/apt/find_package.lua @@ -24,26 +24,8 @@ import("core.project.config") import("core.project.target") import("lib.detect.find_tool") --- find package using the dpkg package manager --- --- @param name the package name --- @param opt the options, e.g. {verbose = true, version = "1.12.0") --- -function main(name, opt) - - -- check - opt = opt or {} - if not is_host(opt.plat) or os.arch() ~= opt.arch then - return - end - - -- find dpkg - local dpkg = find_tool("dpkg") - if not dpkg then - return - end - - -- find package +-- find package +function _find_package(dpkg, name, opt) local result = nil local listinfo = try {function () return os.iorunv(dpkg.program, {"--listfiles", name}) end} if listinfo then @@ -51,14 +33,11 @@ function main(name, opt) line = line:trim() -- get includedirs - -- we need not add it, gcc/clang will use /usr/ as default sysroot - --[[ local pos = line:find("include/", 1, true) if pos then + -- we need not add includedirs, gcc/clang will use /usr/ as default sysroot result = result or {} - result.includedirs = result.includedirs or {} - table.insert(result.includedirs, line:sub(1, pos + 7)) - end]] + end -- get linkdirs and links if line:endswith(".a") or line:endswith(".so") then @@ -67,12 +46,30 @@ function main(name, opt) result.linkdirs = result.linkdirs or {} result.libfiles = result.libfiles or {} table.insert(result.linkdirs, path.directory(line)) - table.insert(result.links, target.linkname(path.filename(line))) + table.insert(result.links, target.linkname(path.filename(line), {plat = opt.plat})) table.insert(result.libfiles, path.join(path.directory(line), path.filename(line))) end end end + -- meta/alias package? e.g. libboost-dev -> libboost1.74-dev + -- @see https://github.com/xmake-io/xmake/issues/1786 + if not result then + local statusinfo = try {function () return os.iorunv(dpkg.program, {"--status", name}) end} + if statusinfo then + for _, line in ipairs(statusinfo:split("\n", {plain = true})) do + -- parse depends, e.g. Depends: libboost1.74-dev + if line:startswith("Depends:") then + local depends = line:sub(9):split("%s+") + if #depends == 1 then + return _find_package(dpkg, depends[1], opt) + end + break + end + end + end + end + -- remove repeat if result then if result.links then @@ -87,3 +84,26 @@ function main(name, opt) end return result end + +-- find package using the dpkg package manager +-- +-- @param name the package name +-- @param opt the options, e.g. {verbose = true, version = "1.12.0") +-- +function main(name, opt) + + -- check + opt = opt or {} + if not is_host(opt.plat) or os.arch() ~= opt.arch then + return + end + + -- find dpkg + local dpkg = find_tool("dpkg") + if not dpkg then + return + end + + -- find package + return _find_package(dpkg, name, opt) +end diff --git a/xmake/modules/package/manager/brew/find_package.lua b/xmake/modules/package/manager/brew/find_package.lua index 49db5a464..9706bfd8e 100644 --- a/xmake/modules/package/manager/brew/find_package.lua +++ b/xmake/modules/package/manager/brew/find_package.lua @@ -67,14 +67,16 @@ function main(name, opt) -- find package from pkg-config/*.pc, attempt to find it from `brew --prefix`/package first local result = nil - local pcfile = find_file(pcname .. ".pc", path.join(brew_pkg_rootdir, nameinfo[1], "*/lib/pkgconfig")) + local pcfile = find_file(pcname .. ".pc", path.join(brew_pkg_rootdir, nameinfo[1], "*/lib/pkgconfig")) or + find_file(pcname .. ".pc", path.join(brew_pkg_rootdir, nameinfo[1], "*/share/pkgconfig")) if not pcfile then -- attempt to find it from `brew --prefix package` local brew = find_tool("brew") local brew_pkgdir = brew and try {function () return os.iorunv(brew.program, {"--prefix", nameinfo[1]}) end} if brew_pkgdir then brew_pkgdir = brew_pkgdir:trim() - pcfile = find_file(pcname .. ".pc", path.join(brew_pkgdir, "lib/pkgconfig")) + pcfile = find_file(pcname .. ".pc", path.join(brew_pkgdir, "lib/pkgconfig")) or + find_file(pcname .. ".pc", path.join(brew_pkgdir, "share/pkgconfig")) end end if pcfile then @@ -97,11 +99,11 @@ function main(name, opt) if pkgdir then local links = {} for _, libfile in ipairs(os.files(path.join(pkgdir, "lib", "*.a"))) do - table.insert(links, target.linkname(path.filename(libfile))) + table.insert(links, target.linkname(path.filename(libfile), {plat = opt.plat})) end for _, libfile in ipairs(os.files(path.join(pkgdir, "lib", opt.plat == "macosx" and "*.dylib" or "*.so"))) do if not os.islink(libfile) then - table.insert(links, target.linkname(path.filename(libfile))) + table.insert(links, target.linkname(path.filename(libfile), {plat = opt.plat})) end end opt.links = links diff --git a/xmake/modules/package/manager/cargo/configurations.lua b/xmake/modules/package/manager/cargo/configurations.lua new file mode 100644 index 000000000..a3f85a0ea --- /dev/null +++ b/xmake/modules/package/manager/cargo/configurations.lua @@ -0,0 +1,28 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file configurations.lua +-- + +-- get configurations +function main() + return + { + features = {description = "set the features of dependency."}, + default_features = {description = "enables or disables any defaults provided by the dependency.", default = true}, + } +end diff --git a/xmake/modules/package/manager/cargo/find_package.lua b/xmake/modules/package/manager/cargo/find_package.lua new file mode 100644 index 000000000..7fd151d5c --- /dev/null +++ b/xmake/modules/package/manager/cargo/find_package.lua @@ -0,0 +1,58 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_package.lua +-- + +-- imports +import("core.base.option") +import("core.base.semver") +import("core.project.config") +import("core.project.target") +import("lib.detect.find_tool") +import("lib.detect.find_file") + +-- find package using the cargo package manager +-- +-- @param name the package name +-- @param opt the options, e.g. {verbose = true, require_version = "1.12.x") +-- +function main(name, opt) + local frameworkdirs + local frameworks + local librarydir = path.join(opt.installdir, "lib") + local libfiles = os.files(path.join(librarydir, "*.rlib")) + for _, libraryfile in ipairs(libfiles) do + local filename = path.filename(libraryfile) + if filename:startswith("lib" .. name .. "-") then + frameworkdirs = frameworkdirs or {} + frameworks = frameworks or {} + table.insert(frameworkdirs, librarydir) + table.insert(frameworks, libraryfile) + break + end + end + local result + if frameworks and frameworkdirs then + result = result or {} + result.libfiles = libfiles + result.frameworkdirs = frameworkdirs + result.frameworks = frameworks + result.version = opt.require_version + end + return result +end diff --git a/xmake/modules/package/manager/cargo/install_package.lua b/xmake/modules/package/manager/cargo/install_package.lua new file mode 100644 index 000000000..9ea709dd7 --- /dev/null +++ b/xmake/modules/package/manager/cargo/install_package.lua @@ -0,0 +1,95 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file install_package.lua +-- + +-- imports +import("core.base.option") +import("core.project.config") +import("lib.detect.find_tool") + +-- install package +-- +-- e.g. +-- add_requires("cargo::base64") +-- add_requires("cargo::base64 0.13.0") +-- add_requires("cargo::flate2 1.0.17", {configs = {features = {"zlib"}, ["default-features"] = false}}) +-- +-- @param name the package name, e.g. cargo::base64 +-- @param opt the options, e.g. { verbose = true, mode = "release", plat = , arch = , require_version = "x.x.x"} +-- +-- @return true or false +-- +function main(name, opt) + + -- find cargo + local cargo = find_tool("cargo") + if not cargo then + raise("cargo not found!") + end + + -- get required version + opt = opt or {} + local configs = opt.configs or {} + local require_version = opt.require_version + if not require_version or require_version == "latest" then + require_version = "*" + end + + -- build dependencies + local sourcedir = path.join(opt.cachedir, "source") + local cargotoml = path.join(sourcedir, "Cargo.toml") + os.tryrm(sourcedir) + local tomlfile = io.open(cargotoml, "w") + tomlfile:print("[package]") + tomlfile:print("name = \"cargodeps\"") + tomlfile:print("version = \"0.1.0\"") + tomlfile:print("edition = \"2018\"") + tomlfile:print("") + tomlfile:print("[dependencies]") + local features = configs.features + if features then + features = table.wrap(features) + tomlfile:print("%s = {version = \"%s\", features = [\"%s\"], default-features = %s}", name, require_version, table.concat(features, "\", \""), configs.default_features) + else + tomlfile:print("%s = \"%s\"", name, require_version) + end + tomlfile:close() + + -- generate main.rs + io.writefile(path.join(sourcedir, "src", "main.rs"), [[ +fn main() { + println!("Hello, world!"); +} + ]]) + + -- do build + local argv = {"build"} + if opt.mode ~= "debug" then + table.insert(argv, "--release") + end + if option.get("verbose") then + table.insert(argv, option.get("diagnosis") and "-vv" or "-v") + end + os.vrunv(cargo.program, argv, {curdir = sourcedir}) + + -- do install + local installdir = opt.installdir + os.tryrm(path.join(installdir, "lib")) + os.vcp(path.join(sourcedir, "target", opt.mode == "debug" and "debug" or "release", "deps"), path.join(installdir, "lib")) +end diff --git a/xmake/modules/package/manager/clib/configurations.lua b/xmake/modules/package/manager/clib/configurations.lua new file mode 100644 index 000000000..4f59ea70d --- /dev/null +++ b/xmake/modules/package/manager/clib/configurations.lua @@ -0,0 +1,30 @@ +--!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, TBOOX Open Source Group. +-- +-- @author Adel Vilkov (aka RaZeR-RBI) +-- @file configurations.lua +-- + +-- get configurations +function main() + return + { + save = {description = "save dependency in project's package.json", default = false, type = "boolean"}, + save_dev = {description = "save as development dependency in project's package.json", default = false, type = "boolean"}, + outputdir = {description = "package installation directory relative to project root", default = "clib"}, + } +end + diff --git a/xmake/modules/package/manager/clib/install_package.lua b/xmake/modules/package/manager/clib/install_package.lua index 1b61af5b4..0257ff8fe 100644 --- a/xmake/modules/package/manager/clib/install_package.lua +++ b/xmake/modules/package/manager/clib/install_package.lua @@ -23,42 +23,35 @@ import("core.base.option") import("core.project.config") import("lib.detect.find_tool") --- get configurations -function configurations() - return - { - save = {description = "save dependency in project's package.json", default = false, type = "boolean"}, - save_dev = {description = "save as development dependency in project's package.json", default = false, type = "boolean"}, - outputdir = {description = "package installation directory relative to project root", default = "clib"}, - } -end - -- install package -- @param name the package name, e.g. clib::clibs/[email protected] -- @param opt the options, e.g. { verbose = true, --- settings = {outputdir = "clib", save = false, save_dev = false}} +-- configs = {outputdir = "clib", save = false, save_dev = false}} -- -- @return true or false -- function main(name, opt) + -- find clib local clib = find_tool("clib") if not clib then raise("clib not found!") end + opt = opt or {} + local configs = opt.configs or {} local argv = {"install", name} - local abs_out = path.join(os.projectdir(), opt.outputdir) + local abs_out = path.join(os.projectdir(), configs.outputdir) dprint("installing %s to %s", name, abs_out) table.insert(argv, "-o " .. abs_out) if not option.get("verbose") then table.insert(argv, "-q") end - if opt.save then + if configs.save then table.insert(argv, "--save") end - if opt.save_dev then + if configs.save_dev then table.insert(argv, "--save-dev") end diff --git a/xmake/modules/package/manager/cmake/configurations.lua b/xmake/modules/package/manager/cmake/configurations.lua new file mode 100644 index 000000000..e65525f6d --- /dev/null +++ b/xmake/modules/package/manager/cmake/configurations.lua @@ -0,0 +1,31 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file configurations.lua +-- + +-- get configurations +function main() + return + { + components = {description = "Set the cmake package components, e.g. {\"regex\", \"system\"}"}, + moduledirs = {description = "Set the cmake modules directories."}, + presets = {description = "Set the preset values, e.g. {Boost_USE_STATIC_LIB = true}"}, + envs = {description = "Set the run environments of cmake, e.g. {CMAKE_PREFIX_PATH = \"xxx\"}"}, + } +end + diff --git a/xmake/modules/package/manager/cmake/find_package.lua b/xmake/modules/package/manager/cmake/find_package.lua new file mode 100644 index 000000000..d9abd6b00 --- /dev/null +++ b/xmake/modules/package/manager/cmake/find_package.lua @@ -0,0 +1,282 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_package.lua +-- + +-- imports +import("core.base.option") +import("core.project.target") +import("lib.detect.find_tool") + +-- find package +function _find_package(cmake, name, opt) + + -- get work directory + local workdir = os.tmpfile() .. ".dir" + os.tryrm(workdir) + os.mkdir(workdir) + io.writefile(path.join(workdir, "test.cpp"), "") + + -- generate CMakeLists.txt + local cmakefile = io.open(path.join(workdir, "CMakeLists.txt"), "w") + if cmake.version then + cmakefile:print("cmake_minimum_required(VERSION %s)", cmake.version) + end + cmakefile:print("project(find_package)") + + -- e.g. OpenCV 4.1.1, Boost COMPONENTS regex system + local requirestr = name + local configs = opt.configs or {} + if opt.required_version then + requirestr = requirestr .. " " .. opt.required_version + end + -- use opt.components is for backward compatibility + local components = configs.components or opt.components + if components then + requirestr = requirestr .. " COMPONENTS" + for _, component in ipairs(components) do + requirestr = requirestr .. " " .. component + end + end + local moduledirs = configs.moduledirs or opt.moduledirs + if moduledirs then + for _, moduledir in ipairs(moduledirs) do + cmakefile:print("add_cmake_modules(%s)", moduledir) + end + end + -- e.g. set(Boost_USE_STATIC_LIB ON) + local presets = configs.presets or opt.presets + if presets then + for k, v in pairs(presets) do + if type(v) == "boolean" then + cmakefile:print("set(%s %s)", k, v and "ON" or "OFF") + else + cmakefile:print("set(%s %s)", k, tostring(v)) + end + end + end + cmakefile:print("find_package(%s REQUIRED)", requirestr) + cmakefile:print("if(%s_FOUND)", name) + cmakefile:print(" add_executable(%s test.cpp)", name) + cmakefile:print(" target_include_directories(%s PRIVATE ${%s_INCLUDE_DIR} ${%s_INCLUDE_DIRS})", + name, name, name) + cmakefile:print(" target_include_directories(%s PRIVATE ${%s_INCLUDE_DIR} ${%s_INCLUDE_DIRS})", + name, name:upper(), name:upper()) + cmakefile:print(" target_include_directories(%s PRIVATE ${%s_CXX_INCLUDE_DIRS})", + name, name) + cmakefile:print(" target_link_libraries(%s ${%s_LIBRARY} ${%s_LIBRARIES} ${%s_LIBS})", + name, name, name, name) + cmakefile:print(" target_link_libraries(%s ${%s_LIBRARY} ${%s_LIBRARIES} ${%s_LIBS})", + name, name:upper(), name:upper(), name:upper()) + cmakefile:print("endif(%s_FOUND)", name) + cmakefile:close() + + -- run cmake + local envs = configs.envs or opt.envs + try {function() return os.vrunv(cmake.program, {workdir}, {curdir = workdir, envs = envs}) end} + + -- pares defines and includedirs for macosx/linux + local links + local linkdirs + local libfiles + local defines + local includedirs + local ldflags + local flagsfile = path.join(workdir, "CMakeFiles", name .. ".dir", "flags.make") + if os.isfile(flagsfile) then + local flagsdata = io.readfile(flagsfile) + if flagsdata then + if option.get("diagnosis") then + vprint(flagsdata) + end + for _, line in ipairs(flagsdata:split("\n", {plain = true})) do + if line:find("CXX_INCLUDES =", 1, true) then + local has_include = false + local flags = os.argv(line:split("=", {plain = true})[2]:trim()) + for _, flag in ipairs(flags) do + if has_include or (flag:startswith("-I") and #flag > 2) then + local includedir = has_include and flag or flag:sub(3) + if includedir and os.isdir(includedir) then + includedirs = includedirs or {} + table.insert(includedirs, includedir) + end + has_include = false + elseif flag == "-isystem" or flag == "-I" then + has_include = true + end + end + elseif line:find("CXX_DEFINES =", 1, true) then + local flags = os.argv(line:split("=", {plain = true})[2]:trim()) + for _, flag in ipairs(flags) do + if flag:startswith("-D") and #flag > 2 then + local define = flag:sub(3) + if define then + defines = defines or {} + table.insert(defines, define) + end + end + end + end + end + end + end + + -- parse links and linkdirs for macosx/linux + local linkfile = path.join(workdir, "CMakeFiles", name .. ".dir", "link.txt") + if os.isfile(linkfile) then + local linkdata = io.readfile(linkfile) + if linkdata then + if option.get("diagnosis") then + vprint(linkdata) + end + for _, line in ipairs(os.argv(linkdata)) do + local is_ldflags = false + local is_library = false + for _, suffix in ipairs({".so", ".dylib", ".dylib", ".tbd", ".lib"}) do + if line:startswith("-Wl,") then + is_ldflags = true + break + elseif line:find(suffix, 1, true) then + is_library = true + break + end + end + if is_ldflags then + ldflags = ldflags or {} + table.insert(ldflags, line) + elseif is_library then + -- strip library version suffix, e.g. libxxx.so.1.1 -> libxxx.so + if line:find(".so", 1, true) then + line = line:gsub("lib(.-)%.so%..+$", "lib%1.so") + end + + -- get libfiles + if os.isfile(line) then + libfiles = libfiles or {} + table.insert(libfiles, line) + end + + -- get links and linkdirs + local linkdir = path.directory(line) + if linkdir ~= "." then + linkdirs = linkdirs or {} + table.insert(linkdirs, linkdir) + end + local link = target.linkname(path.filename(line)) + if link then + links = links or {} + table.insert(links, link) + end + end + end + end + end + + -- pares includedirs and links/linkdirs for windows + local vcprojfile = path.join(workdir, name .. ".vcxproj") + if os.isfile(vcprojfile) then + local vcprojdata = io.readfile(vcprojfile) + if vcprojdata then + for _, line in ipairs(vcprojdata:split("\n", {plain = true})) do + local values = line:match("<AdditionalIncludeDirectories>(.+);%%%(AdditionalIncludeDirectories%)</AdditionalIncludeDirectories>") + if values then + includedirs = includedirs or {} + table.join2(includedirs, path.splitenv(values)) + end + + values = line:match("<AdditionalDependencies>(.+)</AdditionalDependencies>") + if not values then + -- we need also parse libraries from here + -- https://github.com/xmake-io/xmake/issues/1822 + values = line:match("<ImportLibrary>(.+)</ImportLibrary>") + end + if values then + for _, library in ipairs(path.splitenv(values)) do + -- get libfiles + if os.isfile(library) then + libfiles = libfiles or {} + table.insert(libfiles, library) + end + + -- get links and linkdirs + local linkdir = path.directory(library) + linkdir = path.translate(linkdir) + if linkdir ~= "." and not linkdir:startswith(workdir) then + linkdirs = linkdirs or {} + table.insert(linkdirs, linkdir) + local link = target.linkname(path.filename(library)) + if link then + links = links or {} + table.insert(links, link) + end + end + end + end + end + end + end + + -- remove work directory + os.tryrm(workdir) + + -- get results + if links or includedirs then + local results = {} + results.links = table.reverse_unique(links) + results.ldflags = table.reverse_unique(ldflags) + results.linkdirs = table.unique(linkdirs) + results.defines = table.unique(defines) + results.libfiles = table.unique(libfiles) + results.includedirs = table.unique(includedirs) + print(results) + return results + end +end + +-- find package using the cmake package manager +-- +-- e.g. +-- +-- find_package("cmake::ZLIB") +-- find_package("cmake::OpenCV", {required_version = "4.1.1"}) +-- find_package("cmake::Boost", {configs = {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}}) +-- find_package("cmake::Foo", {configs = {moduledirs = "xxx"}}) +-- +-- we can use add_requires with {system = true} +-- +-- add_requires("cmake::ZLIB", {system = true}) +-- add_requires("cmake::OpenCV 4.1.1", {system = true}) +-- add_requires("cmake::Boost", {configs = {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}}) +-- add_requires("cmake::Foo", {configs = {moduledirs = "xxx"}}) +-- +-- @param name the package name +-- @param opt the options, e.g. {verbose = true, required_version = "1.0", +-- configs = { +-- components = {"regex", "system"}, +-- moduledirs = "xxx", +-- presets = {Boost_USE_STATIC_LIB = true}, +-- envs = {CMAKE_PREFIX_PATH = "xxx"}}) +-- +function main(name, opt) + opt = opt or {} + local cmake = find_tool("cmake", {version = true}) + if not cmake then + return + end + return _find_package(cmake, name, opt) +end diff --git a/xmake/modules/package/manager/conan/configurations.lua b/xmake/modules/package/manager/conan/configurations.lua new file mode 100644 index 000000000..b4ed38fc3 --- /dev/null +++ b/xmake/modules/package/manager/conan/configurations.lua @@ -0,0 +1,33 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file configurations.lua +-- + +-- get configurations +function main() + return + { + build = {description = "Use it to choose if you want to build from sources.", default = "missing", values = {"all", "never", "missing", "outdated"}}, + remote = {description = "Set the conan remote server."}, + options = {description = "Set the options values, e.g. OpenSSL:shared=True"}, + imports = {description = "Set the imports for conan."}, + settings = {description = "Set the build settings for conan."}, + build_requires = {description = "Set the build requires for conan.", default = "xmake_generator/0.1.0@bincrafters/testing"} + } +end + diff --git a/xmake/modules/package/manager/conan/find_package.lua b/xmake/modules/package/manager/conan/find_package.lua index d372e2a75..fca3d3891 100644 --- a/xmake/modules/package/manager/conan/find_package.lua +++ b/xmake/modules/package/manager/conan/find_package.lua @@ -59,7 +59,7 @@ end -- find package using the conan package manager -- -- @param name the package name --- @param opt the options, e.g. {verbose = true, version = "1.12.x") +-- @param opt the options, e.g. {verbose = true) -- function main(name, opt) diff --git a/xmake/modules/package/manager/conan/install_package.lua b/xmake/modules/package/manager/conan/install_package.lua index 97af3b4b8..9f5a52a18 100644 --- a/xmake/modules/package/manager/conan/install_package.lua +++ b/xmake/modules/package/manager/conan/install_package.lua @@ -49,15 +49,15 @@ function _conan_get_build_directory(name) end -- generate conanfile.txt -function _conan_generate_conanfile(name, opt) +function _conan_generate_conanfile(name, configs) -- trace dprint("generate %s ..", path.join(_conan_get_build_directory(name), "conanfile.txt")) -- get conan options, imports and build_requires - local options = table.wrap(opt.options) - local imports = table.wrap(opt.imports) - local build_requires = table.wrap(opt.build_requires) + local options = table.wrap(configs.options) + local imports = table.wrap(configs.imports) + local build_requires = table.wrap(configs.build_requires) -- @see https://docs.conan.io/en/latest/systems_cross_building/cross_building.html -- generate it @@ -109,30 +109,22 @@ function _conan_install_xmake_generator(conan) end end --- get configurations -function configurations() - return - { - build = {description = "use it to choose if you want to build from sources.", default = "missing", values = {"all", "never", "missing", "outdated"}}, - remote = {description = "Set the conan remote server."}, - options = {description = "Set the options values, e.g. OpenSSL:shared=True"}, - imports = {description = "Set the imports for conan."}, - settings = {description = "Set the build settings for conan."}, - build_requires = {description = "Set the build requires for conan.", default = "xmake_generator/0.1.0@bincrafters/testing"} - } -end - -- install package -- -- @param name the package name, e.g. conan::OpenSSL/1.0.2n@conan/stable -- @param opt the options, e.g. { verbose = true, mode = "release", plat = , arch = , --- remote = "", build = "all", options = {}, imports = {}, build_requires = {}, --- settings = {"compiler=Visual Studio", "compiler.version=10", "compiler.runtime=MD"}} +-- configs = { +-- remote = "", build = "all", options = {}, imports = {}, build_requires = {}, +-- settings = {"compiler=Visual Studio", "compiler.version=10", "compiler.runtime=MD"}}} -- -- @return true or false -- function main(name, opt) + -- get configs + opt = opt or {} + local configs = opt.configs or {} + -- find conan local conan = find_tool("conan") if not conan then @@ -155,15 +147,15 @@ function main(name, opt) _conan_install_xmake_generator(conan) -- generate conanfile.txt - _conan_generate_conanfile(name, opt) + _conan_generate_conanfile(name, configs) -- install package local argv = {"install", "."} - if opt.build then - if opt.build == "all" then + if configs.build then + if configs.build == "all" then table.insert(argv, "--build") else - table.insert(argv, "--build=" .. opt.build) + table.insert(argv, "--build=" .. configs.build) end end @@ -241,15 +233,15 @@ function main(name, opt) end -- set custom settings - for _, setting in ipairs(opt.settings) do + for _, setting in ipairs(configs.settings) do table.insert(argv, "-s") table.insert(argv, setting) end -- set remote - if opt.remote then + if configs.remote then table.insert(argv, "-r") - table.insert(argv, opt.remote) + table.insert(argv, configs.remote) end -- TODO set environments diff --git a/xmake/modules/package/manager/conda/find_package.lua b/xmake/modules/package/manager/conda/find_package.lua index 5d2634d63..1e09072d4 100644 --- a/xmake/modules/package/manager/conda/find_package.lua +++ b/xmake/modules/package/manager/conda/find_package.lua @@ -52,7 +52,7 @@ end -- find package using the conda package manager -- -- @param name the package name --- @param opt the options, e.g. {verbose = true, version = "1.12.0") +-- @param opt the options, e.g. {verbose = true, require_version = "1.12.0") -- function main(name, opt) @@ -124,7 +124,7 @@ function main(name, opt) result.linkdirs = result.linkdirs or {} result.libfiles = result.libfiles or {} table.insert(result.linkdirs, path.join(packagedir, path.directory(line))) - table.insert(result.links, target.linkname(path.filename(line))) + table.insert(result.links, target.linkname(path.filename(line), {plat = opt.plat})) table.insert(result.libfiles, path.join(packagedir, path.directory(line), path.filename(line))) end diff --git a/xmake/modules/package/manager/dub/find_package.lua b/xmake/modules/package/manager/dub/find_package.lua index 55ad2d6ef..5d49d695e 100644 --- a/xmake/modules/package/manager/dub/find_package.lua +++ b/xmake/modules/package/manager/dub/find_package.lua @@ -30,7 +30,7 @@ import("lib.detect.find_file") -- find package using the dub package manager -- -- @param name the package name --- @param opt the options, e.g. {verbose = true, version = "1.12.x") +-- @param opt the options, e.g. {verbose = true, require_version = "1.12.x") -- function main(name, opt) @@ -67,7 +67,7 @@ function main(name, opt) if pkgdir then local links = {} for _, libraryfile in ipairs(os.files(path.join(pkgdir, libpattern))) do - table.insert(links, target.linkname(path.filename(libraryfile))) + table.insert(links, target.linkname(path.filename(libraryfile), {plat = opt.plat})) end local includedirs = {} local dubjson = path.join(pkgdir, "dub.json") diff --git a/xmake/modules/package/manager/dub/install_package.lua b/xmake/modules/package/manager/dub/install_package.lua index 84406af5f..d459bfd92 100644 --- a/xmake/modules/package/manager/dub/install_package.lua +++ b/xmake/modules/package/manager/dub/install_package.lua @@ -26,7 +26,7 @@ import("lib.detect.find_tool") -- install package -- -- @param name the package name, e.g. dub::log --- @param opt the options, e.g. { verbose = true, mode = "release", plat = , arch = , require_version = "x.x.x", buildhash = "xxxxxx"} +-- @param opt the options, e.g. { verbose = true, mode = "release", plat = , arch = , require_version = "x.x.x"} -- -- @return true or false -- diff --git a/xmake/modules/package/manager/find_package.lua b/xmake/modules/package/manager/find_package.lua index 41c110796..7d82043b2 100644 --- a/xmake/modules/package/manager/find_package.lua +++ b/xmake/modules/package/manager/find_package.lua @@ -174,7 +174,7 @@ function main(name, opt) opt.mode = opt.mode or config.mode() or "release" -- get package manager name - local manager_name, package_name = unpack(name:split("::", {plain = true, strict = true})) + local manager_name, package_name = table.unpack(name:split("::", {plain = true, strict = true})) if package_name == nil then package_name = manager_name manager_name = nil @@ -184,7 +184,7 @@ function main(name, opt) -- get package name and require version local require_version = nil - package_name, require_version = unpack(package_name:trim():split("%s")) + package_name, require_version = table.unpack(package_name:trim():split("%s")) opt.require_version = require_version or opt.require_version -- find package diff --git a/xmake/modules/package/manager/install_package.lua b/xmake/modules/package/manager/install_package.lua index 1c5d5ab07..05e13ed27 100644 --- a/xmake/modules/package/manager/install_package.lua +++ b/xmake/modules/package/manager/install_package.lua @@ -93,7 +93,7 @@ function main(name, opt) opt.mode = opt.mode or config.mode() or "release" -- get package manager name - local manager_name, package_name = unpack(name:split("::", {plain = true, strict = true})) + local manager_name, package_name = table.unpack(name:split("::", {plain = true, strict = true})) if package_name == nil then package_name = manager_name manager_name = nil @@ -103,7 +103,7 @@ function main(name, opt) -- get package name and require version local require_version = nil - package_name, require_version = unpack(package_name:trim():split("%s")) + package_name, require_version = table.unpack(package_name:trim():split("%s")) opt.require_version = require_version or opt.require_version -- do install package diff --git a/xmake/modules/package/manager/nimble/find_package.lua b/xmake/modules/package/manager/nimble/find_package.lua new file mode 100644 index 000000000..c39d6ed34 --- /dev/null +++ b/xmake/modules/package/manager/nimble/find_package.lua @@ -0,0 +1,67 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file find_package.lua +-- + +-- imports +import("core.base.option") +import("core.base.semver") +import("core.project.config") +import("core.project.target") +import("lib.detect.find_tool") +import("lib.detect.find_file") + +-- find package using the nimble package manager +-- +-- @param name the package name +-- @param opt the options, e.g. {verbose = true, require_version = "1.12.x") +-- +function main(name, opt) + + -- find nimble + local nimble = find_tool("nimble") + if not nimble then + raise("nimble not found!") + end + + -- find it from all installed package list + local result + local list = os.iorunv(nimble.program, {"list", "-i"}) + for _, line in ipairs(list:split("\n", {plain = true})) do + local splitinfo = line:split("%s+") + local package_name = splitinfo[1] + local package_version = splitinfo[2] + if package_name == name and package_version then + if package_version then + package_version = package_version:match("%[(.+)%]") + end + if opt.require_version then + if package_version and (opt.require_version == "latest" or semver.match(package_version, 1, opt.require_version)) then + result = {version = package_version} + break + end + else + result = {} + break + end + end + end + -- @note we need not return links and includedirs information, + -- because it's nim source code package and nim will find them automatically + return result +end diff --git a/xmake/modules/package/manager/nimble/install_package.lua b/xmake/modules/package/manager/nimble/install_package.lua new file mode 100644 index 000000000..326758e8c --- /dev/null +++ b/xmake/modules/package/manager/nimble/install_package.lua @@ -0,0 +1,58 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file install_package.lua +-- + +-- imports +import("core.base.option") +import("core.project.config") +import("lib.detect.find_tool") + +-- install package +-- +-- e.g. +-- add_requires("nimble::zip") +-- add_requires("nimble::zip >0.3") +-- add_requires("nimble::zip 0.3.1") +-- +-- @param name the package name, e.g. nimble::zip +-- @param opt the options, e.g. { verbose = true, mode = "release", plat = , arch = , require_version = "x.x.x"} +-- +-- @return true or false +-- +function main(name, opt) + + -- find nimble + local nimble = find_tool("nimble") + if not nimble then + raise("nimble not found!") + end + + -- install the given package + local argv = {"install", "-y"} + if option.get("verbose") then + table.insert(argv, "--verbose") + end + local require_str = name + if opt.require_version and opt.require_version ~= "latest" and opt.require_version ~= "master" then + name = name .. "@" + name = name .. opt.require_version + end + table.insert(argv, name) + os.vrunv(nimble.program, argv) +end diff --git a/xmake/modules/package/manager/pacman/find_package.lua b/xmake/modules/package/manager/pacman/find_package.lua index 4b2be0403..c47edaad6 100644 --- a/xmake/modules/package/manager/pacman/find_package.lua +++ b/xmake/modules/package/manager/pacman/find_package.lua @@ -20,9 +20,74 @@ -- imports import("core.base.option") +import("core.project.target") import("lib.detect.find_tool") import("package.manager.pkgconfig.find_package", {alias = "find_package_from_pkgconfig"}) +-- get result from list of file inside pacman package +function _find_package_from_list(list, name, pacman, opt) + + -- mingw + pacman = cygpath available + local cygpath = nil + if is_subhost("msys") and opt.plat == "mingw" then + cygpath = find_tool("cygpath") + if not cygpath then + return + end + end + + -- iterate over each file path inside the pacman package + local result = {includedirs = {}, linkdirs = {}, links = {}} + for _, line in ipairs(list:split('\n', {plain = true})) do -- on msys cygpath should be used to convert local path to windows path + line = line:trim():split('%s+')[2] + if line:find("/include/", 1, true) and (line:endswith(".h") or line:endswith(".hpp")) then + local hpath = line + if is_subhost("msys") and opt.plat == "mingw" then + hpath = os.iorunv(cygpath.program, {"--windows", line}) + + if opt.arch == "x86_64" then + local basehpath = os.iorunv(cygpath.program, {"--windows", "/mingw64/include"}) + table.insert(result.includedirs, basehpath) + else + local basehpath = os.iorunv(cygpath.program, {"--windows", "/mingw32/include"}) + table.insert(result.includedirs, basehpath) + end + end + table.insert(result.includedirs, path.directory(hpath)) + -- remove lib and .a, .dll.a and .so to have the links + elseif line:endswith(".dll.a") then -- only for mingw + local apath = os.iorunv(cygpath.program, {"--windows", line}) + apath = apath:trim() + table.insert(result.linkdirs, path.directory(apath)) + table.insert(result.links, target.linkname(path.filename(apath), {plat = opt.plat})) + elseif line:endswith(".so") then + table.insert(result.linkdirs, path.directory(line)) + table.insert(result.links, target.linkname(path.filename(line), {plat = opt.plat})) + elseif line:endswith(".a") then + local apath = line + if is_subhost("msys") and opt.plat == "mingw" then + apath = os.iorunv(cygpath.program, {"--windows", line}) + apath = apath:trim() + end + table.insert(result.linkdirs, path.directory(apath)) + table.insert(result.links, target.linkname(path.filename(apath), {plat = opt.plat})) + end + end + result.includedirs = table.unique(result.includedirs) + result.linkdirs = table.unique(result.linkdirs) + result.links = table.reverse_unique(result.links) + + -- use pacman package version as version + local version = try { function() return os.iorunv(pacman.program, {"-Q", name}) end } + if version then + version = version:trim():split('%s+')[2] + result.version = version:split('-')[1] + else + result = nil + end + return result +end + -- find package from the system directories -- -- @param name the package name @@ -30,17 +95,15 @@ import("package.manager.pkgconfig.find_package", {alias = "find_package_from_pkg -- function main(name, opt) - -- init options - opt = opt or {} - -- find pacman + opt = opt or {} local pacman = find_tool("pacman") if not pacman then return end -- for msys2/mingw? mingw-w64-[i686|x86_64]-xxx - if opt.plat == "mingw" then + if is_subhost("msys") and opt.plat == "mingw" then name = (opt.arch == "x86_64" and "mingw-w64-x86_64-" or "mingw-w64-i686-") .. name end @@ -52,41 +115,50 @@ function main(name, opt) -- parse package files list local linkdirs = {} - local has_includes = false local pkgconfig_files = {} for _, line in ipairs(list:split('\n', {plain = true})) do line = line:trim():split('%s+')[2] if line:find("/pkgconfig/", 1, true) and line:endswith(".pc") then - pkgconfig_files[path.basename(line)] = line + table.insert(pkgconfig_files, line) end if line:endswith(".so") or line:endswith(".a") or line:endswith(".lib") then table.insert(linkdirs, path.directory(line)) - elseif line:find("/include/", 1, true) and (line:endswith(".h") or line:endswith(".hpp")) then - has_includes = true end end + linkdirs = table.unique(linkdirs) - -- get pkgconfig file - local pkgconfig_file = pkgconfig_files[name] - if not pkgconfig_file then - for _, file in pairs(pkgconfig_files) do - pkgconfig_file = file - break - end - end - - -- find package - local result = nil - if pkgconfig_file then + -- we iterate over each pkgconfig file to extract the required data + local foundpc = false + local result = {includedirs = {}, linkdirs = {}, links = {}} + for _, pkgconfig_file in ipairs(pkgconfig_files) do local pkgconfig_dir = path.directory(pkgconfig_file) local pkgconfig_name = path.basename(pkgconfig_file) - linkdirs = table.unique(linkdirs) - includedirs = table.unique(includedirs) - result = find_package_from_pkgconfig(pkgconfig_name, {configdirs = pkgconfig_dir, linkdirs = linkdirs}) - if not result and has_includes then - -- header only and hidden /usr/include? we need only return empty {} - result = {} + local pcresult = find_package_from_pkgconfig(pkgconfig_name, {configdirs = pkgconfig_dir, linkdirs = linkdirs}) + + -- the pkgconfig file has been parse successfully + if pcresult then + for _, includedir in ipairs(pcresult.includedirs) do + table.insert(result.includedirs, includedir) + end + for _, linkdir in ipairs(pcresult.linkdirs) do + table.insert(result.linkdirs, linkdir) + end + for _, link in ipairs(pcresult.links) do + table.insert(result.links, link) + end + -- version should be the same if a pacman package contains multiples .pc + result.version = pcresult.version + foundpc = true end end + + if foundpc == true then + result.includedirs = table.unique(result.includedirs) + result.linkdirs = table.unique(result.linkdirs) + result.links = table.reverse_unique(result.links) + else + -- if there is no .pc, we parse the package content to obtain the data we want + result = _find_package_from_list(list, name, pacman, opt) + end return result end diff --git a/xmake/modules/package/manager/pkgconfig/find_package.lua b/xmake/modules/package/manager/pkgconfig/find_package.lua index a7dbf377a..ddc7efeb4 100644 --- a/xmake/modules/package/manager/pkgconfig/find_package.lua +++ b/xmake/modules/package/manager/pkgconfig/find_package.lua @@ -20,7 +20,6 @@ -- imports import("lib.detect.pkgconfig") -import("lib.detect.find_library") import("package.manager.system.find_package", {alias = "find_package_from_system"}) -- find package from the pkg-config package manager diff --git a/xmake/modules/package/manager/system/find_package.lua b/xmake/modules/package/manager/system/find_package.lua index 9bb04566d..1e0d58c77 100644 --- a/xmake/modules/package/manager/system/find_package.lua +++ b/xmake/modules/package/manager/system/find_package.lua @@ -26,41 +26,13 @@ import("lib.detect.pkgconfig") import("detect.sdks.find_xcode") import("core.project.config") --- find package from the unix-like system directories -function _find_package_from_unixdirs(name, links, opt) - - -- add default search includedirs on pc host - local includedirs = table.wrap(opt.includedirs) - if #includedirs == 0 then - if opt.plat == "linux" or opt.plat == "macosx" then - table.insert(includedirs, "/usr/local/include") - table.insert(includedirs, "/usr/include") - table.insert(includedirs, "/opt/local/include") - table.insert(includedirs, "/opt/include") - end - end - - -- add default search linkdirs on pc host - local linkdirs = table.wrap(opt.linkdirs) - if #linkdirs == 0 then - if opt.plat == "linux" or opt.plat == "macosx" then - table.insert(linkdirs, "/usr/local/lib") - table.insert(linkdirs, "/usr/lib") - table.insert(linkdirs, "/opt/local/lib") - table.insert(linkdirs, "/opt/lib") - if opt.plat == "linux" and opt.arch == "x86_64" then - table.insert(linkdirs, "/usr/local/lib/x86_64-linux-gnu") - table.insert(linkdirs, "/usr/lib/x86_64-linux-gnu") - table.insert(linkdirs, "/usr/lib64") - table.insert(linkdirs, "/opt/lib64") - end - end - end +-- find package +function _find_package(name, links, linkdirs, includedirs, opt) -- find library local result = nil for _, link in ipairs(links) do - local libinfo = find_library(link, linkdirs) + local libinfo = find_library(link, linkdirs, {plat = opt.plat}) if libinfo then result = result or {} result.links = table.join(result.links or {}, libinfo.link) @@ -91,6 +63,68 @@ function _find_package_from_unixdirs(name, links, opt) return result end +-- find package from the environment variables +-- @see https://github.com/xmake-io/xmake/issues/1776 +-- +function _find_package_from_envs(name, links, opt) + + -- add default search includedirs on pc host + local includedirs = table.wrap(opt.includedirs) + if #includedirs == 0 then + if opt.plat == "windows" then + table.insert(includedirs, "$(env INCLUDE)") + else + table.insert(includedirs, "$(env CPATH)") + table.insert(includedirs, "$(env C_INCLUDE_PATH)") + table.insert(includedirs, "$(env CPLUS_INCLUDE_PATH)") + end + end + + -- add default search linkdirs on pc host + local linkdirs = table.wrap(opt.linkdirs) + if #linkdirs == 0 then + if opt.plat == "windows" then + table.insert(linkdirs, "$(env LIB)") + else + table.insert(linkdirs, "$(env LIBRARY_PATH)") + end + end + return _find_package(name, links, linkdirs, includedirs, opt) +end + +-- find package from the unix-like system directories +function _find_package_from_unixdirs(name, links, opt) + + -- add default search includedirs on pc host + local includedirs = table.wrap(opt.includedirs) + if #includedirs == 0 then + if opt.plat == "linux" or opt.plat == "macosx" then + table.insert(includedirs, "/usr/local/include") + table.insert(includedirs, "/usr/include") + table.insert(includedirs, "/opt/local/include") + table.insert(includedirs, "/opt/include") + end + end + + -- add default search linkdirs on pc host + local linkdirs = table.wrap(opt.linkdirs) + if #linkdirs == 0 then + if opt.plat == "linux" or opt.plat == "macosx" then + table.insert(linkdirs, "/usr/local/lib") + table.insert(linkdirs, "/usr/lib") + table.insert(linkdirs, "/opt/local/lib") + table.insert(linkdirs, "/opt/lib") + if opt.plat == "linux" and opt.arch == "x86_64" then + table.insert(linkdirs, "/usr/local/lib/x86_64-linux-gnu") + table.insert(linkdirs, "/usr/lib/x86_64-linux-gnu") + table.insert(linkdirs, "/usr/lib64") + table.insert(linkdirs, "/opt/lib64") + end + end + end + return _find_package(name, links, linkdirs, includedirs, opt) +end + -- find package from the xcode directories function _find_package_from_xcodedirs(name, links, opt) @@ -166,6 +200,7 @@ function main(name, opt) if opt.plat ~= "windows" then table.insert(finders, _find_package_from_unixdirs) end + table.insert(finders, _find_package_from_envs) end if opt.plat == "macosx" or opt.plat == "iphoneos" or opt.plat == "watchos" then table.insert(finders, _find_package_from_xcodedirs) diff --git a/xmake/modules/package/manager/vcpkg/configurations.lua b/xmake/modules/package/manager/vcpkg/configurations.lua new file mode 100644 index 000000000..9021edff0 --- /dev/null +++ b/xmake/modules/package/manager/vcpkg/configurations.lua @@ -0,0 +1,48 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file configurations.lua +-- + +-- get architecture for vcpkg +function arch(arch) + local archs = { + x86_64 = "x64", + i386 = "x86", + + -- android: armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mip64 + -- Offers a doc: https://github.com/microsoft/vcpkg/blob/master/docs/users/android.md + ["armeabi-v7a"] = "arm", + ["arm64-v8a"] = "arm64", + + -- ios: arm64 armv7 armv7s i386 + armv7 = "arm", + armv7s = "arm", + arm64 = "arm64", + } + return archs[arch] or arch +end + +-- get configurations +function main() + return { + baseline = {description = "set the builtin baseline."}, + features = {description = "set the features of dependency."}, + default_features = {description = "enables or disables any defaults provided by the dependency.", default = true} + } +end + diff --git a/xmake/modules/package/manager/vcpkg/find_package.lua b/xmake/modules/package/manager/vcpkg/find_package.lua index e965ace73..e61e56c29 100644 --- a/xmake/modules/package/manager/vcpkg/find_package.lua +++ b/xmake/modules/package/manager/vcpkg/find_package.lua @@ -20,29 +20,51 @@ -- imports import("lib.detect.find_file") -import("lib.detect.find_library") import("lib.detect.find_tool") import("core.base.option") import("core.project.config") import("core.project.target") import("detect.sdks.find_vcpkgdir") +import("package.manager.vcpkg.configurations") +import("package.manager.pkgconfig.find_package", {alias = "find_package_from_pkgconfig"}) --- find package from the vcpkg package manager --- --- @param name the package name, e.g. zlib, pcre --- @param opt the options, e.g. {verbose = true, version = "1.12.x") --- -function main(name, opt) +-- we iterate over each pkgconfig file to extract the required data +function _find_package_from_pkgconfig(pkgconfig_files, opt) + opt = opt or {} + local foundpc = false + local result = {includedirs = {}, linkdirs = {}, links = {}} + for _, pkgconfig_file in ipairs(pkgconfig_files) do + local pkgconfig_dir = path.join(opt.installdir, path.directory(pkgconfig_file)) + local pkgconfig_name = path.basename(pkgconfig_file) + local pcresult = find_package_from_pkgconfig(pkgconfig_name, {configdirs = pkgconfig_dir, linkdirs = opt.linkdirs}) - -- attempt to find vcpkg directory - local vcpkgdir = find_vcpkgdir() - if not vcpkgdir then - if option.get("diagnosis") then - cprint("${color.warning}checkinfo: ${clear dim}vcpkg root directory not found, maybe you need set $VCPKG_ROOT!") + -- the pkgconfig file has been parse successfully + if pcresult then + for _, includedir in ipairs(pcresult.includedirs) do + table.insert(result.includedirs, includedir) + end + for _, linkdir in ipairs(pcresult.linkdirs) do + table.insert(result.linkdirs, linkdir) + end + for _, link in ipairs(pcresult.links) do + table.insert(result.links, link) + end + -- version should be the same if a pacman package contains multiples .pc + result.version = pcresult.version + foundpc = true end - return end + if foundpc == true then + result.includedirs = table.unique(result.includedirs) + result.linkdirs = table.unique(result.linkdirs) + result.links = table.reverse_unique(result.links) + return result + end +end + +function _find_package(vcpkgdir, name, opt) + -- fix name, e.g. ffmpeg[x264] as ffmpeg -- @see https://github.com/xmake-io/xmake/issues/925 name = name:gsub("%[.-%]", "") @@ -51,50 +73,36 @@ function main(name, opt) local arch = opt.arch local plat = opt.plat local mode = opt.mode - - -- mapping plat if plat == "macosx" then plat = "osx" end + arch = configurations.arch(arch) - -- archs mapping for vcpkg - local archs = { - x86_64 = "x64", - i386 = "x86", - - -- android: armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mip64 - -- Offers a doc: https://github.com/microsoft/vcpkg/blob/master/docs/users/android.md - ["armeabi-v7a"] = "arm", - ["arm64-v8a"] = "arm64", - - -- ios: arm64 armv7 armv7s i386 - armv7 = "arm", - armv7s = "arm", - arm64 = "arm64", + -- get the vcpkg info directories + local infodirs = { + path.join(opt.installdir, "vcpkg_installed", "vcpkg", "info"), + path.join(vcpkgdir, "installed", "vcpkg", "info") } - -- mapping arch - arch = archs[arch] or arch - - -- get the vcpkg installed directory - local installdir = path.join(vcpkgdir, "installed") - - -- get the vcpkg info directory - local infodir = path.join(installdir, "vcpkg", "info") -- find the package info file, e.g. zlib_1.2.11-3_x86-windows[-static].list local triplet = arch .. "-" .. plat - local pkgconfigs = opt.pkgconfigs - if plat == "windows" and pkgconfigs and pkgconfigs.shared ~= true then + local configs = opt.configs or {} + if plat == "windows" and configs.shared ~= true then triplet = triplet .. "-static" - if pkgconfigs.vs_runtime and pkgconfigs.vs_runtime:startswith("MD") then + if configs.vs_runtime and configs.vs_runtime:startswith("MD") then triplet = triplet .. "-md" end end - local infofile = find_file(format("%s_*_%s.list", name, triplet), infodir) + local infofile = find_file(format("%s_*_%s.list", name, triplet), infodirs) + if not infofile then + return + end + local installdir = path.directory(path.directory(path.directory(infofile))) -- save includedirs, linkdirs and links local result = nil - local info = infofile and io.readfile(infofile) or nil + local pkgconfig_files = {} + local info = io.readfile(infofile) if info then for _, line in ipairs(info:split('\n')) do line = line:trim() @@ -102,6 +110,11 @@ function main(name, opt) line = line:lower() end + -- get pkgconfig files + if line:find(triplet .. (mode == "debug" and "/debug" or "") .. "/lib/pkgconfig/", 1, true) and line:endswith(".pc") then + table.insert(pkgconfig_files, line) + end + -- get includedirs if line:endswith("/include/") then result = result or {} @@ -110,14 +123,14 @@ function main(name, opt) end -- get linkdirs and links - if (plat == "windows" and line:endswith(".lib")) or line:endswith(".a") then + if (plat == "windows" and line:endswith(".lib")) or line:endswith(".a") or line:endswith(".so") then if line:find(triplet .. (mode == "debug" and "/debug" or "") .. "/lib/", 1, true) then result = result or {} result.links = result.links or {} result.linkdirs = result.linkdirs or {} result.libfiles = result.libfiles or {} table.insert(result.linkdirs, path.join(installdir, path.directory(line))) - table.insert(result.links, target.linkname(path.filename(line))) + table.insert(result.links, target.linkname(path.filename(line), {plat = plat})) table.insert(result.libfiles, path.join(installdir, path.directory(line), path.filename(line))) end end @@ -135,8 +148,16 @@ function main(name, opt) end end + -- find result from pkgconfig first + if #pkgconfig_files > 0 then + local pkgconfig_result = _find_package_from_pkgconfig(pkgconfig_files, {installdir = installdir, linkdirs = result and result.linkdirs}) + if pkgconfig_result then + result = pkgconfig_result + end + end + -- save version - if result and infofile then + if result then local infoname = path.basename(infofile) result.version = infoname:match(name .. "_(%d+%.?%d*%.?%d*.-)_" .. arch) if not result.version then @@ -156,3 +177,22 @@ function main(name, opt) return result end +-- find package from the vcpkg package manager +-- +-- @param name the package name, e.g. zlib, pcre +-- @param opt the options, e.g. {verbose = true) +-- +function main(name, opt) + + -- attempt to find vcpkg directory + local vcpkgdir = find_vcpkgdir() + if not vcpkgdir then + if option.get("diagnosis") then + cprint("${color.warning}checkinfo: ${clear dim}vcpkg root directory not found, maybe you need set $VCPKG_ROOT!") + end + return + end + + -- do find package + return _find_package(vcpkgdir, name, opt) +end diff --git a/xmake/modules/package/manager/vcpkg/install_package.lua b/xmake/modules/package/manager/vcpkg/install_package.lua index eaf28710e..962c72f83 100644 --- a/xmake/modules/package/manager/vcpkg/install_package.lua +++ b/xmake/modules/package/manager/vcpkg/install_package.lua @@ -20,52 +20,67 @@ -- imports import("core.base.option") +import("core.base.json") +import("core.base.semver") import("lib.detect.find_tool") +import("package.manager.vcpkg.configurations") --- install package --- --- @param name the package name, e.g. pcre2, pcre2/libpcre2-8 --- @param opt the options, e.g. {verbose = true} --- --- @return true or false --- -function main(name, opt) - - -- attempt to find vcpkg - local vcpkg = find_tool("vcpkg") - if not vcpkg then - raise("vcpkg not found!") +-- need manifest mode? +function _need_manifest(opt) + local require_version = opt.require_version + if require_version ~= nil and require_version ~= "latest" then + return true + end + local configs = opt.configs + if configs and (configs.features or configs.default_features == false or configs.baseline) then + return true end +end + +-- install for classic mode +function _install_for_classic(vcpkg, name, opt) -- get arch, plat and mode local arch = opt.arch local plat = opt.plat local mode = opt.mode - - -- mapping plat if plat == "macosx" then plat = "osx" end + arch = configurations.arch(arch) - -- archs mapping for vcpkg - local archs = { - x86_64 = "x64", - i386 = "x86", + -- init triplet + local triplet = arch .. "-" .. plat + if opt.plat == "windows" and opt.shared ~= true then + triplet = triplet .. "-static" + if opt.vs_runtime and opt.vs_runtime:startswith("MD") then + triplet = triplet .. "-md" + end + end - -- android: armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mip64 - -- Offers a doc: https://github.com/microsoft/vcpkg/blob/master/docs/users/android.md - ["armeabi-v7a"] = "arm", - ["arm64-v8a"] = "arm64", + -- init argv + local argv = {"install", name .. ":" .. triplet} + if option.get("diagnosis") then + table.insert(argv, "--debug") + end + + -- install package + os.vrunv(vcpkg, argv) +end - -- ios: arm64 armv7 armv7s i386 - armv7 = "arm", - armv7s = "arm", - arm64 = "arm64", - } - -- mapping arch - arch = archs[arch] or arch +-- install for manifest mode +function _install_for_manifest(vcpkg, name, opt) + + -- get configs + local configs = opt.configs or {} -- init triplet + local arch = opt.arch + local plat = opt.plat + if plat == "macosx" then + plat = "osx" + end + arch = configurations.arch(arch) local triplet = arch .. "-" .. plat if opt.plat == "windows" and opt.shared ~= true then triplet = triplet .. "-static" @@ -75,11 +90,83 @@ function main(name, opt) end -- init argv - local argv = {"install", name .. ":" .. triplet} + local argv = {"--feature-flags=\"versions\"", "install", "--x-wait-for-lock", "--triplet", triplet} if option.get("diagnosis") then table.insert(argv, "--debug") end + -- generate platform + local platform = plat .. " & " .. arch + + -- generate dependencies + local require_version = opt.require_version + if require_version == "latest" then + require_version = nil + end + -- 1.2.11+13 -> 1.2.11#13 + if require_version then + require_version = require_version:gsub("%+", "#") + end + local minversion = require_version + if minversion and minversion:startswith(">=") then + minversion = minversion:sub(3) + end + local dependencies = {} + table.insert(dependencies, { + name = name, + ["version>="] = minversion, + platform = platform, + features = configs.features, + ["default-features"] = configs.default_features}) + + -- generate overrides to use fixed version + local overrides + if require_version and semver.is_valid(require_version) then + overrides = {{name = name, version = require_version}} + end + + -- generate manifest + local baseline = configs.baseline or "44d94c2edbd44f0c01d66c2ad95eb6982a9a61bc" -- 2021.04.30 + local manifest = { + name = "stub", + version = "1.0", + dependencies = dependencies, + ["builtin-baseline"] = baseline, + overrides = overrides} + local installdir = assert(opt.installdir, "installdir not found!") + json.savefile(path.join(installdir, "vcpkg.json"), manifest) + if not os.isdir(installdir) then + os.mkdir(installdir) + end + if option.get("diagnosis") then + vprint(path.join(installdir, "vcpkg.json")) + vprint(manifest) + end + -- install package - os.vrunv(vcpkg.program, argv) + os.vrunv(vcpkg, argv, {curdir = installdir}) +end + +-- install package +-- +-- @param name the package name, e.g. pcre2, pcre2/libpcre2-8 +-- @param opt the options, e.g. {verbose = true} +-- +-- @return true or false +-- +function main(name, opt) + + -- attempt to find vcpkg + local vcpkg = find_tool("vcpkg") + if not vcpkg then + raise("vcpkg not found!") + end + + -- do install + opt = opt or {} + if _need_manifest(opt) then + _install_for_manifest(vcpkg.program, name, opt) + else + _install_for_classic(vcpkg.program, name, opt) + end end diff --git a/xmake/modules/package/manager/xmake/find_package.lua b/xmake/modules/package/manager/xmake/find_package.lua index dab32de33..503d1beda 100644 --- a/xmake/modules/package/manager/xmake/find_package.lua +++ b/xmake/modules/package/manager/xmake/find_package.lua @@ -38,7 +38,11 @@ function _find_package_from_repo(name, opt) -- find the manifest file of package, e.g. ~/.xmake/packages/z/zlib/1.1.12/ed41d5327fad3fc06fe376b4a94f62ef/manifest.txt local packagedirs = {} - table.insert(packagedirs, path.join(package.installdir(), name:lower():sub(1, 1), name:lower(), opt.require_version, opt.buildhash)) + if opt.installdir then + table.insert(packagedirs, opt.installdir) + else + table.insert(packagedirs, path.join(package.installdir(), name:lower():sub(1, 1), name:lower(), opt.require_version, opt.buildhash)) + end local manifest_file = find_file("manifest.txt", packagedirs) if not manifest_file then return @@ -73,39 +77,35 @@ function _find_package_from_repo(name, opt) local links = {} local linkdirs = {} local libfiles = {} - for _, linkdir in ipairs(vars.linkdirs) do - table.insert(linkdirs, path.join(installdir, linkdir)) - end if vars.links then table.join2(links, vars.links) - end - if not vars.linkdirs or not vars.links then + else + -- we scan links automatically local found = false - for _, file in ipairs(os.files(path.join(installdir, "lib", "*"))) do - if file:endswith(".lib") or file:endswith(".a") then - found = true - if not vars.linkdirs then - table.insert(linkdirs, path.directory(file)) - end - if not vars.links then - table.insert(links, target.linkname(path.filename(file))) + for _, libdir in ipairs(vars.linkdirs or "lib") do + for _, file in ipairs(os.files(path.join(installdir, libdir, "*"))) do + if file:endswith(".lib") or file:endswith(".a") then + found = true + table.insert(links, target.linkname(path.filename(file), {plat = opt.plat})) + table.insert(libfiles, file) end end - end - if not found then - for _, file in ipairs(os.files(path.join(installdir, "lib", "*"))) do - if file:endswith(".so") or file:endswith(".dylib") then - if not vars.linkdirs then - table.insert(linkdirs, path.directory(file)) - end - if not vars.links then - table.insert(links, target.linkname(path.filename(file))) + if not found then + for _, file in ipairs(os.files(path.join(installdir, "lib", "*"))) do + if file:endswith(".so") or file:match(".+%.so%..+$") or file:endswith(".dylib") then -- maybe symlink to libxxx.so.1 + table.insert(links, target.linkname(path.filename(file), {plat = opt.plat})) + table.insert(libfiles, file) end end end end end - if opt.plat == "windows" then + if #links > 0 then + for _, libdir in ipairs(vars.linkdirs or "lib") do + table.insert(linkdirs, path.join(installdir, libdir)) + end + end + if opt.plat == "windows" or opt.plat == "mingw" then for _, file in ipairs(os.files(path.join(installdir, "lib", "*.dll"))) do result.shared = true table.insert(libfiles, file) @@ -128,7 +128,7 @@ function _find_package_from_repo(name, opt) -- find library for _, link in ipairs(links) do - local libinfo = find_library(link, linkdirs) + local libinfo = find_library(link, linkdirs, {plat = opt.plat}) if libinfo then if libinfo.kind == "shared" then result.shared = true @@ -145,7 +145,7 @@ function _find_package_from_repo(name, opt) result.links = table.unique(result.links) end if result.libfiles then - result.libfiles = table.join(result.libfiles, libfiles) + result.libfiles = table.unique(table.join(result.libfiles, libfiles)) end -- inherit the other prefix variables @@ -245,7 +245,7 @@ function _find_package_from_packagedirs(name, opt) -- find library local result = nil for _, link in ipairs(packageinfo:get("links")) do - local libinfo = find_library(link, linkdirs) + local libinfo = find_library(link, linkdirs, {plat = opt.plat}) if libinfo then result = result or {} result.links = table.join(result.links or {}, libinfo.link) diff --git a/xmake/modules/package/manager/xmake/search_package.lua b/xmake/modules/package/manager/xmake/search_package.lua index 5b633852c..f4a235246 100644 --- a/xmake/modules/package/manager/xmake/search_package.lua +++ b/xmake/modules/package/manager/xmake/search_package.lua @@ -19,6 +19,7 @@ -- -- imports +import("core.base.semver") import("core.package.package", {alias = "core_package"}) import("private.action.require.impl.repository") @@ -33,6 +34,10 @@ function main(name) if package then local repo = package:repo() local versions = package:versions() + if versions then + versions = table.copy(versions) + table.sort(versions, function (a, b) return semver.compare(a, b) > 0 end) + end table.insert(results, {name = package:name(), version = versions and versions[1], description = package:get("description"), reponame = repo and repo:name()}) end end diff --git a/xmake/modules/package/tools/autoconf.lua b/xmake/modules/package/tools/autoconf.lua index 209e6c68a..12b94d64f 100644 --- a/xmake/modules/package/tools/autoconf.lua +++ b/xmake/modules/package/tools/autoconf.lua @@ -23,13 +23,22 @@ import("core.base.option") import("core.project.config") import("core.tool.linker") import("core.tool.compiler") +import("lib.detect.find_tool") --- translate path -function _translate_path(package, p) - if p and is_host("windows") and (package:is_plat("mingw") or package:is_plat("msys") or package:is_plat("cygwin")) then - p = p:gsub("\\", "/") +-- translate paths +function _translate_paths(package, paths) + if paths and is_host("windows") and (package:is_plat("mingw") or package:is_plat("msys") or package:is_plat("cygwin")) then + if type(paths) == "string" then + return (paths:gsub("\\", "/")) + elseif type(paths) == "table" then + local result = {} + for _, p in ipairs(paths) do + table.insert(result, (p:gsub("\\", "/"))) + end + return result + end end - return p + return paths end -- translate windows bin path @@ -56,7 +65,7 @@ function _get_configs(package, configs) -- add prefix local configs = configs or {} - table.insert(configs, "--prefix=" .. _translate_path(package, package:installdir())) + table.insert(configs, "--prefix=" .. _translate_paths(package, package:installdir())) -- add host for cross-complation if not configs.host and not package:is_plat(os.subhost()) then @@ -108,10 +117,45 @@ function _get_configs(package, configs) return configs end +-- get cflags from package deps +function _get_cflags_from_packagedeps(package, opt) + local result = {} + for _, depname in ipairs(opt.packagedeps) do + local dep = package:dep(depname) + if dep then + local fetchinfo = dep:fetch({external = false}) + if fetchinfo then + table.join2(result, _map_compflags(package, "cxx", "define", fetchinfo.defines)) + table.join2(result, _translate_paths(package, _map_compflags(package, "cxx", "includedir", fetchinfo.includedirs))) + table.join2(result, _translate_paths(package, _map_compflags(package, "cxx", "sysincludedir", fetchinfo.sysincludedirs))) + end + end + end + return result +end + +-- get ldflags from package deps +function _get_ldflags_from_packagedeps(package, opt) + local result = {} + for _, depname in ipairs(opt.packagedeps) do + local dep = package:dep(depname) + if dep then + local fetchinfo = dep:fetch({external = false}) + if fetchinfo then + table.join2(result, _translate_paths(package, _map_linkflags(package, "binary", {"cxx"}, "linkdir", fetchinfo.linkdirs))) + table.join2(result, _map_linkflags(package, "binary", {"cxx"}, "link", fetchinfo.links)) + table.join2(result, _translate_paths(package, _map_linkflags(package, "binary", {"cxx"}, "syslink", fetchinfo.syslinks))) + end + end + end + return result +end + -- get the build environments function buildenvs(package, opt) opt = opt or {} local envs = {} + local cppflags = {} if package:is_plat(os.subhost()) then local cflags = table.join(table.wrap(package:config("cxflags")), package:config("cflags")) local cxxflags = table.join(table.wrap(package:config("cxflags")), package:config("cxxflags")) @@ -127,10 +171,16 @@ function buildenvs(package, opt) table.join2(cflags, opt.cxflags) table.join2(cxxflags, opt.cxxflags) table.join2(cxxflags, opt.cxflags) + table.join2(cppflags, opt.cppflags) -- @see https://github.com/xmake-io/xmake/issues/1688 table.join2(asflags, opt.asflags) table.join2(ldflags, opt.ldflags) + table.join2(cflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(cxxflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(cppflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(ldflags, _get_ldflags_from_packagedeps(package, opt)) envs.CFLAGS = table.concat(cflags, ' ') envs.CXXFLAGS = table.concat(cxxflags, ' ') + envs.CPPFLAGS = table.concat(cppflags, ' ') envs.ASFLAGS = table.concat(asflags, ' ') envs.LDFLAGS = table.concat(ldflags, ' ') else @@ -150,9 +200,14 @@ function buildenvs(package, opt) table.join2(cflags, opt.cxflags) table.join2(cxxflags, opt.cxxflags) table.join2(cxxflags, opt.cxflags) + table.join2(cppflags, opt.cppflags) -- @see https://github.com/xmake-io/xmake/issues/1688 table.join2(asflags, opt.asflags) table.join2(ldflags, opt.ldflags) table.join2(shflags, opt.shflags) + table.join2(cflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(cxxflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(cppflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(ldflags, _get_ldflags_from_packagedeps(package, opt)) table.join2(cflags, _map_compflags(package, "c", "define", defines)) table.join2(cflags, _map_compflags(package, "c", "includedir", includedirs)) table.join2(cflags, _map_compflags(package, "c", "sysincludedir", sysincludedirs)) @@ -177,6 +232,7 @@ function buildenvs(package, opt) envs.RANLIB = package:build_getenv("ranlib") envs.CFLAGS = table.concat(cflags, ' ') envs.CXXFLAGS = table.concat(cxxflags, ' ') + envs.CPPFLAGS = table.concat(cppflags, ' ') envs.ASFLAGS = table.concat(asflags, ' ') envs.ARFLAGS = table.concat(arflags, ' ') envs.LDFLAGS = table.concat(ldflags, ' ') @@ -291,25 +347,75 @@ function configure(package, configs, opt) os.vrunv("sh", argv, {envs = envs}) end --- install package -function install(package, configs, opt) +-- do make +function make(package, argv, opt) + opt = opt or {} + local program + if package:is_plat("mingw") and is_subhost("windows") then + local mingw = assert(package:build_getenv("mingw") or package:build_getenv("sdk"), "mingw not found!") + program = path.join(mingw, "bin", "mingw32-make.exe") + else + local tool = find_tool("make") + if tool then + program = tool.program + end + end + assert(program, "make not found!") + os.vrunv(program, argv) +end + +-- build package +function build(package, configs, opt) -- do configure configure(package, configs, opt) -- do make and install opt = opt or {} - local njob = opt.jobs or option.get("jobs") or tostring(math.ceil(os.cpuinfo().ncpu * 3 / 2)) + local njob = opt.jobs or option.get("jobs") or tostring(os.default_njob()) local argv = {"-j" .. njob} if option.get("verbose") then table.insert(argv, "V=1") end - if is_host("bsd") then - os.vrunv("gmake", argv) - os.vrun("gmake install") - else - os.vrunv("make", argv) - os.vrun("make install") + if opt.makeconfigs then + for name, value in pairs(opt.makeconfigs) do + value = tostring(value):trim() + if value ~= "" then + if type(name) == "number" then + table.insert(argv, value) + else + table.insert(argv, name .. "=" .. value) + end + end + end + end + make(package, argv, opt) +end + +-- install package +function install(package, configs, opt) + + -- do build + opt = opt or {} + build(package, configs, opt) + + -- do install + local argv = {"install"} + if option.get("verbose") then + table.insert(argv, "V=1") + end + if opt.makeconfigs then + for name, value in pairs(opt.makeconfigs) do + value = tostring(value):trim() + if value ~= "" then + if type(name) == "number" then + table.insert(argv, value) + else + table.insert(argv, name .. "=" .. value) + end + end + end end + make(package, argv, opt) end diff --git a/xmake/modules/package/tools/cmake.lua b/xmake/modules/package/tools/cmake.lua index 3e48fee40..f49cedf0b 100644 --- a/xmake/modules/package/tools/cmake.lua +++ b/xmake/modules/package/tools/cmake.lua @@ -30,7 +30,7 @@ import("package.tools.ninja") -- get the number of parallel jobs function _get_parallel_njobs(opt) - return opt.jobs or option.get("jobs") or tostring(math.ceil(os.cpuinfo().ncpu * 3 / 2)) + return opt.jobs or option.get("jobs") or tostring(os.default_njob()) end -- translate paths @@ -272,10 +272,13 @@ function _get_configs_for_windows(package, configs, opt) table.insert(configs, "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL") end if vs_runtime then - table.insert(configs, '-DCMAKE_CXX_FLAGS_DEBUG="/' .. vs_runtime .. '"') - table.insert(configs, '-DCMAKE_CXX_FLAGS_RELEASE="/' .. vs_runtime .. '"') - table.insert(configs, '-DCMAKE_C_FLAGS_DEBUG="/' .. vs_runtime .. '"') - table.insert(configs, '-DCMAKE_C_FLAGS_RELEASE="/' .. vs_runtime .. '"') + -- CMake default MSVC flags as of 3.21.2 + local default_debug_flags = "/Zi /Ob0 /Od /RTC1" + local default_release_flags = "/O2 /Ob2 /DNDEBUG" + table.insert(configs, '-DCMAKE_CXX_FLAGS_DEBUG=/' .. vs_runtime .. ' ' .. default_debug_flags) + table.insert(configs, '-DCMAKE_CXX_FLAGS_RELEASE=/' .. vs_runtime .. ' ' .. default_release_flags) + table.insert(configs, '-DCMAKE_C_FLAGS_DEBUG=/' .. vs_runtime .. ' ' .. default_debug_flags) + table.insert(configs, '-DCMAKE_C_FLAGS_RELEASE=/' .. vs_runtime .. ' ' .. default_release_flags) end _get_configs_for_generic(package, configs, opt) end @@ -296,6 +299,12 @@ function _get_configs_for_android(package, configs, opt) if ndk_cxxstl then table.insert(configs, "-DANDROID_STL=" .. ndk_cxxstl) end + if is_host("windows") then + local make = path.join(ndk, "prebuilt", "windows-x86_64", "bin", "make.exe") + if os.isfile(make) then + table.insert(configs, "-DCMAKE_MAKE_PROGRAM=" .. make) + end + end end _get_configs_for_generic(package, configs, opt) end @@ -363,6 +372,16 @@ function _get_configs_for_mingw(package, configs, opt) envs.CMAKE_OSX_SYSROOT = "" -- Avoid cmake to add the flags -search_paths_first and -headerpad_max_install_names on macOS envs.HAVE_FLAG_SEARCH_PATHS_FIRST = "0" + -- CMAKE_MAKE_PROGRAM may be required for some CMakeLists.txt (libcurl) + if is_subhost("windows") then + local mingw = assert(package:build_getenv("mingw") or package:build_getenv("sdk"), "mingw not found!") + envs.CMAKE_MAKE_PROGRAM = path.join(mingw, "bin", "mingw32-make.exe") + end + + if opt.cmake_generator == "Ninja" then + envs.CMAKE_MAKE_PROGRAM = "ninja" + end + for k, v in pairs(envs) do table.insert(configs, "-D" .. k .. "=" .. v) end @@ -535,8 +554,12 @@ end -- do build for make function _build_for_make(package, configs, opt) + local argv = {} + if opt.target then + table.insert(argv, opt.target) + end local jobs = _get_parallel_njobs(opt) - local argv = {"-j" .. jobs} + table.insert(argv, "-j" .. jobs) if option.get("verbose") then table.insert(argv, "VERBOSE=1") end @@ -546,6 +569,16 @@ function _build_for_make(package, configs, opt) local mingw = assert(package:build_getenv("mingw") or package:build_getenv("sdk"), "mingw not found!") local mingw_make = path.join(mingw, "bin", "mingw32-make.exe") os.vrunv(mingw_make, argv) + elseif package:is_plat("android") and is_host("windows") then + local make + local ndk = get_config("ndk") + if ndk then + make = path.join(ndk, "prebuilt", "windows-x86_64", "bin", "make.exe") + end + if not make or not os.isfile(make) then + make = "make" + end + os.vrunv(make, argv) else os.vrunv("make", argv) end @@ -602,6 +635,17 @@ function _install_for_make(package, configs, opt) local mingw_make = path.join(mingw, "bin", "mingw32-make.exe") os.vrunv(mingw_make, argv) os.vrunv(mingw_make, {"install"}) + elseif package:is_plat("android") and is_host("windows") then + local make + local ndk = get_config("ndk") + if ndk then + make = path.join(ndk, "prebuilt", "windows-x86_64", "bin", "make.exe") + end + if not make or not os.isfile(make) then + make = "make" + end + os.vrunv(make, argv) + os.vrunv(make, {"install"}) else os.vrunv("make", argv) os.vrunv("make", {"install"}) @@ -629,7 +673,7 @@ function build(package, configs, opt) opt = opt or {} -- enter build directory - local buildir = opt.buildir or "build_" .. hash.uuid4():split('%-')[1] + local buildir = opt.buildir or package:buildir() os.mkdir(path.join(buildir, "install")) local oldir = os.cd(buildir) @@ -688,7 +732,7 @@ function install(package, configs, opt) opt = opt or {} -- enter build directory - local buildir = opt.buildir or "build_" .. hash.uuid4():split('%-')[1] + local buildir = opt.buildir or package:buildir() os.mkdir(path.join(buildir, "install")) local oldir = os.cd(buildir) @@ -733,4 +777,3 @@ function install(package, configs, opt) end os.cd(oldir) end - diff --git a/xmake/modules/package/tools/make.lua b/xmake/modules/package/tools/make.lua index b236b9da6..27e73b412 100644 --- a/xmake/modules/package/tools/make.lua +++ b/xmake/modules/package/tools/make.lua @@ -21,6 +21,15 @@ -- imports import("core.base.option") import("core.project.config") +import("lib.detect.find_tool") + +-- translate bin path +function _translate_bin_path(bin_path) + if is_host("windows") and bin_path then + return bin_path:gsub("\\", "/") .. ".exe" + end + return bin_path +end -- get the build environments function buildenvs(package) @@ -43,13 +52,14 @@ function buildenvs(package) else local cflags = table.join(table.wrap(package:build_getenv("cxflags")), package:build_getenv("cflags")) local cxxflags = table.join(table.wrap(package:build_getenv("cxflags")), package:build_getenv("cxxflags")) - envs.CC = package:build_getenv("cc") - envs.AS = package:build_getenv("as") - envs.AR = package:build_getenv("ar") - envs.LD = package:build_getenv("ld") - envs.LDSHARED = package:build_getenv("sh") - envs.CPP = package:build_getenv("cpp") - envs.RANLIB = package:build_getenv("ranlib") + envs.CC = _translate_bin_path(package:build_getenv("cc")) + envs.CXX = _translate_bin_path(package:build_getenv("cxx")) + envs.AS = _translate_bin_path(package:build_getenv("as")) + envs.AR = _translate_bin_path(package:build_getenv("ar")) + envs.LD = _translate_bin_path(package:build_getenv("ld")) + envs.LDSHARED = _translate_bin_path(package:build_getenv("sh")) + envs.CPP = _translate_bin_path(package:build_getenv("cpp")) + envs.RANLIB = _translate_bin_path(package:build_getenv("ranlib")) envs.CFLAGS = table.concat(cflags, ' ') envs.CXXFLAGS = table.concat(cxxflags, ' ') envs.ASFLAGS = table.concat(table.wrap(package:build_getenv("asflags")), ' ') @@ -75,9 +85,32 @@ function buildenvs(package) end envs.ACLOCAL_PATH = path.joinenv(ACLOCAL_PATH) envs.PKG_CONFIG_PATH = path.joinenv(PKG_CONFIG_PATH) + -- some Makefile use ComSpec to detect Windows (e.g. Makefiles generated by Premake) and require this env + if is_subhost("windows") then + envs.ComSpec = os.getenv("ComSpec") + end + return envs end +-- do make +function make(package, argv, opt) + opt = opt or {} + local program + local runenvs = opt.envs or buildenvs(package) + if package:is_plat("mingw") and is_subhost("windows") then + local mingw = assert(package:build_getenv("mingw") or package:build_getenv("sdk"), "mingw not found!") + program = path.join(mingw, "bin", "mingw32-make.exe") + else + local tool = find_tool("make", {envs = runenvs}) + if tool then + program = tool.program + end + end + assert(program, "make not found!") + os.vrunv(program, argv, {envs = runenvs, curdir = opt.curdir}) +end + -- build package function build(package, configs, opt) @@ -85,7 +118,7 @@ function build(package, configs, opt) opt = opt or {} -- pass configurations - local njob = opt.jobs or option.get("jobs") or tostring(math.ceil(os.cpuinfo().ncpu * 3 / 2)) + local njob = opt.jobs or option.get("jobs") or tostring(os.default_njob()) local argv = {"-j" .. njob} if option.get("verbose") then table.insert(argv, "VERBOSE=1") @@ -102,11 +135,7 @@ function build(package, configs, opt) end -- do build - if is_host("bsd") then - os.vrunv("gmake", argv, {envs = opt.envs or buildenvs(package)}) - else - os.vrunv("make", argv, {envs = opt.envs or buildenvs(package)}) - end + make(package, argv, opt) end -- install package @@ -121,9 +150,5 @@ function install(package, configs, opt) if option.get("verbose") then table.insert(argv, "VERBOSE=1") end - if is_host("bsd") then - os.vrunv("gmake", argv, {envs = opt.envs or buildenvs(package)}) - else - os.vrunv("make", argv, {envs = opt.envs or buildenvs(package)}) - end + make(package, argv, opt) end diff --git a/xmake/modules/package/tools/meson.lua b/xmake/modules/package/tools/meson.lua index 6361f2a0e..0ee1839b6 100644 --- a/xmake/modules/package/tools/meson.lua +++ b/xmake/modules/package/tools/meson.lua @@ -22,18 +22,31 @@ import("core.base.option") import("core.project.config") import("core.tool.toolchain") +import("core.tool.linker") +import("core.tool.compiler") import("package.tools.ninja") +import("lib.detect.find_tool") -- get build directory -function _get_buildir(opt) +function _get_buildir(package, opt) if opt and opt.buildir then return opt.buildir else - _g.buildir = _g.buildir or ("build_" .. hash.uuid4():split('%-')[1]) + _g.buildir = _g.buildir or package:buildir() return _g.buildir end end +-- map compiler flags +function _map_compflags(package, langkind, name, values) + return compiler.map_flags(langkind, name, values, {target = package}) +end + +-- map linker flags +function _map_linkflags(package, targetkind, sourcekinds, name, values) + return linker.map_flags(targetkind, sourcekinds, name, values, {target = package}) +end + -- get configs function _get_configs(package, configs, opt) @@ -56,7 +69,7 @@ function _get_configs(package, configs, opt) end -- add build directory - table.insert(configs, _get_buildir(opt)) + table.insert(configs, _get_buildir(package, opt)) return configs end @@ -79,21 +92,91 @@ function _fix_libname_on_windows(package) end end +-- get cflags from package deps +function _get_cflags_from_packagedeps(package, opt) + local result = {} + for _, depname in ipairs(opt.packagedeps) do + local dep = package:dep(depname) + if dep then + local fetchinfo = dep:fetch({external = false}) + if fetchinfo then + table.join2(result, _map_compflags(package, "cxx", "define", fetchinfo.defines)) + table.join2(result, _map_compflags(package, "cxx", "includedir", fetchinfo.includedirs)) + table.join2(result, _map_compflags(package, "cxx", "sysincludedir", fetchinfo.sysincludedirs)) + end + end + end + return result +end + +-- get ldflags from package deps +function _get_ldflags_from_packagedeps(package, opt) + local result = {} + for _, depname in ipairs(opt.packagedeps) do + local dep = package:dep(depname) + if dep then + local fetchinfo = dep:fetch({external = false}) + if fetchinfo then + table.join2(result, _map_linkflags(package, "binary", {"cxx"}, "linkdir", fetchinfo.linkdirs)) + table.join2(result, _map_linkflags(package, "binary", {"cxx"}, "link", fetchinfo.links)) + table.join2(result, _map_linkflags(package, "binary", {"cxx"}, "syslink", fetchinfo.syslinks)) + end + end + end + return result +end + -- get the build environments -function buildenvs(package) +function buildenvs(package, opt) local envs = {} + opt = opt or {} if package:is_plat(os.host()) then local cflags = table.join(table.wrap(package:config("cxflags")), package:config("cflags")) local cxxflags = table.join(table.wrap(package:config("cxflags")), package:config("cxxflags")) + local asflags = table.wrap(package:config("asflags")) + local ldflags = table.wrap(package:config("ldflags")) + local shflags = table.wrap(package:config("shflags")) + table.join2(cflags, opt.cflags) + table.join2(cflags, opt.cxflags) + table.join2(cxxflags, opt.cxxflags) + table.join2(cxxflags, opt.cxflags) + table.join2(asflags, opt.asflags) + table.join2(ldflags, opt.ldflags) + table.join2(shflags, opt.shflags) + table.join2(cflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(cxxflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(ldflags, _get_ldflags_from_packagedeps(package, opt)) + table.join2(shflags, _get_ldflags_from_packagedeps(package, opt)) envs.CFLAGS = table.concat(cflags, ' ') envs.CXXFLAGS = table.concat(cxxflags, ' ') - envs.ASFLAGS = table.concat(table.wrap(package:config("asflags")), ' ') + envs.ASFLAGS = table.concat(asflags, ' ') + envs.LDFLAGS = table.concat(ldflags, ' ') + envs.SHFLAGS = table.concat(shflags, ' ') if package:is_plat("windows") then envs = os.joinenvs(envs, _get_msvc_runenvs(package)) + local pkgconf = find_tool("pkgconf") + if pkgconf then + envs.PKG_CONFIG = pkgconf.program + end end else local cflags = table.join(table.wrap(package:build_getenv("cxflags")), package:build_getenv("cflags")) local cxxflags = table.join(table.wrap(package:build_getenv("cxflags")), package:build_getenv("cxxflags")) + local asflags = table.wrap(package:build_getenv("asflags")) + local arflags = table.wrap(package:build_getenv("arflags")) + local ldflags = table.wrap(package:build_getenv("ldflags")) + local shflags = table.wrap(package:build_getenv("shflags")) + table.join2(cflags, opt.cflags) + table.join2(cflags, opt.cxflags) + table.join2(cxxflags, opt.cxxflags) + table.join2(cxxflags, opt.cxflags) + table.join2(asflags, opt.asflags) + table.join2(ldflags, opt.ldflags) + table.join2(shflags, opt.shflags) + table.join2(cflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(cxxflags, _get_cflags_from_packagedeps(package, opt)) + table.join2(ldflags, _get_ldflags_from_packagedeps(package, opt)) + table.join2(shflags, _get_ldflags_from_packagedeps(package, opt)) envs.CC = package:build_getenv("cc") envs.AS = package:build_getenv("as") envs.AR = package:build_getenv("ar") @@ -103,10 +186,10 @@ function buildenvs(package) envs.RANLIB = package:build_getenv("ranlib") envs.CFLAGS = table.concat(cflags, ' ') envs.CXXFLAGS = table.concat(cxxflags, ' ') - envs.ASFLAGS = table.concat(table.wrap(package:build_getenv("asflags")), ' ') - envs.ARFLAGS = table.concat(table.wrap(package:build_getenv("arflags")), ' ') - envs.LDFLAGS = table.concat(table.wrap(package:build_getenv("ldflags")), ' ') - envs.SHFLAGS = table.concat(table.wrap(package:build_getenv("shflags")), ' ') + envs.ASFLAGS = table.concat(asflags, ' ') + envs.ARFLAGS = table.concat(arflags, ' ') + envs.LDFLAGS = table.concat(ldflags, ' ') + envs.SHFLAGS = table.concat(shflags, ' ') end local ACLOCAL_PATH = {} local PKG_CONFIG_PATH = {} @@ -149,7 +232,7 @@ function generate(package, configs, opt) end -- do configure - os.vrunv("meson", argv, {envs = opt.envs or buildenvs(package)}) + os.vrunv("meson", argv, {envs = opt.envs or buildenvs(package, opt)}) end -- build package @@ -160,7 +243,7 @@ function build(package, configs, opt) generate(package, configs, opt) -- do build - local buildir = _get_buildir(opt) + local buildir = _get_buildir(package, opt) ninja.build(package, {}, {buildir = buildir, envs = opt.envs or buildenvs(package, opt)}) end @@ -172,7 +255,7 @@ function install(package, configs, opt) generate(package, configs, opt) -- do build and install - local buildir = _get_buildir(opt) + local buildir = _get_buildir(package, opt) ninja.install(package, {}, {buildir = buildir, envs = opt.envs or buildenvs(package, opt)}) -- fix static libname on windows diff --git a/xmake/modules/package/tools/ninja.lua b/xmake/modules/package/tools/ninja.lua index e1feccc14..0d3ad9708 100644 --- a/xmake/modules/package/tools/ninja.lua +++ b/xmake/modules/package/tools/ninja.lua @@ -26,9 +26,14 @@ import("lib.detect.find_tool") function build(package, configs, opt) opt = opt or {} local buildir = opt.buildir or os.curdir() - local njob = opt.jobs or option.get("jobs") or tostring(math.ceil(os.cpuinfo().ncpu * 3 / 2)) + local njob = opt.jobs or option.get("jobs") or tostring(os.default_njob()) local ninja = assert(find_tool("ninja"), "ninja not found!") - local argv = {"-C", buildir} + local argv = {} + if opt.target then + table.insert(argv, opt.target) + end + table.insert(argv, "-C") + table.insert(argv, buildir) if option.get("verbose") then table.insert(argv, "-v") end @@ -44,7 +49,7 @@ end function install(package, configs, opt) opt = opt or {} local buildir = opt.buildir or os.curdir() - local njob = tostring(math.ceil(os.cpuinfo().ncpu * 3 / 2)) + local njob = tostring(os.default_njob()) local ninja = assert(find_tool("ninja"), "ninja not found!") local argv = {"install", "-C", buildir} if option.get("verbose") then diff --git a/xmake/modules/package/tools/scons.lua b/xmake/modules/package/tools/scons.lua index 386645bba..0c197933f 100644 --- a/xmake/modules/package/tools/scons.lua +++ b/xmake/modules/package/tools/scons.lua @@ -86,7 +86,7 @@ end function build(package, configs, opt) opt = opt or {} local buildir = opt.buildir or os.curdir() - local njob = opt.jobs or option.get("jobs") or tostring(math.ceil(os.cpuinfo().ncpu * 3 / 2)) + local njob = opt.jobs or option.get("jobs") or tostring(os.default_njob()) local scons = assert(find_tool("scons"), "scons not found!") local argv = {"-C", buildir, "-j", njob} configs = _get_configs(package, configs) diff --git a/xmake/modules/package/tools/xmake.lua b/xmake/modules/package/tools/xmake.lua index 0c7d9c294..9bfcffaff 100644 --- a/xmake/modules/package/tools/xmake.lua +++ b/xmake/modules/package/tools/xmake.lua @@ -59,13 +59,28 @@ function _get_configs(package, configs) table.insert(configs, "--cross=" .. cross) end local bindir = _get_config_from_toolchains(package, "bindir") or get_config("bin") - if cross then + if bindir then table.insert(configs, "--bin=" .. bindir) end local sdkdir = _get_config_from_toolchains(package, "sdkdir") or get_config("sdk") - if cross then + if sdkdir then table.insert(configs, "--sdk=" .. sdkdir) end + -- we can only modify toolchain for cross-compilation + -- + -- e.g. xrepo install -p cross --toolchain=muslcc meson, + -- we cannot pass muslcc toolchain to it's deps(zlib, ..), because meson is always host binary and zlib is host library. + local toolchain_name = get_config("toolchain") + if toolchain_name then + table.insert(configs, "--toolchain=" .. toolchain_name) + end + local names = {"ld", "sh", "ar", "cc", "cxx"} + for _, name in ipairs(names) do + local value = get_config(name) + if value ~= nil then + table.insert(configs, "--" .. name .. "=" .. tostring(value)) + end + end else local names = {"ndk", "ndk_sdkver", "vs", "mingw", "ld", "sh", "ar", "cc", "cxx", "mm", "mxx"} for _, name in ipairs(names) do diff --git a/xmake/modules/private/action/build/object.lua b/xmake/modules/private/action/build/object.lua index 50397cda4..e07ab76b7 100644 --- a/xmake/modules/private/action/build/object.lua +++ b/xmake/modules/private/action/build/object.lua @@ -25,7 +25,7 @@ import("core.tool.compiler") import("core.project.depend") import("private.tools.ccache") import("private.async.runjobs") -import("private.utils.progress") +import("utils.progress") -- do build file function _do_build_file(target, sourcefile, opt) @@ -85,7 +85,7 @@ function _do_build_file(target, sourcefile, opt) end -- build object -function _build_object(target, sourcefile, opt) +function build_object(target, sourcefile, opt) local script = target:script("build_file", _do_build_file) if script then script(target, sourcefile, opt) @@ -99,7 +99,7 @@ function build(target, sourcebatch, opt) opt.objectfile = sourcebatch.objectfiles[i] opt.dependfile = sourcebatch.dependfiles[i] opt.sourcekind = assert(sourcebatch.sourcekind, "%s: sourcekind not found!", sourcefile) - _build_object(target, sourcefile, opt) + build_object(target, sourcefile, opt) end end @@ -113,7 +113,7 @@ function main(target, batchjobs, sourcebatch, opt) local sourcekind = assert(sourcebatch.sourcekind, "%s: sourcekind not found!", sourcefile) batchjobs:addjob(sourcefile, function (index, total) local build_opt = table.join({objectfile = objectfile, dependfile = dependfile, sourcekind = sourcekind, progress = (index * 100) / total}, opt) - _build_object(target, sourcefile, build_opt) + build_object(target, sourcefile, build_opt) end, {rootjob = rootjob}) end end diff --git a/xmake/modules/private/action/require/impl/actions/download.lua b/xmake/modules/private/action/require/impl/actions/download.lua index bee421052..f2c8eeba7 100644 --- a/xmake/modules/private/action/require/impl/actions/download.lua +++ b/xmake/modules/private/action/require/impl/actions/download.lua @@ -70,13 +70,13 @@ function _checkout(package, url, sourcedir, url_alias) if package:branch() then -- only shadow clone this branch - git.clone(proxy.mirror(url) or url, {depth = 1, recursive = true, longpaths = longpaths, branch = package:branch(), outputdir = packagedir}) + git.clone(url, {depth = 1, recursive = true, longpaths = longpaths, branch = package:branch(), outputdir = packagedir}) -- download package from revision or tag? else -- clone whole history and tags - git.clone(proxy.mirror(url) or url, {longpaths = longpaths, outputdir = packagedir}) + git.clone(url, {longpaths = longpaths, outputdir = packagedir}) -- attempt to checkout the given version local revision = package:revision(url_alias) or package:tag() or package:version_str() @@ -139,7 +139,7 @@ function _download(package, url, sourcedir, url_alias, url_excludes) -- we can use local package from the search directories directly if network is too slow os.cp(localfile, packagefile) else - http.download(proxy.mirror(url) or url, packagefile) + http.download(url, packagefile) end end @@ -151,15 +151,20 @@ function _download(package, url, sourcedir, url_alias, url_excludes) -- extract package file os.rm(sourcedir .. ".tmp") + local extension = archive.extension(packagefile) if archive.extract(packagefile, sourcedir .. ".tmp", {excludes = url_excludes}) then -- move to source directory os.rm(sourcedir) os.mv(sourcedir .. ".tmp", sourcedir) - else + elseif extension and extension ~= "" then -- create an empty source directory if do not extract package file os.tryrm(sourcedir) os.mkdir(sourcedir) - raise("cannot extract %s", packagefile) + raise("cannot extract %s, maybe missing extractor or invalid package file!", packagefile) + else + -- if it is not archive file, we need only create empty source file and use package:originfile() + os.tryrm(sourcedir) + os.mkdir(sourcedir) end -- save original file path @@ -224,7 +229,13 @@ function main(package) -- filter url url = filter.handle(url, package) + -- use proxy url? + if not os.isfile(url) then + url = proxy.mirror(url) or url + end + -- download url + local allerrors = {} ok = try { function () @@ -243,8 +254,12 @@ function main(package) function (errors) -- show or save the last errors - if errors and (option.get("verbose") or option.get("diagnosis")) then - cprint("${dim color.error}error: ${clear}%s", errors) + if errors then + if (option.get("verbose") or option.get("diagnosis")) then + cprint("${dim color.error}error: ${clear}%s", errors) + else + table.insert(allerrors, errors) + end end -- trace @@ -270,7 +285,11 @@ function main(package) cprint(" ${bright}- %s", table.concat(searchnames:to_array(), ", ")) cprint("and we can run `xmake g --pkg_searchdirs=/xxx` to set the search directories.") end - raise("download failed!") + if #allerrors then + raise(table.concat(allerrors, "\n")) + else + raise("download failed!") + end end end } diff --git a/xmake/modules/private/action/require/impl/actions/download_resources.lua b/xmake/modules/private/action/require/impl/actions/download_resources.lua index fa115c04a..c6af29151 100644 --- a/xmake/modules/private/action/require/impl/actions/download_resources.lua +++ b/xmake/modules/private/action/require/impl/actions/download_resources.lua @@ -32,6 +32,7 @@ import("utils.archive") function _checkout(package, resource_name, resource_url, resource_revision) -- trace + resource_url = proxy.mirror(resource_url) or resource_url vprint("cloning resource(%s: %s) to %s-%s ..", resource_name, resource_revision, package:name(), package:version_str()) -- get the resource directory @@ -62,7 +63,7 @@ function _checkout(package, resource_name, resource_url, resource_revision) local longpaths = package:policy("platform.longpaths") -- clone whole history and tags - git.clone(proxy.mirror(resource_url) or resource_url, {longpaths = longpaths, outputdir = resourcedir}) + git.clone(resource_url, {longpaths = longpaths, outputdir = resourcedir}) -- attempt to checkout the given version git.checkout(resource_revision, {repodir = resourcedir}) @@ -77,6 +78,7 @@ end function _download(package, resource_name, resource_url, resource_hash) -- trace + resource_url = proxy.mirror(resource_url) or resource_url vprint("downloading resource(%s: %s) to %s-%s ..", resource_name, resource_url, package:name(), package:version_str()) -- get the resource file @@ -103,7 +105,7 @@ function _download(package, resource_name, resource_url, resource_hash) -- we can use local resource from the search directories directly if network is too slow os.cp(localfile, resource_file) elseif resource_url:find(string.ipattern("https-://")) or resource_url:find(string.ipattern("ftps-://")) then - http.download(proxy.mirror(resource_url) or resource_url, resource_file) + http.download(resource_url, resource_file) else raise("invalid resource url(%s)", resource_url) end diff --git a/xmake/modules/private/action/require/impl/actions/install.lua b/xmake/modules/private/action/require/impl/actions/install.lua index 81f93681e..cbe836ed2 100644 --- a/xmake/modules/private/action/require/impl/actions/install.lua +++ b/xmake/modules/private/action/require/impl/actions/install.lua @@ -55,8 +55,11 @@ function _patch_pkgconfig(package) -- get libs local libs = "" + local installdir = package:installdir() for _, linkdir in ipairs(fetchinfo.linkdirs) do - libs = libs .. "-L" .. linkdir + if linkdir ~= path.join(installdir, "lib") then + libs = libs .. " -L" .. (linkdir:gsub("\\", "/")) + end end libs = libs .. " -L${libdir}" for _, link in ipairs(fetchinfo.links) do @@ -69,14 +72,16 @@ function _patch_pkgconfig(package) -- cflags local cflags = "" for _, includedir in ipairs(fetchinfo.includedirs) do - cflags = cflags .. "-I" .. includedir + if includedir ~= path.join(installdir, "include") then + cflags = cflags .. " -I" .. (includedir:gsub("\\", "/")) + end end cflags = cflags .. " -I${includedir}" -- patch a *.pc file local file = io.open(pcfile, 'w') if file then - file:print("prefix=%s", package:installdir()) + file:print("prefix=%s", installdir:gsub("\\", "/")) file:print("exec_prefix=${prefix}") file:print("libdir=${exec_prefix}/lib") file:print("includedir=${prefix}/include") @@ -91,6 +96,32 @@ function _patch_pkgconfig(package) end end +-- fix paths for the precompiled package +-- @see https://github.com/xmake-io/xmake/issues/1671 +function _fix_paths_for_precompiled_package(package) + local filepaths = {path.join(package:installdir(), "**.cmake|include/**")} + for _, filepath in ipairs(filepaths) do + for _, file in ipairs(os.files(filepath)) do + io.gsub(file, "(\"(.-)\")", function(_, value) + if value:find(package:buildhash(), 1, true) and value:find(package:name(), 1, true) then + local result + local splitinfo = value:split(package:buildhash(), {plain = true}) + if #splitinfo == 2 then + result = path.join(package:installdir(), splitinfo[2]) + elseif #splitinfo == 1 then + result = package:installdir() + end + if result then + result = result:gsub("\\", "/") + vprint("fix path: %s in %s", result, path.filename(file)) + return "\"" .. result .. "\"" + end + end + end) + end + end +end + -- check package toolchains function _check_package_toolchains(package) for _, toolchain_inst in pairs(package:toolchains()) do @@ -111,7 +142,10 @@ function main(package) -- enter the working directory local oldir = nil - if #package:urls() > 0 then + local sourcedir = package:sourcedir() + if sourcedir then + oldir = os.cd(sourcedir) + elseif #package:urls() > 0 then -- only one root directory? skip it local filedirs = os.filedirs(path.join(workdir, "source", "*")) if #filedirs == 1 and os.isdir(filedirs[1]) then @@ -196,6 +230,11 @@ function main(package) -- this package is installed now if installed_now then + -- fix paths for the precompiled package + if package:is_plat("windows") and not package:is_built() and not package:is_system() then + _fix_paths_for_precompiled_package(package) + end + -- patch pkg-config files for package _patch_pkgconfig(package) @@ -247,7 +286,16 @@ function main(package) -- failed if not package:requireinfo().optional then if os.isfile(errorfile) then - print("if you want to get verbose errors, please see:") + if errors then + print("") + for idx, line in ipairs(errors:split("\n")) do + print(line) + if idx > 16 then + break + end + end + end + cprint("if you want to get more verbose errors, please see:") cprint(" -> ${bright}%s", errorfile) end raise("install failed!") diff --git a/xmake/modules/private/action/require/impl/actions/patch_sources.lua b/xmake/modules/private/action/require/impl/actions/patch_sources.lua index 23d46f3d3..d5874f8f5 100644 --- a/xmake/modules/private/action/require/impl/actions/patch_sources.lua +++ b/xmake/modules/private/action/require/impl/actions/patch_sources.lua @@ -50,6 +50,7 @@ end function _patch(package, patch_url, patch_hash) -- trace + patch_url = proxy.mirror(patch_url) or patch_url vprint("patching %s to %s-%s ..", patch_url, package:name(), package:version_str()) -- get the patch file @@ -72,7 +73,7 @@ function _patch(package, patch_url, patch_hash) -- download the patch file if patch_url:find(string.ipattern("https-://")) or patch_url:find(string.ipattern("ftps-://")) then - http.download(proxy.mirror(patch_url) or patch_url, patch_file) + http.download(patch_url, patch_file) else -- copy the patch file if os.isfile(patch_url) then diff --git a/xmake/modules/private/action/require/impl/install_packages.lua b/xmake/modules/private/action/require/impl/install_packages.lua index 7e83ae939..f62a0fb52 100644 --- a/xmake/modules/private/action/require/impl/install_packages.lua +++ b/xmake/modules/private/action/require/impl/install_packages.lua @@ -25,11 +25,12 @@ import("core.base.scheduler") import("core.project.project") import("core.base.tty") import("private.async.runjobs") -import("private.utils.progress") +import("utils.progress") import("actions.install", {alias = "action_install"}) import("actions.download", {alias = "action_download"}) import("net.fasturl") import("private.action.require.impl.package") +import("private.action.require.impl.lock_packages") import("private.action.require.impl.register_packages") -- sort packages urls @@ -242,6 +243,19 @@ function _get_confirm(packages) return result, packages_modified end +-- show upgraded packages +function _show_upgraded_packages(packages) + local upgraded_count = 0 + for _, instance in ipairs(packages) do + local locked_requireinfo = package.get_locked_requireinfo(instance:requireinfo(), {force = true}) + if locked_requireinfo and locked_requireinfo.version and instance:version() and instance:version():gt(locked_requireinfo.version) then + cprint(" ${color.dump.string}%s${clear}: %s -> ${color.success}%s", instance:displayname(), locked_requireinfo.version, instance:version_str()) + upgraded_count = upgraded_count + 1 + end + end + cprint("${bright}%d packages are upgraded!", upgraded_count) +end + -- install packages function _install_packages(packages_install, packages_download, installdeps) @@ -254,6 +268,9 @@ function _install_packages(packages_install, packages_download, installdeps) packages_installed[tostring(instance)] = false end + -- save terminal mode for stdout, @see https://github.com/xmake-io/xmake/issues/1924 + local term_mode_stdout = tty.term_mode("stdout") + -- do install local progress_helper = show_wait and progress.new() or nil local packages_installing = {} @@ -424,6 +441,12 @@ function _install_packages(packages_install, packages_download, installdeps) end end + -- fix terminal mode to avoid some subprocess to change it + -- @see https://github.com/xmake-io/xmake/issues/1924 + if term_mode_stdout ~= tty.term_mode("stdout") then + tty.term_mode("stdout", term_mode_stdout) + end + -- trace progress_helper:clear() tty.erase_line_to_start().cr() @@ -579,6 +602,11 @@ function main(requires, opt) end end + -- show upgraded information + if option.get("upgrade") then + print("upgrading packages ..") + end + -- some packages are modified? we need fix packages list and all deps if packages_modified then order_packages = {} @@ -600,6 +628,14 @@ function main(requires, opt) -- re-register and refresh all root packages to local cache, -- because there may be some missing optional dependencies reinstalled register_packages(packages) + + -- show upgraded packages + if option.get("upgrade") then + _show_upgraded_packages(packages) + end + + -- lock packages + lock_packages(packages) return packages end diff --git a/xmake/modules/private/action/require/impl/lock_packages.lua b/xmake/modules/private/action/require/impl/lock_packages.lua new file mode 100644 index 000000000..6a962ba77 --- /dev/null +++ b/xmake/modules/private/action/require/impl/lock_packages.lua @@ -0,0 +1,72 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file lock_packages.lua +-- + +-- imports +import("core.project.project") +import("core.project.config") +import("devel.git") +import("private.action.require.impl.utils.filter") +import("private.action.require.impl.utils.requirekey") + +-- get locked package key +function _get_packagelock_key(instance) + local requireinfo = instance:requireinfo() + return requireinfo and requireinfo.requirekey +end + +-- lock package +function _lock_package(instance) + local result = {} + local repo = instance:repo() + result.version = instance:version_str() + result.branch = instance:branch() + result.tag = instance:tag() + if repo then + local lastcommit + local manifest = instance:manifest_load() + if manifest and manifest.repo then + lastcommit = manifest.repo.commit + end + if not lastcommit then + lastcommit = repo:commit() + end + result.repo = {url = repo:url(), commit = lastcommit, branch = repo:branch()} + end + return result +end + +-- lock all required packages +function main(packages) + if project.policy("package.requires_lock") then + local plat = config.plat() or os.subhost() + local arch = config.arch() or so.subarch() + local key = plat .. "|" .. arch + local results = os.isfile(project.requireslock()) and io.load(project.requireslock()) or {} + results.__meta__ = results.__meta__ or {} + results.__meta__.version = project.requireslock_version() + results[key] = {} + for _, instance in ipairs(packages) do + local packagelock_key = _get_packagelock_key(instance) + results[key][packagelock_key] = _lock_package(instance) + end + io.writefile(project.requireslock(), string.serialize(results, {orderkeys = true})) + end +end + diff --git a/xmake/modules/private/action/require/impl/package.lua b/xmake/modules/private/action/require/impl/package.lua index 612911429..1d013b91e 100644 --- a/xmake/modules/private/action/require/impl/package.lua +++ b/xmake/modules/private/action/require/impl/package.lua @@ -23,13 +23,15 @@ import("core.base.semver") import("core.base.option") import("core.base.global") import("core.base.hashset") -import("private.utils.progress") +import("utils.progress") import("core.cache.memcache") import("core.project.project") +import("core.project.config") import("core.tool.toolchain") import("core.package.package", {alias = "core_package"}) import("devel.git") import("private.action.require.impl.repository") +import("private.action.require.impl.utils.requirekey", {alias = "_get_requirekey"}) -- get memcache function _memcache() @@ -150,8 +152,23 @@ function _load_require(require_str, requires_extra, parentinfo) -- require packge in the current host platform if require_extra.host then - require_extra.plat = os.host() - require_extra.arch = os.arch() + if is_subhost(core_package.targetplat()) and os.subarch() == core_package.targetarch() then + -- we need pass plat/arch to avoid repeat installation + -- @see https://github.com/xmake-io/xmake/issues/1579 + else + require_extra.plat = os.subhost() + require_extra.arch = os.subarch() + end + end + + -- check require options + local extra_options = hashset.of("plat", "arch", "kind", "host", "targetos", + "alias", "group", "system", "option", "default", "optional", "debug", + "verify", "external", "private", "build", "configs", "version") + for name, value in pairs(require_extra) do + if not extra_options:has(name) then + wprint("add_requires(\"%s\") has unknown option: {%s=%s}!", require_str, name, tostring(value)) + end end -- init required item @@ -193,13 +210,41 @@ function _load_package_from_project(packagename) end -- load package package from repositories -function _load_package_from_repository(packagename, reponame) - local packagedir, repo = repository.packagedir(packagename, reponame) +function _load_package_from_repository(packagename, opt) + local packagedir, repo = repository.packagedir(packagename, opt) if packagedir then return core_package.load_from_repository(packagename, repo, packagedir) end end +-- has locked requires? +function _has_locked_requires(opt) + opt = opt or {} + if not option.get("upgrade") or opt.force then + return project.policy("package.requires_lock") and os.isfile(project.requireslock()) + end +end + +-- get locked requires +function _get_locked_requires(requirekey, opt) + opt = opt or {} + local requireslock = _memcache():get("requireslock") + if requireslock == nil or opt.force then + if _has_locked_requires(opt) then + requireslock = io.load(project.requireslock()) + end + _memcache():set("requireslock", requireslock or false) + end + if requireslock then + local plat = config.plat() or os.subhost() + local arch = config.arch() or os.subarch() + local key = plat .. "|" .. arch + if requireslock[key] then + return requireslock[key][requirekey], requireslock.__meta__.version + end + end +end + -- sort package deps -- -- e.g. @@ -209,18 +254,39 @@ end -- -- orderdeps: c -> b -> a -- -function _sort_packagedeps(package, onlylink) +function _sort_packagedeps(package) -- we must use native deps list instead of package:deps() to generate correct linkdeps local orderdeps = {} for _, dep in ipairs(package:plaindeps()) do - if dep and (onlylink ~= true or (dep:is_library() and not dep:is_private())) then - table.join2(orderdeps, _sort_packagedeps(dep, onlylink)) + if dep then + table.join2(orderdeps, _sort_packagedeps(dep)) table.insert(orderdeps, dep) end end return orderdeps end +-- sort link deps +-- +-- e.g. +-- +-- a.deps = b +-- b.deps = c +-- +-- orderdeps: a -> b -> c +-- +function _sort_linkdeps(package) + -- we must use native deps list instead of package:deps() to generate correct linkdeps + local orderdeps = {} + for _, dep in ipairs(package:plaindeps()) do + if dep and dep:is_library() and not dep:is_private() then + table.insert(orderdeps, dep) + table.join2(orderdeps, _sort_linkdeps(dep)) + end + end + return orderdeps +end + -- add some builtin configurations to package function _add_package_configurations(package) -- we can define configs to override it and it's default value in package() @@ -246,10 +312,22 @@ function _add_package_configurations(package) end -- select package version -function _select_package_version(package, requireinfo) +function _select_package_version(package, requireinfo, locked_requireinfo) - -- exists urls? otherwise be phony package (only as package group) - if #package:urls() > 0 then + -- get it from the locked requireinfo + if locked_requireinfo then + local version = locked_requireinfo.version + local source = "version" + if locked_requireinfo.branch then + source = "branch" + elseif locked_requireinfo.tag then + source = "tag" + end + return version, source + end + + -- if not phony package (only as package group) + if not requireinfo.group then -- has git url? local has_giturl = false @@ -270,14 +348,19 @@ function _select_package_version(package, requireinfo) -- @see https://github.com/xmake-io/xmake/issues/930 -- https://github.com/xmake-io/xmake/issues/1009 version = require_version - source = "versions" + source = "version" elseif #package:versions() > 0 then -- select version? version, source = try { function () return semver.select(require_version, package:versions()) end } end - if not version and has_giturl and not require_version:find('.', 1, true) then -- select branch? - version, source = require_version ~= "latest" and require_version or "master", "branches" + if not version and has_giturl and not semver.is_valid(require_version) then -- select branch? + version, source = require_version ~= "latest" and require_version or "master", "branch" end - if not version then + -- local source package? we use a phony version + if not version and require_version == "latest" and #package:urls() == 0 then + version = "latest" + source = "version" + end + if not version and not package:is_thirdparty() then raise("package(%s): version(%s) not found!", package:name(), require_version) end return version, source @@ -477,26 +560,17 @@ end -- get package key function _get_packagekey(packagename, requireinfo, version) - local key = packagename .. "/" .. (version or requireinfo.version) - if requireinfo.plat then - key = key .. "/" .. requireinfo.plat - end - if requireinfo.arch then - key = key .. "/" .. requireinfo.arch - end - if requireinfo.label then - key = key .. "/" .. requireinfo.label - end - local configs = requireinfo.configs - if configs then - local configs_order = {} - for k, v in pairs(configs) do - table.insert(configs_order, k .. "=" .. tostring(v)) - end - table.sort(configs_order) - key = key .. ":" .. string.serialize(configs_order, true) - end - return key + return _get_requirekey(requireinfo, {name = packagename, + plat = requireinfo.plat, + arch = requireinfo.arch, + version = version or requireinfo.version}) +end + +-- get locked package key +function _get_packagelock_key(requireinfo) + local requirestr = requireinfo.originstr + local key = _get_requirekey(requireinfo, {hash = true}) + return string.format("%s#%s", requirestr, key) end -- inherit some builtin configs of parent package if these config values are not default value @@ -520,9 +594,6 @@ function _inherit_parent_configs(requireinfo, package, parentinfo) if parentinfo.arch then requireinfo.arch = parentinfo.arch end - if parentinfo.private ~= nil then - requireinfo.private = parentinfo.private - end requireinfo_configs.toolchains = requireinfo_configs.toolchains or parentinfo_configs.toolchains requireinfo_configs.vs_runtime = requireinfo_configs.vs_runtime or parentinfo_configs.vs_runtime requireinfo.configs = requireinfo_configs @@ -610,6 +681,13 @@ function _load_package(packagename, requireinfo, opt) requireinfo.label = splitinfo[2] end + -- save requirekey + local requirekey = _get_packagelock_key(requireinfo) + requireinfo.requirekey = requirekey + + -- get locked requireinfo + local locked_requireinfo = get_locked_requireinfo(requireinfo) + -- load package from project first local package if os.isfile(os.projectfile()) then @@ -619,7 +697,8 @@ function _load_package(packagename, requireinfo, opt) -- load package from repositories local from_repo = false if not package then - package = _load_package_from_repository(packagename, requireinfo.reponame) + package = _load_package_from_repository(packagename, { + name = requireinfo.reponame, locked_repo = locked_requireinfo and locked_requireinfo.repo}) if package then from_repo = true end @@ -652,7 +731,7 @@ function _load_package(packagename, requireinfo, opt) _finish_requireinfo(requireinfo, package) -- select package version - local version, source = _select_package_version(package, requireinfo) + local version, source = _select_package_version(package, requireinfo, locked_requireinfo) if version then package:version_set(version, source) end @@ -760,7 +839,7 @@ function _load_packages(requires, opt) package._DEPS = packagedeps package._PLAINDEPS = plaindeps package._ORDERDEPS = table.unique(_sort_packagedeps(package)) - package._LINKDEPS = table.unique(_sort_packagedeps(package, true)) + package._LINKDEPS = table.reverse_unique(_sort_linkdeps(package)) end end @@ -889,7 +968,7 @@ function get_configs_str(package) if type(v) == "boolean" then table.insert(configs, k .. ":" .. (v and "y" or "n")) else - table.insert(configs, k .. ":" .. v) + table.insert(configs, k .. ":" .. string.serialize(v, {strip = true, indent = false})) end end end @@ -898,13 +977,26 @@ function get_configs_str(package) table.insert(configs, "from:" .. parents_str) end local configs_str = #configs > 0 and "[" .. table.concat(configs, ", ") .. "]" or "" - local limitwidth = os.getwinsize().width * 2 / 3 + local limitwidth = math.floor(os.getwinsize().width * 2 / 3) if #configs_str > limitwidth then configs_str = configs_str:sub(1, limitwidth) .. " ..)" end return configs_str end +-- get locked requireinfo +function get_locked_requireinfo(requireinfo, opt) + local requirekey = requireinfo.requirekey + local locked_requireinfo, requireslock_version + if _has_locked_requires(opt) and requirekey then + locked_requireinfo, requireslock_version = _get_locked_requires(requirekey, opt) + if requireslock_version and semver.compare(project.requireslock_version(), requireslock_version) < 0 then + locked_requireinfo = nil + end + end + return locked_requireinfo, requireslock_version +end + -- load requires function load_requires(requires, requires_extra, opt) opt = opt or {} diff --git a/xmake/modules/private/action/require/impl/packagenv.lua b/xmake/modules/private/action/require/impl/packagenv.lua index 12a98d36f..06a70df91 100644 --- a/xmake/modules/private/action/require/impl/packagenv.lua +++ b/xmake/modules/private/action/require/impl/packagenv.lua @@ -33,7 +33,7 @@ function _enter_package(package_name, envs, installdir) end end else - os.addenv(name, unpack(table.wrap(values))) + os.addenv(name, table.unpack(table.wrap(values))) end end end diff --git a/xmake/modules/private/action/require/impl/register_packages.lua b/xmake/modules/private/action/require/impl/register_packages.lua index 4c326e770..afc84eb98 100644 --- a/xmake/modules/private/action/require/impl/register_packages.lua +++ b/xmake/modules/private/action/require/impl/register_packages.lua @@ -88,16 +88,9 @@ function _register_required_package(instance, required_package) _register_required_package_base(instance, required_package) _register_required_package_libs(instance, required_package) _register_required_package_envs(instance, envs) - local linkdeps = instance:linkdeps() - if linkdeps then - local total = #linkdeps - for idx, _ in ipairs(linkdeps) do - local dep = linkdeps[total + 1 - idx] - if dep then - if instance:is_library() then - _register_required_package_libs(dep, required_package, true) - end - end + for _, dep in ipairs(instance:linkdeps()) do + if instance:is_library() then + _register_required_package_libs(dep, required_package, true) end end for _, dep in ipairs(instance:orderdeps()) do diff --git a/xmake/modules/private/action/require/impl/remove_packages.lua b/xmake/modules/private/action/require/impl/remove_packages.lua index b65627256..f8f93c663 100644 --- a/xmake/modules/private/action/require/impl/remove_packages.lua +++ b/xmake/modules/private/action/require/impl/remove_packages.lua @@ -36,7 +36,7 @@ function _get_package_configs_str(manifest_file) end end local configs_str = #configs > 0 and "[" .. table.concat(configs, ", ") .. "]" or "" - local limitwidth = os.getwinsize().width * 2 / 3 + local limitwidth = math.floor(os.getwinsize().width * 2 / 3) if #configs_str > limitwidth then configs_str = configs_str:sub(1, limitwidth) .. " ..)" end diff --git a/xmake/modules/private/action/require/impl/repository.lua b/xmake/modules/private/action/require/impl/repository.lua index 4dbf44e52..189fad594 100644 --- a/xmake/modules/private/action/require/impl/repository.lua +++ b/xmake/modules/private/action/require/impl/repository.lua @@ -19,8 +19,86 @@ -- -- imports +import("core.base.option") import("core.base.global") +import("core.project.config") import("core.package.repository") +import("devel.git") +import("net.proxy") + +-- get package directory from the locked repository +function _get_packagedir_from_locked_repo(packagename, locked_repo) + + -- find global repository directory + local repo_global + for _, repo in ipairs(repositories()) do + if locked_repo.url == repo:url() and locked_repo.branch == repo:branch() then + repo_global = repo + break + end + end + local reponame = hash.uuid(locked_repo.url):gsub("%-", ""):lower() .. ".lock" + + -- get local repodir + local repodir_local + if os.isdir(locked_repo.url) then + repodir_local = locked_repo.url + elseif not locked_repo.commit and repo_global then + repodir_local = repo_global:directory() + else + repodir_local = path.join(config.directory(), "repositories", reponame) + end + + -- clone repository to local + local lastcommit + if not os.isdir(repodir_local) then + if repo_global then + git.clone(repo_global:directory(), {verbose = option.get("verbose"), outputdir = repodir_local}) + lastcommit = repo_global:commit() + elseif global.get("network") ~= "private" then + local remoteurl = proxy.mirror(locked_repo.url) or locked_repo.url + git.clone(remoteurl, {verbose = option.get("verbose"), branch = locked_repo.branch, outputdir = repodir_local}) + else + wprint("we cannot lock repository(%s) in private network mode!", locked_repo.url) + return + end + end + + -- lock commit + if locked_repo.commit and os.isdir(path.join(repodir_local, ".git")) then + lastcommit = lastcommit or try {function() + return git.lastcommit({repodir = repodir_local}) + end} + if locked_repo.commit ~= lastcommit then + -- try checkout to the given commit + local ok = try {function () git.checkout(locked_repo.commit, {verbose = option.get("verbose"), repodir = repodir_local}); return true end} + if not ok then + if global.get("network") ~= "private" then + -- pull the latest commit + local remoteurl = proxy.mirror(locked_repo.url) or locked_repo.url + git.pull({verbose = option.get("verbose"), remote = remoteurl, branch = locked_repo.branch, repodir = repodir_local}) + -- re-checkout to the given commit + ok = try {function () git.checkout(locked_repo.commit, {verbose = option.get("verbose"), repodir = repodir_local}); return true end} + else + wprint("we cannot lock repository(%s) in private network mode!", locked_repo.url) + return + end + end + end + end + + -- find package directory + local foundir + if ok then + local dir = path.join(repodir_local, "packages", packagename:sub(1, 1), packagename) + if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) then + local repo = repository.load(reponame, locked_repo.url, locked_repo.branch, false) + foundir = {dir, repo} + vprint("lock package(%s) in %s from repository(%s)/%s", packagename, dir, locked_repo.url, locked_repo.commit) + end + end + return foundir +end -- get all repositories function repositories() @@ -48,34 +126,66 @@ function pulled() end -- get package directory from repositories -function packagedir(packagename, reponame) +function packagedir(packagename, opt) -- strip trailng ~tag, e.g. zlib~debug + opt = opt or {} packagename = packagename:lower() if packagename:find('~', 1, true) then packagename = packagename:gsub("~.+$", "") end - -- get it from cache it - local packagedirs = _g._PACKAGEDIRS or {} - local foundir = packagedirs[packagename] - if foundir then - return foundir[1], foundir[2] + -- get cache key + local reponame = opt.name + local cachekey = packagename + local locked_repo = opt.locked_repo + if locked_repo then + cachekey = cachekey .. locked_repo.url .. (locked_repo.commit or "") .. (locked_repo.branch or "") + end + local packagedirs = _g._PACKAGEDIRS + if not packagedirs then + packagedirs = {} + _g._PACKAGEDIRS = packagedirs end - -- find the package directory from repositories - for _, repo in ipairs(repositories()) do - local dir = path.join(repo:directory(), "packages", packagename:sub(1, 1), packagename) - if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) and (not reponame or reponame == repo:name()) then - foundir = {dir, repo} - break + -- get the package directory + local foundir = packagedirs[cachekey] + if not foundir then + + -- find the package directory from the locked repository + if locked_repo then + local dir, repo = _get_packagedir_from_locked_repo(packagename, locked_repo) + if dir and repo then + foundir = {dir, repo} + end + end + + -- find the package directory from repositories + if not foundir then + for _, repo in ipairs(repositories()) do + local dir = path.join(repo:directory(), "packages", packagename:sub(1, 1), packagename) + if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) and (not reponame or reponame == repo:name()) then + foundir = {dir, repo} + break + end + end end + foundir = foundir or {} + packagedirs[cachekey] = foundir end - if foundir then - packagedirs[packagename] = foundir - _g._PACKAGEDIRS = packagedirs - return foundir[1], foundir[2] + + -- save the current commit + local dir = foundir[1] + local repo = foundir[2] + if repo and not repo:commit() then + local lastcommit = try {function() + if os.isdir(path.join(repo:directory(), ".git")) then + return git.lastcommit({repodir = repo:directory()}) + end + end} + repo:commit_set(lastcommit) end + return dir, repo end -- get artifacts manifest from repositories diff --git a/xmake/modules/private/action/require/impl/search_packages.lua b/xmake/modules/private/action/require/impl/search_packages.lua index 00cf1ffbf..276747db4 100644 --- a/xmake/modules/private/action/require/impl/search_packages.lua +++ b/xmake/modules/private/action/require/impl/search_packages.lua @@ -22,7 +22,7 @@ function _search_packages(name) -- get package manager name - local manager_name, package_name = unpack(name:split("::", {plain = true, strict = true})) + local manager_name, package_name = table.unpack(name:split("::", {plain = true, strict = true})) if package_name == nil then package_name = manager_name manager_name = "xmake" diff --git a/xmake/modules/private/action/require/impl/utils/requirekey.lua b/xmake/modules/private/action/require/impl/utils/requirekey.lua new file mode 100644 index 000000000..d1792e50c --- /dev/null +++ b/xmake/modules/private/action/require/impl/utils/requirekey.lua @@ -0,0 +1,60 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file requirekey.lua +-- + +-- get require key from requireinfo +function main(requireinfo, opt) + opt = opt or {} + local key = "" + if opt.name then + key = key .. "/" .. opt.name + end + if opt.plat then + key = key .. "/" .. opt.plat + end + if opt.arch then + key = key .. "/" .. opt.arch + end + if opt.version then + key = key .. "/" .. opt.version + end + if requireinfo.label then + key = key .. "/" .. requireinfo.label + end + if key:startswith("/") then + key = key:sub(2) + end + local configs = requireinfo.configs + if configs then + local configs_order = {} + for k, v in pairs(configs) do + table.insert(configs_order, k .. "=" .. tostring(v)) + end + table.sort(configs_order) + key = key .. ":" .. string.serialize(configs_order, true) + end + if opt.hash then + if key == "" then + key = "_" -- we need generate a fixed hash value + end + return hash.uuid(key):split("-", {plain = true})[1]:lower() + else + return key + end +end diff --git a/xmake/modules/private/action/require/install.lua b/xmake/modules/private/action/require/install.lua index 821991dee..e27e627cb 100644 --- a/xmake/modules/private/action/require/install.lua +++ b/xmake/modules/private/action/require/install.lua @@ -76,7 +76,7 @@ function main(requires_raw) -- -- attempt to install git from the builtin-packages first if git not found -- - if git and not repository.pulled() then + if git and (not repository.pulled() or option.get("upgrade")) then task.run("repo", {update = true}) end diff --git a/xmake/modules/private/action/trybuild/autotools.lua b/xmake/modules/private/action/trybuild/autotools.lua index 14434f1c5..1d4090522 100644 --- a/xmake/modules/private/action/trybuild/autotools.lua +++ b/xmake/modules/private/action/trybuild/autotools.lua @@ -19,7 +19,6 @@ -- -- imports -import("core.base.cli") import("core.base.option") import("core.project.config") import("core.platform.platform") @@ -142,7 +141,7 @@ function _get_configs(artifacts_dir) -- add extra user configs local tryconfigs = config.get("tryconfigs") if tryconfigs then - for _, opt in ipairs(cli.parse(tryconfigs)) do + for _, opt in ipairs(os.argv(tryconfigs)) do table.insert(configs, tostring(opt)) end end diff --git a/xmake/modules/private/action/trybuild/cmake.lua b/xmake/modules/private/action/trybuild/cmake.lua index e2022b7b8..4217e98a5 100644 --- a/xmake/modules/private/action/trybuild/cmake.lua +++ b/xmake/modules/private/action/trybuild/cmake.lua @@ -19,7 +19,6 @@ -- -- imports -import("core.base.cli") import("core.base.option") import("core.project.config") import("core.tool.toolchain") @@ -199,7 +198,7 @@ function _get_configs(artifacts_dir) -- add extra user configs local tryconfigs = config.get("tryconfigs") if tryconfigs then - for _, opt in ipairs(cli.parse(tryconfigs)) do + for _, opt in ipairs(os.argv(tryconfigs)) do table.insert(configs, tostring(opt)) end end diff --git a/xmake/modules/private/action/trybuild/meson.lua b/xmake/modules/private/action/trybuild/meson.lua index 312e24d22..f2baaff21 100644 --- a/xmake/modules/private/action/trybuild/meson.lua +++ b/xmake/modules/private/action/trybuild/meson.lua @@ -19,7 +19,6 @@ -- -- imports -import("core.base.cli") import("core.base.option") import("core.project.config") import("lib.detect.find_file") @@ -47,7 +46,7 @@ function _get_configs(artifacts_dir, buildir) -- add extra user configs local tryconfigs = config.get("tryconfigs") if tryconfigs then - for _, opt in ipairs(cli.parse(tryconfigs)) do + for _, opt in ipairs(os.argv(tryconfigs)) do table.insert(configs, tostring(opt)) end end diff --git a/xmake/modules/private/async/jobpool.lua b/xmake/modules/private/async/jobpool.lua index 299700534..f7eef0c1b 100644 --- a/xmake/modules/private/async/jobpool.lua +++ b/xmake/modules/private/async/jobpool.lua @@ -35,8 +35,23 @@ function jobpool:rootjob() return self._rootjob end +-- new run job +-- +-- e.g. +-- local job = jobpool:newjob("xxx", function (index, total) end) +-- jobpool:add(job, rootjob1) +-- jobpool:add(job, rootjob2) +-- jobpool:add(job, rootjob3) +-- +function jobpool:newjob(name, run) + return {name = name, run = run} +end + -- add run job to the given job node -- +-- e.g. +-- local job = jobpool:addjob("xxx", function (index, total) end, {rootjob = rootjob}) +-- -- @param name the job name -- @param run the run command/script -- @param opt the options (rootjob) diff --git a/xmake/modules/private/async/runjobs.lua b/xmake/modules/private/async/runjobs.lua index 0361399dc..a4ea2bc10 100644 --- a/xmake/modules/private/async/runjobs.lua +++ b/xmake/modules/private/async/runjobs.lua @@ -20,7 +20,7 @@ -- imports import("core.base.scheduler") -import("private.utils.progress") +import("utils.progress") -- print back characters function _print_backchars(backnum) @@ -37,7 +37,7 @@ end -- e.g. -- runjobs("test", function (index) print("hello") end, {total = 100, comax = 6, timeout = 1000, on_timer = function (running_jobs_indices) end}) -- runjobs("test", function () os.sleep(10000) end, { progress = true }) --- runjobs("test", function () os.sleep(10000) end, { progress = { chars = {'/','\'} } }) -- see module private.utils.progress +-- runjobs("test", function () os.sleep(10000) end, { progress = { chars = {'/','\'} } }) -- see module utils.progress -- -- local jobs = jobpool.new() -- local root = jobs:addjob("job/root", function (idx, total) diff --git a/xmake/modules/private/tools/gcc/parse_deps.lua b/xmake/modules/private/tools/gcc/parse_deps.lua index cb06d091c..3b682753c 100644 --- a/xmake/modules/private/tools/gcc/parse_deps.lua +++ b/xmake/modules/private/tools/gcc/parse_deps.lua @@ -54,9 +54,18 @@ end -- src/tbox/libc/string/../../prefix/../config.h \ -- build/iphoneos/x86_64/release/tbox.config.h \ -- +-- with c++ modules: +-- build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o: src/foo.mpp\ +-- build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o gcm.cache/foo.gcm: bar.c++m cat.c++m\ +-- foo.c++m: gcm.cache/foo.gcm\ +-- .PHONY: foo.c++m\ +-- gcm.cache/foo.gcm:| build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o\ +-- CXX_IMPORTS += bar.c++m cat.c++m\ +-- function main(depsdata) -- we assume there is only one valid line + local block = 0 local results = hashset.new() local projectdir = os.projectdir() local line = depsdata:rtrim() -- maybe there will be an empty newline at the end. so we trim it first @@ -68,8 +77,15 @@ function main(depsdata) if is_host("windows") and includefile:match("^%w\\:") then includefile = includefile:replace("\\:", ":", plain) end - if not includefile:endswith(":") then -- ignore "xxx.o:" prefix + if includefile:endswith(":") then -- ignore "xxx.o:" prefix + block = block + 1 + if block > 1 then + -- skip other `xxx.o:` block + break + end + else includefile = includefile:replace(space_placeholder, ' ', plain) + includefile = includefile:split("\n", {plain = true})[1] if #includefile > 0 then includefile = _normailize_dep(includefile, projectdir) if includefile then diff --git a/xmake/modules/private/utils/batchcmds.lua b/xmake/modules/private/utils/batchcmds.lua index 371735321..bb5204072 100644 --- a/xmake/modules/private/utils/batchcmds.lua +++ b/xmake/modules/private/utils/batchcmds.lua @@ -28,7 +28,7 @@ import("core.theme.theme") import("core.tool.linker") import("core.tool.compiler") import("core.language.language") -import("private.utils.progress", {alias = "progress_utils"}) +import("utils.progress", {alias = "progress_utils"}) -- define module local batchcmds = batchcmds or object { _init = {"_TARGET", "_CMDS", "_DEPS", "_tip"}} @@ -117,6 +117,14 @@ function _runcmd_mkdir(cmd, opt) end end +-- run command: os.cd +function _runcmd_cd(cmd, opt) + local dir = cmd.dir + if not opt.dryrun then + os.cd(dir) + end +end + -- run command: os.rm function _runcmd_rm(cmd, opt) local filepath = cmd.filepath @@ -128,21 +136,21 @@ end -- run command: os.cp function _runcmd_cp(cmd, opt) if not opt.dryrun then - os.cp(opt.srcpath, opt.dstpath, opt.opt) + os.cp(cmd.srcpath, cmd.dstpath, opt.opt) end end -- run command: os.mv function _runcmd_mv(cmd, opt) if not opt.dryrun then - os.mv(opt.srcpath, opt.dstpath, opt.opt) + os.mv(cmd.srcpath, cmd.dstpath, opt.opt) end end -- run command: os.ln function _runcmd_ln(cmd, opt) if not opt.dryrun then - os.ln(opt.srcpath, opt.dstpath, opt.opt) + os.ln(cmd.srcpath, cmd.dstpath, opt.opt) end end @@ -158,6 +166,7 @@ function _runcmd(cmd, opt) vrunv = _runcmd_vrunv, execv = _runcmd_execv, mkdir = _runcmd_mkdir, + cd = _runcmd_cd, rm = _runcmd_rm, cp = _runcmd_cp, mv = _runcmd_mv, @@ -268,6 +277,11 @@ function batchcmds:ln(srcpath, dstpath, opt) table.insert(self:cmds(), {kind = "ln", srcpath = srcpath, dstpath = dstpath, opt = opt}) end +-- add command: os.cd +function batchcmds:cd(dir, opt) + table.insert(self:cmds(), {kind = "cd", dir = dir, opt = opt}) +end + -- add command: show function batchcmds:show(format, ...) local showtext = string.format(format, ...) diff --git a/xmake/modules/private/utils/bin2c.lua b/xmake/modules/private/utils/bin2c.lua index 91ea1eafe..6f18833fc 100644 --- a/xmake/modules/private/utils/bin2c.lua +++ b/xmake/modules/private/utils/bin2c.lua @@ -23,9 +23,10 @@ import("core.base.bytes") import("core.base.option") local options = { - {'w', "linewidth", "kv", nil, "Set the line width"}, - {'i', "binarypath", "kv", nil, "Set the binary file path."}, - {'o', "outputpath", "kv", nil, "Set the output file path."} + {'w', "linewidth", "kv", nil, "Set the line width"}, + {nil, "nozeroend", "k", false, "Disable to patch zero terminating character"}, + {'i', "binarypath", "kv", nil, "Set the binary file path."}, + {'o', "outputpath", "kv", nil, "Set the output file path."} } function _do_dump(binarydata, outputfile, opt) @@ -84,7 +85,9 @@ function _do_bin2c(binarypath, outputpath, opt) local binarydata = bytes(io.readfile(binarypath, {encoding = "binary"})) local outputfile = io.open(outputpath, 'w') if outputfile then - binarydata = binarydata .. bytes('\0') + if not opt.nozeroend then + binarydata = binarydata .. bytes('\0') + end _do_dump(binarydata, outputfile, opt) outputfile:close() end diff --git a/xmake/modules/private/xrepo/action/env.lua b/xmake/modules/private/xrepo/action/env.lua index cb6936006..bf5b2ec44 100644 --- a/xmake/modules/private/xrepo/action/env.lua +++ b/xmake/modules/private/xrepo/action/env.lua @@ -22,6 +22,7 @@ import("core.base.option") import("core.base.task") import("core.base.hashset") +import("core.base.global") import("core.project.config") import("core.project.project") import("core.tool.toolchain") @@ -47,16 +48,29 @@ function menu_options() {nil, "show", "k", nil, "Only show environment information." }, {'f', "configs", "kv", nil, "Set the given extra package configs.", "e.g.", - " - xrepo env -f \"vs_runtime=MD\" zlib cmake ..", + " - xrepo env -f \"vs_runtime='MD'\" zlib cmake ..", " - xrepo env -f \"regex=true,thread=true\" \"zlib,boost\" cmake .."}, - {'b', "packages", "kv", nil, "Set the packages to be bound", + {nil, "add", "k", nil, "Add global environment config.", "e.g.", + " - xrepo env --add base.lua", + " - xrepo env --add myenv.lua"}, + {nil, "remove", "k", nil, "Remove global environment config.", + "e.g.", + " - xrepo env --remove base", + " - xrepo env --remove myenv"}, + {"l", "list", "k", nil, "List all global environment configs.", + "e.g.", + " - xrepo env --list"}, + {'b', "bind", "kv", nil, "Bind the specified environment or package.", + "e.g.", + " - xrepo env -b base", + " - xrepo env -b myenv", " - xrepo env -b \"python 3.x\" python", " - xrepo env -b \"llvm 11.x\" bash", " $ clang --version", " - xrepo env -p android -b \"zlib,luajit 2.x\" luajit xx.lua"}, {}, - {nil, "program", "v", nil, "Set the program name to be run", + {nil, "program", "v", nil, "Set the program name to be run.", "e.g.", " - xrepo env", " - xrepo env bash", @@ -70,7 +84,7 @@ function menu_options() local function show_options() -- show usage - cprint("${bright}Usage: $${clear cyan}xrepo env [options] [packages] [program] [arguments]") + cprint("${bright}Usage: $${clear cyan}xrepo env [options] [program] [arguments]") -- show description print("") @@ -112,9 +126,10 @@ function _get_requires(packages) end -- enter project -function _enter_project() +function _enter_project(opt) -- enter working project directory + opt = opt or {} local workdir = path.join(os.tmpdir(), "xrepo", "working") if not os.isdir(workdir) then os.mkdir(workdir) @@ -123,6 +138,10 @@ function _enter_project() else os.cd(workdir) end + if opt.enteronly then + project.chdir(workdir) + return + end -- do configure first local config_argv = {"f", "-c"} @@ -157,7 +176,7 @@ function _enter_project() end -- remove repeat environment values -function _remove_repeat_pathenv(value) +function _deduplicate_pathenv(value) if value then local itemset = {} local results = {} @@ -174,6 +193,23 @@ function _remove_repeat_pathenv(value) return value end +-- get environment directory +function _get_envsdir() + return path.join(global.directory(), "envs") +end + +-- get bound environment or packages +function _get_boundenv(opt) + local bind = (opt and opt.bind) or option.get("bind") + if bind then + local envfile = path.join(_get_envsdir(), bind .. ".lua") + if envfile and os.isfile(envfile) then + return envfile + end + end + return bind +end + -- add values to environment variable function _addenvs(envs, name, ...) local values = {...} @@ -208,7 +244,7 @@ function _package_addenvs(envs, instance) end end else - _addenvs(envs, name, unpack(table.wrap(values))) + _addenvs(envs, name, table.unpack(table.wrap(values))) end end @@ -227,6 +263,13 @@ function _package_addenvs(envs, instance) _addenvs(envs, "ACLOCAL_PATH", aclocal) end _addenvs(envs, "CMAKE_PREFIX_PATH", installdir) + if instance:is_plat("windows") then + _addenvs(envs, "INCLUDE", path.join(installdir, "include")) + _addenvs(envs, "LIBPATH", path.join(installdir, "lib")) + else + _addenvs(envs, "CPATH", path.join(installdir, "include")) + _addenvs(envs, "LIBRARY_PATH", path.join(installdir, "lib")) + end end end @@ -237,36 +280,47 @@ function _toolchain_addenvs(envs) local toolchain_inst = toolchain.load(name, toolchain_opt) if toolchain_inst then for k, v in pairs(toolchain_inst:runenvs()) do - _addenvs(envs, k, unpack(path.splitenv(v))) + _addenvs(envs, k, table.unpack(path.splitenv(v))) end end end end -- get package environments -function _package_getenvs() +function _package_getenvs(opt) local envs = os.getenvs() - if os.isfile(os.projectfile()) and not option.get("packages") then + local boundenv = _get_boundenv(opt) + local has_envfile = false + local packages = nil + if boundenv and os.isfile(boundenv) then + has_envfile = true + else + packages = boundenv or option.get("program") + end + if os.isfile(os.projectfile()) or has_envfile then + if not os.isfile(os.projectfile()) then + _enter_project({enteronly = true}) + end + if has_envfile then + table.insert(project.rcfiles(), boundenv) + end task.run("config", {target = "all"}, {disable_dump = true}) _toolchain_addenvs(envs) local requires, requires_extra = get_requires() for _, instance in ipairs(package.load_packages(requires, {requires_extra = requires_extra})) do _package_addenvs(envs, instance) end - else - local packages = option.get("packages") or option.get("program") - if packages then - _enter_project() - packages = packages:split(',', {plain = true}) - local requires, requires_extra = _get_requires(packages) - for _, instance in ipairs(package.load_packages(requires, {requires_extra = requires_extra})) do - _package_addenvs(envs, instance) - end + elseif packages then + _enter_project() + packages = packages:split(',', {plain = true}) + local requires, requires_extra = _get_requires(packages) + for _, instance in ipairs(package.load_packages(requires, {requires_extra = requires_extra})) do + _package_addenvs(envs, instance) end end local results = {} for k, v in pairs(envs) do - results[k] = _remove_repeat_pathenv(v) + results[k] = _deduplicate_pathenv(v) end return results end @@ -285,40 +339,66 @@ function _get_env_script(envs, shell, del) elseif shell == "cmd" then prefix = "@set \"" suffix = "\"" + elseif shell:endswith("sh") then + if del then + prefix = "unset '" + connector = "'" + else + prefix = "export '" + connector = "'='" + suffix = "'" + end end + local exceptions = hashset.of("_", "PS1", "PROMPT", "!;", "!EXITCODE") local ret = "" if del then for name, _ in pairs(envs) do - ret = ret .. prefix .. name .. connector .. default .. suffix .. "\n" + if not exceptions:has(name) then + ret = ret .. prefix .. name .. connector .. default .. suffix .. "\n" + end end else for name, value in pairs(envs) do - ret = ret .. prefix .. name .. connector .. value .. suffix .. "\n" + if not exceptions:has(name) then + ret = ret .. prefix .. name .. connector .. value .. suffix .. "\n" + end end end return ret end -- get information of current virtual environment -function info(key) +function info(key, bnd) if key == "prompt" then - assert(os.isfile(os.projectfile()), "xmake.lua not found!") - print("[%s]", path.filename(os.projectdir())) + local boundenv = _get_boundenv({bind = bnd}) + if boundenv then + assert(os.isfile(boundenv), "environment not found!") + io.write("[" .. path.basename(boundenv) .. "]") + elseif not bnd then + assert(os.isfile(os.projectfile()), "xmake.lua not found!") + io.write("[" .. path.filename(os.projectdir()) .. "]") + end elseif key == "envfile" then print(os.tmpfile()) elseif key == "config" then - if os.isfile(os.projectfile()) then + local boundenv = _get_boundenv({bind = bnd}) + local has_envfile = (boundenv and os.isfile(boundenv)) and true or false + if has_envfile or os.isfile(os.projectfile()) then + if has_envfile then + _enter_project({enteronly = true}) + table.insert(project.rcfiles(), boundenv) + end task.run("config", {target = "all"}, {disable_dump = true}) end elseif key:startswith("script.") then local shell = key:match("script%.(.+)") - print(_get_env_script(_package_getenvs(), shell, false)) + io.write(_get_env_script(_package_getenvs({bind = bnd}), shell, false)) elseif key:startswith("backup.") then local shell = key:match("backup%.(.+)") -- remove current environment variables first - print(_get_env_script(_package_getenvs(), shell, true)) - print(_get_env_script(os.getenvs(), shell, false)) + io.write(_get_env_script(_package_getenvs({bind = bnd}), shell, true)) + io.write(_get_env_script(os.getenvs(), shell, false)) end end @@ -350,18 +430,37 @@ end -- main entry function main() - local envs = _package_getenvs() - local program = option.get("program") - if program and not option.get("show") then - if envs and envs.PATH then - os.setenv("PATH", envs.PATH) + if option.get("list") then + print("%s:", _get_envsdir()) + local count = 0 + for _, envfile in ipairs(os.files(path.join(_get_envsdir(), "*.lua"))) do + local envname = path.basename(envfile) + print(" - %s", envname) + count = count + 1 end - if program == "shell" then - _run_shell(envs) - else - os.execv(program, option.get("arguments"), {envs = envs}) + print("envs(%d) found!", count) + elseif option.get("add") then + local envfile = assert(option.get("program"), "please set environment config file!") + if os.isfile(envfile) then + os.vcp(envfile, path.join(_get_envsdir(), path.filename(envfile))) end + elseif option.get("remove") then + local envname = assert(option.get("program"), "please set environment config name!") + os.rm(path.join(_get_envsdir(), envname .. ".lua")) else - print(envs) + local envs = _package_getenvs() + local program = option.get("program") + if program and not option.get("show") then + if envs and envs.PATH then + os.setenv("PATH", envs.PATH) + end + if program == "shell" then + _run_shell(envs) + else + os.execv(program, option.get("arguments"), {envs = envs}) + end + else + print(envs) + end end end diff --git a/xmake/modules/private/xrepo/action/export.lua b/xmake/modules/private/xrepo/action/export.lua index 4c2e8c445..e6c4b3ffc 100644 --- a/xmake/modules/private/xrepo/action/export.lua +++ b/xmake/modules/private/xrepo/action/export.lua @@ -39,7 +39,7 @@ function menu_options() values = {"release", "debug"} }, {'f', "configs", "kv", nil, "Set the given extra package configs.", "e.g.", - " - xrepo export -f \"vs_runtime=MD\" zlib", + " - xrepo export -f \"vs_runtime='MD'\" zlib", " - xrepo export -f \"regex=true,thread=true\" boost"}, {}, {nil, "shallow", "k", nil, "Does not export dependent packages."}, diff --git a/xmake/modules/private/xrepo/action/fetch.lua b/xmake/modules/private/xrepo/action/fetch.lua index a577b7c14..57e56c71b 100644 --- a/xmake/modules/private/xrepo/action/fetch.lua +++ b/xmake/modules/private/xrepo/action/fetch.lua @@ -38,7 +38,7 @@ function menu_options() values = {"release", "debug"} }, {'f', "configs", "kv", nil, "Set the given extra package configs.", "e.g.", - " - xrepo fetch --configs=\"vs_runtime=MD\" zlib", + " - xrepo fetch --configs=\"vs_runtime='MD'\" zlib", " - xrepo fetch --configs=\"regex=true,thread=true\" boost"}, {}, {nil, "deps", "k", nil, "Fetch packages with dependencies." }, diff --git a/xmake/modules/private/xrepo/action/import.lua b/xmake/modules/private/xrepo/action/import.lua index e3805135d..fb7cb15ef 100644 --- a/xmake/modules/private/xrepo/action/import.lua +++ b/xmake/modules/private/xrepo/action/import.lua @@ -39,7 +39,7 @@ function menu_options() values = {"release", "debug"} }, {'f', "configs", "kv", nil, "Set the given extra package configs.", "e.g.", - " - xrepo import -f \"vs_runtime=MD\" zlib", + " - xrepo import -f \"vs_runtime='MD'\" zlib", " - xrepo import -f \"regex=true,thread=true\" boost"}, {}, {'i', "packagedir", "kv", "packages","Set the imported packages directory."}, diff --git a/xmake/modules/private/xrepo/action/info.lua b/xmake/modules/private/xrepo/action/info.lua index 3258397cf..4d570da19 100644 --- a/xmake/modules/private/xrepo/action/info.lua +++ b/xmake/modules/private/xrepo/action/info.lua @@ -38,7 +38,7 @@ function menu_options() values = {"release", "debug"} }, {'f', "configs", "kv", nil, "Set the given extra package configs.", "e.g.", - " - xrepo fetch --configs=\"vs_runtime=MD\" zlib", + " - xrepo fetch --configs=\"vs_runtime='MD'\" zlib", " - xrepo fetch --configs=\"regex=true,thread=true\" boost"}, {}, {nil, "packages", "vs", nil, "The packages list.", diff --git a/xmake/modules/private/xrepo/action/install.lua b/xmake/modules/private/xrepo/action/install.lua index ccd14c839..e14d6701e 100644 --- a/xmake/modules/private/xrepo/action/install.lua +++ b/xmake/modules/private/xrepo/action/install.lua @@ -38,9 +38,9 @@ function menu_options() values = {"release", "debug"} }, {'f', "configs", "kv", nil, "Set the given extra package configs.", "e.g.", - " - xrepo install -f \"vs_runtime=MD\" zlib", + " - xrepo install -f \"vs_runtime='MD'\" zlib", " - xrepo install -f \"regex=true,thread=true\" boost"}, - {'j', "jobs", "kv", tostring(math.ceil(os.cpuinfo().ncpu * 3 / 2)), + {'j', "jobs", "kv", tostring(os.default_njob()), "Set the number of parallel compilation jobs."}, {nil, "linkjobs", "kv", nil, "Set the number of parallel link jobs."}, {nil, "includes", "kv", nil, "Includes extra lua configuration files.", diff --git a/xmake/modules/private/xrepo/action/remove.lua b/xmake/modules/private/xrepo/action/remove.lua index 7624fa9cf..c9fce0831 100644 --- a/xmake/modules/private/xrepo/action/remove.lua +++ b/xmake/modules/private/xrepo/action/remove.lua @@ -39,7 +39,7 @@ function menu_options() values = {"release", "debug"} }, {'f', "configs", "kv", nil, "Set the given extra package configs.", "e.g.", - " - xrepo remove -f \"vs_runtime=MD\" zlib", + " - xrepo remove -f \"vs_runtime='MD'\" zlib", " - xrepo remove -f \"regex=true,thread=true\" boost"}, {}, {nil, "all", "k", nil, "Remove all packages and ignore extra package configs.", diff --git a/xmake/modules/target/action/install/cmake_importfiles.lua b/xmake/modules/target/action/install/cmake_importfiles.lua index 12d044251..1249c9c8b 100644 --- a/xmake/modules/target/action/install/cmake_importfiles.lua +++ b/xmake/modules/target/action/install/cmake_importfiles.lua @@ -21,18 +21,33 @@ -- imports import("core.project.project") +-- get the lib file of the target +function _get_libfile(target, installdir) + local libfile = path.filename(target:targetfile()) + if target:is_plat("windows") then + libfile = libfile:gsub("%.dll$", ".lib") + elseif target:is_plat("mingw") then + if os.isfile(path.join(installdir, "lib", libfile:gsub("%.dll$", ".dll.a"))) then + libfile = libfile:gsub("%.dll$", ".dll.a") + else + libfile = libfile:gsub("%.dll$", ".lib") + end + end + return libfile +end + -- get the builtin variables function _get_builtinvars(target, installdir) return {TARGETNAME = target:name(), PROJECTNAME = project.name() or target:name(), - TARGETFILENAME = path.filename(target:targetfile()), - TARGETKIND = target:is_shared() and "SHARED" or "STATIC", + TARGETFILENAME = target:targetfile() and _get_libfile(target, installdir), + TARGETKIND = target:is_headeronly() and "INTERFACE" or (target:is_shared() and "SHARED" or "STATIC"), PACKAGE_VERSION = target:get("version") or "1.0.0", TARGET_PTRBYTES = target:is_arch("x86", "i386") and "4" or "8"} end --- install cmake import file -function _install_cmake_importfile(target, installdir, filename, opt) +-- install cmake config file +function _install_cmake_configfile(target, installdir, filename, opt) -- get import file path local projectname = project.name() or target:name() @@ -48,6 +63,68 @@ function _install_cmake_importfile(target, installdir, filename, opt) -- copy and replace builtin variables local content = io.readfile(importfile_src) if content then + content = content:split("#######################+#")[1] + content = content:gsub("(@(.-)@)", function(_, variable) + variable = variable:trim() + local value = builtinvars[variable] + return type(value) == "function" and value() or value + end) + io.writefile(importfile_dst, content) + end +end + +-- append target to cmake config file +function _append_cmake_configfile(target, installdir, filename, opt) + + -- get import file path + local projectname = project.name() or target:name() + local importfile_src = path.join(os.programdir(), "scripts", "cmake_importfiles", filename) + local importfile_dst = path.join(installdir, opt and opt.libdir or "lib", "cmake", projectname, (filename:gsub("xxx", projectname))) + + -- get the builtin variables + local builtinvars = _get_builtinvars(target, installdir) + + -- generate the file if not exist / file is outdated + if target:is_headeronly() or not os.isfile(importfile_dst) or os.mtime(importfile_dst) < os.mtime(target:targetfile()) then + _install_cmake_configfile(target, installdir, filename, opt) + end + + -- copy and replace builtin variables + local content = io.readfile(importfile_src) + local dst_content = io.readfile(importfile_dst) + if content then + content = content:split("#######################+#")[2] + content = content:gsub("(@(.-)@)", function(_, variable) + variable = variable:trim() + local value = builtinvars[variable] + return type(value) == "function" and value() or value + end) + content = content:trim() + + -- check if the target already exists + if not dst_content:match(format("%sTargets.cmake", target:name())) then + io.writefile(importfile_dst, dst_content:trim() .. "\n\n" .. content .. "\n") + end + end +end + +-- install cmake target file +function _install_cmake_targetfile(target, installdir, filename, opt) + + -- get import file path + local projectname = project.name() or target:name() + local importfile_src = path.join(os.programdir(), "scripts", "cmake_importfiles", filename) + local importfile_dst = path.join(installdir, opt and opt.libdir or "lib", "cmake", projectname, (filename:gsub("xxx", target:name()))) + + -- trace + vprint("generating %s ..", importfile_dst) + + -- get the builtin variables + local builtinvars = _get_builtinvars(target, installdir) + + -- copy and replace builtin variables + local content = io.readfile(importfile_src) + if content then content = content:gsub("(@(.-)@)", function(_, variable) variable = variable:trim() local value = builtinvars[variable] @@ -71,13 +148,15 @@ function main(target, opt) end -- do install - _install_cmake_importfile(target, installdir, "xxxConfig.cmake", opt) - _install_cmake_importfile(target, installdir, "xxxConfigVersion.cmake", opt) - _install_cmake_importfile(target, installdir, "xxxTargets.cmake", opt) - if is_mode("debug") then - _install_cmake_importfile(target, installdir, "xxxTargets-debug.cmake", opt) - else - _install_cmake_importfile(target, installdir, "xxxTargets-release.cmake", opt) + _append_cmake_configfile(target, installdir, "xxxConfig.cmake", opt) + _install_cmake_configfile(target, installdir, "xxxConfigVersion.cmake", opt) + _install_cmake_targetfile(target, installdir, "xxxTargets.cmake", opt) + if not target:is_headeronly() then + if is_mode("debug") then + _install_cmake_targetfile(target, installdir, "xxxTargets-debug.cmake", opt) + else + _install_cmake_targetfile(target, installdir, "xxxTargets-release.cmake", opt) + end end end diff --git a/xmake/modules/target/action/install/main.lua b/xmake/modules/target/action/install/main.lua index 289d258f0..c115e836e 100644 --- a/xmake/modules/target/action/install/main.lua +++ b/xmake/modules/target/action/install/main.lua @@ -20,7 +20,6 @@ -- install files function _install_files(target) - local srcfiles, dstfiles = target:installfiles() if srcfiles and dstfiles then local i = 1 @@ -47,12 +46,10 @@ function main(target, opt) print("installing %s to %s ..", target:name(), installdir) -- call script - if not target:is_phony() then - local install_style = target:is_plat("windows", "mingw") and "windows" or "unix" - local script = import(install_style, {anonymous = true})["install_" .. target:kind()] - if script then - script(target, opt) - end + local install_style = target:is_plat("windows", "mingw") and "windows" or "unix" + local script = import(install_style, {anonymous = true})["install_" .. target:kind()] + if script then + script(target, opt) end -- install other files diff --git a/xmake/modules/target/action/install/pkgconfig_importfiles.lua b/xmake/modules/target/action/install/pkgconfig_importfiles.lua index 1187bb5e6..979feaee2 100644 --- a/xmake/modules/target/action/install/pkgconfig_importfiles.lua +++ b/xmake/modules/target/action/install/pkgconfig_importfiles.lua @@ -24,10 +24,8 @@ function main(target, opt) -- check opt = opt or {} assert(target:is_library(), 'pkgconfig_importfiles: only support for library target(%s)!', target:name()) - - -- only for unix platform local installdir = target:installdir() - if target:is_plat("windows") or not installdir then + if not installdir then return end @@ -35,26 +33,32 @@ function main(target, opt) local pcfile = path.join(installdir, opt and opt.libdir or "lib", "pkgconfig", opt.filename or (target:basename() .. ".pc")) -- get includedirs - local includedirs = opt.includedirs or {path.join(installdir, "include")} + local includedirs = opt.includedirs -- get links and linkdirs local links = opt.links or target:basename() - local linkdirs = opt.linkdirs or {path.join(installdir, "lib")} + local linkdirs = opt.linkdirs -- get libs local libs = "" for _, linkdir in ipairs(linkdirs) do - libs = libs .. "-L" .. linkdir + if linkdir ~= path.join(installdir, "lib") then + libs = libs .. " -L" .. (linkdir:gsub("\\", "/")) + end end libs = libs .. " -L${libdir}" - for _, link in ipairs(links) do - libs = libs .. " -l" .. link + if not target:is_headeronly() then + for _, link in ipairs(links) do + libs = libs .. " -l" .. link + end end -- get cflags local cflags = "" for _, includedir in ipairs(includedirs) do - cflags = cflags .. "-I" .. includedir + if includedir ~= path.join(installdir, "include") then + cflags = cflags .. " -I" .. (includedir:gsub("\\", "/")) + end end cflags = cflags .. " -I${includedir}" @@ -64,7 +68,7 @@ function main(target, opt) -- generate a *.pc file local file = io.open(pcfile, 'w') if file then - file:print("prefix=%s", installdir) + file:print("prefix=%s", installdir:gsub("\\", "/")) file:print("exec_prefix=${prefix}") file:print("libdir=${exec_prefix}/lib") file:print("includedir=${prefix}/include") @@ -76,7 +80,6 @@ function main(target, opt) file:print("Version: %s", version) end file:print("Libs: %s", libs) - file:print("Libs.private: ") file:print("Cflags: %s", cflags) file:close() end diff --git a/xmake/modules/target/action/install/unix.lua b/xmake/modules/target/action/install/unix.lua index d4311aeea..af641e5bc 100644 --- a/xmake/modules/target/action/install/unix.lua +++ b/xmake/modules/target/action/install/unix.lua @@ -18,15 +18,8 @@ -- @file unix.lua -- --- install library -function _install_library(target, opt) - - -- install libraries - local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") - os.mkdir(librarydir) - os.vcp(target:targetfile(), librarydir) - - -- install headers +-- install headers +function _install_headers(target, opt) local includedir = path.join(target:installdir(), opt and opt.includedir or "include") os.mkdir(includedir) local srcheaders, dstheaders = target:headerfiles(includedir) @@ -42,19 +35,99 @@ function _install_library(target, opt) end end +-- install shared libraries for package +function _install_shared_for_package(target, pkg, outputdir) + _g.installed_libfiles = _g.installed_libfiles or {} + for _, sopath in ipairs(table.wrap(pkg:get("libfiles"))) do + if sopath:endswith(".so") or sopath:match(".+%.so%..+$") or sopath:endswith(".dylib") then + -- prevent packages using the same system libfiles from overwriting each other + if not _g.installed_libfiles[sopath] then + local soname = path.filename(sopath) + local targetname = path.join(outputdir, soname) + if os.isfile(targetname) then + wprint("'%s' already exists in install dir, overwriting it from package(%s).", soname, pkg:name()) + -- rm because symlink cannot overwrite existing file + os.rm(targetname) + end + -- we need reserve symlink + -- @see https://github.com/xmake-io/xmake/issues/1582 + os.vcp(sopath, outputdir, {symlink = true}) + _g.installed_libfiles[sopath] = true + end + end + end +end + +-- install shared libraries for packages +function _install_shared_for_packages(target, outputdir) + _g.installed_packages = _g.installed_packages or {} + for _, pkg in ipairs(target:orderpkgs()) do + if not _g.installed_packages[pkg:name()] then + if pkg:enabled() and pkg:get("libfiles") then + _install_shared_for_package(target, pkg, outputdir) + end + _g.installed_packages[pkg:name()] = true + end + end +end + -- install binary function install_binary(target, opt) + + -- install binary local binarydir = path.join(target:installdir(), opt and opt.bindir or "bin") os.mkdir(binarydir) os.vcp(target:targetfile(), binarydir) + + -- install libraries + local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") + os.mkdir(librarydir) + + -- install the dependent shared (*.so) target + -- @see https://github.com/xmake-io/xmake/issues/961 + for _, dep in ipairs(target:orderdeps()) do + if dep:kind() == "shared" then + local depfile = dep:targetfile() + if os.isfile(depfile) then + os.vcp(depfile, librarydir) + end + end + -- install all shared libraries in packages in all deps + _install_shared_for_packages(dep, librarydir) + end + + -- install shared libraries for all packages + _install_shared_for_packages(target, librarydir) end -- install shared library function install_shared(target, opt) - _install_library(target, opt) + + -- install libraries + local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") + os.mkdir(librarydir) + os.vcp(target:targetfile(), librarydir) + + -- install shared libraries for all packages + _install_shared_for_packages(target, librarydir) + + -- install headers + _install_headers(target, opt) end -- install static library function install_static(target, opt) - _install_library(target, opt) + + -- install libraries + local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") + os.mkdir(librarydir) + os.vcp(target:targetfile(), librarydir) + + -- install headers + _install_headers(target, opt) +end + +-- install headeronly library +function install_headeronly(target, opt) + _install_headers(target, opt) end diff --git a/xmake/modules/target/action/install/windows.lua b/xmake/modules/target/action/install/windows.lua index 73e8f5ee3..dfbcad11f 100644 --- a/xmake/modules/target/action/install/windows.lua +++ b/xmake/modules/target/action/install/windows.lua @@ -40,12 +40,18 @@ end -- install shared libraries for package function _install_shared_for_package(target, pkg, outputdir) + _g.installed_dllfiles = _g.installed_dllfiles or {} for _, dllpath in ipairs(table.wrap(pkg:get("libfiles"))) do if dllpath:endswith(".dll") then - if os.isfile(path.join(outputdir, dllname)) then - wprint("'%s' already exists in install dir, overwriting it from package(%s).", dllname, pkg:name()) + -- prevent packages using the same libfiles from overwriting each other + if not _g.installed_dllfiles[dllpath] then + local dllname = path.filename(dllpath) + if os.isfile(path.join(outputdir, dllname)) then + wprint("'%s' already exists in install dir, overwriting it from package(%s).", dllname, pkg:name()) + end + os.vcp(dllpath, outputdir) + _g.installed_dllfiles[dllpath] = true end - os.vcp(dllpath, outputdir) end end end @@ -70,14 +76,19 @@ function install_binary(target, opt) local binarydir = path.join(target:installdir(), opt and opt.bindir or "bin") os.mkdir(binarydir) os.vcp(target:targetfile(), binarydir) + os.trycp(target:symbolfile(), binarydir) -- install the dependent shared/windows (*.dll) target -- @see https://github.com/xmake-io/xmake/issues/961 + _g.installed_dllfiles = _g.installed_dllfiles or {} for _, dep in ipairs(target:orderdeps()) do if dep:kind() == "shared" then local depfile = dep:targetfile() if os.isfile(depfile) then - os.vcp(depfile, binarydir) + if not _g.installed_dllfiles[depfile] then + os.vcp(depfile, binarydir) + _g.installed_dllfiles[depfile] = true + end end end -- install all shared libraries in packages in all deps @@ -95,12 +106,13 @@ function install_shared(target, opt) local binarydir = path.join(target:installdir(), opt and opt.bindir or "bin") os.mkdir(binarydir) os.vcp(target:targetfile(), binarydir) + os.trycp(target:symbolfile(), binarydir) -- install *.lib for shared/windows (*.dll) target -- @see https://github.com/xmake-io/xmake/issues/714 local targetfile = target:targetfile() local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") - local targetfile_lib = path.join(path.directory(targetfile), path.basename(targetfile) .. ".lib") + local targetfile_lib = path.join(path.directory(targetfile), path.basename(targetfile) .. (target:is_plat("mingw") and ".dll.a" or ".lib")) if os.isfile(targetfile_lib) then os.mkdir(librarydir) os.vcp(targetfile_lib, librarydir) @@ -120,7 +132,13 @@ function install_static(target, opt) local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") os.mkdir(librarydir) os.vcp(target:targetfile(), librarydir) + os.trycp(target:symbolfile(), librarydir) -- install headers _install_headers(target, opt) end + +-- install headeronly +function install_headeronly(target, opt) + _install_headers(target, opt) +end diff --git a/xmake/modules/target/action/uninstall/unix.lua b/xmake/modules/target/action/uninstall/unix.lua index b582aecf6..e11446dd4 100644 --- a/xmake/modules/target/action/uninstall/unix.lua +++ b/xmake/modules/target/action/uninstall/unix.lua @@ -18,14 +18,8 @@ -- @file unix.lua -- --- uninstall library -function _uninstall_library(target, opt) - - -- remove the target file - local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") - os.vrm(path.join(librarydir, path.filename(target:targetfile()))) - - -- remove headers from the include directory +-- uninstall headers +function _uninstall_headers(target, opt) local includedir = path.join(target:installdir(), opt and opt.includedir or "include") local _, dstheaders = target:headerfiles(includedir) for _, dstheader in ipairs(dstheaders) do @@ -33,18 +27,76 @@ function _uninstall_library(target, opt) end end +-- uninstall shared libraries for package +function _uninstall_shared_for_package(target, pkg, outputdir) + for _, sopath in ipairs(table.wrap(pkg:get("libfiles"))) do + if sopath:endswith(".so") or sopath:endswith(".dylib") then + local soname = path.filename(sopath) + os.vrm(path.join(outputdir, soname)) + end + end +end + +-- uninstall shared libraries for packages +function _uninstall_shared_for_packages(target, outputdir) + _g.uninstalled_packages = _g.uninstalled_packages or {} + for _, pkg in ipairs(target:orderpkgs()) do + if not _g.uninstalled_packages[pkg:name()] then + if pkg:enabled() and pkg:get("libfiles") then + _uninstall_shared_for_package(target, pkg, outputdir) + end + _g.uninstalled_packages[pkg:name()] = true + end + end +end + -- uninstall binary function uninstall_binary(target, opt) + + -- remove the target file local binarydir = path.join(target:installdir(), opt and opt.bindir or "bin") os.vrm(path.join(binarydir, path.filename(target:targetfile()))) + + -- remove the dependent shared (*.so) target + -- @see https://github.com/xmake-io/xmake/issues/961 + local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") + for _, dep in ipairs(target:orderdeps()) do + if dep:kind() == "shared" then + os.vrm(path.join(librarydir, path.filename(dep:targetfile()))) + end + _uninstall_shared_for_packages(dep, librarydir) + end + + -- uninstall shared libraries for packages + _uninstall_shared_for_packages(target, librarydir) end -- uninstall shared library function uninstall_shared(target, opt) - _uninstall_library(target, opt) + + -- remove the target file + local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") + os.vrm(path.join(librarydir, path.filename(target:targetfile()))) + + -- remove headers from the include directory + _uninstall_headers(target, opt) + + -- uninstall shared libraries for packages + _uninstall_shared_for_packages(target, librarydir) end -- uninstall static library function uninstall_static(target, opt) - _uninstall_library(target, opt) + + -- remove the target file + local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") + os.vrm(path.join(librarydir, path.filename(target:targetfile()))) + + -- remove headers from the include directory + _uninstall_headers(target, opt) +end + +-- uninstall headeronly library +function uninstall_headeronly(target, opt) + _uninstall_headers(target, opt) end diff --git a/xmake/modules/target/action/uninstall/windows.lua b/xmake/modules/target/action/uninstall/windows.lua index f45028303..ed6923567 100644 --- a/xmake/modules/target/action/uninstall/windows.lua +++ b/xmake/modules/target/action/uninstall/windows.lua @@ -31,6 +31,7 @@ end function _uninstall_shared_for_package(target, pkg, outputdir) for _, dllpath in ipairs(table.wrap(pkg:get("libfiles"))) do if dllpath:endswith(".dll") then + local dllname = path.filename(dllpath) os.vrm(path.join(outputdir, dllname)) end end @@ -55,6 +56,7 @@ function uninstall_binary(target, opt) -- remove the target file local binarydir = path.join(target:installdir(), opt and opt.bindir or "bin") os.vrm(path.join(binarydir, path.filename(target:targetfile()))) + os.tryrm(path.join(binarydir, path.filename(target:symbolfile()))) -- remove the dependent shared/windows (*.dll) target -- @see https://github.com/xmake-io/xmake/issues/961 @@ -75,12 +77,13 @@ function uninstall_shared(target, opt) -- remove the target file local binarydir = path.join(target:installdir(), opt and opt.bindir or "bin") os.vrm(path.join(binarydir, path.filename(target:targetfile()))) + os.tryrm(path.join(binarydir, path.filename(target:symbolfile()))) -- remove *.lib for shared/windows (*.dll) target -- @see https://github.com/xmake-io/xmake/issues/714 local targetfile = target:targetfile() local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") - os.vrm(path.join(librarydir, path.basename(targetfile) .. ".lib")) + os.vrm(path.join(librarydir, path.basename(targetfile) .. (target:is_plat("mingw") and ".dll.a" or ".lib"))) -- remove headers from the include directory _uninstall_headers(target, opt) @@ -95,7 +98,13 @@ function uninstall_static(target, opt) -- remove the target file local librarydir = path.join(target:installdir(), opt and opt.libdir or "lib") os.vrm(path.join(librarydir, path.filename(target:targetfile()))) + os.tryrm(path.join(librarydir, path.filename(target:symbolfile()))) -- remove headers from the include directory _uninstall_headers(target, opt) end + +-- uninstall headeronly library +function uninstall_headeronly(target, opt) + _uninstall_headers(target, opt) +end diff --git a/xmake/modules/utils/archive/extension.lua b/xmake/modules/utils/archive/extension.lua index 3a8554efd..fe33a395f 100644 --- a/xmake/modules/utils/archive/extension.lua +++ b/xmake/modules/utils/archive/extension.lua @@ -23,7 +23,6 @@ import("core.base.hashset") -- get the archive extension function main(archivefile) - local extension = "" local filename = path.filename(archivefile) local extensionset = hashset.from({".zip", ".7z", ".gz", ".xz", ".tgz", ".bz2", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"}) @@ -33,5 +32,5 @@ function main(archivefile) if p and extensionset:has(filename:sub(p)) then i = p end extension = filename:sub(i) end - return extension + return extensionset:has(extension) and extension or "" end diff --git a/xmake/modules/utils/archive/extract.lua b/xmake/modules/utils/archive/extract.lua index 4e54bc4dc..d4a41e542 100644 --- a/xmake/modules/utils/archive/extract.lua +++ b/xmake/modules/utils/archive/extract.lua @@ -43,16 +43,23 @@ function _extract_using_tar(archivefile, outputdir, extension, opt) return false end - -- on msys2/cygwin? we need translate input path to cygwin-like path - if is_subhost("msys", "cygwin") and program:gsub("\\", "/"):find("/usr/bin") then - archivefile = path.cygwin_path(archivefile) - end - -- init argv local argv = {} - if is_subhost("windows") then + if is_host("windows") then -- force "x:\\xx" as local file - table.insert(argv, "--force-local") + local force_local = _g.force_local + if force_local == nil then + force_local = try {function () + local result = os.iorunv(program, {"--help"}) + if result and result:find("--force-local", 1, true) then + return true + end + end} + _g.force_local = force_local or false + end + if force_local then + table.insert(argv, "--force-local") + end end table.insert(argv, option.get("verbose") and "-xvf" or "-xf") table.insert(argv, archivefile) @@ -78,14 +85,10 @@ function _extract_using_tar(archivefile, outputdir, extension, opt) -- extract it if is_host("windows") then - local oldir = os.cd(outputdir) - os.vrunv(program, argv) - os.cd(oldir) + os.vrunv(program, argv, {curdir = outputdir}) else os.vrunv(program, argv) end - - -- ok return true end @@ -98,6 +101,12 @@ function _extract_using_7z(archivefile, outputdir, extension, opt) return false end + -- p7zip cannot extract other archive format on msys/cygwin + -- https://github.com/xmake-io/xmake/issues/1575#issuecomment-898205462 + if is_subhost("msys", "cygwin") and extension ~= ".7z" and program:startswith("sh ") then + return false + end + -- extract to *.tar file first local outputdir_old = nil if extension:startswith(".tar.") or extension == ".tgz" then @@ -140,7 +149,7 @@ function _extract_using_7z(archivefile, outputdir, extension, opt) -- remove unused pax_global_header file after extracting .tar file if extension == ".tar" then os.tryrm(path.join(outputdir, "pax_global_header")) - os.tryrm(path.join(outputdir, "PaxHeaders.*")) + os.tryrm(path.join(outputdir, "PaxHeaders*")) os.tryrm(path.join(outputdir, "@PaxHeader")) end @@ -151,8 +160,6 @@ function _extract_using_7z(archivefile, outputdir, extension, opt) return _extract(tarfile, outputdir_old, ".tar", {_extract_using_7z, _extract_using_tar}, opt) end end - - -- ok return true end @@ -192,14 +199,8 @@ function _extract_using_gzip(archivefile, outputdir, extension, opt) os.cp(archivefile, tmpfile) end - -- enter outputdir - local oldir = os.cd(outputdir) - -- extract it - os.vrunv(program, argv) - - -- leave outputdir - os.cd(oldir) + os.vrunv(program, argv, {curdir = outputdir}) -- continue to extract *.tar file if outputdir_old then @@ -208,8 +209,6 @@ function _extract_using_gzip(archivefile, outputdir, extension, opt) return _extract(tarfile, outputdir_old, ".tar", {_extract_using_7z, _extract_using_tar}, opt) end end - - -- ok return true end @@ -249,14 +248,8 @@ function _extract_using_xz(archivefile, outputdir, extension, opt) os.cp(archivefile, tmpfile) end - -- enter outputdir - local oldir = os.cd(outputdir) - -- extract it - os.vrunv(program, argv) - - -- leave outputdir - os.cd(oldir) + os.vrunv(program, argv, {curdir = outputdir}) -- continue to extract *.tar file if outputdir_old then @@ -265,8 +258,6 @@ function _extract_using_xz(archivefile, outputdir, extension, opt) return _extract(tarfile, outputdir_old, ".tar", {_extract_using_7z, _extract_using_tar}, opt) end end - - -- ok return true end @@ -320,8 +311,6 @@ function _extract_using_unzip(archivefile, outputdir, extension, opt) return _extract(tarfile, outputdir_old, ".tar", {_extract_using_tar, _extract_using_7z}, opt) end end - - -- ok return true end @@ -366,14 +355,8 @@ function _extract_using_bzip2(archivefile, outputdir, extension, opt) os.cp(archivefile, tmpfile) end - -- enter outputdir - local oldir = os.cd(outputdir) - -- extract it - os.vrunv(program, argv) - - -- leave outputdir - os.cd(oldir) + os.vrunv(program, argv, {curdir = outputdir}) -- continue to extract *.tar file if outputdir_old then @@ -382,22 +365,16 @@ function _extract_using_bzip2(archivefile, outputdir, extension, opt) return _extract(tarfile, outputdir_old, ".tar", {_extract_using_7z, _extract_using_tar}, opt) end end - - -- ok return true end -- extract archive file using extractors function _extract(archivefile, outputdir, extension, extractors, opt) - - -- extract it for _, extract in ipairs(extractors) do if extract(archivefile, outputdir, extension, opt) then return true end end - - -- failed return false end @@ -417,7 +394,7 @@ function main(archivefile, outputdir, opt) -- init extractors local extractors - if is_host("windows") then + if is_subhost("windows") then -- we use 7z first, becase xmake package has builtin 7z program on windows -- tar/windows can not extract .bz2 ... extractors = @@ -432,6 +409,7 @@ function main(archivefile, outputdir, opt) , [".tar.gz"] = {_extract_using_7z, _extract_using_gzip} , [".tar.xz"] = {_extract_using_7z, _extract_using_xz} , [".tar.bz2"] = {_extract_using_7z, _extract_using_bzip2} + , [".tar.lz"] = {_extract_using_7z} } else extractors = @@ -446,6 +424,7 @@ function main(archivefile, outputdir, opt) , [".tar.gz"] = {_extract_using_tar, _extract_using_7z, _extract_using_gzip} , [".tar.xz"] = {_extract_using_tar, _extract_using_7z, _extract_using_xz} , [".tar.bz2"] = {_extract_using_tar, _extract_using_7z, _extract_using_bzip2} + , [".tar.lz"] = {_extract_using_tar, _extract_using_7z} } end diff --git a/xmake/modules/utils/archive/merge_staticlib.lua b/xmake/modules/utils/archive/merge_staticlib.lua new file mode 100644 index 000000000..dc0130219 --- /dev/null +++ b/xmake/modules/utils/archive/merge_staticlib.lua @@ -0,0 +1,73 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file merge_staticlib.lua +-- + +-- imports +import("core.base.option") +import("private.tools.vstool") + +-- merge *.a archive libraries for ar +function _merge_for_ar(target, program, outputfile, libraryfiles, opt) + opt = opt or {} + if target:is_plat("macosx", "iphoneos", "watchos", "appletvos") then + os.vrunv("libtool", table.join("-static", "-o", outputfile, libraryfiles)) + else + local tmpfile = os.tmpfile() + local mrifile = io.open(tmpfile, "w") + mrifile:print("create %s", outputfile) + for _, libraryfile in ipairs(libraryfiles) do + mrifile:print("addlib %s", libraryfile) + end + mrifile:print("save") + mrifile:print("end") + mrifile:close() + os.vrunv(program, {"-M"}, {stdin = tmpfile}) + os.rm(tmpfile) + end +end + +-- merge *.a archive libraries for msvc/lib.exe +function _merge_for_msvclib(target, program, outputfile, libraryfiles, opt) + opt = opt or {} + vstool.runv(program, table.join("-nologo", "-out:" .. outputfile, libraryfiles), {envs = opt.runenvs}) +end + +-- merge *.a archive libraries +function main(target, outputfile, libraryfiles) + local program, toolname = target:tool("ar") + if program and toolname then + if toolname:find("ar") then + _merge_for_ar(target, program, outputfile, libraryfiles) + elseif toolname == "link" and target:is_plat("windows") then + local msvc + for _, toolchain_inst in ipairs(target:toolchains()) do + if toolchain_inst:name() == "msvc" then + msvc = toolchain_inst + break + end + end + _merge_for_msvclib(target, (program:gsub("link%.exe", "lib.exe")), outputfile, libraryfiles, {runenvs = msvc and msvc:runenvs()}) + else + raise("cannot merge (%s): unknown ar tool %s!", table.concat(libraryfiles, ", "), toolname) + end + else + raise("cannot merge (%s): ar not found!", table.concat(libraryfiles, ", ")) + end +end + diff --git a/xmake/modules/private/utils/progress.lua b/xmake/modules/utils/progress.lua index 5c08cbf8f..074046a69 100644 --- a/xmake/modules/private/utils/progress.lua +++ b/xmake/modules/utils/progress.lua @@ -75,6 +75,7 @@ end -- show the message with process function show(progress, format, ...) + progress = math.floor(progress) local progress_prefix = "${color.build.progress}" .. theme.get("text.build.progress_format") .. ":${clear} " if option.get("verbose") then cprint(progress_prefix .. "${dim}" .. format, progress, ...) @@ -113,6 +114,7 @@ end -- get the message text with process function text(progress, format, ...) + progress = math.floor(progress) local progress_prefix = "${color.build.progress}" .. theme.get("text.build.progress_format") .. ":${clear} " if option.get("verbose") then return string.format(progress_prefix .. "${dim}" .. format, progress, ...) |
