summaryrefslogtreecommitdiff
path: root/xmake/core/project/addons.lua
diff options
context:
space:
mode:
Diffstat (limited to 'xmake/core/project/addons.lua')
-rw-r--r--xmake/core/project/addons.lua188
1 files changed, 188 insertions, 0 deletions
diff --git a/xmake/core/project/addons.lua b/xmake/core/project/addons.lua
new file mode 100644
index 000000000..994d52ec0
--- /dev/null
+++ b/xmake/core/project/addons.lua
@@ -0,0 +1,188 @@
+--!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 addons.lua
+--
+
+-- define module
+local addons = addons or {}
+
+-- load modules
+local os = require("base/os")
+local io = require("base/io")
+local path = require("base/path")
+local table = require("base/table")
+local addon = require("package/addon")
+
+-- the file which declares the addons of a project, e.g. <projectdir>/xmake-addons.lua
+--
+-- it's loaded before the project file, so that the addons are always installed when
+-- we load the project, e.g. includes("@addon/esp32-devel/board")
+--
+-- e.g.
+-- add_addons("esp32-devel 1.0.x")
+-- add_addons("serial-tools", {optional = true})
+--
+function addons.filename()
+ return "xmake-addons.lua"
+end
+
+function addons.file(projectdir)
+ return path.join(projectdir or os.projectdir(), addons.filename())
+end
+
+-- the lock file of the declared addons, e.g. <projectdir>/xmake-addons.lock
+--
+-- @note it's independent of `xmake-requires.lock`, the addons are always locked,
+-- they provide the build scripts and we should never change them silently
+--
+function addons.lockfile(projectdir)
+ return path.join(projectdir or os.projectdir(), "xmake-addons.lock")
+end
+
+-- get the apis of the addons file
+--
+-- @note it only declares which addons this project needs, the addon resources
+-- are always referenced from the project file, e.g. add_rules("@addon/esp32-devel/app")
+--
+function addons.apis()
+ return {
+ values = {
+ "add_addons"
+ }
+ }
+end
+
+-- the interpreter of the addons file
+function addons._interpreter()
+ local interp = addons._INTERPRETER
+ if interp == nil then
+ -- we need to load it lazily, the interpreter also depends on the addon module
+ local interpreter = require("base/interpreter")
+ interp = interpreter.new()
+ interp:api_define(addons.apis())
+ -- the sub-projects declare their addons in this file too,
+ -- e.g. includes("sub") -> sub/xmake-addons.lua
+ interp:includes_rootfilename_set(addons.filename())
+ -- and we cannot reference the addons here, they have not been installed yet,
+ -- e.g. includes("@addon/esp32-devel/board")
+ interp:includes_references_set(false, "please move it to the project file(xmake.lua)!")
+ addons._INTERPRETER = interp
+ end
+ return interp
+end
+
+-- load the declared addons of the given project directory
+--
+-- @return the addons information and errors, it will be nil if this project declares nothing,
+-- e.g. {addons = {"esp32-devel 1.0.x"}, addons_extra = {...}}
+--
+function addons.load(projectdir)
+ local filepath = addons.file(projectdir)
+ if not os.isfile(filepath) then
+ return
+ end
+
+ -- enter the project directory, the include paths are relative to it,
+ -- e.g. includes("sub")
+ local oldir, errors = os.cd(path.directory(filepath))
+ if not oldir then
+ return nil, errors
+ end
+
+ local rootinfo
+ local interp = addons._interpreter()
+ local ok, errors = interp:load(filepath)
+ if ok then
+ rootinfo, errors = interp:make("root", true, true)
+ end
+ os.cd(oldir)
+ if not rootinfo then
+ return nil, errors
+ end
+
+ local addonsinfo = {addons = table.wrap(rootinfo:get("addons")),
+ addons_extra = rootinfo:extraconf("addons")}
+
+ -- check the declared addons
+ local declared = {}
+ for _, requirestr in ipairs(addonsinfo.addons) do
+
+ -- this file is loaded before the addons are installed, so it cannot reference them
+ if requirestr:startswith("@") then
+ return nil, string.format("%s: cannot reference the addon resources(%s) here, please move it to the project file(xmake.lua)!",
+ filepath, requirestr)
+ end
+
+ local name = requirestr:split("%s")[1]
+ if name == "addon" or name == "self" then
+ return nil, string.format("%s: the addon name(%s) is reserved by xmake for the addon references, please rename it!",
+ filepath, name)
+ end
+ if name == "." or name == ".." or name:find("[/\\:]") then
+ return nil, string.format("%s: invalid addon name(%s)!", filepath, name)
+ end
+
+ -- we can only install one version of an addon for a project
+ if declared[name] then
+ return nil, string.format("%s: the addon(%s) is declared twice, e.g. `%s` and `%s`, please merge them!",
+ filepath, name, declared[name], requirestr)
+ end
+ declared[name] = requirestr
+ end
+ return addonsinfo
+end
+
+-- get the locked addons, e.g. {["esp32-devel"] = {version = "1.0.3"}}
+function addons.locked(projectdir)
+ local lockfile = addons.lockfile(projectdir)
+ if os.isfile(lockfile) then
+ local lockinfo = io.load(lockfile)
+ if lockinfo then
+ lockinfo.__meta__ = nil
+ return lockinfo
+ end
+ end
+end
+
+-- are all the declared addons installed already?
+--
+-- @note we need to check it in-process for every command which loads the project,
+-- so we only check the locked versions here, the installer will resolve them again
+--
+function addons.satisfied(addonsinfo, projectdir)
+ local locked = addons.locked(projectdir)
+ if not locked then
+ return false
+ end
+ local installed = addon.addons()
+ for _, requirestr in ipairs(addonsinfo.addons) do
+ local name = requirestr:split("%s")[1]
+ local lockinfo = locked[name]
+ if not lockinfo then
+ return false
+ end
+ local addoninfo = installed[addon.dirname(name)]
+ if not addoninfo or addoninfo.version ~= lockinfo.version then
+ return false
+ end
+ end
+ return true
+end
+
+-- return module
+return addons