From 8d8601bb1655094e1c711f8a48150453f076b5d1 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 11 Apr 2026 20:15:02 +0800 Subject: improve custom toolchain --- tests/apis/custom_toolchain/xmake.lua | 1 - .../xmake/modules/core/tools/ar6x.lua | 27 --- .../xmake/modules/core/tools/cl6x.lua | 204 --------------------- .../xmake/modules/core/tools/cl6x/has_flags.lua | 120 ------------ .../xmake/modules/core/tools/cl6x/parse_deps.lua | 77 -------- .../xmake/modules/detect/tools/find_ar6x.lua | 58 ------ .../xmake/modules/detect/tools/find_cl6x.lua | 48 ----- .../my-c6000/modules/core/tools/ar6x.lua | 27 +++ .../my-c6000/modules/core/tools/cl6x.lua | 204 +++++++++++++++++++++ .../my-c6000/modules/core/tools/cl6x/has_flags.lua | 120 ++++++++++++ .../modules/core/tools/cl6x/parse_deps.lua | 77 ++++++++ .../my-c6000/modules/detect/tools/find_ar6x.lua | 58 ++++++ .../my-c6000/modules/detect/tools/find_cl6x.lua | 48 +++++ xmake/core/project/project.lua | 12 ++ 14 files changed, 546 insertions(+), 535 deletions(-) delete mode 100644 tests/apis/custom_toolchain/xmake/modules/core/tools/ar6x.lua delete mode 100644 tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x.lua delete mode 100644 tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x/has_flags.lua delete mode 100644 tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x/parse_deps.lua delete mode 100644 tests/apis/custom_toolchain/xmake/modules/detect/tools/find_ar6x.lua delete mode 100644 tests/apis/custom_toolchain/xmake/modules/detect/tools/find_cl6x.lua create mode 100644 tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/ar6x.lua create mode 100644 tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x.lua create mode 100644 tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x/has_flags.lua create mode 100644 tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x/parse_deps.lua create mode 100644 tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/detect/tools/find_ar6x.lua create mode 100644 tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/detect/tools/find_cl6x.lua diff --git a/tests/apis/custom_toolchain/xmake.lua b/tests/apis/custom_toolchain/xmake.lua index 03d2b5344..b06229a33 100644 --- a/tests/apis/custom_toolchain/xmake.lua +++ b/tests/apis/custom_toolchain/xmake.lua @@ -1,6 +1,5 @@ add_rules("mode.debug", "mode.release") -add_moduledirs("xmake/modules") add_toolchaindirs("xmake/toolchains") set_toolchains("my-c6000") diff --git a/tests/apis/custom_toolchain/xmake/modules/core/tools/ar6x.lua b/tests/apis/custom_toolchain/xmake/modules/core/tools/ar6x.lua deleted file mode 100644 index 7e51734ff..000000000 --- a/tests/apis/custom_toolchain/xmake/modules/core/tools/ar6x.lua +++ /dev/null @@ -1,27 +0,0 @@ ---!A cross-platform build utility based on Lua --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- --- Copyright (C) 2015-present, Xmake Open Source Community. --- --- @author ruki --- @file ar6x.lua --- - -inherit("core.tools.ar") - --- init it -function init(self) - self:set("arflags", "-r") -end - diff --git a/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x.lua b/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x.lua deleted file mode 100644 index 37ceec0b8..000000000 --- a/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x.lua +++ /dev/null @@ -1,204 +0,0 @@ ---!A cross-platform build utility based on Lua --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- --- Copyright (C) 2015-present, Xmake Open Source Community. --- --- @author ruki --- @file cl6x.lua --- - --- imports -import("core.base.option") -import("core.base.global") -import("core.project.policy") -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 = "-m3" - , aggressive = "-O3" - } - return maps[level] -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 link flag -function nf_link(self, lib) - if not lib:endswith(".a") and not lib:endswith(".so") then - lib = "lib" .. lib .. ".a" - end - return "-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 {"-i" .. path.translate(dir)} -end - --- make the rpathdir flag -function nf_rpathdir(self, dir, opt) - opt = opt or {} - local extra = opt.extra - if extra and extra.installonly then - return - end - dir = path.translate(dir) - return {"-rpath=" .. dir} -end - --- make the link arguments list -function linkargv(self, objectfiles, targetkind, targetfile, flags, opt) - local argv = table.join("-z", "--output_file=" .. targetfile, objectfiles, flags) - return self:program(), argv -end - --- link the target file --- --- maybe we need to use os.vrunv() to show link output when enable verbose information --- @see https://github.com/xmake-io/xmake/discussions/2916 --- -function link(self, objectfiles, targetkind, targetfile, flags, opt) - opt = opt or {} - os.mkdir(path.directory(targetfile)) - local program, argv = linkargv(self, objectfiles, targetkind, targetfile, flags) - if option.get("verbose") then - os.execv(program, argv, {envs = self:runenvs(), shell = opt.shell}) - else - os.vrunv(program, argv, {envs = self:runenvs(), shell = opt.shell}) - end -end - --- make the compile arguments list -function compargv(self, sourcefile, objectfile, flags) - return self:program(), table.join("-c", "--preproc_with_compile", flags, "--output_file=" .. objectfile, sourcefile) -end - --- compile the source file -function compile(self, sourcefile, objectfile, dependinfo, flags, opt) - os.mkdir(path.directory(objectfile)) - local depfile = dependinfo and os.tmpfile() or nil - try - { - function () - local compflags = flags - if depfile then - compflags = table.join(compflags, "-ppd=" .. depfile) - end - local outdata, errdata = os.iorunv(compargv(self, sourcefile, objectfile, compflags)) - 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 policy.build_warnings(opt) then - progress.show_output("${color.warning}%s", table.concat(table.slice(warnings:split('\n'), 1, 8), '\n')) - end - - -- generate the dependent includes - if depfile and os.isfile(depfile) then - if dependinfo then - dependinfo.depfiles_format = "cl6x" - dependinfo.depfiles = io.readfile(depfile, {continuation = "\\"}) - end - - -- remove the temporary dependent file - os.tryrm(depfile) - end - end - } - } -end - - diff --git a/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x/has_flags.lua b/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x/has_flags.lua deleted file mode 100644 index 629f25c12..000000000 --- a/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x/has_flags.lua +++ /dev/null @@ -1,120 +0,0 @@ ---!A cross-platform build utility based on Lua --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- --- Copyright (C) 2015-present, Xmake Open Source Community. --- --- @author ruki --- @file has_flags.lua --- - --- imports -import("core.cache.detectcache") -import("core.language.language") - --- is linker? -function _islinker(flags, opt) - 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(program, argv, opt) - local errors = nil - return try { function () os.runv(program, argv, opt); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors -end - --- attempt to check it from known flags -function _check_from_knownargs(flags, opt, islinker) - local flag = flags[1] - if not islinker then - if flag:startswith("-D") or - flag:startswith("-U") or - flag:startswith("-I") then - return true - end - end -end - --- attempt to check it from the argument list -function _check_from_arglist(flags, opt, islinker) - local key = "core.tools.cl6x." .. (islinker and "has_ldflags" or "has_cflags") - local flagskey = opt.program .. "_" .. (opt.programver or "") - local allflags = detectcache:get2(key, flagskey) - if not allflags then - allflags = {} - local arglist = try {function () return os.iorunv(opt.program, {"--help"}, {envs = opt.envs}) end} - if arglist then - for arg in arglist:gmatch("%s+(%-[%-%a%d]+)%s+") do - allflags[arg] = true - end - end - detectcache:set2(key, flagskey, allflags) - end - local flag = flags[1] - return allflags[flag] -end - --- get extension -function _get_extension(opt) - -- @note we need to 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) - - -- make an stub source file - local snippet = opt.snippet or "int main(int argc, char** argv)\n{return 0;}\n" - local sourcefile = os.tmpfile("cl6x_has_flags:" .. snippet) .. _get_extension(opt) - if not os.isfile(sourcefile) then - io.writefile(sourcefile, snippet) - end - - -- check flags for linker - local tmpfile = os.tmpfile() - if islinker then - return _try_running(opt.program, table.join(flags, "-z", "--output_file=" .. tmpfile, sourcefile), opt) - end - - -- check flags for compiler - -- @note we cannot use os.nuldev() as the output file, maybe run failed for some flags, e.g. --coverage - return _try_running(opt.program, table.join(flags, "-c", "--output_file=" .. tmpfile, sourcefile), opt) -end - --- has_flags(flags)? --- --- @param opt the argument options, e.g. {toolname = "", program = "", programver = "", toolkind = "[cc|cxx|ld|ar|sh|gc|mm|mxx]"} --- --- @return true or false --- -function main(flags, opt) - - -- is linker? - opt = opt or {} - local islinker = _islinker(flags, opt) - - -- attempt to check it from the argument list - if not opt.tryrun then - if _check_from_arglist(flags, opt, islinker) then - return true - end - if _check_from_knownargs(flags, opt, islinker) then - return true - end - end - - -- try running to check it - return _check_try_running(flags, opt, islinker) -end - diff --git a/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x/parse_deps.lua b/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x/parse_deps.lua deleted file mode 100644 index 301d696aa..000000000 --- a/tests/apis/custom_toolchain/xmake/modules/core/tools/cl6x/parse_deps.lua +++ /dev/null @@ -1,77 +0,0 @@ ---!A cross-platform build utility based on Lua --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- --- Copyright (C) 2015-present, Xmake Open Source Community. --- --- @author ruki --- @file parse_deps.lua --- - --- imports -import("core.project.config") -import("core.project.project") -import("core.base.hashset") - --- normailize path of a dependecy -function _normailize_dep(dep, projectdir) - -- escape characters, e.g. \#Qt.Widget_pch.h -> #Qt.Widget_pch.h - -- @see https://github.com/xmake-io/xmake/issues/4134 - -- https://github.com/xmake-io/xmake/issues/4273 - if not is_host("windows") then - dep = dep:gsub("\\(.)", "%1") - end - if path.is_absolute(dep) then - dep = path.translate(dep) - else - dep = path.absolute(dep, projectdir) - end - if dep:startswith(projectdir) then - return path.relative(dep, projectdir) - else - -- we also need to check header files outside project - -- https://github.com/xmake-io/xmake/issues/1154 - return dep - end -end - --- parse depsfiles from string --- --- parse_deps(io.readfile(depfile, {continuation = "\\"})) --- --- eg. --- --- build/.objs/foo/linux/x86_64/release/src/foo.c.o: src/foo.c --- build/.objs/foo/linux/x86_64/release/src/foo.c.o: src/foo.h --- --- build/.objs/tests/linux/x86_64/release/src/main.c.o: src/main.c --- build/.objs/tests/linux/x86_64/release/src/main.c.o: src/foo.h --- build/.objs/tests/linux/x86_64/release/src/main.c.o: src/bar.h --- build/.objs/tests/linux/x86_64/release/src/main.c.o: src/zoo.h --- -function main(depsdata, opt) - 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 - local plain = {plain = true} - for _, includefile in ipairs(line:split('\n', plain)) do -- it will trim all internal spaces without `{strict = true}` - includefile = includefile:split(": ", plain)[2] - if includefile and #includefile > 0 then - includefile = _normailize_dep(includefile, projectdir) - if includefile then - results:insert(includefile) - end - end - end - return results:to_array() -end diff --git a/tests/apis/custom_toolchain/xmake/modules/detect/tools/find_ar6x.lua b/tests/apis/custom_toolchain/xmake/modules/detect/tools/find_ar6x.lua deleted file mode 100644 index 2ee4c4853..000000000 --- a/tests/apis/custom_toolchain/xmake/modules/detect/tools/find_ar6x.lua +++ /dev/null @@ -1,58 +0,0 @@ ---!A cross-platform build utility based on Lua --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- --- Copyright (C) 2015-present, Xmake Open Source Community. --- --- @author ruki --- @file find_ar6x.lua --- - --- imports -import("lib.detect.find_program") - --- check -function _check(program) - - -- make a stub object file - local libraryfile = os.tmpfile() .. ".a" - local objectfile = os.tmpfile() .. ".o" - io.writefile(objectfile, "") - - -- archive it - os.execv(program, {"-r", libraryfile, objectfile}) - - -- remove files - os.rm(objectfile) - os.rm(libraryfile) -end - --- find ar --- --- @param opt the argument options, e.g. {version = true} --- --- @return program, version --- --- @code --- --- local ar = find_ar6x() --- local ar, version = find_ar6x({program = "xcrun -sdk macosx g++", version = true}) --- --- @endcode --- -function main(opt) - opt = opt or {} - opt.check = opt.check or _check - return find_program(opt.program or "ar6x", opt) -end - diff --git a/tests/apis/custom_toolchain/xmake/modules/detect/tools/find_cl6x.lua b/tests/apis/custom_toolchain/xmake/modules/detect/tools/find_cl6x.lua deleted file mode 100644 index 1db484762..000000000 --- a/tests/apis/custom_toolchain/xmake/modules/detect/tools/find_cl6x.lua +++ /dev/null @@ -1,48 +0,0 @@ ---!A cross-platform build utility based on Lua --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- --- Copyright (C) 2015-present, Xmake Open Source Community. --- --- @author ruki --- @file find_cl6x.lua --- - --- imports -import("lib.detect.find_program") -import("lib.detect.find_programver") - --- find cl6x --- --- @param opt the argument options, e.g. {version = true} --- --- @return program, version --- --- @code --- --- local cl6x = find_cl6x() --- local cl6x, version = find_cl6x({program = "cl6x", version = true}) --- --- @endcode --- -function main(opt) - opt = opt or {} - opt.check = "--help" - opt.command = "--help" - local program = find_program(opt.program or "cl6x", opt) - local version = nil - if program and opt.version then - version = find_programver(program, opt) - end - return program, version -end diff --git a/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/ar6x.lua b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/ar6x.lua new file mode 100644 index 000000000..7e51734ff --- /dev/null +++ b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/ar6x.lua @@ -0,0 +1,27 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file ar6x.lua +-- + +inherit("core.tools.ar") + +-- init it +function init(self) + self:set("arflags", "-r") +end + diff --git a/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x.lua b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x.lua new file mode 100644 index 000000000..37ceec0b8 --- /dev/null +++ b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x.lua @@ -0,0 +1,204 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file cl6x.lua +-- + +-- imports +import("core.base.option") +import("core.base.global") +import("core.project.policy") +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 = "-m3" + , aggressive = "-O3" + } + return maps[level] +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 link flag +function nf_link(self, lib) + if not lib:endswith(".a") and not lib:endswith(".so") then + lib = "lib" .. lib .. ".a" + end + return "-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 {"-i" .. path.translate(dir)} +end + +-- make the rpathdir flag +function nf_rpathdir(self, dir, opt) + opt = opt or {} + local extra = opt.extra + if extra and extra.installonly then + return + end + dir = path.translate(dir) + return {"-rpath=" .. dir} +end + +-- make the link arguments list +function linkargv(self, objectfiles, targetkind, targetfile, flags, opt) + local argv = table.join("-z", "--output_file=" .. targetfile, objectfiles, flags) + return self:program(), argv +end + +-- link the target file +-- +-- maybe we need to use os.vrunv() to show link output when enable verbose information +-- @see https://github.com/xmake-io/xmake/discussions/2916 +-- +function link(self, objectfiles, targetkind, targetfile, flags, opt) + opt = opt or {} + os.mkdir(path.directory(targetfile)) + local program, argv = linkargv(self, objectfiles, targetkind, targetfile, flags) + if option.get("verbose") then + os.execv(program, argv, {envs = self:runenvs(), shell = opt.shell}) + else + os.vrunv(program, argv, {envs = self:runenvs(), shell = opt.shell}) + end +end + +-- make the compile arguments list +function compargv(self, sourcefile, objectfile, flags) + return self:program(), table.join("-c", "--preproc_with_compile", flags, "--output_file=" .. objectfile, sourcefile) +end + +-- compile the source file +function compile(self, sourcefile, objectfile, dependinfo, flags, opt) + os.mkdir(path.directory(objectfile)) + local depfile = dependinfo and os.tmpfile() or nil + try + { + function () + local compflags = flags + if depfile then + compflags = table.join(compflags, "-ppd=" .. depfile) + end + local outdata, errdata = os.iorunv(compargv(self, sourcefile, objectfile, compflags)) + 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 policy.build_warnings(opt) then + progress.show_output("${color.warning}%s", table.concat(table.slice(warnings:split('\n'), 1, 8), '\n')) + end + + -- generate the dependent includes + if depfile and os.isfile(depfile) then + if dependinfo then + dependinfo.depfiles_format = "cl6x" + dependinfo.depfiles = io.readfile(depfile, {continuation = "\\"}) + end + + -- remove the temporary dependent file + os.tryrm(depfile) + end + end + } + } +end + + diff --git a/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x/has_flags.lua b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x/has_flags.lua new file mode 100644 index 000000000..629f25c12 --- /dev/null +++ b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x/has_flags.lua @@ -0,0 +1,120 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file has_flags.lua +-- + +-- imports +import("core.cache.detectcache") +import("core.language.language") + +-- is linker? +function _islinker(flags, opt) + 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(program, argv, opt) + local errors = nil + return try { function () os.runv(program, argv, opt); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors +end + +-- attempt to check it from known flags +function _check_from_knownargs(flags, opt, islinker) + local flag = flags[1] + if not islinker then + if flag:startswith("-D") or + flag:startswith("-U") or + flag:startswith("-I") then + return true + end + end +end + +-- attempt to check it from the argument list +function _check_from_arglist(flags, opt, islinker) + local key = "core.tools.cl6x." .. (islinker and "has_ldflags" or "has_cflags") + local flagskey = opt.program .. "_" .. (opt.programver or "") + local allflags = detectcache:get2(key, flagskey) + if not allflags then + allflags = {} + local arglist = try {function () return os.iorunv(opt.program, {"--help"}, {envs = opt.envs}) end} + if arglist then + for arg in arglist:gmatch("%s+(%-[%-%a%d]+)%s+") do + allflags[arg] = true + end + end + detectcache:set2(key, flagskey, allflags) + end + local flag = flags[1] + return allflags[flag] +end + +-- get extension +function _get_extension(opt) + -- @note we need to 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) + + -- make an stub source file + local snippet = opt.snippet or "int main(int argc, char** argv)\n{return 0;}\n" + local sourcefile = os.tmpfile("cl6x_has_flags:" .. snippet) .. _get_extension(opt) + if not os.isfile(sourcefile) then + io.writefile(sourcefile, snippet) + end + + -- check flags for linker + local tmpfile = os.tmpfile() + if islinker then + return _try_running(opt.program, table.join(flags, "-z", "--output_file=" .. tmpfile, sourcefile), opt) + end + + -- check flags for compiler + -- @note we cannot use os.nuldev() as the output file, maybe run failed for some flags, e.g. --coverage + return _try_running(opt.program, table.join(flags, "-c", "--output_file=" .. tmpfile, sourcefile), opt) +end + +-- has_flags(flags)? +-- +-- @param opt the argument options, e.g. {toolname = "", program = "", programver = "", toolkind = "[cc|cxx|ld|ar|sh|gc|mm|mxx]"} +-- +-- @return true or false +-- +function main(flags, opt) + + -- is linker? + opt = opt or {} + local islinker = _islinker(flags, opt) + + -- attempt to check it from the argument list + if not opt.tryrun then + if _check_from_arglist(flags, opt, islinker) then + return true + end + if _check_from_knownargs(flags, opt, islinker) then + return true + end + end + + -- try running to check it + return _check_try_running(flags, opt, islinker) +end + diff --git a/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x/parse_deps.lua b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x/parse_deps.lua new file mode 100644 index 000000000..301d696aa --- /dev/null +++ b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/core/tools/cl6x/parse_deps.lua @@ -0,0 +1,77 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file parse_deps.lua +-- + +-- imports +import("core.project.config") +import("core.project.project") +import("core.base.hashset") + +-- normailize path of a dependecy +function _normailize_dep(dep, projectdir) + -- escape characters, e.g. \#Qt.Widget_pch.h -> #Qt.Widget_pch.h + -- @see https://github.com/xmake-io/xmake/issues/4134 + -- https://github.com/xmake-io/xmake/issues/4273 + if not is_host("windows") then + dep = dep:gsub("\\(.)", "%1") + end + if path.is_absolute(dep) then + dep = path.translate(dep) + else + dep = path.absolute(dep, projectdir) + end + if dep:startswith(projectdir) then + return path.relative(dep, projectdir) + else + -- we also need to check header files outside project + -- https://github.com/xmake-io/xmake/issues/1154 + return dep + end +end + +-- parse depsfiles from string +-- +-- parse_deps(io.readfile(depfile, {continuation = "\\"})) +-- +-- eg. +-- +-- build/.objs/foo/linux/x86_64/release/src/foo.c.o: src/foo.c +-- build/.objs/foo/linux/x86_64/release/src/foo.c.o: src/foo.h +-- +-- build/.objs/tests/linux/x86_64/release/src/main.c.o: src/main.c +-- build/.objs/tests/linux/x86_64/release/src/main.c.o: src/foo.h +-- build/.objs/tests/linux/x86_64/release/src/main.c.o: src/bar.h +-- build/.objs/tests/linux/x86_64/release/src/main.c.o: src/zoo.h +-- +function main(depsdata, opt) + 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 + local plain = {plain = true} + for _, includefile in ipairs(line:split('\n', plain)) do -- it will trim all internal spaces without `{strict = true}` + includefile = includefile:split(": ", plain)[2] + if includefile and #includefile > 0 then + includefile = _normailize_dep(includefile, projectdir) + if includefile then + results:insert(includefile) + end + end + end + return results:to_array() +end diff --git a/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/detect/tools/find_ar6x.lua b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/detect/tools/find_ar6x.lua new file mode 100644 index 000000000..2ee4c4853 --- /dev/null +++ b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/detect/tools/find_ar6x.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, Xmake Open Source Community. +-- +-- @author ruki +-- @file find_ar6x.lua +-- + +-- imports +import("lib.detect.find_program") + +-- check +function _check(program) + + -- make a stub object file + local libraryfile = os.tmpfile() .. ".a" + local objectfile = os.tmpfile() .. ".o" + io.writefile(objectfile, "") + + -- archive it + os.execv(program, {"-r", libraryfile, objectfile}) + + -- remove files + os.rm(objectfile) + os.rm(libraryfile) +end + +-- find ar +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local ar = find_ar6x() +-- local ar, version = find_ar6x({program = "xcrun -sdk macosx g++", version = true}) +-- +-- @endcode +-- +function main(opt) + opt = opt or {} + opt.check = opt.check or _check + return find_program(opt.program or "ar6x", opt) +end + diff --git a/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/detect/tools/find_cl6x.lua b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/detect/tools/find_cl6x.lua new file mode 100644 index 000000000..1db484762 --- /dev/null +++ b/tests/apis/custom_toolchain/xmake/toolchains/my-c6000/modules/detect/tools/find_cl6x.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, Xmake Open Source Community. +-- +-- @author ruki +-- @file find_cl6x.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find cl6x +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local cl6x = find_cl6x() +-- local cl6x, version = find_cl6x({program = "cl6x", version = true}) +-- +-- @endcode +-- +function main(opt) + opt = opt or {} + opt.check = "--help" + opt.command = "--help" + local program = find_program(opt.program or "cl6x", opt) + local version = nil + if program and opt.version then + version = find_programver(program, opt) + end + return program, version +end diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua index beab21a22..5376f406d 100644 --- a/xmake/core/project/project.lua +++ b/xmake/core/project/project.lua @@ -215,6 +215,18 @@ function project._api_add_toolchaindirs(interp, ...) dir = path.absolute(dir, scriptdir) end toolchain.add_directories(dir) + -- auto-register modules directories under each toolchain + -- e.g. toolchains/my-c6000/modules/ will be added as module search path + -- so that custom toolchain can bundle its tool modules together + local toolchain_subdirs = os.dirs(path.join(dir, "*")) + if toolchain_subdirs then + for _, toolchain_subdir in ipairs(toolchain_subdirs) do + local modulesdir = path.join(toolchain_subdir, "modules") + if os.isdir(modulesdir) then + sandbox_module.add_directories(modulesdir) + end + end + end end end -- cgit v1.3.1 From 5522c0c028562b4ede39bd5dbc5ee0aba91b4049 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 11 Apr 2026 21:37:20 +0800 Subject: improve moduledirs --- xmake/core/project/project.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua index 5376f406d..2ea22e655 100644 --- a/xmake/core/project/project.lua +++ b/xmake/core/project/project.lua @@ -220,12 +220,16 @@ function project._api_add_toolchaindirs(interp, ...) -- so that custom toolchain can bundle its tool modules together local toolchain_subdirs = os.dirs(path.join(dir, "*")) if toolchain_subdirs then + local modulesdirs = {} for _, toolchain_subdir in ipairs(toolchain_subdirs) do local modulesdir = path.join(toolchain_subdir, "modules") if os.isdir(modulesdir) then - sandbox_module.add_directories(modulesdir) + table.insert(modulesdirs, modulesdir) end end + if #modulesdirs > 0 then + sandbox_module.add_directories(table.unpack(modulesdirs)) + end end end end -- cgit v1.3.1