comparison share/lua/5.2/luarocks/util.lua @ 1132:d137f631bad5

<GreyKnight> (cd luabuild/luarocks-2.0.12; make install)
author HackBot
date Fri, 14 Dec 2012 22:24:27 +0000
parents
children
comparison
equal deleted inserted replaced
1131:ff65dfb63263 1132:d137f631bad5
1
2 --- Assorted utilities for managing tables, plus a scheduler for rollback functions.
3 -- Does not requires modules directly (only as locals
4 -- inside specific functions) to avoid interdependencies,
5 -- as this is used in the bootstrapping stage of luarocks.cfg.
6
7 local global_env = _G
8
9 module("luarocks.util", package.seeall)
10
11 local scheduled_functions = {}
12
13 --- Schedule a function to be executed upon program termination.
14 -- This is useful for actions such as deleting temporary directories
15 -- or failure rollbacks.
16 -- @param f function: Function to be executed.
17 -- @param ... arguments to be passed to function.
18 -- @return table: A token representing the scheduled execution,
19 -- which can be used to remove the item later from the list.
20 function schedule_function(f, ...)
21 assert(type(f) == "function")
22
23 local item = { fn = f, args = {...} }
24 table.insert(scheduled_functions, item)
25 return item
26 end
27
28 --- Unschedule a function.
29 -- This is useful for cancelling a rollback of a completed operation.
30 -- @param item table: The token representing the scheduled function that was
31 -- returned from the schedule_function call.
32 function remove_scheduled_function(item)
33 for k, v in pairs(scheduled_functions) do
34 if v == item then
35 table.remove(scheduled_functions, k)
36 return
37 end
38 end
39 end
40
41 --- Execute scheduled functions.
42 -- Some calls create temporary files and/or directories and register
43 -- corresponding cleanup functions. Calling this function will run
44 -- these function, erasing temporaries.
45 -- Functions are executed in the inverse order they were scheduled.
46 function run_scheduled_functions()
47 local fs = require("luarocks.fs")
48 fs.change_dir_to_root()
49 for i = #scheduled_functions, 1, -1 do
50 local item = scheduled_functions[i]
51 item.fn(unpack(item.args))
52 end
53 end
54
55 --- Extract flags from an arguments list.
56 -- Given string arguments, extract flag arguments into a flags set.
57 -- For example, given "foo", "--tux=beep", "--bla", "bar", "--baz",
58 -- it would return the following:
59 -- {["bla"] = true, ["tux"] = "beep", ["baz"] = true}, "foo", "bar".
60 function parse_flags(...)
61 local args = {...}
62 local flags = {}
63 for i = #args, 1, -1 do
64 local flag = args[i]:match("^%-%-(.*)")
65 if flag then
66 local var,val = flag:match("([a-z_%-]*)=(.*)")
67 if val then
68 flags[var] = val
69 else
70 flags[flag] = true
71 end
72 table.remove(args, i)
73 end
74 end
75 return flags, unpack(args)
76 end
77
78 --- Merges contents of src on top of dst's contents.
79 -- @param dst Destination table, which will receive src's contents.
80 -- @param src Table which provides new contents to dst.
81 -- @see platform_overrides
82 function deep_merge(dst, src)
83 for k, v in pairs(src) do
84 if type(v) == "table" then
85 if not dst[k] then
86 dst[k] = {}
87 end
88 if type(dst[k]) == "table" then
89 deep_merge(dst[k], v)
90 else
91 dst[k] = v
92 end
93 else
94 dst[k] = v
95 end
96 end
97 end
98
99 --- Perform platform-specific overrides on a table.
100 -- Overrides values of table with the contents of the appropriate
101 -- subset of its "platforms" field. The "platforms" field should
102 -- be a table containing subtables keyed with strings representing
103 -- platform names. Names that match the contents of the global
104 -- cfg.platforms setting are used. For example, if
105 -- cfg.platforms= {"foo"}, then the fields of
106 -- tbl.platforms.foo will overwrite those of tbl with the same
107 -- names. For table values, the operation is performed recursively
108 -- (tbl.platforms.foo.x.y.z overrides tbl.x.y.z; other contents of
109 -- tbl.x are preserved).
110 -- @param tbl table or nil: Table which may contain a "platforms" field;
111 -- if it doesn't (or if nil is passed), this function does nothing.
112 function platform_overrides(tbl)
113 assert(type(tbl) == "table" or not tbl)
114
115 local cfg = require("luarocks.cfg")
116
117 if not tbl then return end
118
119 if tbl.platforms then
120 for _, platform in ipairs(cfg.platforms) do
121 local platform_tbl = tbl.platforms[platform]
122 if platform_tbl then
123 deep_merge(tbl, platform_tbl)
124 end
125 end
126 end
127 tbl.platforms = nil
128 end
129
130 local var_format_pattern = "%$%((%a[%a%d_]+)%)"
131
132 --- Create a new shallow copy of a table: a new table with
133 -- the same keys and values. Keys point to the same objects as
134 -- the original table (ie, does not copy recursively).
135 -- @param tbl table: the input table
136 -- @return table: a new table with the same contents.
137 local function make_shallow_copy(tbl)
138 local copy = {}
139 for k,v in pairs(tbl) do
140 copy[k] = v
141 end
142 return copy
143 end
144
145 -- Check if a set of needed variables are referenced
146 -- somewhere in a list of definitions, warning the user
147 -- about any unused ones. Each key in needed_set should
148 -- appear as a $(XYZ) variable at least once as a
149 -- substring of some value of var_defs.
150 -- @param var_defs: a table with string keys and string
151 -- values, containing variable definitions.
152 -- @param needed_set: a set where keys are the names of
153 -- needed variables.
154 -- @param msg string: the warning message to display.
155 function warn_if_not_used(var_defs, needed_set, msg)
156 needed_set = make_shallow_copy(needed_set)
157 for var,val in pairs(var_defs) do
158 for used in val:gmatch(var_format_pattern) do
159 needed_set[used] = nil
160 end
161 end
162 for var,_ in pairs(needed_set) do
163 warning(msg:format(var))
164 end
165 end
166
167 -- Output any entries that might remain in $(XYZ) format,
168 -- warning the user that substitutions have failed.
169 -- @param line string: the input string
170 local function warn_failed_matches(line)
171 local any_failed = false
172 if line:match(var_format_pattern) then
173 for unmatched in line:gmatch(var_format_pattern) do
174 warning("unmatched variable " .. unmatched)
175 any_failed = true
176 end
177 end
178 return any_failed
179 end
180
181 --- Perform make-style variable substitutions on string values of a table.
182 -- For every string value tbl.x which contains a substring of the format
183 -- "$(XYZ)" will have this substring replaced by vars["XYZ"], if that field
184 -- exists in vars. Only string values are processed; this function
185 -- does not scan subtables recursively.
186 -- @param tbl table: Table to have its string values modified.
187 -- @param vars table: Table containing string-string key-value pairs
188 -- representing variables to replace in the strings values of tbl.
189 function variable_substitutions(tbl, vars)
190 assert(type(tbl) == "table")
191 assert(type(vars) == "table")
192
193 local updated = {}
194 for k, v in pairs(tbl) do
195 if type(v) == "string" then
196 updated[k] = v:gsub(var_format_pattern, vars)
197 if warn_failed_matches(updated[k]) then
198 updated[k] = updated[k]:gsub(var_format_pattern, "")
199 end
200 end
201 end
202 for k, v in pairs(updated) do
203 tbl[k] = v
204 end
205 end
206
207 --- Return an array of keys of a table.
208 -- @param tbl table: The input table.
209 -- @return table: The array of keys.
210 function keys(tbl)
211 local ks = {}
212 for k,_ in pairs(tbl) do
213 table.insert(ks, k)
214 end
215 return ks
216 end
217
218 local function default_sort(a, b)
219 local ta = type(a)
220 local tb = type(b)
221 if ta == "number" and tb == "number" then
222 return a < b
223 elseif ta == "number" then
224 return true
225 elseif tb == "number" then
226 return false
227 else
228 return tostring(a) < tostring(b)
229 end
230 end
231
232 -- The iterator function used internally by util.sortedpairs.
233 -- @param tbl table: The table to be iterated.
234 -- @param sort_function function or nil: An optional comparison function
235 -- to be used by table.sort when sorting keys.
236 -- @see sortedpairs
237 local function sortedpairs_iterator(tbl, sort_function)
238 local ks = keys(tbl)
239 if not sort_function or type(sort_function) == "function" then
240 table.sort(ks, sort_function or default_sort)
241 for _, k in ipairs(ks) do
242 coroutine.yield(k, tbl[k])
243 end
244 else
245 local order = sort_function
246 local done = {}
247 for _, k in ipairs(order) do
248 local sub_order
249 if type(k) == "table" then
250 sub_order = k[2]
251 k = k[1]
252 end
253 if tbl[k] then
254 done[k] = true
255 coroutine.yield(k, tbl[k], sub_order)
256 end
257 end
258 table.sort(ks, default_sort)
259 for _, k in ipairs(ks) do
260 if not done[k] then
261 coroutine.yield(k, tbl[k])
262 end
263 end
264 end
265 end
266
267 --- A table iterator generator that returns elements sorted by key,
268 -- to be used in "for" loops.
269 -- @param tbl table: The table to be iterated.
270 -- @param sort_function function or table or nil: An optional comparison function
271 -- to be used by table.sort when sorting keys, or an array listing an explicit order
272 -- for keys. If a value itself is an array, it is taken so that the first element
273 -- is a string representing the field name, and the second element is a priority table
274 -- for that key.
275 -- @return function: the iterator function.
276 function sortedpairs(tbl, sort_function)
277 return coroutine.wrap(function() sortedpairs_iterator(tbl, sort_function) end)
278 end
279
280 function starts_with(s, prefix)
281 return s:sub(1,#prefix) == prefix
282 end
283
284 --- Print a line to standard output
285 function printout(...)
286 io.stdout:write(table.concat({...},"\t"))
287 io.stdout:write("\n")
288 end
289
290 --- Print a line to standard error
291 function printerr(...)
292 io.stderr:write(table.concat({...},"\t"))
293 io.stderr:write("\n")
294 end
295
296 --- Display a warning message.
297 -- @param msg string: the warning message
298 function warning(msg)
299 printerr("Warning: "..msg)
300 end
301
302 function title(msg, porcelain, underline)
303 if porcelain then return end
304 printout()
305 printout(msg)
306 printout((underline or "-"):rep(#msg))
307 printout()
308 end
309
310 -- from http://lua-users.org/wiki/SplitJoin
311 -- by PhilippeLhoste
312 function split_string(str, delim, maxNb)
313 -- Eliminate bad cases...
314 if string.find(str, delim) == nil then
315 return { str }
316 end
317 if maxNb == nil or maxNb < 1 then
318 maxNb = 0 -- No limit
319 end
320 local result = {}
321 local pat = "(.-)" .. delim .. "()"
322 local nb = 0
323 local lastPos
324 for part, pos in string.gmatch(str, pat) do
325 nb = nb + 1
326 result[nb] = part
327 lastPos = pos
328 if nb == maxNb then break end
329 end
330 -- Handle the last field
331 if nb ~= maxNb then
332 result[nb + 1] = string.sub(str, lastPos)
333 end
334 return result
335 end
336
337 --- Remove repeated entries from a path-style string.
338 -- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d".
339 -- @param list string: A path string (from $PATH or package.path)
340 -- @param sep string: The separator
341 function remove_path_dupes(list, sep)
342 assert(type(list) == "string")
343 assert(type(sep) == "string")
344 local parts = split_string(list, sep)
345 local final, entries = {}, {}
346 for _, part in ipairs(parts) do
347 if not entries[part] then
348 table.insert(final, part)
349 entries[part] = true
350 end
351 end
352 return table.concat(final, sep)
353 end
354
355 ---
356 -- Formats tables with cycles recursively to any depth.
357 -- References to other tables are shown as values.
358 -- Self references are indicated.
359 -- The string returned is "Lua code", which can be procesed
360 -- (in the case in which indent is composed by spaces or "--").
361 -- Userdata and function keys and values are shown as strings,
362 -- which logically are exactly not equivalent to the original code.
363 -- This routine can serve for pretty formating tables with
364 -- proper indentations, apart from printing them:
365 -- io.write(table.show(t, "t")) -- a typical use
366 -- Written by Julio Manuel Fernandez-Diaz,
367 -- Heavily based on "Saving tables with cycles", PIL2, p. 113.
368 -- @param t table: is the table.
369 -- @param name string: is the name of the table (optional)
370 -- @param indent string: is a first indentation (optional).
371 -- @return string: the pretty-printed table
372 function show_table(t, name, indent)
373 local cart -- a container
374 local autoref -- for self references
375
376 local function isemptytable(t) return next(t) == nil end
377
378 local function basicSerialize (o)
379 local so = tostring(o)
380 if type(o) == "function" then
381 local info = debug.getinfo(o, "S")
382 -- info.name is nil because o is not a calling level
383 if info.what == "C" then
384 return ("%q"):format(so .. ", C function")
385 else
386 -- the information is defined through lines
387 return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source)
388 end
389 elseif type(o) == "number" then
390 return so
391 else
392 return ("%q"):format(so)
393 end
394 end
395
396 local function addtocart (value, name, indent, saved, field)
397 indent = indent or ""
398 saved = saved or {}
399 field = field or name
400
401 cart = cart .. indent .. field
402
403 if type(value) ~= "table" then
404 cart = cart .. " = " .. basicSerialize(value) .. ";\n"
405 else
406 if saved[value] then
407 cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n"
408 autoref = autoref .. name .. " = " .. saved[value] .. ";\n"
409 else
410 saved[value] = name
411 --if tablecount(value) == 0 then
412 if isemptytable(value) then
413 cart = cart .. " = {};\n"
414 else
415 cart = cart .. " = {\n"
416 for k, v in pairs(value) do
417 k = basicSerialize(k)
418 local fname = ("%s[%s]"):format(name, k)
419 field = ("[%s]"):format(k)
420 -- three spaces between levels
421 addtocart(v, fname, indent .. " ", saved, field)
422 end
423 cart = cart .. indent .. "};\n"
424 end
425 end
426 end
427 end
428
429 name = name or "__unnamed__"
430 if type(t) ~= "table" then
431 return name .. " = " .. basicSerialize(t)
432 end
433 cart, autoref = "", ""
434 addtocart(t, name, indent)
435 return cart .. autoref
436 end