9e23534ec3
So far, we were using a sort operation on the generated .config to implement precedence of =y packages over =m, and =m over unset. Unfortunately, this sort not only used for packages, but for all config lines. This made it impossible to override settings from targets/generic in a target config when the new setting was sorted before the generic setting. To fix this, track configurations by their keys, so we can properly override config keys that were set before. Value-based precedence is only preserved for package configuration. The config() and try_config() calls always take key and value as separate arguments now. Strings are quoted automatically; the values true, nil and false map to y, m and unset for tristate options. config() can take an optional third argument to override the error message to display when the setting fails to apply. All existing target configs generate the same .config with the old and the new code. The new code is also a bit faster on targets with many devices.
332 lines
6.5 KiB
Lua
332 lines
6.5 KiB
Lua
-- Functions for use in targets/*
|
|
local F = {}
|
|
|
|
-- To be accessed by scripts using target_lib
|
|
local M = setmetatable({}, { __index = F })
|
|
|
|
local funcs = setmetatable({}, {
|
|
__index = function(_, k)
|
|
return F[k] or _G[k]
|
|
end,
|
|
})
|
|
|
|
local env = setmetatable({}, {
|
|
__index = function(_, k) return os.getenv(k) end
|
|
})
|
|
F.env = env
|
|
|
|
assert(env.GLUON_SITEDIR)
|
|
assert(env.GLUON_TARGETSDIR)
|
|
assert(env.GLUON_RELEASE)
|
|
assert(env.GLUON_DEPRECATED)
|
|
|
|
|
|
M.site_code = assert(assert(dofile('scripts/site_config.lua')('site.conf')).site_code)
|
|
M.target_packages = {}
|
|
M.configs = {}
|
|
M.devices = {}
|
|
M.images = {}
|
|
M.opkg = true
|
|
|
|
|
|
local default_options = {
|
|
profile = false,
|
|
factory = '-squashfs-factory',
|
|
factory_ext = '.bin',
|
|
sysupgrade = '-squashfs-sysupgrade',
|
|
sysupgrade_ext = '.bin',
|
|
extra_images = {},
|
|
aliases = {},
|
|
manifest_aliases = {},
|
|
packages = {},
|
|
class = 'standard',
|
|
deprecated = false,
|
|
broken = false,
|
|
}
|
|
|
|
|
|
local gluon_devices, unknown_devices = {}, {}
|
|
for dev in string.gmatch(env.GLUON_DEVICES or '', '%S+') do
|
|
gluon_devices[dev] = true
|
|
unknown_devices[dev] = true
|
|
end
|
|
|
|
function F.istrue(v)
|
|
return (tonumber(v) or 0) > 0
|
|
end
|
|
|
|
local function want_device(dev, options)
|
|
if options.broken and not F.istrue(env.BROKEN) then
|
|
return false
|
|
end
|
|
if options.deprecated and env.GLUON_DEPRECATED == '0' then
|
|
return false
|
|
end
|
|
|
|
if (env.GLUON_DEVICES or '') == '' then
|
|
return true
|
|
end
|
|
|
|
unknown_devices[dev] = nil
|
|
return gluon_devices[dev]
|
|
end
|
|
|
|
local full_deprecated = env.GLUON_DEPRECATED == 'full'
|
|
|
|
|
|
local function merge(a, b)
|
|
local ret = {}
|
|
for k, v in pairs(a) do
|
|
ret[k] = v
|
|
end
|
|
for k, v in pairs(b or {}) do
|
|
assert(ret[k] ~= nil, string.format("unknown option '%s'", k))
|
|
ret[k] = v
|
|
end
|
|
return ret
|
|
end
|
|
|
|
-- Escapes a single argument to be used in a shell command
|
|
-- The argument is surrounded by single quotes, single quotes inside the
|
|
-- argument are replaced by '\''.
|
|
-- To allow using shell wildcards, zero bytes in the arguments are replaced
|
|
-- by unquoted asterisks.
|
|
function F.escape(s)
|
|
s = string.gsub(s, "'", "'\\''")
|
|
s = string.gsub(s, "%z", "'*'")
|
|
return "'" .. s .. "'"
|
|
end
|
|
|
|
local function escape_command(command, raw)
|
|
local ret = ''
|
|
if not raw then
|
|
ret = 'exec'
|
|
end
|
|
for _, arg in ipairs(command) do
|
|
ret = ret .. ' ' .. F.escape(arg)
|
|
end
|
|
if raw then
|
|
ret = ret .. ' ' .. raw
|
|
end
|
|
return ret
|
|
end
|
|
|
|
function F.exec_raw(command, may_fail)
|
|
local ret = os.execute(command)
|
|
assert((ret == 0) or may_fail)
|
|
return ret
|
|
end
|
|
|
|
function F.exec(command, may_fail, raw)
|
|
return F.exec_raw(escape_command(command, raw), may_fail)
|
|
end
|
|
|
|
function F.exec_capture_raw(command)
|
|
local f = io.popen(command)
|
|
assert(f)
|
|
|
|
local data = f:read('*a')
|
|
f:close()
|
|
return data
|
|
end
|
|
|
|
function F.exec_capture(command, raw)
|
|
return F.exec_capture_raw(escape_command(command, raw))
|
|
end
|
|
|
|
|
|
local image_mt = {
|
|
__index = {
|
|
dest_name = function(image, name, site, release)
|
|
return env.GLUON_IMAGEDIR..'/'..image.subdir,
|
|
'gluon-'..(site or M.site_code)..'-'..(release or env.GLUON_RELEASE)..'-'..name..image.out_suffix..image.extension
|
|
end,
|
|
},
|
|
}
|
|
|
|
local function add_image(image)
|
|
local device = image.image
|
|
M.images[device] = M.images[device] or {}
|
|
table.insert(M.images[device], setmetatable(image, image_mt))
|
|
end
|
|
|
|
|
|
local function format_config(k, v)
|
|
local format
|
|
if type(v) == 'string' then
|
|
format = '%s=%q'
|
|
elseif v == true then
|
|
format = '%s=y'
|
|
elseif v == nil then
|
|
format = '%s=m'
|
|
elseif v == false then
|
|
format = '# %s is not set'
|
|
else
|
|
format = '%s=%d'
|
|
end
|
|
return string.format(format, k, v)
|
|
end
|
|
|
|
local config_mt = {
|
|
__index = {
|
|
format = function(config)
|
|
return format_config(config.key, config.value)
|
|
end,
|
|
}
|
|
}
|
|
|
|
local function do_config(k, v, required)
|
|
M.configs[k] = setmetatable({
|
|
key = k,
|
|
value = v,
|
|
required = required,
|
|
}, config_mt)
|
|
end
|
|
|
|
function F.try_config(k, v)
|
|
do_config(k, v)
|
|
end
|
|
|
|
function F.config(k, v, message)
|
|
if not message then
|
|
message = string.format("unable to set '%s'", format_config(k, v))
|
|
end
|
|
do_config(k, v, message)
|
|
end
|
|
|
|
|
|
function F.packages(pkgs)
|
|
for _, pkg in ipairs(pkgs) do
|
|
table.insert(M.target_packages, pkg)
|
|
end
|
|
end
|
|
M.packages = F.packages
|
|
|
|
function F.device(image, name, options)
|
|
options = merge(default_options, options)
|
|
|
|
if not want_device(image, options) then
|
|
return
|
|
end
|
|
|
|
table.insert(M.devices, {
|
|
image = image,
|
|
name = name,
|
|
options = options,
|
|
})
|
|
|
|
if options.sysupgrade then
|
|
add_image {
|
|
image = image,
|
|
name = name,
|
|
subdir = 'sysupgrade',
|
|
in_suffix = options.sysupgrade,
|
|
out_suffix = '-sysupgrade',
|
|
extension = options.sysupgrade_ext,
|
|
aliases = options.aliases,
|
|
manifest_aliases = options.manifest_aliases,
|
|
}
|
|
end
|
|
|
|
if options.deprecated and not full_deprecated then
|
|
return
|
|
end
|
|
|
|
if options.factory then
|
|
add_image {
|
|
image = image,
|
|
name = name,
|
|
subdir = 'factory',
|
|
in_suffix = options.factory,
|
|
out_suffix = '',
|
|
extension = options.factory_ext,
|
|
aliases = options.aliases,
|
|
manifest_aliases = options.manifest_aliases,
|
|
}
|
|
end
|
|
for _, extra_image in ipairs(options.extra_images) do
|
|
add_image {
|
|
image = image,
|
|
name = name,
|
|
subdir = 'other',
|
|
in_suffix = extra_image[1],
|
|
out_suffix = extra_image[2],
|
|
extension = extra_image[3],
|
|
aliases = options.aliases,
|
|
manifest_aliases = options.manifest_aliases,
|
|
}
|
|
end
|
|
end
|
|
|
|
function F.factory_image(image, name, ext, options)
|
|
options = merge(default_options, options)
|
|
|
|
if not want_device(image, options) then
|
|
return
|
|
end
|
|
|
|
if options.deprecated and not full_deprecated then
|
|
return
|
|
end
|
|
|
|
add_image {
|
|
image = image,
|
|
name = name,
|
|
subdir = 'factory',
|
|
in_suffix = '',
|
|
out_suffix = '',
|
|
extension = ext,
|
|
aliases = options.aliases,
|
|
manifest_aliases = options.manifest_aliases,
|
|
}
|
|
end
|
|
|
|
function F.sysupgrade_image(image, name, ext, options)
|
|
options = merge(default_options, options)
|
|
|
|
if not want_device(image, options) then
|
|
return
|
|
end
|
|
|
|
add_image {
|
|
image = image,
|
|
name = name,
|
|
subdir = 'sysupgrade',
|
|
in_suffix = '',
|
|
out_suffix = '-sysupgrade',
|
|
extension = ext,
|
|
aliases = options.aliases,
|
|
manifest_aliases = options.manifest_aliases,
|
|
}
|
|
end
|
|
|
|
function F.no_opkg()
|
|
M.opkg = false
|
|
end
|
|
|
|
function F.defaults(options)
|
|
default_options = merge(default_options, options)
|
|
end
|
|
|
|
function F.include(name)
|
|
local f = assert(loadfile(env.GLUON_TARGETSDIR .. '/' .. name))
|
|
setfenv(f, funcs)
|
|
return f()
|
|
end
|
|
|
|
|
|
function M.check_devices()
|
|
local device_list = {}
|
|
for device in pairs(unknown_devices) do
|
|
table.insert(device_list, device)
|
|
end
|
|
if #device_list ~= 0 then
|
|
table.sort(device_list)
|
|
io.stderr:write('Error: unknown devices given: ', table.concat(device_list, ' '), '\n')
|
|
os.exit(1)
|
|
end
|
|
end
|
|
|
|
|
|
return M
|