summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2023-02-01 16:12:11 +0800
committerGitHub <[email protected]>2023-02-01 16:12:11 +0800
commit40a30dac7eafaf562a84f9c8354e46a41bd08739 (patch)
tree57cb84e0806decd8a8d6c3212bd7a73830245c82
parent3e5f4ce74a98bb1d8b3fc6d7ff9a9bd04aedf710 (diff)
parent8d09077f31b043b1d94494f42f6ae2a3109535ea (diff)
Merge pull request #3323 from xmake-io/check
add `xmake check`
-rw-r--r--xmake/core/base/string.lua37
-rw-r--r--xmake/core/project/policy.lua3
-rw-r--r--xmake/core/sandbox/modules/import/core/sandbox/module.lua4
-rw-r--r--xmake/plugins/check/checker.lua77
-rw-r--r--xmake/plugins/check/checkers/api/api_checker.lua101
-rw-r--r--xmake/plugins/check/checkers/api/target/languages.lua40
-rw-r--r--xmake/plugins/check/checkers/api/target/packages.lua32
-rw-r--r--xmake/plugins/check/checkers/clang/tidy.lua22
-rw-r--r--xmake/plugins/check/main.lua91
-rw-r--r--xmake/plugins/check/xmake.lua45
-rw-r--r--xmake/plugins/show/main.lua1
-rw-r--r--xmake/plugins/show/xmake.lua37
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}
+ }
+ }