summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-08-15 00:51:42 +0800
committerruki <[email protected]>2026-08-15 00:51:42 +0800
commitcd3721d7fbd63387383050b6d345b69822b75ce1 (patch)
treea8c67180b968976db95dcf449a9ef6d66fb4b0bc
parentfe6b792385a0b56716d703be3fd024380e1baea0 (diff)
improve project.load
-rw-r--r--tests/actions/addon/test.lua21
-rw-r--r--xmake/core/project/project.lua58
-rw-r--r--xmake/core/sandbox/modules/import/core/project/addons.lua15
3 files changed, 70 insertions, 24 deletions
diff --git a/tests/actions/addon/test.lua b/tests/actions/addon/test.lua
index 4db9c0ddf..a14b7b3a5 100644
--- a/tests/actions/addon/test.lua
+++ b/tests/actions/addon/test.lua
@@ -258,8 +258,11 @@ function test_autofetch(t)
_remove("custom-include")
_with_project("autofetch", function (projectdir)
- -- it should be installed when loading the project, so that its includes file can be found
- t:require(os.iorunv("xmake", {"config", "-y"}):find("custom-include: includes check is loaded", 1, true))
+ -- it should be installed when loading the project, so that its includes file can be found,
+ -- and we should tell the user why we install something, it may need to confirm and download
+ local output = os.iorunv("xmake", {"config", "-y"})
+ t:require(output:find("custom-include: includes check is loaded", 1, true))
+ t:require(output:find("this project needs the addons", 1, true))
-- and it should be locked
local lockfile = path.join(projectdir, "xmake-addons.lock")
@@ -272,6 +275,20 @@ function test_autofetch(t)
end)
end
+-- every command builds the option menu, which merges the project tasks in a best-effort way,
+-- so the commands which need not the project should never install its addons
+function test_autofetch_skipped_for_option_menu(t)
+ local recipes = {["custom-include"] = ("set_sourcedir(%q)"):format(_addondir("custom-include"))}
+ _with_repo(recipes, function ()
+ _remove("custom-include")
+ _with_project("autofetch", function (projectdir)
+ os.runv("xmake", {"addon", "--list"})
+ os.runv("xmake", {"lua", "-c", "print(\"hello\")"})
+ t:require_not(os.isfile(path.join(projectdir, "xmake-addons.lock")))
+ end)
+ end)
+end
+
-- the addons file only declares the addons, it cannot reference them
function test_autofetch_invalid(t)
for _, name in ipairs({"autofetch-badinclude", "autofetch-badname"}) do
diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua
index 2c68976dd..3b975cccd 100644
--- a/xmake/core/project/project.lua
+++ b/xmake/core/project/project.lua
@@ -275,6 +275,16 @@ function project._do_install_addons()
return true
end
+ -- tell the user why we are installing something, it may need to confirm and download,
+ -- e.g. `xmake --help` in a project directory which declares some addons
+ utils.cprint("${color.warning}note: ${clear}%s: this project needs the addons(${bright}%s${clear}), installing them ..",
+ addons.filename(), table.concat(addonsinfo.addons, ", "))
+ if baseoption.get("help") then
+ -- the help menu also shows the options which the addons provide, but the user
+ -- did not ask for an installation, so we tell them how to skip it
+ utils.cprint("${dim}we can run it outside of the project directory to skip the installation${clear}")
+ end
+
-- @note we run it in a working directory which has no project, @see addon.workdir(),
-- otherwise it would load this project again
--
@@ -309,19 +319,32 @@ function project._do_install_addons()
end
-- load the project file
-function project._load(force, disable_filter)
-
- -- has already been loaded?
- if project._memcache():get("rootinfo") and not force then
- return true
- end
+--
+-- @param opt the options
+-- - force: load the project file again even if it has been loaded
+-- - disable_filter: disable the interpreter filter, e.g. `$(plat)`
+-- - skip_addons: do not install the addons which this project declares
+--
+function project._load(opt)
+ opt = opt or {}
-- install the addons which this project declares in `xmake-addons.lua` first,
-- it may use their rules, toolchains and includes files,
-- e.g. includes("@addon/esp32-devel/board")
- local ok, errors = project._install_addons()
- if not ok then
- return false, errors
+ --
+ -- @note we need to check it before the cache, the project file may have been loaded
+ -- already without them, e.g. by the option menu
+ --
+ if not opt.skip_addons then
+ local ok, errors = project._install_addons()
+ if not ok then
+ return false, errors
+ end
+ end
+
+ -- has already been loaded?
+ if project._memcache():get("rootinfo") and not opt.force then
+ return true
end
-- enter the project directory
@@ -350,13 +373,13 @@ function project._load(force, disable_filter)
end
-- load the root info of the project
- local rootinfo, errors = project._load_scope("root", true, not disable_filter)
+ local rootinfo, errors = project._load_scope("root", true, not opt.disable_filter)
if not rootinfo then
return false, errors
end
-- load the root info of the target
- local rootinfo_target, errors = project._load_scope("root.target", true, not disable_filter)
+ local rootinfo_target, errors = project._load_scope("root.target", true, not opt.disable_filter)
if not rootinfo_target then
return false, errors
end
@@ -400,6 +423,11 @@ function project._load_scope(scope_kind, deduplicate, enable_filter)
end
-- load tasks
+--
+-- @note we should not install the addons which this project declares here, the option menu
+-- merges the project tasks in a best-effort way and every command builds it,
+-- e.g. `xmake lua`, `xmake addon --remove --all`, @see xmake/core/main.lua
+--
function project._load_tasks()
-- the project file is not found?
@@ -408,7 +436,7 @@ function project._load_tasks()
end
-- load the project file first and disable filter
- local ok, errors = project._load(true, true)
+ local ok, errors = project._load({force = true, disable_filter = true, skip_addons = true})
if not ok then
return nil, errors
end
@@ -489,7 +517,7 @@ function project._load_targets()
-- load all requires first and reload the project file to ensure has_package() works for targets
local requires = project.required_packages()
- local ok, errors = project._load(true)
+ local ok, errors = project._load({force = true})
if not ok then
return nil, errors
end
@@ -570,7 +598,7 @@ function project._load_options(disable_filter)
end
-- reload the project file to ensure `if is_plat() then add_packagedirs() end` works
- local ok, errors = project._load(true, disable_filter)
+ local ok, errors = project._load({force = true, disable_filter = disable_filter})
if not ok then
return nil, errors
end
@@ -1218,7 +1246,7 @@ function project.requires_str()
if not requires_str then
-- reload the project file to handle `has_config()`
- local ok, errors = project._load(true)
+ local ok, errors = project._load({force = true})
if not ok then
os.raise(errors)
end
diff --git a/xmake/core/sandbox/modules/import/core/project/addons.lua b/xmake/core/sandbox/modules/import/core/project/addons.lua
index 0c3ad438c..542cbaf4f 100644
--- a/xmake/core/sandbox/modules/import/core/project/addons.lua
+++ b/xmake/core/sandbox/modules/import/core/project/addons.lua
@@ -26,13 +26,14 @@ local addons = require("project/addons")
local raise = require("sandbox/modules/raise")
-- inherit some builtin interfaces
-sandbox_core_project_addons.file = addons.file
-sandbox_core_project_addons.filename = addons.filename
-sandbox_core_project_addons.lockfile = addons.lockfile
-sandbox_core_project_addons.locked = addons.locked
-sandbox_core_project_addons.locked_valid = addons.locked_valid
-sandbox_core_project_addons.requirename = addons.requirename
-sandbox_core_project_addons.satisfied = addons.satisfied
+sandbox_core_project_addons.file = addons.file
+sandbox_core_project_addons.filename = addons.filename
+sandbox_core_project_addons.lockfile = addons.lockfile
+sandbox_core_project_addons.lockfile_version = addons.lockfile_version
+sandbox_core_project_addons.locked = addons.locked
+sandbox_core_project_addons.locked_valid = addons.locked_valid
+sandbox_core_project_addons.requirename = addons.requirename
+sandbox_core_project_addons.satisfied = addons.satisfied
-- load the declared addons of the given project directory
--