diff options
| author | ruki <[email protected]> | 2023-02-01 16:12:11 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-02-01 16:12:11 +0800 |
| commit | 40a30dac7eafaf562a84f9c8354e46a41bd08739 (patch) | |
| tree | 57cb84e0806decd8a8d6c3212bd7a73830245c82 | |
| parent | 3e5f4ce74a98bb1d8b3fc6d7ff9a9bd04aedf710 (diff) | |
| parent | 8d09077f31b043b1d94494f42f6ae2a3109535ea (diff) | |
Merge pull request #3323 from xmake-io/check
add `xmake check`
| -rw-r--r-- | xmake/core/base/string.lua | 37 | ||||
| -rw-r--r-- | xmake/core/project/policy.lua | 3 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/sandbox/module.lua | 4 | ||||
| -rw-r--r-- | xmake/plugins/check/checker.lua | 77 | ||||
| -rw-r--r-- | xmake/plugins/check/checkers/api/api_checker.lua | 101 | ||||
| -rw-r--r-- | xmake/plugins/check/checkers/api/target/languages.lua | 40 | ||||
| -rw-r--r-- | xmake/plugins/check/checkers/api/target/packages.lua | 32 | ||||
| -rw-r--r-- | xmake/plugins/check/checkers/clang/tidy.lua | 22 | ||||
| -rw-r--r-- | xmake/plugins/check/main.lua | 91 | ||||
| -rw-r--r-- | xmake/plugins/check/xmake.lua | 45 | ||||
| -rw-r--r-- | xmake/plugins/show/main.lua | 1 | ||||
| -rw-r--r-- | xmake/plugins/show/xmake.lua | 37 |
12 files changed, 461 insertions, 29 deletions
diff --git a/xmake/core/base/string.lua b/xmake/core/base/string.lua index 43b0096b4..625678c6c 100644 --- a/xmake/core/base/string.lua +++ b/xmake/core/base/string.lua @@ -379,5 +379,42 @@ function string:wcswidth(idx) return width end +-- compute the Levenshtein distance between two strings +function string:levenshtein(str2) + local str1 = self + local len1 = #str1 + local len2 = #str2 + local matrix = {} + local cost = 0 + + if len1 == 0 then + return len2 + elseif len2 == 0 then + return len1 + elseif str1 == str2 then + return 0 + end + + for i = 0, len1, 1 do + matrix[i] = {} + matrix[i][0] = i + end + for j = 0, len2, 1 do + matrix[0][j] = j + end + + for i = 1, len1, 1 do + for j = 1, len2, 1 do + if (str1:byte(i) == str2:byte(j)) then + cost = 0 + else + cost = 1 + end + matrix[i][j] = math.min(matrix[i-1][j] + 1, matrix[i][j-1] + 1, matrix[i-1][j-1] + cost) + end + end + return matrix[len1][len2] +end + -- return module: string return string diff --git a/xmake/core/project/policy.lua b/xmake/core/project/policy.lua index 86c790cd9..1a466aa68 100644 --- a/xmake/core/project/policy.lua +++ b/xmake/core/project/policy.lua @@ -33,8 +33,7 @@ local string = require("base/string") function policy.policies() local policies = policy._POLICIES if not policies then - policies = - { + policies = { -- we will check and ignore all unsupported flags by default, but we can also pass `{force = true}` to force to set flags, e.g. add_ldflags("-static", {force = true}) ["check.auto_ignore_flags"] = {description = "Enable check and ignore unsupported flags automatically.", default = true, type = "boolean"}, -- we will map gcc flags to the current compiler and linker by default. diff --git a/xmake/core/sandbox/modules/import/core/sandbox/module.lua b/xmake/core/sandbox/modules/import/core/sandbox/module.lua index ab859ab95..1c2dc6fbc 100644 --- a/xmake/core/sandbox/modules/import/core/sandbox/module.lua +++ b/xmake/core/sandbox/modules/import/core/sandbox/module.lua @@ -114,10 +114,10 @@ function core_sandbox_module._find(dir, name) -- the single module? if os.isfile(key .. ".lua") then - return path.absolute(key), false + return path.normalize(path.absolute(key)), false -- modules? elseif os.isdir(key) then - return path.absolute(key), true + return path.normalize(path.absolute(key)), true end end diff --git a/xmake/plugins/check/checker.lua b/xmake/plugins/check/checker.lua new file mode 100644 index 000000000..257bc6b41 --- /dev/null +++ b/xmake/plugins/check/checker.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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file checker.lua +-- + +-- imports +import("core.base.option") + +-- get all checkers +function checkers() + local checkers = _g._CHECKERS + if not checkers then + checkers = { + -- target api checkers + ["api.target.languages"] = {description = "Check languages configuration in target."}, + ["api.target.packages"] = {description = "Check packages configuration in target."}, + -- clang tidy checker + ["clang.tidy"] = {description = "Check project code using clang-tidy."} + } + _g._CHECKERS = checkers + end + return checkers +end + +-- complete checkers +function complete(complete, opt) + return try + { + function () + local list = {} + for name, _ in table.orderpairs(checkers()) do + if not complete then + if #list < 16 then + table.insert(list, name) + else + table.insert(list, "...") + end + elseif name:startswith(complete) then + table.insert(list, name) + end + end + return list + end + } +end + +-- update stats +function update_stats(level, count) + local stats = _g.stats + if not stats then + stats = {} + _g.stats = stats + end + count = count or 1 + stats[level] = (stats[level] or 0) + count +end + +-- show stats +function show_stats() + local stats = _g.stats or {} + cprint("${bright}%d${clear} notes, ${color.warning}%d${clear} warnings, ${color.error}%d${clear} errors", stats.note or 0, stats.warning or 0, stats.error or 0) +end diff --git a/xmake/plugins/check/checkers/api/api_checker.lua b/xmake/plugins/check/checkers/api/api_checker.lua new file mode 100644 index 000000000..ba7e81255 --- /dev/null +++ b/xmake/plugins/check/checkers/api/api_checker.lua @@ -0,0 +1,101 @@ +--!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 api_checker.lua +-- + +-- imports +import("core.base.option") +import("core.base.hashset") +import("core.project.project") +import("..checker") + +-- get the most probable value +function _get_most_probable_value(value, valueset) + local result + local mindist + for v in valueset:keys() do + local dist = value:levenshtein(v) + if not mindist or dist < mindist then + mindist = dist + result = v + end + end + return result +end + +-- show result +function _show(apiname, value, target, opt) + opt = opt or {} + + -- match level? verbose: note/warning/error, default: warning/error + local level = opt.level + if not option.get("verbose") and level == "note" then + return + end + + -- get source information + local sourceinfo = (target:get("__sourceinfo_" .. apiname) or {})[value] or {} + local sourcetips = sourceinfo.file or "" + if sourceinfo.line then + sourcetips = sourcetips .. ":" .. sourceinfo.line .. ": " + end + if #sourcetips == 0 then + sourcetips = string.format("target(%s)", target:name()) + end + + -- do show + local level_tips = "note" + if level == "warning" then + level_tips = "${color.warning}${text.warning}${clear}" + elseif level == "error" then + level_tips = "${color.error}${text.error}${clear}" + end + if apiname:endswith("s") then + apiname = apiname:sub(1, #apiname - 1) + end + _g.showed = _g.showed or {} + local showed = _g.showed + local infostr = string.format("%s%s: unknown %s value '%s'", sourcetips, level_tips, apiname, value) + local probable_value = _get_most_probable_value(value, opt.valueset) + if probable_value then + infostr = string.format("%s, it may be '%s'", infostr, probable_value) + end + if not showed[infostr] then + cprint(infostr) + showed[infostr] = true + return true + end +end + +-- check api configuration in targets +function check_targets(apiname, opt) + opt = opt or {} + local level = opt.level or "warning" + local valueset = hashset.from(opt.values) + for _, target in pairs(project.targets()) do + local values = target:get(apiname) + for _, value in ipairs(values) do + if not valueset:has(value) then + local reported = _show(apiname, value, target, {valueset = valueset, level = level}) + if reported then + checker.update_stats(level) + end + end + end + end +end diff --git a/xmake/plugins/check/checkers/api/target/languages.lua b/xmake/plugins/check/checkers/api/target/languages.lua new file mode 100644 index 000000000..9831bc2f5 --- /dev/null +++ b/xmake/plugins/check/checkers/api/target/languages.lua @@ -0,0 +1,40 @@ +--!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 languages.lua +-- + +-- imports +import(".api_checker") + +function main() + local values = { + "ansi", "c89", "c90", "c99", "c11", "c17", "clatest", + "cxx98", "cxx11", "cxx14", "cxx17", "cxx1z", "cxx20", "cxx2a", "cxx23", "cxx2b", "cxxlatest" + } + local languages = {} + for _, value in ipairs(values) do + table.insert(languages, value) + if value:find("xx", 1, true) then + table.insert(languages, (value:gsub("xx", "++"))) + end + if value:startswith("c") then + table.insert(languages, "gnu" .. value:sub(2)) + end + end + api_checker.check_targets("languages", {values = languages}) +end diff --git a/xmake/plugins/check/checkers/api/target/packages.lua b/xmake/plugins/check/checkers/api/target/packages.lua new file mode 100644 index 000000000..e34f602db --- /dev/null +++ b/xmake/plugins/check/checkers/api/target/packages.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 packages.lua +-- + +-- imports +import("core.project.project") +import(".api_checker") + +function main() + local packages = {} + local requires = project.required_packages() + if requires then + table.join2(packages, table.orderkeys(requires)) + end + api_checker.check_targets("packages", {values = packages, level = "note"}) +end diff --git a/xmake/plugins/check/checkers/clang/tidy.lua b/xmake/plugins/check/checkers/clang/tidy.lua new file mode 100644 index 000000000..d3eb28c18 --- /dev/null +++ b/xmake/plugins/check/checkers/clang/tidy.lua @@ -0,0 +1,22 @@ +--!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 tidy.lua +-- + +function main() +end diff --git a/xmake/plugins/check/main.lua b/xmake/plugins/check/main.lua new file mode 100644 index 000000000..dd190624d --- /dev/null +++ b/xmake/plugins/check/main.lua @@ -0,0 +1,91 @@ +--!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 main.lua +-- + +-- imports +import("core.base.option") +import("core.base.text") +import("core.project.config") +import("checker") + +-- show checkers list +function _show_list() + local tbl = {align = 'l', sep = " "} + local checkers = checker.checkers() + local groups = {} + for name, info in table.orderpairs(checkers) do + local groupname = name:split(".", {plain = true})[1] + if not groups[groupname] then + table.insert(tbl, {}) + table.insert(tbl, {groupname:sub(1, 1):upper() .. groupname:sub(2) .. " checkers:"}) + groups[groupname] = true + end + table.insert(tbl, {{" " .. name, style = "${color.dump.string_quote}"}, info.description}) + end + cprint(text.table(tbl)) +end + +-- show checker information +function _show_info(name) + local checkers = checker.checkers() + local info = checkers[name] + if info then + cprint("${color.dump.string}checker${clear}(%s):", name) + cprint(" -> ${color.dump.string_quote}description${clear}: %s", info.description) + else + raise("checker(%s) not found!", name) + end +end + +-- do check +function _check(group_or_name, arguments) + + -- load config + config.load() + + -- get checkers + local checked_checkers = {} + local checkers = checker.checkers() + if checkers[group_or_name] then + table.insert(checked_checkers, group_or_name) + else + for name, _ in table.orderpairs(checkers) do + if name:startswith(group_or_name .. ".") then + table.insert(checked_checkers, name) + end + end + end + assert(#checked_checkers > 0, "checker(%s) not found!", group_or_name) + + -- do checkers + for _, name in ipairs(checked_checkers) do + import("checkers." .. name, {anonymous = true})(arguments) + end + checker.show_stats() +end + +function main() + if option.get("list") then + _show_list() + elseif option.get("info") then + _show_info(option.get("info")) + elseif option.get("checkers") then + _check(option.get("checkers"), option.get("arguments")) + end +end diff --git a/xmake/plugins/check/xmake.lua b/xmake/plugins/check/xmake.lua new file mode 100644 index 000000000..d4a7dc19a --- /dev/null +++ b/xmake/plugins/check/xmake.lua @@ -0,0 +1,45 @@ +--!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 xmake.lua +-- + +task("check") + set_category("plugin") + on_run("main") + set_menu { + usage = "xmake check [options] [arguments]", + description = "Check the project sourcecode and configuration.", + options = { + {'l', "list", "k", nil, "Show all supported checkers list."}, + {nil, "info", "kv", nil, "Show the given checker information."}, + {nil, "checkers", "v", "api", "Use the given checkers to check project.", + "e.g.", + " - xmake check api", + " - xmake check -v api.target", + " - xmake check api.target.languages", + "", + "The supported checkers list:", + values = function (complete, opt) + return import("plugins.check.checker", {rootdir = os.programdir()}).complete(complete, opt) + end}, + {nil, "arguments", "vs", nil, "Set the checker arguments.", + "e.g.", + " - xmake check clang.tidy [arguments]"} + } + } + diff --git a/xmake/plugins/show/main.lua b/xmake/plugins/show/main.lua index 39b2bc0a0..71538649a 100644 --- a/xmake/plugins/show/main.lua +++ b/xmake/plugins/show/main.lua @@ -26,7 +26,6 @@ function _show_list(name) assert(#name > 0 and import("lists." .. name, {try = true, anonymous = true}), "unknown list name(%s)", name)() end --- main entry function main() -- show list? diff --git a/xmake/plugins/show/xmake.lua b/xmake/plugins/show/xmake.lua index 79eef1c0d..f652ed620 100644 --- a/xmake/plugins/show/xmake.lua +++ b/xmake/plugins/show/xmake.lua @@ -18,34 +18,23 @@ -- @file xmake.lua -- --- define task task("show") - - -- set category set_category("plugin") - - -- on run on_run("main") - - -- set menu set_menu { - -- usage - usage = "xmake show [options] [arguments]" - - -- description - , description = "Show the given project information." - - -- options - , options = - { - {'l', "list" , "kv" , nil , "Show the values list of the given name." - , values = function (complete, opt) - return import("list").lists() - end}, - {'t', "target" , "kv" , nil , "Show the information of the given target." - , values = function (complete, opt) return import("private.utils.complete_helper.targets")(complete, opt) end } - } - } + usage = "xmake show [options] [arguments]", + description = "Show the given project information.", + options = { + {'l', "list", "kv", nil, "Show the values list of the given name.", + values = function (complete, opt) + return import("list").lists() + end}, + {'t', "target", "kv", nil, "Show the information of the given target.", + values = function (complete, opt) + return import("private.utils.complete_helper.targets")(complete, opt) + end} + } + } |
