aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-08-14 19:14:17 +0300
committerV1K1NGbg <victor@ilchev.com>2024-08-14 19:14:17 +0300
commit572f059ddc5c80e036956ad157fc4ea3175ce349 (patch)
tree5bdc54ac17c77dbc24462d307b868a3d9a7383b0
parent719d2e29e2834c8226c6b2afaeb2ab402a0ba7d8 (diff)
downloadluarocks-572f059ddc5c80e036956ad157fc4ea3175ce349.tar.gz
luarocks-572f059ddc5c80e036956ad157fc4ea3175ce349.tar.bz2
luarocks-572f059ddc5c80e036956ad157fc4ea3175ce349.zip
writer
-rw-r--r--.editorconfig4
-rw-r--r--src/luarocks/build.tl496
-rw-r--r--src/luarocks/build/builtin.tl400
-rw-r--r--src/luarocks/build/cmake.tl78
-rw-r--r--src/luarocks/build/command.tl41
-rw-r--r--src/luarocks/build/make.tl98
-rw-r--r--src/luarocks/core/cfg.d.tl1
-rw-r--r--src/luarocks/core/types/manifest.d.tl37
-rw-r--r--src/luarocks/core/types/rockspec.lua11
-rw-r--r--src/luarocks/core/types/rockspec.tl21
-rw-r--r--src/luarocks/fs.d.tl4
-rw-r--r--src/luarocks/manif/writer-original.lua469
-rw-r--r--src/luarocks/manif/writer.lua310
-rw-r--r--src/luarocks/manif/writer.tl4
-rw-r--r--src/luarocks/repos.lua2
-rw-r--r--src/luarocks/repos.tl2
16 files changed, 1793 insertions, 185 deletions
diff --git a/.editorconfig b/.editorconfig
index dc5519a7..e927faee 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,6 +10,10 @@ charset = utf-8
10indent_style = space 10indent_style = space
11indent_size = 3 11indent_size = 3
12 12
13[*.tl]
14indent_style = space
15indent_size = 3
16
13[Makefile] 17[Makefile]
14indent_style = tab 18indent_style = tab
15 19
diff --git a/src/luarocks/build.tl b/src/luarocks/build.tl
new file mode 100644
index 00000000..ccb894a4
--- /dev/null
+++ b/src/luarocks/build.tl
@@ -0,0 +1,496 @@
1
2local record build
3 record Op_b
4 build_only_deps: boolean
5 deps_mode: string
6 verify: boolean
7 minimal_mode: boolean
8 need_to_fetch: boolean
9 branch: string
10 no_install: boolean
11 end
12
13 record Builds
14 skip_lua_inc_lib_check: boolean
15
16 end
17end
18
19local path = require("luarocks.path")
20local util = require("luarocks.util")
21local fun = require("luarocks.fun")
22local fetch = require("luarocks.fetch")
23local fs = require("luarocks.fs")
24local dir = require("luarocks.dir")
25local deps = require("luarocks.deps")
26local cfg = require("luarocks.core.cfg")
27local vers = require("luarocks.core.vers")
28local repos = require("luarocks.repos")
29local repo_writer = require("luarocks.repo_writer")
30local deplocks = require("luarocks.deplocks")
31
32local type r = require("luarocks.core.types.rockspec")
33local type Rockspec = r.Rockspec
34local type Installs = r.Installs
35local type Install = r.Install
36
37local type t = require("luarocks.core.types.tree")
38local type Tree = t.Tree
39
40local type Op_b = build.Op_b
41
42do
43 --- Write to the current directory the contents of a table,
44 -- where each key is a file name and its value is the file content.
45 -- @param files table: The table of files to be written.
46 local function extract_from_rockspec(files: {string: string})
47 for name, content in pairs(files) do
48 local fd = io.open(dir.path(fs.current_dir(), name), "w+")
49 fd:write(content)
50 fd:close()
51 end
52 end
53
54 --- Applies patches inlined in the build.patches section
55 -- and extracts files inlined in the build.extra_files section
56 -- of a rockspec.
57 -- @param rockspec table: A rockspec table.
58 -- @return boolean or (nil, string): True if succeeded or
59 -- nil and an error message.
60 function build.apply_patches(rockspec: Rockspec): boolean, string
61
62 if not (rockspec.build.extra_files or rockspec.build.patches) then
63 return true
64 end
65
66 local fd = io.open(fs.absolute_name(".luarocks.patches.applied"), "r")
67 if fd then
68 fd:close()
69 return true
70 end
71
72 if rockspec.build.extra_files then
73 extract_from_rockspec(rockspec.build.extra_files)
74 end
75 if rockspec.build.patches then
76 extract_from_rockspec(rockspec.build.patches)
77 for patch, patchdata in util.sortedpairs(rockspec.build.patches) do
78 util.printout("Applying patch "..patch.."...")
79 local create_delete = rockspec:format_is_at_least("3.0")
80 local ok, err = fs.apply_patch(tostring(patch), patchdata, create_delete)
81 if not ok then
82 return nil, "Failed applying patch "..patch
83 end
84 end
85 end
86
87 fd = io.open(fs.absolute_name(".luarocks.patches.applied"), "w")
88 if fd then
89 fd:close()
90 end
91 return true
92 end
93end
94
95local function check_macosx_deployment_target(rockspec: Rockspec): boolean, string
96 local target = rockspec.build.macosx_deployment_target
97 local function patch_variable(var: string)
98 local rockspec_variables = rockspec.variables as {string: string}
99 if rockspec_variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then
100 rockspec_variables[var] = (rockspec_variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target)
101 else
102 rockspec_variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec_variables[var]
103 end
104 end
105 if cfg.is_platform("macosx") and rockspec:format_is_at_least("3.0") and target then
106 local version = util.popen_read("sw_vers -productVersion")
107 if version:match("^%d+%.%d+%.%d+$") or version:match("^%d+%.%d+$") then
108 if vers.compare_versions(target, version) then
109 return nil, ("This rock requires Mac OSX %s, and you are running %s."):format(target, version)
110 end
111 end
112 patch_variable("CC")
113 patch_variable("LD")
114 end
115 return true
116end
117
118local function process_dependencies(rockspec: Rockspec, opts: Op_b, cwd: string): boolean, string, string
119 if not opts.build_only_deps then
120 local ok, err, errcode = deps.check_external_deps(rockspec, "build")
121 if err then
122 return nil, err, errcode
123 end
124 end
125
126 if opts.deps_mode == "none" then
127 return true
128 end
129
130 local deplock_dir = fs.exists(dir.path(cwd, "luarocks.lock")) and cwd or nil
131
132 if not opts.build_only_deps then
133 if next(rockspec.build_dependencies) ~= nil then
134
135 local user_lua_version = cfg.lua_version
136 local running_lua_version = _VERSION:sub(5)
137
138 if running_lua_version ~= user_lua_version then
139 -- Temporarily flip the user-selected Lua version,
140 -- so that we install build dependencies for the
141 -- Lua version on which the LuaRocks program is running.
142
143 -- HACK: we have to do this by flipping a bunch of
144 -- global config settings, and this list may not be complete.
145 cfg.lua_version = running_lua_version
146 cfg.lua_modules_path = cfg.lua_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
147 cfg.lib_modules_path = cfg.lib_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
148 cfg.rocks_subdir = cfg.rocks_subdir:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
149 path.use_tree(cfg.root_dir as Tree)
150 end
151
152 local ok, err, errcode = deps.fulfill_dependencies(rockspec, "build_dependencies", "all", opts.verify, deplock_dir)
153
154 path.add_to_package_paths(cfg.root_dir)
155
156 if running_lua_version ~= user_lua_version then
157 -- flip the settings back
158 cfg.lua_version = user_lua_version
159 cfg.lua_modules_path = cfg.lua_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
160 cfg.lib_modules_path = cfg.lib_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
161 cfg.rocks_subdir = cfg.rocks_subdir:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
162 path.use_tree(cfg.root_dir as Tree)
163 end
164
165 if err then
166 return nil, err, errcode
167 end
168 end
169 end
170
171 return deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir)
172end
173
174local function fetch_and_change_to_source_dir(rockspec: Rockspec, opts: Op_b): boolean, string, string
175 if opts.minimal_mode or opts.build_only_deps then
176 return true
177 end
178 if opts.need_to_fetch then
179 if opts.branch then
180 rockspec.source.branch = opts.branch
181 end
182 local oks, source_dir, errcode = fetch.fetch_sources(rockspec, true)
183 if not oks then
184 return nil, source_dir, errcode
185 end
186 local ok, err: boolean, string
187 ok, err = fs.change_dir(source_dir)
188 if not ok then
189 return nil, err
190 end
191 else
192 if rockspec.source.file then
193 local ok, err = fs.unpack_archive(rockspec.source.file)
194 if not ok then
195 return nil, err
196 end
197 end
198 local ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
199 if not ok then
200 return nil, err
201 end
202 end
203 fs.change_dir(rockspec.source.dir)
204 return true
205end
206
207local function prepare_install_dirs(name: string, version: string): Installs, string
208 local dirs = {
209 lua = { name = path.lua_dir(name, version), is_module_path = true, perms = "read" },
210 lib = { name = path.lib_dir(name, version), is_module_path = true, perms = "exec" },
211 bin = { name = path.bin_dir(name, version), is_module_path = false, perms = "exec" },
212 conf = { name = path.conf_dir(name, version), is_module_path = false, perms = "read" },
213 }
214
215 for _, d in pairs(dirs as {string: Install}) do
216 local ok, err = fs.make_dir(d.name)
217 if not ok then
218 return nil, err
219 end
220 end
221
222 return dirs
223end
224
225local function run_build_driver(rockspec: Rockspec, no_install: boolean): boolean, string, string
226 local btype = rockspec.build.type
227 if btype == "none" then
228 return true
229 end
230 -- Temporary compatibility
231 if btype == "module" then
232 util.printout("Do not use 'module' as a build type. Use 'builtin' instead.")
233 btype = "builtin"
234 rockspec.build.type = btype
235 end
236 if cfg.accepted_build_types and not fun.contains(cfg.accepted_build_types, btype) then
237 return nil, "This rockspec uses the '"..btype.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration."
238 end
239 local pok, driver = pcall(require, "luarocks.build." .. btype)
240 if not pok or type(driver) ~= "table" then
241 return nil, "Failed initializing build back-end for build type '"..btype.."': "..driver
242 end
243
244 if not driver.skip_lua_inc_lib_check then
245 local ok, err, errcode = deps.check_lua_incdir(rockspec.variables)
246 if not ok then
247 return nil, err, errcode
248 end
249
250 if cfg.link_lua_explicitly then
251 local ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
252 if not ok then
253 return nil, err, errcode
254 end
255 end
256 end
257
258 local ok, err = driver.run(rockspec, no_install)
259 if not ok then
260 return nil, "Build error: " .. err
261 end
262 return true
263end
264
265local install_files
266do
267 --- Install files to a given location.
268 -- Takes a table where the array part is a list of filenames to be copied.
269 -- In the hash part, other keys, if is_module_path is set, are identifiers
270 -- in Lua module format, to indicate which subdirectory the file should be
271 -- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo")
272 -- will copy src/bar.lua to boo/foo.
273 -- @param files table or nil: A table containing a list of files to copy in
274 -- the format described above. If nil is passed, this function is a no-op.
275 -- Directories should be delimited by forward slashes as in internet URLs.
276 -- @param location string: The base directory files should be copied to.
277 -- @param is_module_path boolean: True if string keys in files should be
278 -- interpreted as dotted module paths.
279 -- @param perms string ("read" or "exec"): Permissions of the newly created
280 -- files installed.
281 -- Directories are always created with the default permissions.
282 -- @return boolean or (nil, string): True if succeeded or
283 -- nil and an error message.
284 local function install_to(files, location, is_module_path, perms)
285 assert(type(files) == "table" or not files)
286 assert(type(location) == "string")
287 if not files then
288 return true
289 end
290 for k, file in pairs(files) do
291 local dest = location
292 local filename = dir.base_name(file)
293 if type(k) == "string" then
294 local modname = k
295 if is_module_path then
296 dest = dir.path(location, path.module_to_path(modname))
297 local ok, err = fs.make_dir(dest)
298 if not ok then return nil, err end
299 if filename:match("%.lua$") then
300 local basename = modname:match("([^.]+)$")
301 filename = basename..".lua"
302 end
303 else
304 dest = dir.path(location, dir.dir_name(modname))
305 local ok, err = fs.make_dir(dest)
306 if not ok then return nil, err end
307 filename = dir.base_name(modname)
308 end
309 else
310 local ok, err = fs.make_dir(dest)
311 if not ok then return nil, err end
312 end
313 local ok = fs.copy(file, dir.path(dest, filename), perms)
314 if not ok then
315 return nil, "Failed copying "..file
316 end
317 end
318 return true
319 end
320
321 local function install_default_docs(name, version)
322 local patterns = { "readme", "license", "copying", ".*%.md" }
323 local dest = dir.path(path.install_dir(name, version), "doc")
324 local has_dir = false
325 for file in fs.dir() do
326 for _, pattern in ipairs(patterns) do
327 if file:lower():match("^"..pattern) then
328 if not has_dir then
329 fs.make_dir(dest)
330 has_dir = true
331 end
332 fs.copy(file, dest, "read")
333 break
334 end
335 end
336 end
337 end
338
339 install_files = function(rockspec, dirs)
340 local name, version = rockspec.name, rockspec.version
341
342 if rockspec.build.install then
343 for k, d in pairs(dirs) do
344 local ok, err = install_to(rockspec.build.install[k], d.name, d.is_module_path, d.perms)
345 if not ok then return nil, err end
346 end
347 end
348
349 local copy_directories = rockspec.build.copy_directories
350 local copying_default = false
351 if not copy_directories then
352 copy_directories = {"doc"}
353 copying_default = true
354 end
355
356 local any_docs = false
357 for _, copy_dir in pairs(copy_directories) do
358 if fs.is_dir(copy_dir) then
359 local dest = dir.path(path.install_dir(name, version), copy_dir)
360 fs.make_dir(dest)
361 fs.copy_contents(copy_dir, dest)
362 any_docs = true
363 else
364 if not copying_default then
365 return nil, "Directory '"..copy_dir.."' not found"
366 end
367 end
368 end
369 if not any_docs then
370 install_default_docs(name, version)
371 end
372
373 return true
374 end
375end
376
377--- Build and install a rock given a rockspec.
378-- @param rockspec rockspec: the rockspec to build
379-- @param opts table: build options table
380-- @param cwd string or nil: The current working directory
381-- @return (string, string) or (nil, string, [string]): Name and version of
382-- installed rock if succeeded or nil and an error message followed by an error code.
383function build.build_rockspec(rockspec, opts, cwd)
384 assert(rockspec:type() == "rockspec")
385
386 cwd = cwd or dir.path(".")
387
388 if not rockspec.build then
389 if rockspec:format_is_at_least("3.0") then
390 rockspec.build = {
391 type = "builtin"
392 }
393 else
394 return nil, "Rockspec error: build table not specified"
395 end
396 end
397
398 if not rockspec.build.type then
399 if rockspec:format_is_at_least("3.0") then
400 rockspec.build.type = "builtin"
401 else
402 return nil, "Rockspec error: build type not specified"
403 end
404 end
405
406 local ok, err = fetch_and_change_to_source_dir(rockspec, opts)
407 if not ok then return nil, err end
408
409 if opts.pin then
410 deplocks.init(rockspec.name, ".")
411 end
412
413 ok, err = process_dependencies(rockspec, opts, cwd)
414 if not ok then return nil, err end
415
416 local name, version = rockspec.name, rockspec.version
417 if opts.build_only_deps then
418 if opts.pin then
419 deplocks.write_file()
420 end
421 return name, version
422 end
423
424 local dirs, err
425 local rollback
426 if not opts.no_install then
427 if repos.is_installed(name, version) then
428 repo_writer.delete_version(name, version, opts.deps_mode)
429 end
430
431 dirs, err = prepare_install_dirs(name, version)
432 if not dirs then return nil, err end
433
434 rollback = util.schedule_function(function()
435 fs.delete(path.install_dir(name, version))
436 fs.remove_dir_if_empty(path.versions_dir(name))
437 end)
438 end
439
440 ok, err = build.apply_patches(rockspec)
441 if not ok then return nil, err end
442
443 ok, err = check_macosx_deployment_target(rockspec)
444 if not ok then return nil, err end
445
446 ok, err = run_build_driver(rockspec, opts.no_install)
447 if not ok then return nil, err end
448
449 if opts.no_install then
450 fs.pop_dir()
451 if opts.need_to_fetch then
452 fs.pop_dir()
453 end
454 return name, version
455 end
456
457 ok, err = install_files(rockspec, dirs)
458 if not ok then return nil, err end
459
460 for _, d in pairs(dirs) do
461 fs.remove_dir_if_empty(d.name)
462 end
463
464 fs.pop_dir()
465 if opts.need_to_fetch then
466 fs.pop_dir()
467 end
468
469 if opts.pin then
470 deplocks.write_file()
471 end
472
473 fs.copy(rockspec.local_abs_filename, path.rockspec_file(name, version), "read")
474
475 local deplock_file = deplocks.get_abs_filename(name)
476 if deplock_file then
477 fs.copy(deplock_file, dir.path(path.install_dir(name, version), "luarocks.lock"), "read")
478 end
479
480 ok, err = repo_writer.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), opts.deps_mode, opts.namespace)
481 if not ok then return nil, err end
482
483 util.remove_scheduled_function(rollback)
484 rollback = util.schedule_function(function()
485 repo_writer.delete_version(name, version, opts.deps_mode)
486 end)
487
488 ok, err = repos.run_hook(rockspec, "post_install")
489 if not ok then return nil, err end
490
491 util.announce_install(rockspec)
492 util.remove_scheduled_function(rollback)
493 return name, version
494end
495
496return build
diff --git a/src/luarocks/build/builtin.tl b/src/luarocks/build/builtin.tl
new file mode 100644
index 00000000..c778c2bb
--- /dev/null
+++ b/src/luarocks/build/builtin.tl
@@ -0,0 +1,400 @@
1
2--- A builtin build system: back-end to provide a portable way of building C-based Lua modules.
3local record builtin
4 skip_lua_inc_lib_check: boolean
5end
6
7local type r = require("luarocks.core.types.rockspec")
8local type Rockspec = r.Rockspec
9local type Variables = r.Variables
10local type Installs = r.Installs
11
12-- This build driver checks LUA_INCDIR and LUA_LIBDIR on demand,
13-- so that pure-Lua rocks don't need to have development headers
14-- installed.
15builtin.skip_lua_inc_lib_check = true
16
17local dir_sep = package.config:sub(1, 1)
18
19local fs = require("luarocks.fs")
20local path = require("luarocks.path")
21local util = require("luarocks.util")
22local cfg = require("luarocks.core.cfg")
23local dir = require("luarocks.dir")
24local deps = require("luarocks.deps")
25
26local function autoextract_libs(external_dependencies: {string: {string: string}}, variables: Variables): {string}, {string}, {string}
27 if not external_dependencies then
28 return nil, nil, nil
29 end
30 local libs: {string} = {}
31 local incdirs: {string} = {}
32 local libdirs: {string} = {}
33 for name, data in pairs(external_dependencies) do
34 if data.library then
35 table.insert(libs, data.library)
36 table.insert(incdirs, (variables as {string: string})[name .. "_INCDIR"])
37 table.insert(libdirs, (variables as {string: string})[name .. "_LIBDIR"])
38 end
39 end
40 return libs, incdirs, libdirs
41end
42
43do
44 local function get_cmod_name(file: string): string
45 local fd = io.open(dir.path(fs.current_dir(), file), "r")
46 if not fd then return nil end
47 local data = fd:read("*a")
48 fd:close()
49 return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)"))
50 end
51
52 local skiplist = {
53 ["spec"] = true,
54 [".luarocks"] = true,
55 ["lua_modules"] = true,
56 ["test.lua"] = true,
57 ["tests.lua"] = true,
58 }
59
60 function builtin.autodetect_modules(libs: {string}, incdirs: {string}, libdirs: {string})
61 local modules = {}
62 local install: Installs
63 local copy_directories: {string}
64
65 local prefix = ""
66 for _, parent in ipairs({"src", "lua", "lib"}) do
67 if fs.is_dir(parent) then
68 fs.change_dir(parent)
69 prefix = parent .. dir_sep
70 break
71 end
72 end
73
74 for _, file in ipairs(fs.find()) do
75 local base = file:match("^([^\\/]*)")
76 if not (skiplist as {string: boolean})[base] then
77 local luamod = file:match("(.*)%.lua$")
78 if luamod then
79 modules[path.path_to_module(file)] = prefix .. file
80 else
81 local cmod = file:match("(.*)%.c$")
82 if cmod then
83 local modname = get_cmod_name(file) or path.path_to_module((file:gsub("%.c$", ".lua"))) --!
84 modules[modname] = {
85 sources = prefix..file,
86 libraries = libs,
87 incdirs = incdirs,
88 libdirs = libdirs,
89 }
90 end
91 end
92 end
93 end
94
95 if prefix ~= "" then
96 fs.pop_dir()
97 end
98
99 local bindir = (fs.is_dir(dir.path("src", "bin")) and dir.path("src", "bin"))
100 or (fs.is_dir("bin") and "bin")
101 if bindir then
102 install = { bin = {} }
103 for _, file in ipairs(fs.list_dir(bindir)) do
104 table.insert(install.bin, dir.path(bindir, file)) --!
105 end
106 end
107
108 for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do
109 if fs.is_dir(directory) then
110 if not copy_directories then
111 copy_directories = {}
112 end
113 table.insert(copy_directories, directory)
114 end
115 end
116
117 return modules, install, copy_directories
118 end
119end
120
121--- Run a command displaying its execution on standard output.
122-- @return boolean: true if command succeeds (status code 0), false
123-- otherwise.
124local function execute(...: string): boolean, string, string
125 io.stdout:write(table.concat({...}, " ").."\n")
126 return fs.execute(...)
127end
128
129--- Driver function for the builtin build back-end.
130-- @param rockspec table: the loaded rockspec.
131-- @return boolean or (nil, string): true if no errors occurred,
132-- nil and an error message otherwise.
133function builtin.run(rockspec: Rockspec, no_install: boolean)
134 local compile_object, compile_library, compile_static_library
135
136 local build = rockspec.build
137 local variables = rockspec.variables as {string: string}
138 local checked_lua_h = false
139
140 for _, var in ipairs{ "CC", "CFLAGS", "LDFLAGS" } do
141 variables[var] = variables[var] or os.getenv(var) or ""
142 end
143
144 local function add_flags(extras: {string}, flag: string, flags: {string})
145 if flags then
146 if not flags is {string} then
147 flags = { tostring(flags) }
148 end
149 util.variable_substitutions(flags, variables)
150 for _, v in ipairs(flags) do
151 table.insert(extras, flag:format(v))
152 end
153 end
154 end
155
156 if cfg.is_platform("mingw32") then
157 compile_object = function(object: string, source: string, defines: {string}, incdirs: {string}): boolean, string, string
158 local extras = {}
159 add_flags(extras, "-D%s", defines)
160 add_flags(extras, "-I%s", incdirs)
161 return execute(variables.CC.." "..variables.CFLAGS, "-c", "-o", object, "-I"..variables.LUA_INCDIR, source, table.unpack(extras))
162 end
163 compile_library = function(library, objects, libraries, libdirs, name)
164 local extras = { table.unpack(objects) }
165 add_flags(extras, "-L%s", libdirs)
166 add_flags(extras, "-l%s", libraries)
167 extras[#extras+1] = dir.path(variables.LUA_LIBDIR, variables.LUALIB)
168
169 if variables.CC == "clang" or variables.CC == "clang-cl" then
170 local exported_name = name:gsub("%.", "_")
171 exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
172 extras[#extras+1] = string.format("-Wl,-export:luaopen_%s", exported_name)
173 else
174 extras[#extras+1] = "-l" .. (variables.MSVCRT or "m")
175 end
176
177 local ok = execute(variables.LD.." "..variables.LDFLAGS.." "..variables.LIBFLAG, "-o", library, table.unpack(extras))
178 return ok
179 end
180 --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
181 compile_static_library = function(library, objects, libraries, libdirs, name)
182 local ok = execute(variables.AR, "rc", library, unpack(objects))
183 if ok then
184 ok = execute(variables.RANLIB, library)
185 end
186 return ok
187 end
188 ]]
189 elseif cfg.is_platform("win32") then
190 compile_object = function(object, source, defines, incdirs)
191 local extras = {}
192 add_flags(extras, "-D%s", defines)
193 add_flags(extras, "-I%s", incdirs)
194 return execute(variables.CC.." "..variables.CFLAGS, "-c", "-Fo"..object, "-I"..variables.LUA_INCDIR, source, table.unpack(extras))
195 end
196 compile_library = function(library, objects, libraries, libdirs, name)
197 local extras = { table.unpack(objects) }
198 add_flags(extras, "-libpath:%s", libdirs)
199 add_flags(extras, "%s.lib", libraries)
200 local basename = dir.base_name(library):gsub(".[^.]*$", "")
201 local deffile = basename .. ".def"
202 local def = io.open(dir.path(fs.current_dir(), deffile), "w+")
203 local exported_name = name:gsub("%.", "_")
204 exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
205 def:write("EXPORTS\n")
206 def:write("luaopen_"..exported_name.."\n")
207 def:close()
208 local ok = execute(variables.LD, "-dll", "-def:"..deffile, "-out:"..library, dir.path(variables.LUA_LIBDIR, variables.LUALIB), table.unpack(extras))
209 local basedir = ""
210 if name:find("%.") ~= nil then
211 basedir = name:gsub("%.%w+$", "\\")
212 basedir = basedir:gsub("%.", "\\")
213 end
214 local manifestfile = basedir .. basename..".dll.manifest"
215
216 if ok and fs.exists(manifestfile) then
217 ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basedir..basename..".dll;2")
218 end
219 return ok
220 end
221 --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
222 compile_static_library = function(library, objects, libraries, libdirs, name)
223 local ok = execute(variables.AR, "-out:"..library, unpack(objects))
224 return ok
225 end
226 ]]
227 else
228 compile_object = function(object, source, defines, incdirs)
229 local extras = {}
230 add_flags(extras, "-D%s", defines)
231 add_flags(extras, "-I%s", incdirs)
232 return execute(variables.CC.." "..variables.CFLAGS, "-I"..variables.LUA_INCDIR, "-c", source, "-o", object, table.unpack(extras))
233 end
234 compile_library = function (library, objects, libraries, libdirs)
235 local extras = { table.unpack(objects) }
236 add_flags(extras, "-L%s", libdirs)
237 if cfg.gcc_rpath then
238 add_flags(extras, "-Wl,-rpath,%s", libdirs)
239 end
240 add_flags(extras, "-l%s", libraries)
241 if cfg.link_lua_explicitly then
242 extras[#extras+1] = "-L"..variables.LUA_LIBDIR
243 extras[#extras+1] = "-llua"
244 end
245 return execute(variables.LD.." "..variables.LDFLAGS.." "..variables.LIBFLAG, "-o", library, table.unpack(extras))
246 end
247 compile_static_library = function(library, objects, libraries, libdirs, name) -- luacheck: ignore 211
248 local ok = execute(variables.AR, "rc", library, table.unpack(objects))
249 if ok then
250 ok = execute(variables.RANLIB, library)
251 end
252 return ok
253 end
254 end
255
256 local ok, err
257 local lua_modules = {}
258 local lib_modules = {}
259 local luadir = path.lua_dir(rockspec.name, rockspec.version)
260 local libdir = path.lib_dir(rockspec.name, rockspec.version)
261
262 local autolibs, autoincdirs, autolibdirs = autoextract_libs(rockspec.external_dependencies, rockspec.variables)
263
264 if not build.modules then
265 if rockspec:format_is_at_least("3.0") then
266 local install, copy_directories
267 build.modules, install, copy_directories = builtin.autodetect_modules(autolibs, autoincdirs, autolibdirs)
268 build.install = build.install or install
269 build.copy_directories = build.copy_directories or copy_directories
270 else
271 return nil, "Missing build.modules table"
272 end
273 end
274
275 local compile_temp_dir
276
277 local mkdir_cache = {}
278 local function cached_make_dir(name)
279 if name == "" or mkdir_cache[name] then
280 return true
281 end
282 mkdir_cache[name] = true
283 return fs.make_dir(name)
284 end
285
286 for name, info in pairs(build.modules) do
287 local moddir = path.module_to_path(name)
288 if type(info) == "string" then
289 local ext = info:match("%.([^.]+)$")
290 if ext == "lua" then
291 local filename = dir.base_name(info)
292 if filename == "init.lua" and not name:match("%.init$") then
293 moddir = path.module_to_path(name..".init")
294 else
295 local basename = name:match("([^.]+)$")
296 filename = basename..".lua"
297 end
298 local dest = dir.path(luadir, moddir, filename)
299 lua_modules[info] = dest
300 else
301 info = {info}
302 end
303 end
304 if type(info) == "table" then
305 if not checked_lua_h then
306 local ok, err, errcode = deps.check_lua_incdir(rockspec.variables)
307 if not ok then
308 return nil, err, errcode
309 end
310
311 if cfg.link_lua_explicitly then
312 local ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
313 if not ok then
314 return nil, err, errcode
315 end
316 end
317 checked_lua_h = true
318 end
319 local objects = {}
320 local sources = info.sources
321 if info[1] then sources = info end
322 if type(sources) == "string" then sources = {sources} end
323 if type(sources) ~= "table" then
324 return nil, "error in rockspec: module '" .. name .. "' entry has no 'sources' list"
325 end
326 for _, source in ipairs(sources) do
327 if type(source) ~= "string" then
328 return nil, "error in rockspec: module '" .. name .. "' does not specify source correctly."
329 end
330 local object = source:gsub("%.[^.]*$", "."..cfg.obj_extension)
331 if not object then
332 object = source.."."..cfg.obj_extension
333 end
334 ok = compile_object(object, source, info.defines, info.incdirs or autoincdirs)
335 if not ok then
336 return nil, "Failed compiling object "..object
337 end
338 table.insert(objects, object)
339 end
340
341 if not compile_temp_dir then
342 compile_temp_dir = fs.make_temp_dir("build-" .. rockspec.package .. "-" .. rockspec.version)
343 util.schedule_function(fs.delete, compile_temp_dir)
344 end
345
346 local module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.lib_extension)
347 if moddir ~= "" then
348 module_name = dir.path(moddir, module_name)
349 end
350
351 local build_name = dir.path(compile_temp_dir, module_name)
352 local build_dir = dir.dir_name(build_name)
353 cached_make_dir(build_dir)
354
355 lib_modules[build_name] = dir.path(libdir, module_name)
356 ok = compile_library(build_name, objects, info.libraries, info.libdirs or autolibdirs, name)
357 if not ok then
358 return nil, "Failed compiling module "..module_name
359 end
360
361 -- for backwards compatibility, try keeping a copy of the module
362 -- in the old location (luasec-1.3.2-1 rockspec breaks otherwise)
363 if cached_make_dir(dir.dir_name(module_name)) then
364 fs.copy(build_name, module_name)
365 end
366
367 --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
368 module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.static_lib_extension)
369 if moddir ~= "" then
370 module_name = dir.path(moddir, module_name)
371 end
372 lib_modules[module_name] = dir.path(libdir, module_name)
373 ok = compile_static_library(module_name, objects, info.libraries, info.libdirs, name)
374 if not ok then
375 return nil, "Failed compiling static library "..module_name
376 end
377 ]]
378 end
379 end
380 if not no_install then
381 for _, mods in ipairs({{ tbl = lua_modules, perms = "read" }, { tbl = lib_modules, perms = "exec" }}) do
382 for name, dest in pairs(mods.tbl) do
383 cached_make_dir(dir.dir_name(dest))
384 ok, err = fs.copy(name, dest, mods.perms)
385 if not ok then
386 return nil, "Failed installing "..name.." in "..dest..": "..err
387 end
388 end
389 end
390 if fs.is_dir("lua") then
391 ok, err = fs.copy_contents("lua", luadir)
392 if not ok then
393 return nil, "Failed copying contents of 'lua' directory: "..err
394 end
395 end
396 end
397 return true
398end
399
400return builtin
diff --git a/src/luarocks/build/cmake.tl b/src/luarocks/build/cmake.tl
new file mode 100644
index 00000000..b7a4786e
--- /dev/null
+++ b/src/luarocks/build/cmake.tl
@@ -0,0 +1,78 @@
1
2--- Build back-end for CMake-based modules.
3local cmake = {}
4
5local fs = require("luarocks.fs")
6local util = require("luarocks.util")
7local cfg = require("luarocks.core.cfg")
8
9--- Driver function for the "cmake" build back-end.
10-- @param rockspec table: the loaded rockspec.
11-- @return boolean or (nil, string): true if no errors occurred,
12-- nil and an error message otherwise.
13function cmake.run(rockspec, no_install)
14 assert(rockspec:type() == "rockspec")
15 local build = rockspec.build
16 local variables = build.variables or {}
17
18 -- Pass Env variables
19 variables.CMAKE_MODULE_PATH=os.getenv("CMAKE_MODULE_PATH")
20 variables.CMAKE_LIBRARY_PATH=os.getenv("CMAKE_LIBRARY_PATH")
21 variables.CMAKE_INCLUDE_PATH=os.getenv("CMAKE_INCLUDE_PATH")
22
23 util.variable_substitutions(variables, rockspec.variables)
24
25 local ok, err_msg = fs.is_tool_available(rockspec.variables.CMAKE, "CMake")
26 if not ok then
27 return nil, err_msg
28 end
29
30 -- If inline cmake is present create CMakeLists.txt from it.
31 if type(build.cmake) == "string" then
32 local cmake_handler = assert(io.open(fs.current_dir().."/CMakeLists.txt", "w"))
33 cmake_handler:write(build.cmake)
34 cmake_handler:close()
35 end
36
37 -- Execute cmake with variables.
38 local args = ""
39
40 -- Try to pick the best generator. With msvc and x64, CMake does not select it by default so we need to be explicit.
41 if cfg.cmake_generator then
42 args = args .. ' -G"'..cfg.cmake_generator.. '"'
43 elseif cfg.is_platform("windows") and cfg.target_cpu:match("x86_64$") then
44 args = args .. " -DCMAKE_GENERATOR_PLATFORM=x64"
45 end
46
47 for k,v in pairs(variables) do
48 args = args .. ' -D' ..k.. '="' ..tostring(v).. '"'
49 end
50
51 if not fs.execute_string(rockspec.variables.CMAKE.." -H. -Bbuild.luarocks "..args) then
52 return nil, "Failed cmake."
53 end
54
55 local do_build, do_install
56 if rockspec:format_is_at_least("3.0") then
57 do_build = (build.build_pass == nil) and true or build.build_pass
58 do_install = (build.install_pass == nil) and true or build.install_pass
59 else
60 do_build = true
61 do_install = true
62 end
63
64 if do_build then
65 if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --config Release") then
66 return nil, "Failed building."
67 end
68 end
69 if do_install and not no_install then
70 if not fs.execute_string(rockspec.variables.CMAKE.." --build build.luarocks --target install --config Release") then
71 return nil, "Failed installing."
72 end
73 end
74
75 return true
76end
77
78return cmake
diff --git a/src/luarocks/build/command.tl b/src/luarocks/build/command.tl
new file mode 100644
index 00000000..b0c4aa79
--- /dev/null
+++ b/src/luarocks/build/command.tl
@@ -0,0 +1,41 @@
1
2--- Build back-end for raw listing of commands in rockspec files.
3local command = {}
4
5local fs = require("luarocks.fs")
6local util = require("luarocks.util")
7local cfg = require("luarocks.core.cfg")
8
9--- Driver function for the "command" build back-end.
10-- @param rockspec table: the loaded rockspec.
11-- @return boolean or (nil, string): true if no errors occurred,
12-- nil and an error message otherwise.
13function command.run(rockspec, not_install)
14 assert(rockspec:type() == "rockspec")
15
16 local build = rockspec.build
17
18 util.variable_substitutions(build, rockspec.variables)
19
20 local env = {
21 CC = cfg.variables.CC,
22 --LD = cfg.variables.LD,
23 --CFLAGS = cfg.variables.CFLAGS,
24 }
25
26 if build.build_command then
27 util.printout(build.build_command)
28 if not fs.execute_env(env, build.build_command) then
29 return nil, "Failed building."
30 end
31 end
32 if build.install_command and not not_install then
33 util.printout(build.install_command)
34 if not fs.execute_env(env, build.install_command) then
35 return nil, "Failed installing."
36 end
37 end
38 return true
39end
40
41return command
diff --git a/src/luarocks/build/make.tl b/src/luarocks/build/make.tl
new file mode 100644
index 00000000..4345ddff
--- /dev/null
+++ b/src/luarocks/build/make.tl
@@ -0,0 +1,98 @@
1
2--- Build back-end for using Makefile-based packages.
3local make = {}
4
5local unpack = unpack or table.unpack
6
7local fs = require("luarocks.fs")
8local util = require("luarocks.util")
9local cfg = require("luarocks.core.cfg")
10
11--- Call "make" with given target and variables
12-- @param make_cmd string: the make command to be used (typically
13-- configured through variables.MAKE in the config files, or
14-- the appropriate platform-specific default).
15-- @param pass boolean: If true, run make; if false, do nothing.
16-- @param target string: The make target; an empty string indicates
17-- the default target.
18-- @param variables table: A table containing string-string key-value
19-- pairs representing variable assignments to be passed to make.
20-- @return boolean: false if any errors occurred, true otherwise.
21local function make_pass(make_cmd, pass, target, variables)
22 assert(type(pass) == "boolean")
23 assert(type(target) == "string")
24 assert(type(variables) == "table")
25
26 local assignments = {}
27 for k,v in pairs(variables) do
28 table.insert(assignments, k.."="..v)
29 end
30 if pass then
31 return fs.execute(make_cmd.." "..target, unpack(assignments))
32 else
33 return true
34 end
35end
36
37--- Driver function for the "make" build back-end.
38-- @param rockspec table: the loaded rockspec.
39-- @return boolean or (nil, string): true if no errors occurred,
40-- nil and an error message otherwise.
41function make.run(rockspec, not_install)
42 assert(rockspec:type() == "rockspec")
43
44 local build = rockspec.build
45
46 if build.build_pass == nil then build.build_pass = true end
47 if build.install_pass == nil then build.install_pass = true end
48 build.build_variables = build.build_variables or {}
49 build.install_variables = build.install_variables or {}
50 build.build_target = build.build_target or ""
51 build.install_target = build.install_target or "install"
52 local makefile = build.makefile or cfg.makefile
53 if makefile then
54 -- Assumes all make's accept -f. True for POSIX make, GNU make and Microsoft nmake.
55 build.build_target = "-f "..makefile.." "..build.build_target
56 build.install_target = "-f "..makefile.." "..build.install_target
57 end
58
59 if build.variables then
60 for var, val in pairs(build.variables) do
61 build.build_variables[var] = val
62 build.install_variables[var] = val
63 end
64 end
65
66 util.warn_if_not_used(build.build_variables, { CFLAGS=true }, "variable %s was not passed in build_variables")
67
68 util.variable_substitutions(build.build_variables, rockspec.variables)
69 util.variable_substitutions(build.install_variables, rockspec.variables)
70
71 local auto_variables = { "CC" }
72
73 for _, variable in pairs(auto_variables) do
74 if not build.build_variables[variable] then
75 build.build_variables[variable] = rockspec.variables[variable]
76 end
77 if not build.install_variables[variable] then
78 build.install_variables[variable] = rockspec.variables[variable]
79 end
80 end
81
82 -- backwards compatibility
83 local make_cmd = cfg.make or rockspec.variables.MAKE
84
85 local ok = make_pass(make_cmd, build.build_pass, build.build_target, build.build_variables)
86 if not ok then
87 return nil, "Failed building."
88 end
89 if not not_install then
90 ok = make_pass(make_cmd, build.install_pass, build.install_target, build.install_variables)
91 if not ok then
92 return nil, "Failed installing."
93 end
94 end
95 return true
96end
97
98return make
diff --git a/src/luarocks/core/cfg.d.tl b/src/luarocks/core/cfg.d.tl
index 2c32569a..82b7c502 100644
--- a/src/luarocks/core/cfg.d.tl
+++ b/src/luarocks/core/cfg.d.tl
@@ -79,6 +79,7 @@ local record cfg
79 wrapper_suffix: string 79 wrapper_suffix: string
80 -- writer 80 -- writer
81 no_manifest: boolean 81 no_manifest: boolean
82 accepted_build_types: {boolean}
82end 83end
83 84
84return cfg \ No newline at end of file 85return cfg \ No newline at end of file
diff --git a/src/luarocks/core/types/manifest.d.tl b/src/luarocks/core/types/manifest.d.tl
index de1319d4..1e986867 100644
--- a/src/luarocks/core/types/manifest.d.tl
+++ b/src/luarocks/core/types/manifest.d.tl
@@ -4,19 +4,26 @@ local type Query = q.Query
4local type t = require("luarocks.core.types.tree") 4local type t = require("luarocks.core.types.tree")
5local type Tree = t.Tree 5local type Tree = t.Tree
6 6
7local record manifest 7local record manifest
8 record Manifest 8 record Manifest
9 arch: string 9 record Entry
10 commands: {string: {string}} 10 arch: string
11 dependencies: {string: {string: {Query}}} 11 commands: {string: string}
12 modules: {string: {string}} 12 dependencies: {string: string}
13 repository: {string: {string: {Manifest}}} 13 modules: {string: string}
14 end 14 end
15
16 record Tree_manifest
17 tree: Tree
18 manifest: Manifest
19 end
20 end
21 15
22 return manifest \ No newline at end of file 16 arch: string
17 commands: {string: {string}}
18 dependencies: {string: {string: {Query}}}
19 modules: {string: {string}}
20 repository: {string: {string: {Entry}}}
21 end
22
23 record Tree_manifest
24 tree: Tree
25 manifest: Manifest
26 end
27end
28
29return manifest \ No newline at end of file
diff --git a/src/luarocks/core/types/rockspec.lua b/src/luarocks/core/types/rockspec.lua
index 9ee7de37..a13c3f58 100644
--- a/src/luarocks/core/types/rockspec.lua
+++ b/src/luarocks/core/types/rockspec.lua
@@ -1,7 +1,16 @@
1 1
2 2
3 3
4local rockspec = {Description = {}, Variables = {}, Source = {}, Test = {}, Install = {}, Build = {}, Dependencies = {}, Hooks = {}, Deploy = {}, Rockspec = {}, } 4local rockspec = {Description = {}, Variables = {}, Source = {}, Test = {}, Install = {}, Installs = {}, Build = {}, Dependencies = {}, Hooks = {}, Deploy = {}, Rockspec = {}, }
5
6
7
8
9
10
11
12
13
5 14
6 15
7 16
diff --git a/src/luarocks/core/types/rockspec.tl b/src/luarocks/core/types/rockspec.tl
index 2375b888..6cd708cf 100644
--- a/src/luarocks/core/types/rockspec.tl
+++ b/src/luarocks/core/types/rockspec.tl
@@ -60,19 +60,28 @@ local record rockspec
60 busted_executable: string 60 busted_executable: string
61 flags: {string} 61 flags: {string}
62 end 62 end
63 63
64 record Install 64 record Install
65 lua: any 65 name: string
66 lib: any 66 is_module_path: boolean
67 conf: any 67 perms: string
68 bin: any 68 end
69
70 record Installs
71 lua: Install
72 lib: Install
73 conf: Install
74 bin: Install
69 end 75 end
70 76
71 record Build 77 record Build
72 type: string 78 type: string
73 modules: {string: {string: string | {string}}} 79 modules: {string: {string: string | {string}}}
74 copy_directories: string 80 copy_directories: string
75 install: Install 81 install: Installs
82 extra_files: {string : string}
83 patches: {string : string}
84 macosx_deployment_target: string
76 end 85 end
77 86
78 record Dependencies 87 record Dependencies
diff --git a/src/luarocks/fs.d.tl b/src/luarocks/fs.d.tl
index 680369de..86499bee 100644
--- a/src/luarocks/fs.d.tl
+++ b/src/luarocks/fs.d.tl
@@ -33,7 +33,7 @@ local record fs
33 set_time: function(string, number) 33 set_time: function(string, number)
34 set_time: function(string, os.DateTable) 34 set_time: function(string, os.DateTable)
35 -- zip 35 -- zip
36 find: function(string): {string} 36 find: function(?string): {string}
37 filter_file: function(function, string, string): boolean, string 37 filter_file: function(function, string, string): boolean, string
38 -- fetch 38 -- fetch
39 file_age: function(string): number 39 file_age: function(string): number
@@ -59,6 +59,8 @@ local record fs
59 -- writer 59 -- writer
60 replace_file: function(string, string): boolean, string 60 replace_file: function(string, string): boolean, string
61 get_md5: function(string): string, string 61 get_md5: function(string): string, string
62 -- build
63 apply_patch: function(string, string, boolean): boolean, string
62end 64end
63 65
64return fs 66return fs
diff --git a/src/luarocks/manif/writer-original.lua b/src/luarocks/manif/writer-original.lua
new file mode 100644
index 00000000..72d35754
--- /dev/null
+++ b/src/luarocks/manif/writer-original.lua
@@ -0,0 +1,469 @@
1
2local writer = {}
3
4local cfg = require("luarocks.core.cfg")
5local search = require("luarocks.search")
6local repos = require("luarocks.repos")
7local deps = require("luarocks.deps")
8local vers = require("luarocks.core.vers")
9local fs = require("luarocks.fs")
10local util = require("luarocks.util")
11local dir = require("luarocks.dir")
12local fetch = require("luarocks.fetch")
13local path = require("luarocks.path")
14local persist = require("luarocks.persist")
15local manif = require("luarocks.manif")
16local queries = require("luarocks.queries")
17
18--- Update storage table to account for items provided by a package.
19-- @param storage table: a table storing items in the following format:
20-- keys are item names and values are arrays of packages providing each item,
21-- where a package is specified as string `name/version`.
22-- @param items table: a table mapping item names to paths.
23-- @param name string: package name.
24-- @param version string: package version.
25local function store_package_items(storage, name, version, items)
26 assert(type(storage) == "table")
27 assert(type(items) == "table")
28 assert(type(name) == "string" and not name:match("/"))
29 assert(type(version) == "string")
30
31 local package_identifier = name.."/"..version
32
33 for item_name, path in pairs(items) do -- luacheck: ignore 431
34 if not storage[item_name] then
35 storage[item_name] = {}
36 end
37
38 table.insert(storage[item_name], package_identifier)
39 end
40end
41
42--- Update storage table removing items provided by a package.
43-- @param storage table: a table storing items in the following format:
44-- keys are item names and values are arrays of packages providing each item,
45-- where a package is specified as string `name/version`.
46-- @param items table: a table mapping item names to paths.
47-- @param name string: package name.
48-- @param version string: package version.
49local function remove_package_items(storage, name, version, items)
50 assert(type(storage) == "table")
51 assert(type(items) == "table")
52 assert(type(name) == "string" and not name:match("/"))
53 assert(type(version) == "string")
54
55 local package_identifier = name.."/"..version
56
57 for item_name, path in pairs(items) do -- luacheck: ignore 431
58 local key = item_name
59 local all_identifiers = storage[key]
60 if not all_identifiers then
61 key = key .. ".init"
62 all_identifiers = storage[key]
63 end
64
65 if all_identifiers then
66 for i, identifier in ipairs(all_identifiers) do
67 if identifier == package_identifier then
68 table.remove(all_identifiers, i)
69 break
70 end
71 end
72
73 if #all_identifiers == 0 then
74 storage[key] = nil
75 end
76 else
77 util.warning("Cannot find entry for " .. item_name .. " in manifest -- corrupted manifest?")
78 end
79 end
80end
81
82--- Process the dependencies of a manifest table to determine its dependency
83-- chains for loading modules. The manifest dependencies information is filled
84-- and any dependency inconsistencies or missing dependencies are reported to
85-- standard error.
86-- @param manifest table: a manifest table.
87-- @param deps_mode string: Dependency mode: "one" for the current default tree,
88-- "all" for all trees, "order" for all trees with priority >= the current default,
89-- "none" for no trees.
90local function update_dependencies(manifest, deps_mode)
91 assert(type(manifest) == "table")
92 assert(type(deps_mode) == "string")
93
94 if not manifest.dependencies then manifest.dependencies = {} end
95 local mdeps = manifest.dependencies
96
97 for pkg, versions in pairs(manifest.repository) do
98 for version, repositories in pairs(versions) do
99 for _, repo in ipairs(repositories) do
100 if repo.arch == "installed" then
101 local rd = {}
102 repo.dependencies = rd
103 deps.scan_deps(rd, mdeps, pkg, version, deps_mode)
104 rd[pkg] = nil
105 end
106 end
107 end
108 end
109end
110
111
112
113--- Sort function for ordering rock identifiers in a manifest's
114-- modules table. Rocks are ordered alphabetically by name, and then
115-- by version which greater first.
116-- @param a string: Version to compare.
117-- @param b string: Version to compare.
118-- @return boolean: The comparison result, according to the
119-- rule outlined above.
120local function sort_pkgs(a, b)
121 assert(type(a) == "string")
122 assert(type(b) == "string")
123
124 local na, va = a:match("(.*)/(.*)$")
125 local nb, vb = b:match("(.*)/(.*)$")
126
127 return (na == nb) and vers.compare_versions(va, vb) or na < nb
128end
129
130--- Sort items of a package matching table by version number (higher versions first).
131-- @param tbl table: the package matching table: keys should be strings
132-- and values arrays of strings with packages names in "name/version" format.
133local function sort_package_matching_table(tbl)
134 assert(type(tbl) == "table")
135
136 if next(tbl) then
137 for item, pkgs in pairs(tbl) do
138 if #pkgs > 1 then
139 table.sort(pkgs, sort_pkgs)
140 -- Remove duplicates from the sorted array.
141 local prev = nil
142 local i = 1
143 while pkgs[i] do
144 local curr = pkgs[i]
145 if curr == prev then
146 table.remove(pkgs, i)
147 else
148 prev = curr
149 i = i + 1
150 end
151 end
152 end
153 end
154 end
155end
156
157--- Filter manifest table by Lua version, removing rockspecs whose Lua version
158-- does not match.
159-- @param manifest table: a manifest table.
160-- @param lua_version string or nil: filter by Lua version
161-- @param repodir string: directory of repository being scanned
162-- @param cache table: temporary rockspec cache table
163local function filter_by_lua_version(manifest, lua_version, repodir, cache)
164 assert(type(manifest) == "table")
165 assert(type(repodir) == "string")
166 assert((not cache) or type(cache) == "table")
167
168 cache = cache or {}
169 lua_version = vers.parse_version(lua_version)
170 for pkg, versions in pairs(manifest.repository) do
171 local to_remove = {}
172 for version, repositories in pairs(versions) do
173 for _, repo in ipairs(repositories) do
174 if repo.arch == "rockspec" then
175 local pathname = dir.path(repodir, pkg.."-"..version..".rockspec")
176 local rockspec, err = cache[pathname]
177 if not rockspec then
178 rockspec, err = fetch.load_local_rockspec(pathname, true)
179 end
180 if rockspec then
181 cache[pathname] = rockspec
182 for _, dep in ipairs(rockspec.dependencies) do
183 if dep.name == "lua" then
184 if not vers.match_constraints(lua_version, dep.constraints) then
185 table.insert(to_remove, version)
186 end
187 break
188 end
189 end
190 else
191 util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err)
192 end
193 end
194 end
195 end
196 if next(to_remove) then
197 for _, incompat in ipairs(to_remove) do
198 versions[incompat] = nil
199 end
200 if not next(versions) then
201 manifest.repository[pkg] = nil
202 end
203 end
204 end
205end
206
207--- Store search results in a manifest table.
208-- @param results table: The search results as returned by search.disk_search.
209-- @param manifest table: A manifest table (must contain repository, modules, commands tables).
210-- It will be altered to include the search results.
211-- @return boolean or (nil, string): true in case of success, or nil followed by an error message.
212local function store_results(results, manifest)
213 assert(type(results) == "table")
214 assert(type(manifest) == "table")
215
216 for name, versions in pairs(results) do
217 local pkgtable = manifest.repository[name] or {}
218 for version, entries in pairs(versions) do
219 local versiontable = {}
220 for _, entry in ipairs(entries) do
221 local entrytable = {}
222 entrytable.arch = entry.arch
223 if entry.arch == "installed" then
224 local rock_manifest, err = manif.load_rock_manifest(name, version)
225 if not rock_manifest then return nil, err end
226
227 entrytable.modules = repos.package_modules(name, version)
228 store_package_items(manifest.modules, name, version, entrytable.modules)
229 entrytable.commands = repos.package_commands(name, version)
230 store_package_items(manifest.commands, name, version, entrytable.commands)
231 end
232 table.insert(versiontable, entrytable)
233 end
234 pkgtable[version] = versiontable
235 end
236 manifest.repository[name] = pkgtable
237 end
238 sort_package_matching_table(manifest.modules)
239 sort_package_matching_table(manifest.commands)
240 return true
241end
242
243--- Commit a table to disk in given local path.
244-- @param where string: The directory where the table should be saved.
245-- @param name string: The filename.
246-- @param tbl table: The table to be saved.
247-- @return boolean or (nil, string): true if successful, or nil and a
248-- message in case of errors.
249local function save_table(where, name, tbl)
250 assert(type(where) == "string")
251 assert(type(name) == "string" and not name:match("/"))
252 assert(type(tbl) == "table")
253
254 local filename = dir.path(where, name)
255 local ok, err = persist.save_from_table(filename..".tmp", tbl)
256 if ok then
257 ok, err = fs.replace_file(filename, filename..".tmp")
258 end
259 return ok, err
260end
261
262function writer.make_rock_manifest(name, version)
263 local install_dir = path.install_dir(name, version)
264 local tree = {}
265 for _, file in ipairs(fs.find(install_dir)) do
266 local full_path = dir.path(install_dir, file)
267 local walk = tree
268 local last
269 local last_name
270 for filename in file:gmatch("[^\\/]+") do
271 local next = walk[filename]
272 if not next then
273 next = {}
274 walk[filename] = next
275 end
276 last = walk
277 last_name = filename
278 walk = next
279 end
280 if fs.is_file(full_path) then
281 local sum, err = fs.get_md5(full_path)
282 if not sum then
283 return nil, "Failed producing checksum: "..tostring(err)
284 end
285 last[last_name] = sum
286 end
287 end
288 local rock_manifest = { rock_manifest=tree }
289 manif.rock_manifest_cache[name.."/"..version] = rock_manifest
290 save_table(install_dir, "rock_manifest", rock_manifest )
291 return true
292end
293
294-- Writes a 'rock_namespace' file in a locally installed rock directory.
295-- @param name string: the rock name, without a namespace
296-- @param version string: the rock version
297-- @param namespace string?: the namespace
298-- @return true if successful (or unnecessary, if there is no namespace),
299-- or nil and an error message.
300function writer.make_namespace_file(name, version, namespace)
301 assert(type(name) == "string" and not name:match("/"))
302 assert(type(version) == "string")
303 assert(type(namespace) == "string" or not namespace)
304 if not namespace then
305 return true
306 end
307 local fd, err = io.open(path.rock_namespace_file(name, version), "w")
308 if not fd then
309 return nil, err
310 end
311 local ok, err = fd:write(namespace)
312 if not ok then
313 return nil, err
314 end
315 fd:close()
316 return true
317end
318
319--- Scan a LuaRocks repository and output a manifest file.
320-- A file called 'manifest' will be written in the root of the given
321-- repository directory.
322-- @param repo A local repository directory.
323-- @param deps_mode string: Dependency mode: "one" for the current default tree,
324-- "all" for all trees, "order" for all trees with priority >= the current default,
325-- "none" for the default dependency mode from the configuration.
326-- @param remote boolean: 'true' if making a manifest for a rocks server.
327-- @return boolean or (nil, string): True if manifest was generated,
328-- or nil and an error message.
329function writer.make_manifest(repo, deps_mode, remote)
330 assert(type(repo) == "string")
331 assert(type(deps_mode) == "string")
332
333 if deps_mode == "none" then deps_mode = cfg.deps_mode end
334
335 if not fs.is_dir(repo) then
336 return nil, "Cannot access repository at "..repo
337 end
338
339 local query = queries.all("any")
340 local results = search.disk_search(repo, query)
341 local manifest = { repository = {}, modules = {}, commands = {} }
342
343 manif.cache_manifest(repo, nil, manifest)
344
345 local ok, err = store_results(results, manifest)
346 if not ok then return nil, err end
347
348 if remote then
349 local cache = {}
350 for luaver in util.lua_versions() do
351 local vmanifest = { repository = {}, modules = {}, commands = {} }
352 local ok, err = store_results(results, vmanifest)
353 filter_by_lua_version(vmanifest, luaver, repo, cache)
354 if not cfg.no_manifest then
355 save_table(repo, "manifest-"..luaver, vmanifest)
356 end
357 end
358 else
359 update_dependencies(manifest, deps_mode)
360 end
361
362 if cfg.no_manifest then
363 -- We want to have cache updated; but exit before save_table is called
364 return true
365 end
366 return save_table(repo, "manifest", manifest)
367end
368
369--- Update manifest file for a local repository
370-- adding information about a version of a package installed in that repository.
371-- @param name string: Name of a package from the repository.
372-- @param version string: Version of a package from the repository.
373-- @param repo string or nil: Pathname of a local repository. If not given,
374-- the default local repository is used.
375-- @param deps_mode string: Dependency mode: "one" for the current default tree,
376-- "all" for all trees, "order" for all trees with priority >= the current default,
377-- "none" for using the default dependency mode from the configuration.
378-- @return boolean or (nil, string): True if manifest was updated successfully,
379-- or nil and an error message.
380function writer.add_to_manifest(name, version, repo, deps_mode)
381 assert(type(name) == "string" and not name:match("/"))
382 assert(type(version) == "string")
383 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
384 assert(type(deps_mode) == "string")
385
386 if deps_mode == "none" then deps_mode = cfg.deps_mode end
387
388 local manifest, err = manif.load_manifest(rocks_dir)
389 if not manifest then
390 util.printerr("No existing manifest. Attempting to rebuild...")
391 -- Manifest built by `writer.make_manifest` should already
392 -- include information about given name and version,
393 -- no need to update it.
394 return writer.make_manifest(rocks_dir, deps_mode)
395 end
396
397 local results = {[name] = {[version] = {{arch = "installed", repo = rocks_dir}}}}
398
399 local ok, err = store_results(results, manifest)
400 if not ok then return nil, err end
401
402 update_dependencies(manifest, deps_mode)
403
404 if cfg.no_manifest then
405 return true
406 end
407 return save_table(rocks_dir, "manifest", manifest)
408end
409
410--- Update manifest file for a local repository
411-- removing information about a version of a package.
412-- @param name string: Name of a package removed from the repository.
413-- @param version string: Version of a package removed from the repository.
414-- @param repo string or nil: Pathname of a local repository. If not given,
415-- the default local repository is used.
416-- @param deps_mode string: Dependency mode: "one" for the current default tree,
417-- "all" for all trees, "order" for all trees with priority >= the current default,
418-- "none" for using the default dependency mode from the configuration.
419-- @return boolean or (nil, string): True if manifest was updated successfully,
420-- or nil and an error message.
421function writer.remove_from_manifest(name, version, repo, deps_mode)
422 assert(type(name) == "string" and not name:match("/"))
423 assert(type(version) == "string")
424 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
425 assert(type(deps_mode) == "string")
426
427 if deps_mode == "none" then deps_mode = cfg.deps_mode end
428
429 local manifest, err = manif.load_manifest(rocks_dir)
430 if not manifest then
431 util.printerr("No existing manifest. Attempting to rebuild...")
432 -- Manifest built by `writer.make_manifest` should already
433 -- include up-to-date information, no need to update it.
434 return writer.make_manifest(rocks_dir, deps_mode)
435 end
436
437 local package_entry = manifest.repository[name]
438 if package_entry == nil or package_entry[version] == nil then
439 -- entry is already missing from repository, no need to do anything
440 return true
441 end
442
443 local version_entry = package_entry[version][1]
444 if not version_entry then
445 -- manifest looks corrupted, rebuild
446 return writer.make_manifest(rocks_dir, deps_mode)
447 end
448
449 remove_package_items(manifest.modules, name, version, version_entry.modules)
450 remove_package_items(manifest.commands, name, version, version_entry.commands)
451
452 package_entry[version] = nil
453 manifest.dependencies[name][version] = nil
454
455 if not next(package_entry) then
456 -- No more versions of this package.
457 manifest.repository[name] = nil
458 manifest.dependencies[name] = nil
459 end
460
461 update_dependencies(manifest, deps_mode)
462
463 if cfg.no_manifest then
464 return true
465 end
466 return save_table(rocks_dir, "manifest", manifest)
467end
468
469return writer
diff --git a/src/luarocks/manif/writer.lua b/src/luarocks/manif/writer.lua
index 72d35754..10dd6743 100644
--- a/src/luarocks/manif/writer.lua
+++ b/src/luarocks/manif/writer.lua
@@ -1,6 +1,7 @@
1 1local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io; local ipairs = _tl_compat and _tl_compat.ipairs or ipairs; local pairs = _tl_compat and _tl_compat.pairs or pairs; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
2local writer = {} 2local writer = {}
3 3
4
4local cfg = require("luarocks.core.cfg") 5local cfg = require("luarocks.core.cfg")
5local search = require("luarocks.search") 6local search = require("luarocks.search")
6local repos = require("luarocks.repos") 7local repos = require("luarocks.repos")
@@ -15,22 +16,35 @@ local persist = require("luarocks.persist")
15local manif = require("luarocks.manif") 16local manif = require("luarocks.manif")
16local queries = require("luarocks.queries") 17local queries = require("luarocks.queries")
17 18
18--- Update storage table to account for items provided by a package. 19
19-- @param storage table: a table storing items in the following format: 20
20-- keys are item names and values are arrays of packages providing each item, 21
21-- where a package is specified as string `name/version`. 22
22-- @param items table: a table mapping item names to paths. 23
23-- @param name string: package name. 24
24-- @param version string: package version. 25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
25local function store_package_items(storage, name, version, items) 42local function store_package_items(storage, name, version, items)
26 assert(type(storage) == "table") 43 assert(not name:match("/"))
27 assert(type(items) == "table")
28 assert(type(name) == "string" and not name:match("/"))
29 assert(type(version) == "string")
30 44
31 local package_identifier = name.."/"..version 45 local package_identifier = name .. "/" .. version
32 46
33 for item_name, path in pairs(items) do -- luacheck: ignore 431 47 for item_name, _ in pairs(items) do
34 if not storage[item_name] then 48 if not storage[item_name] then
35 storage[item_name] = {} 49 storage[item_name] = {}
36 end 50 end
@@ -39,22 +53,19 @@ local function store_package_items(storage, name, version, items)
39 end 53 end
40end 54end
41 55
42--- Update storage table removing items provided by a package. 56
43-- @param storage table: a table storing items in the following format: 57
44-- keys are item names and values are arrays of packages providing each item, 58
45-- where a package is specified as string `name/version`. 59
46-- @param items table: a table mapping item names to paths. 60
47-- @param name string: package name. 61
48-- @param version string: package version. 62
49local function remove_package_items(storage, name, version, items) 63local function remove_package_items(storage, name, version, items)
50 assert(type(storage) == "table") 64 assert(not name:match("/"))
51 assert(type(items) == "table")
52 assert(type(name) == "string" and not name:match("/"))
53 assert(type(version) == "string")
54 65
55 local package_identifier = name.."/"..version 66 local package_identifier = name .. "/" .. version
56 67
57 for item_name, path in pairs(items) do -- luacheck: ignore 431 68 for item_name, _ in pairs(items) do
58 local key = item_name 69 local key = item_name
59 local all_identifiers = storage[key] 70 local all_identifiers = storage[key]
60 if not all_identifiers then 71 if not all_identifiers then
@@ -79,17 +90,15 @@ local function remove_package_items(storage, name, version, items)
79 end 90 end
80end 91end
81 92
82--- Process the dependencies of a manifest table to determine its dependency 93
83-- chains for loading modules. The manifest dependencies information is filled 94
84-- and any dependency inconsistencies or missing dependencies are reported to 95
85-- standard error. 96
86-- @param manifest table: a manifest table. 97
87-- @param deps_mode string: Dependency mode: "one" for the current default tree, 98
88-- "all" for all trees, "order" for all trees with priority >= the current default, 99
89-- "none" for no trees. 100
90local function update_dependencies(manifest, deps_mode) 101local function update_dependencies(manifest, deps_mode)
91 assert(type(manifest) == "table")
92 assert(type(deps_mode) == "string")
93 102
94 if not manifest.dependencies then manifest.dependencies = {} end 103 if not manifest.dependencies then manifest.dependencies = {} end
95 local mdeps = manifest.dependencies 104 local mdeps = manifest.dependencies
@@ -110,34 +119,30 @@ end
110 119
111 120
112 121
113--- Sort function for ordering rock identifiers in a manifest's
114-- modules table. Rocks are ordered alphabetically by name, and then
115-- by version which greater first.
116-- @param a string: Version to compare.
117-- @param b string: Version to compare.
118-- @return boolean: The comparison result, according to the
119-- rule outlined above.
120local function sort_pkgs(a, b)
121 assert(type(a) == "string")
122 assert(type(b) == "string")
123 122
123
124
125
126
127
128
129local function sort_pkgs(a, b)
124 local na, va = a:match("(.*)/(.*)$") 130 local na, va = a:match("(.*)/(.*)$")
125 local nb, vb = b:match("(.*)/(.*)$") 131 local nb, vb = b:match("(.*)/(.*)$")
126 132
127 return (na == nb) and vers.compare_versions(va, vb) or na < nb 133 return (na == nb) and vers.compare_versions(va, vb) or na < nb
128end 134end
129 135
130--- Sort items of a package matching table by version number (higher versions first). 136
131-- @param tbl table: the package matching table: keys should be strings 137
132-- and values arrays of strings with packages names in "name/version" format. 138
133local function sort_package_matching_table(tbl) 139local function sort_package_matching_table(tbl)
134 assert(type(tbl) == "table")
135 140
136 if next(tbl) then 141 if next(tbl) ~= nil then
137 for item, pkgs in pairs(tbl) do 142 for item, pkgs in pairs(tbl) do
138 if #pkgs > 1 then 143 if #pkgs > 1 then
139 table.sort(pkgs, sort_pkgs) 144 table.sort(pkgs, sort_pkgs)
140 -- Remove duplicates from the sorted array. 145
141 local prev = nil 146 local prev = nil
142 local i = 1 147 local i = 1
143 while pkgs[i] do 148 while pkgs[i] do
@@ -154,32 +159,30 @@ local function sort_package_matching_table(tbl)
154 end 159 end
155end 160end
156 161
157--- Filter manifest table by Lua version, removing rockspecs whose Lua version 162
158-- does not match. 163
159-- @param manifest table: a manifest table. 164
160-- @param lua_version string or nil: filter by Lua version 165
161-- @param repodir string: directory of repository being scanned 166
162-- @param cache table: temporary rockspec cache table 167
163local function filter_by_lua_version(manifest, lua_version, repodir, cache) 168local function filter_by_lua_version(manifest, lua_version_str, repodir, cache)
164 assert(type(manifest) == "table")
165 assert(type(repodir) == "string")
166 assert((not cache) or type(cache) == "table")
167 169
168 cache = cache or {} 170 cache = cache or {}
169 lua_version = vers.parse_version(lua_version) 171 local lua_version = vers.parse_version(lua_version_str)
170 for pkg, versions in pairs(manifest.repository) do 172 for pkg, versions in pairs(manifest.repository) do
171 local to_remove = {} 173 local to_remove = {}
172 for version, repositories in pairs(versions) do 174 for version, repositories in pairs(versions) do
173 for _, repo in ipairs(repositories) do 175 for _, repo in ipairs(repositories) do
174 if repo.arch == "rockspec" then 176 if repo.arch == "rockspec" then
175 local pathname = dir.path(repodir, pkg.."-"..version..".rockspec") 177 local pathname = dir.path(repodir, pkg .. "-" .. version .. ".rockspec")
176 local rockspec, err = cache[pathname] 178 local rockspec = cache[pathname]
179 local err
177 if not rockspec then 180 if not rockspec then
178 rockspec, err = fetch.load_local_rockspec(pathname, true) 181 rockspec, err = fetch.load_local_rockspec(pathname, true)
179 end 182 end
180 if rockspec then 183 if rockspec then
181 cache[pathname] = rockspec 184 cache[pathname] = rockspec
182 for _, dep in ipairs(rockspec.dependencies) do 185 for _, dep in ipairs(rockspec.dependencies.queries) do
183 if dep.name == "lua" then 186 if dep.name == "lua" then
184 if not vers.match_constraints(lua_version, dep.constraints) then 187 if not vers.match_constraints(lua_version, dep.constraints) then
185 table.insert(to_remove, version) 188 table.insert(to_remove, version)
@@ -188,12 +191,12 @@ local function filter_by_lua_version(manifest, lua_version, repodir, cache)
188 end 191 end
189 end 192 end
190 else 193 else
191 util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err) 194 util.printerr("Error loading rockspec for " .. pkg .. " " .. version .. ": " .. err)
192 end 195 end
193 end 196 end
194 end 197 end
195 end 198 end
196 if next(to_remove) then 199 if next(to_remove) ~= nil then
197 for _, incompat in ipairs(to_remove) do 200 for _, incompat in ipairs(to_remove) do
198 versions[incompat] = nil 201 versions[incompat] = nil
199 end 202 end
@@ -204,14 +207,12 @@ local function filter_by_lua_version(manifest, lua_version, repodir, cache)
204 end 207 end
205end 208end
206 209
207--- Store search results in a manifest table. 210
208-- @param results table: The search results as returned by search.disk_search. 211
209-- @param manifest table: A manifest table (must contain repository, modules, commands tables). 212
210-- It will be altered to include the search results. 213
211-- @return boolean or (nil, string): true in case of success, or nil followed by an error message. 214
212local function store_results(results, manifest) 215local function store_results(results, manifest)
213 assert(type(results) == "table")
214 assert(type(manifest) == "table")
215 216
216 for name, versions in pairs(results) do 217 for name, versions in pairs(results) do
217 local pkgtable = manifest.repository[name] or {} 218 local pkgtable = manifest.repository[name] or {}
@@ -240,21 +241,19 @@ local function store_results(results, manifest)
240 return true 241 return true
241end 242end
242 243
243--- Commit a table to disk in given local path. 244
244-- @param where string: The directory where the table should be saved. 245
245-- @param name string: The filename. 246
246-- @param tbl table: The table to be saved. 247
247-- @return boolean or (nil, string): true if successful, or nil and a 248
248-- message in case of errors. 249
249local function save_table(where, name, tbl) 250local function save_table(where, name, tbl)
250 assert(type(where) == "string") 251 assert(not name:match("/"))
251 assert(type(name) == "string" and not name:match("/"))
252 assert(type(tbl) == "table")
253 252
254 local filename = dir.path(where, name) 253 local filename = dir.path(where, name)
255 local ok, err = persist.save_from_table(filename..".tmp", tbl) 254 local ok, err = persist.save_from_table(filename .. ".tmp", tbl)
256 if ok then 255 if ok then
257 ok, err = fs.replace_file(filename, filename..".tmp") 256 ok, err = fs.replace_file(filename, filename .. ".tmp")
258 end 257 end
259 return ok, err 258 return ok, err
260end 259end
@@ -267,40 +266,44 @@ function writer.make_rock_manifest(name, version)
267 local walk = tree 266 local walk = tree
268 local last 267 local last
269 local last_name 268 local last_name
269 local next
270 for filename in file:gmatch("[^\\/]+") do 270 for filename in file:gmatch("[^\\/]+") do
271 local next = walk[filename] 271 next = walk[filename]
272 if not next then 272 if not next then
273 next = {} 273 next = {}
274 walk[filename] = next 274 walk[filename] = next
275 end 275 end
276 last = walk 276 last = walk
277 last_name = filename 277 last_name = filename
278 walk = next 278 if not (type(next) == "string") then
279 walk = next
280 else
281 return nil, "Next was string, expected a table"
282 end
279 end 283 end
280 if fs.is_file(full_path) then 284 if fs.is_file(full_path) then
285
281 local sum, err = fs.get_md5(full_path) 286 local sum, err = fs.get_md5(full_path)
282 if not sum then 287 if not sum then
283 return nil, "Failed producing checksum: "..tostring(err) 288 return nil, "Failed producing checksum: " .. tostring(err)
284 end 289 end
285 last[last_name] = sum 290 last[last_name] = sum
286 end 291 end
287 end 292 end
288 local rock_manifest = { rock_manifest=tree } 293 local rock_manifest = { rock_manifest = tree }
289 manif.rock_manifest_cache[name.."/"..version] = rock_manifest 294 manif.rock_manifest_cache[name .. "/" .. version] = rock_manifest
290 save_table(install_dir, "rock_manifest", rock_manifest ) 295 save_table(install_dir, "rock_manifest", rock_manifest)
291 return true 296 return true
292end 297end
293 298
294-- Writes a 'rock_namespace' file in a locally installed rock directory. 299
295-- @param name string: the rock name, without a namespace 300
296-- @param version string: the rock version 301
297-- @param namespace string?: the namespace 302
298-- @return true if successful (or unnecessary, if there is no namespace), 303
299-- or nil and an error message. 304
300function writer.make_namespace_file(name, version, namespace) 305function writer.make_namespace_file(name, version, namespace)
301 assert(type(name) == "string" and not name:match("/")) 306 assert(not name:match("/"))
302 assert(type(version) == "string")
303 assert(type(namespace) == "string" or not namespace)
304 if not namespace then 307 if not namespace then
305 return true 308 return true
306 end 309 end
@@ -316,24 +319,22 @@ function writer.make_namespace_file(name, version, namespace)
316 return true 319 return true
317end 320end
318 321
319--- Scan a LuaRocks repository and output a manifest file. 322
320-- A file called 'manifest' will be written in the root of the given 323
321-- repository directory. 324
322-- @param repo A local repository directory. 325
323-- @param deps_mode string: Dependency mode: "one" for the current default tree, 326
324-- "all" for all trees, "order" for all trees with priority >= the current default, 327
325-- "none" for the default dependency mode from the configuration. 328
326-- @param remote boolean: 'true' if making a manifest for a rocks server. 329
327-- @return boolean or (nil, string): True if manifest was generated, 330
328-- or nil and an error message. 331
329function writer.make_manifest(repo, deps_mode, remote) 332function writer.make_manifest(repo, deps_mode, remote)
330 assert(type(repo) == "string")
331 assert(type(deps_mode) == "string")
332 333
333 if deps_mode == "none" then deps_mode = cfg.deps_mode end 334 if deps_mode == "none" then deps_mode = cfg.deps_mode end
334 335
335 if not fs.is_dir(repo) then 336 if not fs.is_dir(repo) then
336 return nil, "Cannot access repository at "..repo 337 return nil, "Cannot access repository at " .. repo
337 end 338 end
338 339
339 local query = queries.all("any") 340 local query = queries.all("any")
@@ -349,10 +350,10 @@ function writer.make_manifest(repo, deps_mode, remote)
349 local cache = {} 350 local cache = {}
350 for luaver in util.lua_versions() do 351 for luaver in util.lua_versions() do
351 local vmanifest = { repository = {}, modules = {}, commands = {} } 352 local vmanifest = { repository = {}, modules = {}, commands = {} }
352 local ok, err = store_results(results, vmanifest) 353 ok, err = store_results(results, vmanifest)
353 filter_by_lua_version(vmanifest, luaver, repo, cache) 354 filter_by_lua_version(vmanifest, luaver, repo, cache)
354 if not cfg.no_manifest then 355 if not cfg.no_manifest then
355 save_table(repo, "manifest-"..luaver, vmanifest) 356 save_table(repo, "manifest-" .. luaver, vmanifest)
356 end 357 end
357 end 358 end
358 else 359 else
@@ -360,43 +361,42 @@ function writer.make_manifest(repo, deps_mode, remote)
360 end 361 end
361 362
362 if cfg.no_manifest then 363 if cfg.no_manifest then
363 -- We want to have cache updated; but exit before save_table is called 364
364 return true 365 return true
365 end 366 end
366 return save_table(repo, "manifest", manifest) 367 return save_table(repo, "manifest", manifest)
367end 368end
368 369
369--- Update manifest file for a local repository 370
370-- adding information about a version of a package installed in that repository. 371
371-- @param name string: Name of a package from the repository. 372
372-- @param version string: Version of a package from the repository. 373
373-- @param repo string or nil: Pathname of a local repository. If not given, 374
374-- the default local repository is used. 375
375-- @param deps_mode string: Dependency mode: "one" for the current default tree, 376
376-- "all" for all trees, "order" for all trees with priority >= the current default, 377
377-- "none" for using the default dependency mode from the configuration. 378
378-- @return boolean or (nil, string): True if manifest was updated successfully, 379
379-- or nil and an error message. 380
380function writer.add_to_manifest(name, version, repo, deps_mode) 381function writer.add_to_manifest(name, version, repo, deps_mode)
381 assert(type(name) == "string" and not name:match("/")) 382 assert(not name:match("/"))
382 assert(type(version) == "string")
383 local rocks_dir = path.rocks_dir(repo or cfg.root_dir) 383 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
384 assert(type(deps_mode) == "string")
385 384
386 if deps_mode == "none" then deps_mode = cfg.deps_mode end 385 if deps_mode == "none" then deps_mode = cfg.deps_mode end
387 386
388 local manifest, err = manif.load_manifest(rocks_dir) 387 local manifest, err = manif.load_manifest(rocks_dir)
389 if not manifest then 388 if not manifest then
390 util.printerr("No existing manifest. Attempting to rebuild...") 389 util.printerr("No existing manifest. Attempting to rebuild...")
391 -- Manifest built by `writer.make_manifest` should already 390
392 -- include information about given name and version, 391
393 -- no need to update it. 392
394 return writer.make_manifest(rocks_dir, deps_mode) 393 return writer.make_manifest(rocks_dir, deps_mode)
395 end 394 end
396 395
397 local results = {[name] = {[version] = {{arch = "installed", repo = rocks_dir}}}} 396 local results = { [name] = { [version] = { { arch = "installed", repo = rocks_dir } } } }
398 397
399 local ok, err = store_results(results, manifest) 398 local ok
399 ok, err = store_results(results, manifest)
400 if not ok then return nil, err end 400 if not ok then return nil, err end
401 401
402 update_dependencies(manifest, deps_mode) 402 update_dependencies(manifest, deps_mode)
@@ -407,42 +407,40 @@ function writer.add_to_manifest(name, version, repo, deps_mode)
407 return save_table(rocks_dir, "manifest", manifest) 407 return save_table(rocks_dir, "manifest", manifest)
408end 408end
409 409
410--- Update manifest file for a local repository 410
411-- removing information about a version of a package. 411
412-- @param name string: Name of a package removed from the repository. 412
413-- @param version string: Version of a package removed from the repository. 413
414-- @param repo string or nil: Pathname of a local repository. If not given, 414
415-- the default local repository is used. 415
416-- @param deps_mode string: Dependency mode: "one" for the current default tree, 416
417-- "all" for all trees, "order" for all trees with priority >= the current default, 417
418-- "none" for using the default dependency mode from the configuration. 418
419-- @return boolean or (nil, string): True if manifest was updated successfully, 419
420-- or nil and an error message. 420
421function writer.remove_from_manifest(name, version, repo, deps_mode) 421function writer.remove_from_manifest(name, version, repo, deps_mode)
422 assert(type(name) == "string" and not name:match("/")) 422 assert(not name:match("/"))
423 assert(type(version) == "string")
424 local rocks_dir = path.rocks_dir(repo or cfg.root_dir) 423 local rocks_dir = path.rocks_dir(repo or cfg.root_dir)
425 assert(type(deps_mode) == "string")
426 424
427 if deps_mode == "none" then deps_mode = cfg.deps_mode end 425 if deps_mode == "none" then deps_mode = cfg.deps_mode end
428 426
429 local manifest, err = manif.load_manifest(rocks_dir) 427 local manifest, err = manif.load_manifest(rocks_dir)
430 if not manifest then 428 if not manifest then
431 util.printerr("No existing manifest. Attempting to rebuild...") 429 util.printerr("No existing manifest. Attempting to rebuild...")
432 -- Manifest built by `writer.make_manifest` should already 430
433 -- include up-to-date information, no need to update it. 431
434 return writer.make_manifest(rocks_dir, deps_mode) 432 return writer.make_manifest(rocks_dir, deps_mode)
435 end 433 end
436 434
437 local package_entry = manifest.repository[name] 435 local package_entry = manifest.repository[name]
438 if package_entry == nil or package_entry[version] == nil then 436 if package_entry == nil or package_entry[version] == nil then
439 -- entry is already missing from repository, no need to do anything 437
440 return true 438 return true
441 end 439 end
442 440
443 local version_entry = package_entry[version][1] 441 local version_entry = package_entry[version][1]
444 if not version_entry then 442 if not version_entry then
445 -- manifest looks corrupted, rebuild 443
446 return writer.make_manifest(rocks_dir, deps_mode) 444 return writer.make_manifest(rocks_dir, deps_mode)
447 end 445 end
448 446
@@ -453,7 +451,7 @@ function writer.remove_from_manifest(name, version, repo, deps_mode)
453 manifest.dependencies[name][version] = nil 451 manifest.dependencies[name][version] = nil
454 452
455 if not next(package_entry) then 453 if not next(package_entry) then
456 -- No more versions of this package. 454
457 manifest.repository[name] = nil 455 manifest.repository[name] = nil
458 manifest.dependencies[name] = nil 456 manifest.dependencies[name] = nil
459 end 457 end
diff --git a/src/luarocks/manif/writer.tl b/src/luarocks/manif/writer.tl
index b803bff6..5af74e6a 100644
--- a/src/luarocks/manif/writer.tl
+++ b/src/luarocks/manif/writer.tl
@@ -60,7 +60,7 @@ end
60-- @param items table: a table mapping item names to paths. 60-- @param items table: a table mapping item names to paths.
61-- @param name string: package name. 61-- @param name string: package name.
62-- @param version string: package version. 62-- @param version string: package version.
63local function remove_package_items(storage: {string: {string}}, name: string, version: string, items: {string: {string}}) 63local function remove_package_items(storage: {string: {string}}, name: string, version: string, items: {string: string})
64 assert(not name:match("/")) 64 assert(not name:match("/"))
65 65
66 local package_identifier = name.."/"..version 66 local package_identifier = name.."/"..version
@@ -219,7 +219,7 @@ local function store_results(results: {string: {string: {Result}}}, manifest: Ma
219 for version, entries in pairs(versions) do 219 for version, entries in pairs(versions) do
220 local versiontable = {} 220 local versiontable = {}
221 for _, entry in ipairs(entries) do 221 for _, entry in ipairs(entries) do
222 local entrytable: Manifest = {} 222 local entrytable: Manifest.Entry = {}
223 entrytable.arch = entry.arch 223 entrytable.arch = entry.arch
224 if entry.arch == "installed" then 224 if entry.arch == "installed" then
225 local rock_manifest, err = manif.load_rock_manifest(name, version) 225 local rock_manifest, err = manif.load_rock_manifest(name, version)
diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua
index 186bc1c1..c1d80fc8 100644
--- a/src/luarocks/repos.lua
+++ b/src/luarocks/repos.lua
@@ -78,8 +78,6 @@ function repos.is_installed(name, version)
78end 78end
79 79
80function repos.recurse_rock_manifest_entry(entry, action) 80function repos.recurse_rock_manifest_entry(entry, action)
81 assert(type(action) == "function")
82
83 if entry == nil then 81 if entry == nil then
84 return true 82 return true
85 end 83 end
diff --git a/src/luarocks/repos.tl b/src/luarocks/repos.tl
index e98ffedb..7b974221 100644
--- a/src/luarocks/repos.tl
+++ b/src/luarocks/repos.tl
@@ -78,8 +78,6 @@ function repos.is_installed(name: string, version: string): boolean
78end 78end
79 79
80function repos.recurse_rock_manifest_entry(entry: Entry, action: (function(string): boolean, string)): boolean, string 80function repos.recurse_rock_manifest_entry(entry: Entry, action: (function(string): boolean, string)): boolean, string
81 assert(type(action) == "function")
82
83 if entry == nil then 81 if entry == nil then
84 return true 82 return true
85 end 83 end