Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/release/packages/generate-ucl.lua
102428 views
1
#!/usr/libexec/flua
2
--
3
-- Copyright (c) 2024-2025 Baptiste Daroussin <[email protected]>
4
-- Copyright (c) 2025 Lexi Winter <[email protected]>
5
--
6
-- SPDX-License-Identifier: BSD-2-Clause
7
--
8
9
--[[ usage:
10
generate-ucl.lua [<variablename> <variablevalue>]... <sourceucl> <destucl>
11
12
Build a package's UCL configuration by loading the template UCL file
13
<sourceucl>, replacing any $VARIABLES in the UCL based on the provided
14
variables, then writing the result to <destucl>.
15
]]--
16
17
local ucl = require("ucl")
18
19
-- Give subpackages a special comment and description suffix to indicate what
20
-- they contain, so e.g. "foo-man" has " (manual pages)" appended to its
21
-- comment. This avoids having to create a separate ucl files for every
22
-- subpackage just to set this.
23
--
24
-- Note that this is not a key table because the order of the pattern matches
25
-- is important.
26
pkg_suffixes = {
27
{
28
"%-dev%-lib32$", "(32-bit development files)",
29
"This package contains development files for compiling "..
30
"32-bit applications on a 64-bit host."
31
},
32
{
33
"%-dbg%-lib32$", "(32-bit debugging symbols)",
34
"This package contains 32-bit external debugging symbols "..
35
"for use with a source-level debugger.",
36
},
37
{
38
"%-man%-lib32$", "(32-bit manual pages)",
39
"This package contains the online manual pages for 32-bit "..
40
"components on a 64-bit host.",
41
},
42
{
43
"%-lib32$", "(32-bit libraries)",
44
"This package contains 32-bit libraries for running 32-bit "..
45
"applications on a 64-bit host.",
46
},
47
{
48
"%-lib$", "(libraries)",
49
"This package contains runtime shared libraries.",
50
},
51
{
52
"%-dev$", "(development files)",
53
"This package contains development files for "..
54
"compiling applications."
55
},
56
{
57
"%-man$", "(manual pages)",
58
"This package contains the online manual pages."
59
},
60
{
61
"%-dbg$", "(debugging symbols)",
62
"This package contains external debugging symbols for use "..
63
"with a source-level debugger.",
64
},
65
}
66
67
-- A list of packages which don't get the automatic suffix handling,
68
-- e.g. -man packages with no corresponding base package.
69
local no_suffix_pkgs = {
70
["kernel-man"] = true,
71
}
72
73
function add_suffixes(obj)
74
local pkgname = obj["name"]
75
76
for _,pattern in pairs(pkg_suffixes) do
77
if pkgname:match(pattern[1]) ~= nil then
78
obj["comment"] = obj["comment"] .. " " .. pattern[2]
79
obj["desc"] = obj["desc"] .. "\n\n" .. pattern[3]
80
return
81
end
82
end
83
end
84
85
-- Hardcode a list of packages which don't get the automatic pkggenname
86
-- dependency because the base package doesn't exist. We should have a better
87
-- way to handle this.
88
local no_gen_deps = {
89
["libcompat-dev"] = true,
90
["libcompat-dev-lib32"] = true,
91
["libcompat-man"] = true,
92
["libcompiler_rt-dev"] = true,
93
["libcompiler_rt-dev-lib32"] = true,
94
["liby-dev"] = true,
95
["liby-dev-lib32"] = true,
96
["kernel-man"] = true,
97
}
98
99
-- Return true if the package 'pkgname' should have a dependency on the package
100
-- pkggenname.
101
function add_gen_dep(pkgname, pkggenname)
102
if pkgname == pkggenname then
103
return false
104
end
105
if pkgname == nil or pkggenname == nil then
106
return false
107
end
108
if no_gen_deps[pkgname] ~= nil then
109
return false
110
end
111
if pkgname:match("%-lib$") ~= nil then
112
return false
113
end
114
if pkggenname == "kernel" then
115
return false
116
end
117
118
return true
119
end
120
121
local pkgname = nil
122
local pkggenname = nil
123
local pkgprefix = nil
124
local pkgversion = nil
125
126
-- This parser is the output UCL we want to build.
127
local parser = ucl.parser()
128
129
-- Set any $VARIABLES from the command line in the parser. This causes ucl to
130
-- automatically replace them when we load the source ucl.
131
if #arg < 2 or #arg % 2 ~= 0 then
132
io.stderr:write(arg[0] .. ": expected an even number of arguments, got " .. #arg)
133
os.exit(1)
134
end
135
136
for i = 2, #arg - 2, 2 do
137
local varname = arg[i - 1]
138
local varvalue = arg[i]
139
140
if varname == "PKGNAME" and #varvalue > 0 then
141
pkgname = varvalue
142
elseif varname == "PKGGENNAME" and #varvalue > 0 then
143
pkggenname = varvalue
144
elseif varname == "VERSION" and #varvalue > 0 then
145
pkgversion = varvalue
146
elseif varname == "PKG_NAME_PREFIX" and #varvalue > 0 then
147
pkgprefix = varvalue
148
end
149
150
parser:register_variable(varname, varvalue)
151
end
152
153
-- Load the source ucl file.
154
local res,err = parser:parse_file(arg[#arg - 1])
155
if not res then
156
io.stderr:write(arg[0] .. ": fail to parse("..arg[#arg - 1].."): "..err)
157
os.exit(1)
158
end
159
160
local obj = parser:get_object()
161
162
-- If pkgname is different from pkggenname, add a dependency on pkggenname.
163
-- This means that e.g. -dev packages depend on their respective base package.
164
if add_gen_dep(pkgname, pkggenname) then
165
if obj["deps"] == nil then
166
obj["deps"] = {}
167
end
168
obj["deps"][pkggenname] = {
169
["version"] = pkgversion,
170
["origin"] = "base/"..pkgprefix.."-"..pkggenname,
171
}
172
end
173
174
--
175
-- Handle the 'set' annotation, a comma-separated list of sets which this
176
-- package should be placed in. If it's not specified, the package goes
177
-- in the default set which is base.
178
--
179
-- Ensure we have an annotations table to work with.
180
obj["annotations"] = obj["annotations"] or {}
181
-- If no set is provided, use the default set which is "base".
182
sets = obj["annotations"]["set"] or "base"
183
-- For subpackages, we may need to rewrite the set name. This is done a little
184
-- differently from the normal pkg suffix processing, because we don't need sets
185
-- to be as a granular as the base packages.
186
--
187
-- Create a single lib32 set for all lib32 packages. Most users don't need
188
-- lib32, so this avoids creating a large number of unnecessary lib32 sets.
189
-- However, lib32 debug symbols still go into their own package since they're
190
-- quite large.
191
if pkgname:match("%-dbg%-lib32$") then
192
sets = "lib32-dbg"
193
elseif pkgname:match("%-lib32$") then
194
sets = "lib32"
195
-- If this is a -dev package, put it in a single set called "devel" which
196
-- contains all development files. Also include lib*-man packages, which
197
-- contain manpages for libraries. Having a separate <set>-dev for every
198
-- set is not necessary, because generally you either want development
199
-- support or you don't.
200
elseif pkgname:match("%-dev$") or pkgname:match("^lib.*%-man$") then
201
sets = "devel"
202
-- Don't separate tests and tests-dbg into 2 sets, if the user wants tests
203
-- they should be able to debug failures.
204
elseif sets == "tests" then
205
sets = sets
206
-- If this is a -dbg package, put it in the -dbg subpackage of each set,
207
-- which means the user can install debug symbols only for the sets they
208
-- have installed.
209
elseif pkgname:match("%-dbg$") then
210
local newsets = {}
211
for set in sets:gmatch("[^,]+") do
212
newsets[#newsets + 1] = set .. "-dbg"
213
end
214
sets = table.concat(newsets, ",")
215
end
216
-- Put our new sets back into the package.
217
obj["annotations"]["set"] = sets
218
219
-- If PKG_NAME_PREFIX is provided, rewrite the names of dependency packages.
220
-- We can't do this in UCL since variable substitution doesn't work in array
221
-- keys.
222
if pkgprefix ~= nil and obj["deps"] ~= nil then
223
newdeps = {}
224
for dep, opts in pairs(obj["deps"]) do
225
local newdep = pkgprefix .. "-" .. dep
226
-- Make sure origin is set.
227
opts["origin"] = opts["origin"] or "base/"..newdep
228
newdeps[newdep] = opts
229
end
230
obj["deps"] = newdeps
231
end
232
233
-- Add comment and desc suffix.
234
if no_suffix_pkgs[pkgname] == nil then
235
add_suffixes(obj)
236
end
237
238
-- Write the output file.
239
local f,err = io.open(arg[#arg], "w")
240
if not f then
241
io.stderr:write(arg[0] .. ": fail to open("..arg[#arg].."): ".. err)
242
os.exit(1)
243
end
244
245
f:write(ucl.to_format(obj, 'ucl', true))
246
f:close()
247
248