summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-02-11 11:28:55 +0800
committerGitHub <[email protected]>2025-02-11 11:28:55 +0800
commit4ed3612d7d91182e26972eca47f30d99e67f2882 (patch)
tree311bd88d398aed9709d641c9e66e8cc810122af9
parent53e316a5620c1a4647ba0bb2eac67d22d790a115 (diff)
parentbf41f963c129968e94905b10de9df10248eca8f7 (diff)
Merge pull request #6142 from Shiffted/invalid-target-errors
Improve targetname input checks
-rw-r--r--xmake/actions/clean/main.lua3
-rw-r--r--xmake/actions/package/local/main.lua3
-rw-r--r--xmake/actions/package/oldpkg/main.lua3
-rw-r--r--xmake/actions/package/remote/main.lua3
-rw-r--r--xmake/actions/run/main.lua27
-rw-r--r--xmake/core/base/string.lua44
-rw-r--r--xmake/modules/cli/amalgamate.lua3
-rw-r--r--xmake/modules/private/detect/check_targetname.lua56
-rw-r--r--xmake/modules/private/detect/find_similar_targetnames.lua71
-rw-r--r--xmake/plugins/show/info/target.lua4
10 files changed, 166 insertions, 51 deletions
diff --git a/xmake/actions/clean/main.lua b/xmake/actions/clean/main.lua
index 0cc887651..ec89a4965 100644
--- a/xmake/actions/clean/main.lua
+++ b/xmake/actions/clean/main.lua
@@ -29,6 +29,7 @@ import("core.platform.platform")
import("private.action.clean.remove_files")
import("target.action.clean", {alias = "_do_clean_target"})
import("private.service.remote_build.action", {alias = "remote_build_action"})
+import("private.detect.check_targetname")
-- on clean target
function _on_clean_target(target)
@@ -105,7 +106,7 @@ end
-- clean target
function _clean(targetname)
if targetname then
- local target = project.target(targetname)
+ local target = assert(check_targetname(targetname))
_clean_target(target)
else
_clean_targets(project.ordertargets())
diff --git a/xmake/actions/package/local/main.lua b/xmake/actions/package/local/main.lua
index 3035946ff..bbe1bee74 100644
--- a/xmake/actions/package/local/main.lua
+++ b/xmake/actions/package/local/main.lua
@@ -25,6 +25,7 @@ import("core.project.rule")
import("core.project.config")
import("core.project.project")
import("target.action.install")
+import("private.detect.check_targetname")
-- get library deps
function _get_librarydeps(target)
@@ -230,7 +231,7 @@ function main()
-- package the given target?
if targetname then
- local target = project.target(targetname)
+ local target = assert(check_targetname(targetname))
_package_targets(target:orderdeps())
_package_target(target)
else
diff --git a/xmake/actions/package/oldpkg/main.lua b/xmake/actions/package/oldpkg/main.lua
index 10b902491..d0854635f 100644
--- a/xmake/actions/package/oldpkg/main.lua
+++ b/xmake/actions/package/oldpkg/main.lua
@@ -24,6 +24,7 @@ import("core.base.task")
import("core.project.rule")
import("core.project.config")
import("core.project.project")
+import("private.detect.check_targetname")
-- package library
function _package_library(target)
@@ -193,7 +194,7 @@ function main()
-- package the given target?
if targetname then
- local target = project.target(targetname)
+ local target = assert(check_targetname(targetname))
_package_targets(target:orderdeps())
_package_target(target)
else
diff --git a/xmake/actions/package/remote/main.lua b/xmake/actions/package/remote/main.lua
index 58c89b302..67ce68f1c 100644
--- a/xmake/actions/package/remote/main.lua
+++ b/xmake/actions/package/remote/main.lua
@@ -25,6 +25,7 @@ import("core.project.rule")
import("core.project.config")
import("core.project.project")
import("core.base.bit")
+import("private.detect.check_targetname")
-- get library deps
function _get_librarydeps(target)
@@ -153,7 +154,7 @@ function main()
-- package the given target?
local targetname = option.get("target")
if targetname then
- local target = project.target(targetname)
+ local target = assert(check_targetname(targetname))
_package_targets(target:orderdeps())
_package_target(target)
else
diff --git a/xmake/actions/run/main.lua b/xmake/actions/run/main.lua
index c11eb2d35..ab2db5cd1 100644
--- a/xmake/actions/run/main.lua
+++ b/xmake/actions/run/main.lua
@@ -29,6 +29,7 @@ import("devel.debugger")
import("async.runjobs")
import("private.action.run.runenvs")
import("private.service.remote_build.action", {alias = "remote_build_action"})
+import("private.detect.check_targetname")
import("lib.detect.find_tool")
-- run target
@@ -95,20 +96,6 @@ function _add_target_pkgenvs(target, targets_added)
end
end
--- find target names matching a specific name
-function _find_matching_target_names(targetname)
- targetname = targetname:lower()
- local matching_targetnames = {}
- for _, target in ipairs(project.ordertargets()) do
- if target:name():lower():find(targetname, 1, true) then
- table.insert(matching_targetnames, target:name())
- end
- end
-
- table.sort(matching_targetnames)
- return matching_targetnames
-end
-
-- run the given target
function _run(target)
@@ -163,17 +150,7 @@ function _check_targets(targetname, group_pattern)
-- get targets
local targets = {}
if targetname then
- local target = project.target(targetname)
- if not target then
- -- check if the name is part of other target to help
- local possible_targetnames = _find_matching_target_names(targetname)
- local errors = targetname .. " is not a valid target name for this project"
- if #possible_targetnames > 0 then
- errors = errors .. "\nlist of valid target names close to your input:\n - " .. table.concat(possible_targetnames, '\n - ')
- end
- raise(errors)
- end
-
+ local target = assert(check_targetname(targetname))
table.insert(targets, target)
else
for _, target in ipairs(project.ordertargets()) do
diff --git a/xmake/core/base/string.lua b/xmake/core/base/string.lua
index 2b89f6550..595682193 100644
--- a/xmake/core/base/string.lua
+++ b/xmake/core/base/string.lua
@@ -383,12 +383,21 @@ function string:wcswidth(idx)
end
-- compute the Levenshtein distance between two strings
-function string:levenshtein(str2)
+--
+-- @param str2 the string to compare against
+-- @param opt the options, e.g. {sub = 1, ins = 1, del = 1}
+--
+-- @return the levenshtein distance
+--
+function string:levenshtein(str2, opt)
+ opt = opt or {}
+ local sub = opt.sub or 1
+ local ins = opt.ins or 1
+ local del = opt.del or 1
+
local str1 = self
local len1 = #str1
local len2 = #str2
- local matrix = {}
- local cost = 0
if len1 == 0 then
return len2
@@ -398,25 +407,22 @@ function string:levenshtein(str2)
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
+ local row1 = {}
+ local row2 = {}
+ local sub_cost = 0
- 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)
+ for i = 1, len2 + 1 do
+ row1[i] = (i - 1) * ins
+ end
+ for i = 1, len1 do
+ row2[1] = i * del
+ for j = 1, len2 do
+ sub_cost = (str1:byte(i) == str2:byte(j)) and 0 or sub
+ row2[j + 1] = math.min(row1[j + 1] + del, row2[j] + ins, row1[j] + sub_cost)
end
+ row1, row2 = row2, row1
end
- return matrix[len1][len2]
+ return row1[len2 + 1]
end
-- return module: string
diff --git a/xmake/modules/cli/amalgamate.lua b/xmake/modules/cli/amalgamate.lua
index b1ec3bf7c..8c7ec6fd7 100644
--- a/xmake/modules/cli/amalgamate.lua
+++ b/xmake/modules/cli/amalgamate.lua
@@ -24,6 +24,7 @@ import("core.base.graph")
import("core.project.config")
import("core.project.task")
import("core.project.project")
+import("private.detect.check_targetname")
-- the options
local options =
@@ -159,7 +160,7 @@ function main(...)
-- generate amalgamate code
args.outputdir = args.outputdir or config.buildir()
if args.target then
- local target = assert(project.target(args.target), "target(%s): not found!", args.target)
+ local target = assert(check_targetname(args.target))
_generate_amalgamate_code(target, args)
else
for _, target in ipairs(project.ordertargets()) do
diff --git a/xmake/modules/private/detect/check_targetname.lua b/xmake/modules/private/detect/check_targetname.lua
new file mode 100644
index 000000000..8beabe2b7
--- /dev/null
+++ b/xmake/modules/private/detect/check_targetname.lua
@@ -0,0 +1,56 @@
+--!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 Shiffted
+-- @file check_targetname.lua
+--
+
+-- imports
+import("core.project.project")
+import("private.detect.find_similar_targetnames")
+
+-- check if a target name is valid
+--
+-- @param targetname the target name to check for
+-- @param opt the argument options, e.g. {find_similar = false, max_similar = 5}
+-- @return target or nil, errors
+--
+-- @code
+--
+-- local target, errors = check_targetname("mytarget")
+-- local target, errors = check_targetname("mytarget", {find_similar = false})
+--
+-- @endcode
+--
+function main(targetname, opt)
+ opt = opt or {}
+
+ local target = project.target(targetname)
+ if target then
+ return target
+ end
+
+ local errors = "'" .. targetname .. "' is not a valid target name for this project."
+ if opt.find_similar ~= false then
+ local matching_targetnames = find_similar_targetnames(targetname)
+ if #matching_targetnames > 0 then
+ local max_index = math.min(#matching_targetnames, opt.max_similar or 14)
+ errors = errors .. "\nValid target names closest to input:\n - "
+ .. table.concat(matching_targetnames, '\n - ', 1, max_index)
+ end
+ end
+ return nil, errors
+end
diff --git a/xmake/modules/private/detect/find_similar_targetnames.lua b/xmake/modules/private/detect/find_similar_targetnames.lua
new file mode 100644
index 000000000..3ef023254
--- /dev/null
+++ b/xmake/modules/private/detect/find_similar_targetnames.lua
@@ -0,0 +1,71 @@
+--!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 Shiffted
+-- @file find_similar_targetnames.lua
+--
+
+-- imports
+import("core.project.project")
+
+-- find targets with a similar name
+--
+-- @param targetname the target name to check against
+-- @return table of matching target names
+--
+-- @code
+--
+-- local tool = find_similar_targetnames("mytarget")
+--
+-- @endcode
+--
+function main(targetname)
+ local targetname_lower = targetname:lower()
+ local matching_targetnames = {}
+ local matching_levenshtein = {}
+
+ for _, target in ipairs(project.ordertargets()) do
+ local name = target:name()
+ if name:lower():find(targetname_lower, 1, true) then
+ table.insert(matching_targetnames, name)
+ else
+ local distance = targetname:levenshtein(name, {sub = 2})
+ if distance < 5 then
+ matching_levenshtein[name] = distance
+ end
+ end
+ end
+
+ table.sort(matching_targetnames, function(a, b)
+ if #a == #b then
+ return a < b
+ end
+ return #a < #b
+ end)
+
+ local levenshtein_keys = table.keys(matching_levenshtein)
+ table.sort(levenshtein_keys, function(a, b)
+ local a_distance = matching_levenshtein[a]
+ local b_distance = matching_levenshtein[b]
+ if a_distance == b_distance then
+ return a < b
+ end
+ return a_distance < b_distance
+ end)
+
+ table.join2(matching_targetnames, levenshtein_keys)
+ return matching_targetnames
+end
diff --git a/xmake/plugins/show/info/target.lua b/xmake/plugins/show/info/target.lua
index 871ed321a..9a2ba71e4 100644
--- a/xmake/plugins/show/info/target.lua
+++ b/xmake/plugins/show/info/target.lua
@@ -22,8 +22,8 @@
import("core.base.option")
import("core.base.hashset")
import("core.project.config")
-import("core.project.project")
import("core.language.language")
+import("private.detect.check_targetname")
-- get source info string
function _get_sourceinfo_str(target, name, item, opt)
@@ -247,7 +247,7 @@ function main(name)
-- get target
config.load()
- local target = assert(project.target(name), "target(%s) not found!", name)
+ local target = assert(check_targetname(name))
-- show target information
_show_target(target)