diff options
author | V1K1NGbg <victor@ilchev.com> | 2024-08-14 19:14:17 +0300 |
---|---|---|
committer | V1K1NGbg <victor@ilchev.com> | 2024-08-14 19:14:17 +0300 |
commit | 572f059ddc5c80e036956ad157fc4ea3175ce349 (patch) | |
tree | 5bdc54ac17c77dbc24462d307b868a3d9a7383b0 | |
parent | 719d2e29e2834c8226c6b2afaeb2ab402a0ba7d8 (diff) | |
download | luarocks-572f059ddc5c80e036956ad157fc4ea3175ce349.tar.gz luarocks-572f059ddc5c80e036956ad157fc4ea3175ce349.tar.bz2 luarocks-572f059ddc5c80e036956ad157fc4ea3175ce349.zip |
writer
-rw-r--r-- | .editorconfig | 4 | ||||
-rw-r--r-- | src/luarocks/build.tl | 496 | ||||
-rw-r--r-- | src/luarocks/build/builtin.tl | 400 | ||||
-rw-r--r-- | src/luarocks/build/cmake.tl | 78 | ||||
-rw-r--r-- | src/luarocks/build/command.tl | 41 | ||||
-rw-r--r-- | src/luarocks/build/make.tl | 98 | ||||
-rw-r--r-- | src/luarocks/core/cfg.d.tl | 1 | ||||
-rw-r--r-- | src/luarocks/core/types/manifest.d.tl | 37 | ||||
-rw-r--r-- | src/luarocks/core/types/rockspec.lua | 11 | ||||
-rw-r--r-- | src/luarocks/core/types/rockspec.tl | 21 | ||||
-rw-r--r-- | src/luarocks/fs.d.tl | 4 | ||||
-rw-r--r-- | src/luarocks/manif/writer-original.lua | 469 | ||||
-rw-r--r-- | src/luarocks/manif/writer.lua | 310 | ||||
-rw-r--r-- | src/luarocks/manif/writer.tl | 4 | ||||
-rw-r--r-- | src/luarocks/repos.lua | 2 | ||||
-rw-r--r-- | src/luarocks/repos.tl | 2 |
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 | |||
10 | indent_style = space | 10 | indent_style = space |
11 | indent_size = 3 | 11 | indent_size = 3 |
12 | 12 | ||
13 | [*.tl] | ||
14 | indent_style = space | ||
15 | indent_size = 3 | ||
16 | |||
13 | [Makefile] | 17 | [Makefile] |
14 | indent_style = tab | 18 | indent_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 | |||
2 | local 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 | ||
17 | end | ||
18 | |||
19 | local path = require("luarocks.path") | ||
20 | local util = require("luarocks.util") | ||
21 | local fun = require("luarocks.fun") | ||
22 | local fetch = require("luarocks.fetch") | ||
23 | local fs = require("luarocks.fs") | ||
24 | local dir = require("luarocks.dir") | ||
25 | local deps = require("luarocks.deps") | ||
26 | local cfg = require("luarocks.core.cfg") | ||
27 | local vers = require("luarocks.core.vers") | ||
28 | local repos = require("luarocks.repos") | ||
29 | local repo_writer = require("luarocks.repo_writer") | ||
30 | local deplocks = require("luarocks.deplocks") | ||
31 | |||
32 | local type r = require("luarocks.core.types.rockspec") | ||
33 | local type Rockspec = r.Rockspec | ||
34 | local type Installs = r.Installs | ||
35 | local type Install = r.Install | ||
36 | |||
37 | local type t = require("luarocks.core.types.tree") | ||
38 | local type Tree = t.Tree | ||
39 | |||
40 | local type Op_b = build.Op_b | ||
41 | |||
42 | do | ||
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 | ||
93 | end | ||
94 | |||
95 | local 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 | ||
116 | end | ||
117 | |||
118 | local 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) | ||
172 | end | ||
173 | |||
174 | local 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 | ||
205 | end | ||
206 | |||
207 | local 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 | ||
223 | end | ||
224 | |||
225 | local 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 | ||
263 | end | ||
264 | |||
265 | local install_files | ||
266 | do | ||
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 | ||
375 | end | ||
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. | ||
383 | function 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 | ||
494 | end | ||
495 | |||
496 | return 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. | ||
3 | local record builtin | ||
4 | skip_lua_inc_lib_check: boolean | ||
5 | end | ||
6 | |||
7 | local type r = require("luarocks.core.types.rockspec") | ||
8 | local type Rockspec = r.Rockspec | ||
9 | local type Variables = r.Variables | ||
10 | local 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. | ||
15 | builtin.skip_lua_inc_lib_check = true | ||
16 | |||
17 | local dir_sep = package.config:sub(1, 1) | ||
18 | |||
19 | local fs = require("luarocks.fs") | ||
20 | local path = require("luarocks.path") | ||
21 | local util = require("luarocks.util") | ||
22 | local cfg = require("luarocks.core.cfg") | ||
23 | local dir = require("luarocks.dir") | ||
24 | local deps = require("luarocks.deps") | ||
25 | |||
26 | local 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 | ||
41 | end | ||
42 | |||
43 | do | ||
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 | ||
119 | end | ||
120 | |||
121 | --- Run a command displaying its execution on standard output. | ||
122 | -- @return boolean: true if command succeeds (status code 0), false | ||
123 | -- otherwise. | ||
124 | local function execute(...: string): boolean, string, string | ||
125 | io.stdout:write(table.concat({...}, " ").."\n") | ||
126 | return fs.execute(...) | ||
127 | end | ||
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. | ||
133 | function 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 | ||
398 | end | ||
399 | |||
400 | return 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. | ||
3 | local cmake = {} | ||
4 | |||
5 | local fs = require("luarocks.fs") | ||
6 | local util = require("luarocks.util") | ||
7 | local 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. | ||
13 | function 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 | ||
76 | end | ||
77 | |||
78 | return 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. | ||
3 | local command = {} | ||
4 | |||
5 | local fs = require("luarocks.fs") | ||
6 | local util = require("luarocks.util") | ||
7 | local 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. | ||
13 | function 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 | ||
39 | end | ||
40 | |||
41 | return 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. | ||
3 | local make = {} | ||
4 | |||
5 | local unpack = unpack or table.unpack | ||
6 | |||
7 | local fs = require("luarocks.fs") | ||
8 | local util = require("luarocks.util") | ||
9 | local 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. | ||
21 | local 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 | ||
35 | end | ||
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. | ||
41 | function 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 | ||
96 | end | ||
97 | |||
98 | return 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} | ||
82 | end | 83 | end |
83 | 84 | ||
84 | return cfg \ No newline at end of file | 85 | return 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 | |||
4 | local type t = require("luarocks.core.types.tree") | 4 | local type t = require("luarocks.core.types.tree") |
5 | local type Tree = t.Tree | 5 | local type Tree = t.Tree |
6 | 6 | ||
7 | local record manifest | 7 | local 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 | ||
27 | end | ||
28 | |||
29 | return 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 | ||
4 | local rockspec = {Description = {}, Variables = {}, Source = {}, Test = {}, Install = {}, Build = {}, Dependencies = {}, Hooks = {}, Deploy = {}, Rockspec = {}, } | 4 | local 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 | ||
62 | end | 64 | end |
63 | 65 | ||
64 | return fs | 66 | return 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 | |||
2 | local writer = {} | ||
3 | |||
4 | local cfg = require("luarocks.core.cfg") | ||
5 | local search = require("luarocks.search") | ||
6 | local repos = require("luarocks.repos") | ||
7 | local deps = require("luarocks.deps") | ||
8 | local vers = require("luarocks.core.vers") | ||
9 | local fs = require("luarocks.fs") | ||
10 | local util = require("luarocks.util") | ||
11 | local dir = require("luarocks.dir") | ||
12 | local fetch = require("luarocks.fetch") | ||
13 | local path = require("luarocks.path") | ||
14 | local persist = require("luarocks.persist") | ||
15 | local manif = require("luarocks.manif") | ||
16 | local 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. | ||
25 | local 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 | ||
40 | end | ||
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. | ||
49 | local 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 | ||
80 | end | ||
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. | ||
90 | local 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 | ||
109 | end | ||
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. | ||
120 | local 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 | ||
128 | end | ||
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. | ||
133 | local 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 | ||
155 | end | ||
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 | ||
163 | local 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 | ||
205 | end | ||
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. | ||
212 | local 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 | ||
241 | end | ||
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. | ||
249 | local 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 | ||
260 | end | ||
261 | |||
262 | function 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 | ||
292 | end | ||
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. | ||
300 | function 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 | ||
317 | end | ||
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. | ||
329 | function 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) | ||
367 | end | ||
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. | ||
380 | function 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) | ||
408 | end | ||
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. | ||
421 | function 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) | ||
467 | end | ||
468 | |||
469 | return 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 | 1 | local _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 | |
2 | local writer = {} | 2 | local writer = {} |
3 | 3 | ||
4 | |||
4 | local cfg = require("luarocks.core.cfg") | 5 | local cfg = require("luarocks.core.cfg") |
5 | local search = require("luarocks.search") | 6 | local search = require("luarocks.search") |
6 | local repos = require("luarocks.repos") | 7 | local repos = require("luarocks.repos") |
@@ -15,22 +16,35 @@ local persist = require("luarocks.persist") | |||
15 | local manif = require("luarocks.manif") | 16 | local manif = require("luarocks.manif") |
16 | local queries = require("luarocks.queries") | 17 | local 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 | |||
25 | local function store_package_items(storage, name, version, items) | 42 | local 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 |
40 | end | 54 | end |
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 | |
49 | local function remove_package_items(storage, name, version, items) | 63 | local 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 |
80 | end | 91 | end |
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 | |
90 | local function update_dependencies(manifest, deps_mode) | 101 | local 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. | ||
120 | local function sort_pkgs(a, b) | ||
121 | assert(type(a) == "string") | ||
122 | assert(type(b) == "string") | ||
123 | 122 | ||
123 | |||
124 | |||
125 | |||
126 | |||
127 | |||
128 | |||
129 | local 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 |
128 | end | 134 | end |
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 | |
133 | local function sort_package_matching_table(tbl) | 139 | local 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 |
155 | end | 160 | end |
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 | |
163 | local function filter_by_lua_version(manifest, lua_version, repodir, cache) | 168 | local 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 |
205 | end | 208 | end |
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 | |
212 | local function store_results(results, manifest) | 215 | local 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 |
241 | end | 242 | end |
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 | |
249 | local function save_table(where, name, tbl) | 250 | local 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 |
260 | end | 259 | end |
@@ -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 |
292 | end | 297 | end |
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 | |
300 | function writer.make_namespace_file(name, version, namespace) | 305 | function 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 |
317 | end | 320 | end |
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 | |
329 | function writer.make_manifest(repo, deps_mode, remote) | 332 | function 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) |
367 | end | 368 | end |
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 | |
380 | function writer.add_to_manifest(name, version, repo, deps_mode) | 381 | function 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) |
408 | end | 408 | end |
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 | |
421 | function writer.remove_from_manifest(name, version, repo, deps_mode) | 421 | function 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. |
63 | local function remove_package_items(storage: {string: {string}}, name: string, version: string, items: {string: {string}}) | 63 | local 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) | |||
78 | end | 78 | end |
79 | 79 | ||
80 | function repos.recurse_rock_manifest_entry(entry, action) | 80 | function 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 | |||
78 | end | 78 | end |
79 | 79 | ||
80 | function repos.recurse_rock_manifest_entry(entry: Entry, action: (function(string): boolean, string)): boolean, string | 80 | function 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 |