aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-08-15 20:10:40 +0300
committerV1K1NGbg <victor@ilchev.com>2024-08-15 20:10:40 +0300
commit337fe2d921ba84f71a4088ec2d115e8e2432da17 (patch)
tree66d5d936684ca1cfb329d3838e7058ef72a9ca0b
parentc64e6b23b630c52ed28f581376cdb10c9360fd54 (diff)
downloadluarocks-337fe2d921ba84f71a4088ec2d115e8e2432da17.tar.gz
luarocks-337fe2d921ba84f71a4088ec2d115e8e2432da17.tar.bz2
luarocks-337fe2d921ba84f71a4088ec2d115e8e2432da17.zip
build
-rw-r--r--src/luarocks/build-original.lua471
-rw-r--r--src/luarocks/build.lua190
-rw-r--r--src/luarocks/build.tl71
-rw-r--r--src/luarocks/build/builtin.tl4
-rw-r--r--src/luarocks/core/types/build.d.tl8
-rw-r--r--src/luarocks/fs.d.tl1
-rw-r--r--src/luarocks/repo_writer.tl2
7 files changed, 654 insertions, 93 deletions
diff --git a/src/luarocks/build-original.lua b/src/luarocks/build-original.lua
new file mode 100644
index 00000000..b027c81a
--- /dev/null
+++ b/src/luarocks/build-original.lua
@@ -0,0 +1,471 @@
1
2local build = {}
3
4local path = require("luarocks.path")
5local util = require("luarocks.util")
6local fun = require("luarocks.fun")
7local fetch = require("luarocks.fetch")
8local fs = require("luarocks.fs")
9local dir = require("luarocks.dir")
10local deps = require("luarocks.deps")
11local cfg = require("luarocks.core.cfg")
12local vers = require("luarocks.core.vers")
13local repos = require("luarocks.repos")
14local repo_writer = require("luarocks.repo_writer")
15local deplocks = require("luarocks.deplocks")
16
17do
18 --- Write to the current directory the contents of a table,
19 -- where each key is a file name and its value is the file content.
20 -- @param files table: The table of files to be written.
21 local function extract_from_rockspec(files)
22 for name, content in pairs(files) do
23 local fd = io.open(dir.path(fs.current_dir(), name), "w+")
24 fd:write(content)
25 fd:close()
26 end
27 end
28
29 --- Applies patches inlined in the build.patches section
30 -- and extracts files inlined in the build.extra_files section
31 -- of a rockspec.
32 -- @param rockspec table: A rockspec table.
33 -- @return boolean or (nil, string): True if succeeded or
34 -- nil and an error message.
35 function build.apply_patches(rockspec)
36 assert(rockspec:type() == "rockspec")
37
38 if not (rockspec.build.extra_files or rockspec.build.patches) then
39 return true
40 end
41
42 local fd = io.open(fs.absolute_name(".luarocks.patches.applied"), "r")
43 if fd then
44 fd:close()
45 return true
46 end
47
48 if rockspec.build.extra_files then
49 extract_from_rockspec(rockspec.build.extra_files)
50 end
51 if rockspec.build.patches then
52 extract_from_rockspec(rockspec.build.patches)
53 for patch, patchdata in util.sortedpairs(rockspec.build.patches) do
54 util.printout("Applying patch "..patch.."...")
55 local create_delete = rockspec:format_is_at_least("3.0")
56 local ok, err = fs.apply_patch(tostring(patch), patchdata, create_delete)
57 if not ok then
58 return nil, "Failed applying patch "..patch
59 end
60 end
61 end
62
63 fd = io.open(fs.absolute_name(".luarocks.patches.applied"), "w")
64 if fd then
65 fd:close()
66 end
67 return true
68 end
69end
70
71local function check_macosx_deployment_target(rockspec)
72 local target = rockspec.build.macosx_deployment_target
73 local function patch_variable(var)
74 if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then
75 rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target)
76 else
77 rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var]
78 end
79 end
80 if cfg.is_platform("macosx") and rockspec:format_is_at_least("3.0") and target then
81 local version = util.popen_read("sw_vers -productVersion")
82 if version:match("^%d+%.%d+%.%d+$") or version:match("^%d+%.%d+$") then
83 if vers.compare_versions(target, version) then
84 return nil, ("This rock requires Mac OSX %s, and you are running %s."):format(target, version)
85 end
86 end
87 patch_variable("CC")
88 patch_variable("LD")
89 end
90 return true
91end
92
93local function process_dependencies(rockspec, opts, cwd)
94 if not opts.build_only_deps then
95 local ok, err, errcode = deps.check_external_deps(rockspec, "build")
96 if err then
97 return nil, err, errcode
98 end
99 end
100
101 if opts.deps_mode == "none" then
102 return true
103 end
104
105 local deplock_dir = fs.exists(dir.path(cwd, "luarocks.lock")) and cwd or nil
106
107 if not opts.build_only_deps then
108 if next(rockspec.build_dependencies) then
109
110 local user_lua_version = cfg.lua_version
111 local running_lua_version = _VERSION:sub(5)
112
113 if running_lua_version ~= user_lua_version then
114 -- Temporarily flip the user-selected Lua version,
115 -- so that we install build dependencies for the
116 -- Lua version on which the LuaRocks program is running.
117
118 -- HACK: we have to do this by flipping a bunch of
119 -- global config settings, and this list may not be complete.
120 cfg.lua_version = running_lua_version
121 cfg.lua_modules_path = cfg.lua_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
122 cfg.lib_modules_path = cfg.lib_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
123 cfg.rocks_subdir = cfg.rocks_subdir:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
124 path.use_tree(cfg.root_dir)
125 end
126
127 local ok, err, errcode = deps.fulfill_dependencies(rockspec, "build_dependencies", "all", opts.verify, deplock_dir)
128
129 path.add_to_package_paths(cfg.root_dir)
130
131 if running_lua_version ~= user_lua_version then
132 -- flip the settings back
133 cfg.lua_version = user_lua_version
134 cfg.lua_modules_path = cfg.lua_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
135 cfg.lib_modules_path = cfg.lib_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
136 cfg.rocks_subdir = cfg.rocks_subdir:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
137 path.use_tree(cfg.root_dir)
138 end
139
140 if err then
141 return nil, err, errcode
142 end
143 end
144 end
145
146 return deps.fulfill_dependencies(rockspec, "dependencies", opts.deps_mode, opts.verify, deplock_dir)
147end
148
149local function fetch_and_change_to_source_dir(rockspec, opts)
150 if opts.minimal_mode or opts.build_only_deps then
151 return true
152 end
153 if opts.need_to_fetch then
154 if opts.branch then
155 rockspec.source.branch = opts.branch
156 end
157 local ok, source_dir, errcode = fetch.fetch_sources(rockspec, true)
158 if not ok then
159 return nil, source_dir, errcode
160 end
161 local err
162 ok, err = fs.change_dir(source_dir)
163 if not ok then
164 return nil, err
165 end
166 else
167 if rockspec.source.file then
168 local ok, err = fs.unpack_archive(rockspec.source.file)
169 if not ok then
170 return nil, err
171 end
172 end
173 local ok, err = fetch.find_rockspec_source_dir(rockspec, ".")
174 if not ok then
175 return nil, err
176 end
177 end
178 fs.change_dir(rockspec.source.dir)
179 return true
180end
181
182local function prepare_install_dirs(name, version)
183 local dirs = {
184 lua = { name = path.lua_dir(name, version), is_module_path = true, perms = "read" },
185 lib = { name = path.lib_dir(name, version), is_module_path = true, perms = "exec" },
186 bin = { name = path.bin_dir(name, version), is_module_path = false, perms = "exec" },
187 conf = { name = path.conf_dir(name, version), is_module_path = false, perms = "read" },
188 }
189
190 for _, d in pairs(dirs) do
191 local ok, err = fs.make_dir(d.name)
192 if not ok then
193 return nil, err
194 end
195 end
196
197 return dirs
198end
199
200local function run_build_driver(rockspec, no_install)
201 local btype = rockspec.build.type
202 if btype == "none" then
203 return true
204 end
205 -- Temporary compatibility
206 if btype == "module" then
207 util.printout("Do not use 'module' as a build type. Use 'builtin' instead.")
208 btype = "builtin"
209 rockspec.build.type = btype
210 end
211 if cfg.accepted_build_types and not fun.contains(cfg.accepted_build_types, btype) then
212 return nil, "This rockspec uses the '"..btype.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration."
213 end
214 local pok, driver = pcall(require, "luarocks.build." .. btype)
215 if not pok or type(driver) ~= "table" then
216 return nil, "Failed initializing build back-end for build type '"..btype.."': "..driver
217 end
218
219 if not driver.skip_lua_inc_lib_check then
220 local ok, err, errcode = deps.check_lua_incdir(rockspec.variables)
221 if not ok then
222 return nil, err, errcode
223 end
224
225 if cfg.link_lua_explicitly then
226 local ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
227 if not ok then
228 return nil, err, errcode
229 end
230 end
231 end
232
233 local ok, err = driver.run(rockspec, no_install)
234 if not ok then
235 return nil, "Build error: " .. err
236 end
237 return true
238end
239
240local install_files
241do
242 --- Install files to a given location.
243 -- Takes a table where the array part is a list of filenames to be copied.
244 -- In the hash part, other keys, if is_module_path is set, are identifiers
245 -- in Lua module format, to indicate which subdirectory the file should be
246 -- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo")
247 -- will copy src/bar.lua to boo/foo.
248 -- @param files table or nil: A table containing a list of files to copy in
249 -- the format described above. If nil is passed, this function is a no-op.
250 -- Directories should be delimited by forward slashes as in internet URLs.
251 -- @param location string: The base directory files should be copied to.
252 -- @param is_module_path boolean: True if string keys in files should be
253 -- interpreted as dotted module paths.
254 -- @param perms string ("read" or "exec"): Permissions of the newly created
255 -- files installed.
256 -- Directories are always created with the default permissions.
257 -- @return boolean or (nil, string): True if succeeded or
258 -- nil and an error message.
259 local function install_to(files, location, is_module_path, perms)
260 assert(type(files) == "table" or not files)
261 assert(type(location) == "string")
262 if not files then
263 return true
264 end
265 for k, file in pairs(files) do
266 local dest = location
267 local filename = dir.base_name(file)
268 if type(k) == "string" then
269 local modname = k
270 if is_module_path then
271 dest = dir.path(location, path.module_to_path(modname))
272 local ok, err = fs.make_dir(dest)
273 if not ok then return nil, err end
274 if filename:match("%.lua$") then
275 local basename = modname:match("([^.]+)$")
276 filename = basename..".lua"
277 end
278 else
279 dest = dir.path(location, dir.dir_name(modname))
280 local ok, err = fs.make_dir(dest)
281 if not ok then return nil, err end
282 filename = dir.base_name(modname)
283 end
284 else
285 local ok, err = fs.make_dir(dest)
286 if not ok then return nil, err end
287 end
288 local ok = fs.copy(file, dir.path(dest, filename), perms)
289 if not ok then
290 return nil, "Failed copying "..file
291 end
292 end
293 return true
294 end
295
296 local function install_default_docs(name, version)
297 local patterns = { "readme", "license", "copying", ".*%.md" }
298 local dest = dir.path(path.install_dir(name, version), "doc")
299 local has_dir = false
300 for file in fs.dir() do
301 for _, pattern in ipairs(patterns) do
302 if file:lower():match("^"..pattern) then
303 if not has_dir then
304 fs.make_dir(dest)
305 has_dir = true
306 end
307 fs.copy(file, dest, "read")
308 break
309 end
310 end
311 end
312 end
313
314 install_files = function(rockspec, dirs)
315 local name, version = rockspec.name, rockspec.version
316
317 if rockspec.build.install then
318 for k, d in pairs(dirs) do
319 local ok, err = install_to(rockspec.build.install[k], d.name, d.is_module_path, d.perms)
320 if not ok then return nil, err end
321 end
322 end
323
324 local copy_directories = rockspec.build.copy_directories
325 local copying_default = false
326 if not copy_directories then
327 copy_directories = {"doc"}
328 copying_default = true
329 end
330
331 local any_docs = false
332 for _, copy_dir in pairs(copy_directories) do
333 if fs.is_dir(copy_dir) then
334 local dest = dir.path(path.install_dir(name, version), copy_dir)
335 fs.make_dir(dest)
336 fs.copy_contents(copy_dir, dest)
337 any_docs = true
338 else
339 if not copying_default then
340 return nil, "Directory '"..copy_dir.."' not found"
341 end
342 end
343 end
344 if not any_docs then
345 install_default_docs(name, version)
346 end
347
348 return true
349 end
350end
351
352--- Build and install a rock given a rockspec.
353-- @param rockspec rockspec: the rockspec to build
354-- @param opts table: build options table
355-- @param cwd string or nil: The current working directory
356-- @return (string, string) or (nil, string, [string]): Name and version of
357-- installed rock if succeeded or nil and an error message followed by an error code.
358function build.build_rockspec(rockspec, opts, cwd)
359 assert(rockspec:type() == "rockspec")
360
361 cwd = cwd or dir.path(".")
362
363 if not rockspec.build then
364 if rockspec:format_is_at_least("3.0") then
365 rockspec.build = {
366 type = "builtin"
367 }
368 else
369 return nil, "Rockspec error: build table not specified"
370 end
371 end
372
373 if not rockspec.build.type then
374 if rockspec:format_is_at_least("3.0") then
375 rockspec.build.type = "builtin"
376 else
377 return nil, "Rockspec error: build type not specified"
378 end
379 end
380
381 local ok, err = fetch_and_change_to_source_dir(rockspec, opts)
382 if not ok then return nil, err end
383
384 if opts.pin then
385 deplocks.init(rockspec.name, ".")
386 end
387
388 ok, err = process_dependencies(rockspec, opts, cwd)
389 if not ok then return nil, err end
390
391 local name, version = rockspec.name, rockspec.version
392 if opts.build_only_deps then
393 if opts.pin then
394 deplocks.write_file()
395 end
396 return name, version
397 end
398
399 local dirs, err
400 local rollback
401 if not opts.no_install then
402 if repos.is_installed(name, version) then
403 repo_writer.delete_version(name, version, opts.deps_mode)
404 end
405
406 dirs, err = prepare_install_dirs(name, version)
407 if not dirs then return nil, err end
408
409 rollback = util.schedule_function(function()
410 fs.delete(path.install_dir(name, version))
411 fs.remove_dir_if_empty(path.versions_dir(name))
412 end)
413 end
414
415 ok, err = build.apply_patches(rockspec)
416 if not ok then return nil, err end
417
418 ok, err = check_macosx_deployment_target(rockspec)
419 if not ok then return nil, err end
420
421 ok, err = run_build_driver(rockspec, opts.no_install)
422 if not ok then return nil, err end
423
424 if opts.no_install then
425 fs.pop_dir()
426 if opts.need_to_fetch then
427 fs.pop_dir()
428 end
429 return name, version
430 end
431
432 ok, err = install_files(rockspec, dirs)
433 if not ok then return nil, err end
434
435 for _, d in pairs(dirs) do
436 fs.remove_dir_if_empty(d.name)
437 end
438
439 fs.pop_dir()
440 if opts.need_to_fetch then
441 fs.pop_dir()
442 end
443
444 if opts.pin then
445 deplocks.write_file()
446 end
447
448 fs.copy(rockspec.local_abs_filename, path.rockspec_file(name, version), "read")
449
450 local deplock_file = deplocks.get_abs_filename(name)
451 if deplock_file then
452 fs.copy(deplock_file, dir.path(path.install_dir(name, version), "luarocks.lock"), "read")
453 end
454
455 ok, err = repo_writer.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), opts.deps_mode, opts.namespace)
456 if not ok then return nil, err end
457
458 util.remove_scheduled_function(rollback)
459 rollback = util.schedule_function(function()
460 repo_writer.delete_version(name, version, opts.deps_mode)
461 end)
462
463 ok, err = repos.run_hook(rockspec, "post_install")
464 if not ok then return nil, err end
465
466 util.announce_install(rockspec)
467 util.remove_scheduled_function(rollback)
468 return name, version
469end
470
471return build
diff --git a/src/luarocks/build.lua b/src/luarocks/build.lua
index b027c81a..bc674a1a 100644
--- a/src/luarocks/build.lua
+++ b/src/luarocks/build.lua
@@ -1,5 +1,23 @@
1local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local 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 pcall = _tl_compat and _tl_compat.pcall or pcall; local string = _tl_compat and _tl_compat.string or string
2local build = {Op_b = {}, Builds = {}, }
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1 20
2local build = {}
3 21
4local path = require("luarocks.path") 22local path = require("luarocks.path")
5local util = require("luarocks.util") 23local util = require("luarocks.util")
@@ -14,10 +32,27 @@ local repos = require("luarocks.repos")
14local repo_writer = require("luarocks.repo_writer") 32local repo_writer = require("luarocks.repo_writer")
15local deplocks = require("luarocks.deplocks") 33local deplocks = require("luarocks.deplocks")
16 34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
17do 52do
18 --- Write to the current directory the contents of a table, 53
19 -- where each key is a file name and its value is the file content. 54
20 -- @param files table: The table of files to be written. 55
21 local function extract_from_rockspec(files) 56 local function extract_from_rockspec(files)
22 for name, content in pairs(files) do 57 for name, content in pairs(files) do
23 local fd = io.open(dir.path(fs.current_dir(), name), "w+") 58 local fd = io.open(dir.path(fs.current_dir(), name), "w+")
@@ -26,14 +61,13 @@ do
26 end 61 end
27 end 62 end
28 63
29 --- Applies patches inlined in the build.patches section 64
30 -- and extracts files inlined in the build.extra_files section 65
31 -- of a rockspec. 66
32 -- @param rockspec table: A rockspec table. 67
33 -- @return boolean or (nil, string): True if succeeded or 68
34 -- nil and an error message. 69
35 function build.apply_patches(rockspec) 70 function build.apply_patches(rockspec)
36 assert(rockspec:type() == "rockspec")
37 71
38 if not (rockspec.build.extra_files or rockspec.build.patches) then 72 if not (rockspec.build.extra_files or rockspec.build.patches) then
39 return true 73 return true
@@ -51,11 +85,11 @@ do
51 if rockspec.build.patches then 85 if rockspec.build.patches then
52 extract_from_rockspec(rockspec.build.patches) 86 extract_from_rockspec(rockspec.build.patches)
53 for patch, patchdata in util.sortedpairs(rockspec.build.patches) do 87 for patch, patchdata in util.sortedpairs(rockspec.build.patches) do
54 util.printout("Applying patch "..patch.."...") 88 util.printout("Applying patch " .. patch .. "...")
55 local create_delete = rockspec:format_is_at_least("3.0") 89 local create_delete = rockspec:format_is_at_least("3.0")
56 local ok, err = fs.apply_patch(tostring(patch), patchdata, create_delete) 90 local ok, err = fs.apply_patch(tostring(patch), patchdata, create_delete)
57 if not ok then 91 if not ok then
58 return nil, "Failed applying patch "..patch 92 return nil, "Failed applying patch " .. patch
59 end 93 end
60 end 94 end
61 end 95 end
@@ -71,10 +105,11 @@ end
71local function check_macosx_deployment_target(rockspec) 105local function check_macosx_deployment_target(rockspec)
72 local target = rockspec.build.macosx_deployment_target 106 local target = rockspec.build.macosx_deployment_target
73 local function patch_variable(var) 107 local function patch_variable(var)
74 if rockspec.variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then 108 local rockspec_variables = rockspec.variables
75 rockspec.variables[var] = (rockspec.variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET="..target) 109 if rockspec_variables[var]:match("MACOSX_DEPLOYMENT_TARGET") then
110 rockspec_variables[var] = (rockspec_variables[var]):gsub("MACOSX_DEPLOYMENT_TARGET=[^ ]*", "MACOSX_DEPLOYMENT_TARGET=" .. target)
76 else 111 else
77 rockspec.variables[var] = "env MACOSX_DEPLOYMENT_TARGET="..target.." "..rockspec.variables[var] 112 rockspec_variables[var] = "env MACOSX_DEPLOYMENT_TARGET=" .. target .. " " .. rockspec_variables[var]
78 end 113 end
79 end 114 end
80 if cfg.is_platform("macosx") and rockspec:format_is_at_least("3.0") and target then 115 if cfg.is_platform("macosx") and rockspec:format_is_at_least("3.0") and target then
@@ -105,18 +140,18 @@ local function process_dependencies(rockspec, opts, cwd)
105 local deplock_dir = fs.exists(dir.path(cwd, "luarocks.lock")) and cwd or nil 140 local deplock_dir = fs.exists(dir.path(cwd, "luarocks.lock")) and cwd or nil
106 141
107 if not opts.build_only_deps then 142 if not opts.build_only_deps then
108 if next(rockspec.build_dependencies) then 143 if next(rockspec.build_dependencies) ~= nil then
109 144
110 local user_lua_version = cfg.lua_version 145 local user_lua_version = cfg.lua_version
111 local running_lua_version = _VERSION:sub(5) 146 local running_lua_version = _VERSION:sub(5)
112 147
113 if running_lua_version ~= user_lua_version then 148 if running_lua_version ~= user_lua_version then
114 -- Temporarily flip the user-selected Lua version,
115 -- so that we install build dependencies for the
116 -- Lua version on which the LuaRocks program is running.
117 149
118 -- HACK: we have to do this by flipping a bunch of 150
119 -- global config settings, and this list may not be complete. 151
152
153
154
120 cfg.lua_version = running_lua_version 155 cfg.lua_version = running_lua_version
121 cfg.lua_modules_path = cfg.lua_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version) 156 cfg.lua_modules_path = cfg.lua_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
122 cfg.lib_modules_path = cfg.lib_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version) 157 cfg.lib_modules_path = cfg.lib_modules_path:gsub(user_lua_version:gsub("%.", "%%."), running_lua_version)
@@ -129,7 +164,7 @@ local function process_dependencies(rockspec, opts, cwd)
129 path.add_to_package_paths(cfg.root_dir) 164 path.add_to_package_paths(cfg.root_dir)
130 165
131 if running_lua_version ~= user_lua_version then 166 if running_lua_version ~= user_lua_version then
132 -- flip the settings back 167
133 cfg.lua_version = user_lua_version 168 cfg.lua_version = user_lua_version
134 cfg.lua_modules_path = cfg.lua_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version) 169 cfg.lua_modules_path = cfg.lua_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
135 cfg.lib_modules_path = cfg.lib_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version) 170 cfg.lib_modules_path = cfg.lib_modules_path:gsub(running_lua_version:gsub("%.", "%%."), user_lua_version)
@@ -154,11 +189,11 @@ local function fetch_and_change_to_source_dir(rockspec, opts)
154 if opts.branch then 189 if opts.branch then
155 rockspec.source.branch = opts.branch 190 rockspec.source.branch = opts.branch
156 end 191 end
157 local ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) 192 local oks, source_dir, errcode = fetch.fetch_sources(rockspec, true)
158 if not ok then 193 if not oks then
159 return nil, source_dir, errcode 194 return nil, source_dir, errcode
160 end 195 end
161 local err 196 local ok, err
162 ok, err = fs.change_dir(source_dir) 197 ok, err = fs.change_dir(source_dir)
163 if not ok then 198 if not ok then
164 return nil, err 199 return nil, err
@@ -202,18 +237,44 @@ local function run_build_driver(rockspec, no_install)
202 if btype == "none" then 237 if btype == "none" then
203 return true 238 return true
204 end 239 end
205 -- Temporary compatibility 240
206 if btype == "module" then 241 if btype == "module" then
207 util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") 242 util.printout("Do not use 'module' as a build type. Use 'builtin' instead.")
208 btype = "builtin" 243 btype = "builtin"
209 rockspec.build.type = btype 244 rockspec.build.type = btype
210 end 245 end
211 if cfg.accepted_build_types and not fun.contains(cfg.accepted_build_types, btype) then 246 if cfg.accepted_build_types and not fun.contains(cfg.accepted_build_types, btype) then
212 return nil, "This rockspec uses the '"..btype.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." 247 return nil, "This rockspec uses the '" .. btype .. "' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration."
213 end 248 end
214 local pok, driver = pcall(require, "luarocks.build." .. btype) 249 local pok, driver_str, driver
215 if not pok or type(driver) ~= "table" then 250 if btype == "builtin" then
216 return nil, "Failed initializing build back-end for build type '"..btype.."': "..driver 251 pok, driver_str = pcall(require, "luarocks.build.builtin")
252 if not pok or not (type(driver_str) == "table") then
253 return nil, "Failed initializing build back-end for build type '" .. btype .. "': " .. driver_str
254 else
255 driver = driver_str
256 end
257 elseif btype == "cmake" then
258 pok, driver_str = pcall(require, "luarocks.build.cmake")
259 if not pok or not (type(driver_str) == "table") then
260 return nil, "Failed initializing build back-end for build type '" .. btype .. "': " .. driver_str
261 else
262 driver = driver_str
263 end
264 elseif btype == "make" then
265 pok, driver_str = pcall(require, "luarocks.build.make")
266 if not pok or not (type(driver_str) == "table") then
267 return nil, "Failed initializing build back-end for build type '" .. btype .. "': " .. driver_str
268 else
269 driver = driver_str
270 end
271 elseif btype == "command" then
272 pok, driver_str = pcall(require, "luarocks.build.command")
273 if not pok or not (type(driver_str) == "table") then
274 return nil, "Failed initializing build back-end for build type '" .. btype .. "': " .. driver_str
275 else
276 driver = driver_str
277 end
217 end 278 end
218 279
219 if not driver.skip_lua_inc_lib_check then 280 if not driver.skip_lua_inc_lib_check then
@@ -223,7 +284,7 @@ local function run_build_driver(rockspec, no_install)
223 end 284 end
224 285
225 if cfg.link_lua_explicitly then 286 if cfg.link_lua_explicitly then
226 local ok, err, errcode = deps.check_lua_libdir(rockspec.variables) 287 ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
227 if not ok then 288 if not ok then
228 return nil, err, errcode 289 return nil, err, errcode
229 end 290 end
@@ -239,26 +300,24 @@ end
239 300
240local install_files 301local install_files
241do 302do
242 --- Install files to a given location. 303
243 -- Takes a table where the array part is a list of filenames to be copied. 304
244 -- In the hash part, other keys, if is_module_path is set, are identifiers 305
245 -- in Lua module format, to indicate which subdirectory the file should be 306
246 -- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") 307
247 -- will copy src/bar.lua to boo/foo. 308
248 -- @param files table or nil: A table containing a list of files to copy in 309
249 -- the format described above. If nil is passed, this function is a no-op. 310
250 -- Directories should be delimited by forward slashes as in internet URLs. 311
251 -- @param location string: The base directory files should be copied to. 312
252 -- @param is_module_path boolean: True if string keys in files should be 313
253 -- interpreted as dotted module paths. 314
254 -- @param perms string ("read" or "exec"): Permissions of the newly created 315
255 -- files installed. 316
256 -- Directories are always created with the default permissions. 317
257 -- @return boolean or (nil, string): True if succeeded or 318
258 -- nil and an error message. 319
259 local function install_to(files, location, is_module_path, perms) 320 local function install_to(files, location, is_module_path, perms)
260 assert(type(files) == "table" or not files)
261 assert(type(location) == "string")
262 if not files then 321 if not files then
263 return true 322 return true
264 end 323 end
@@ -273,7 +332,7 @@ do
273 if not ok then return nil, err end 332 if not ok then return nil, err end
274 if filename:match("%.lua$") then 333 if filename:match("%.lua$") then
275 local basename = modname:match("([^.]+)$") 334 local basename = modname:match("([^.]+)$")
276 filename = basename..".lua" 335 filename = basename .. ".lua"
277 end 336 end
278 else 337 else
279 dest = dir.path(location, dir.dir_name(modname)) 338 dest = dir.path(location, dir.dir_name(modname))
@@ -287,7 +346,7 @@ do
287 end 346 end
288 local ok = fs.copy(file, dir.path(dest, filename), perms) 347 local ok = fs.copy(file, dir.path(dest, filename), perms)
289 if not ok then 348 if not ok then
290 return nil, "Failed copying "..file 349 return nil, "Failed copying " .. file
291 end 350 end
292 end 351 end
293 return true 352 return true
@@ -299,7 +358,7 @@ do
299 local has_dir = false 358 local has_dir = false
300 for file in fs.dir() do 359 for file in fs.dir() do
301 for _, pattern in ipairs(patterns) do 360 for _, pattern in ipairs(patterns) do
302 if file:lower():match("^"..pattern) then 361 if file:lower():match("^" .. pattern) then
303 if not has_dir then 362 if not has_dir then
304 fs.make_dir(dest) 363 fs.make_dir(dest)
305 has_dir = true 364 has_dir = true
@@ -316,7 +375,7 @@ do
316 375
317 if rockspec.build.install then 376 if rockspec.build.install then
318 for k, d in pairs(dirs) do 377 for k, d in pairs(dirs) do
319 local ok, err = install_to(rockspec.build.install[k], d.name, d.is_module_path, d.perms) 378 local ok, err = install_to((rockspec.build.install)[k], d.name, d.is_module_path, d.perms)
320 if not ok then return nil, err end 379 if not ok then return nil, err end
321 end 380 end
322 end 381 end
@@ -324,12 +383,12 @@ do
324 local copy_directories = rockspec.build.copy_directories 383 local copy_directories = rockspec.build.copy_directories
325 local copying_default = false 384 local copying_default = false
326 if not copy_directories then 385 if not copy_directories then
327 copy_directories = {"doc"} 386 copy_directories = { "doc" }
328 copying_default = true 387 copying_default = true
329 end 388 end
330 389
331 local any_docs = false 390 local any_docs = false
332 for _, copy_dir in pairs(copy_directories) do 391 for _, copy_dir in ipairs(copy_directories) do
333 if fs.is_dir(copy_dir) then 392 if fs.is_dir(copy_dir) then
334 local dest = dir.path(path.install_dir(name, version), copy_dir) 393 local dest = dir.path(path.install_dir(name, version), copy_dir)
335 fs.make_dir(dest) 394 fs.make_dir(dest)
@@ -337,7 +396,7 @@ do
337 any_docs = true 396 any_docs = true
338 else 397 else
339 if not copying_default then 398 if not copying_default then
340 return nil, "Directory '"..copy_dir.."' not found" 399 return nil, "Directory '" .. copy_dir .. "' not found"
341 end 400 end
342 end 401 end
343 end 402 end
@@ -349,21 +408,20 @@ do
349 end 408 end
350end 409end
351 410
352--- Build and install a rock given a rockspec. 411
353-- @param rockspec rockspec: the rockspec to build 412
354-- @param opts table: build options table 413
355-- @param cwd string or nil: The current working directory 414
356-- @return (string, string) or (nil, string, [string]): Name and version of 415
357-- installed rock if succeeded or nil and an error message followed by an error code. 416
358function build.build_rockspec(rockspec, opts, cwd) 417function build.build_rockspec(rockspec, opts, cwd)
359 assert(rockspec:type() == "rockspec")
360 418
361 cwd = cwd or dir.path(".") 419 cwd = cwd or dir.path(".")
362 420
363 if not rockspec.build then 421 if not rockspec.build then
364 if rockspec:format_is_at_least("3.0") then 422 if rockspec:format_is_at_least("3.0") then
365 rockspec.build = { 423 rockspec.build = {
366 type = "builtin" 424 type = "builtin",
367 } 425 }
368 else 426 else
369 return nil, "Rockspec error: build table not specified" 427 return nil, "Rockspec error: build table not specified"
diff --git a/src/luarocks/build.tl b/src/luarocks/build.tl
index cbafb409..610391b4 100644
--- a/src/luarocks/build.tl
+++ b/src/luarocks/build.tl
@@ -8,11 +8,14 @@ local record build
8 need_to_fetch: boolean 8 need_to_fetch: boolean
9 branch: string 9 branch: string
10 no_install: boolean 10 no_install: boolean
11 pin: boolean
12 namespace: string
11 end 13 end
12 14
13 record Builds 15 record Builds
14 skip_lua_inc_lib_check: boolean 16 skip_lua_inc_lib_check: boolean
15 17 run: function(Rockspec, boolean): boolean, string, string
18 autodetect_modules: function({string}, {string}, {string}): {string : string | Module}, Build.Install, {string}
16 end 19 end
17end 20end
18 21
@@ -39,7 +42,12 @@ local type InstallDir = i.InstallDir
39local type t = require("luarocks.core.types.tree") 42local type t = require("luarocks.core.types.tree")
40local type Tree = t.Tree 43local type Tree = t.Tree
41 44
45local type b = require("luarocks.core.types.build")
46local type Build = b.Build
47local type Module = b.BuiltinBuild.Module
48
42local type Op_b = build.Op_b 49local type Op_b = build.Op_b
50local type Builds = build.Builds
43 51
44do 52do
45 --- Write to the current directory the contents of a table, 53 --- Write to the current directory the contents of a table,
@@ -238,9 +246,35 @@ local function run_build_driver(rockspec: Rockspec, no_install: boolean): boolea
238 if cfg.accepted_build_types and not fun.contains(cfg.accepted_build_types, btype) then 246 if cfg.accepted_build_types and not fun.contains(cfg.accepted_build_types, btype) then
239 return nil, "This rockspec uses the '"..btype.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." 247 return nil, "This rockspec uses the '"..btype.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration."
240 end 248 end
241 local pok, driver = pcall(require, "luarocks.build." .. btype) 249 local pok, driver_str, driver: boolean, Builds | string, Builds
242 if not pok or type(driver) ~= "table" then 250 if btype == "builtin" then
243 return nil, "Failed initializing build back-end for build type '"..btype.."': "..driver 251 pok, driver_str = pcall(require, "luarocks.build.builtin") as (boolean, Builds | string)
252 if not pok or not driver_str is Builds then
253 return nil, "Failed initializing build back-end for build type '"..btype.."': "..driver_str as string
254 else
255 driver = driver_str as Builds
256 end
257 elseif btype == "cmake" then
258 pok, driver_str = pcall(require, "luarocks.build.cmake") as (boolean, Builds | string)
259 if not pok or not driver_str is Builds then
260 return nil, "Failed initializing build back-end for build type '"..btype.."': "..driver_str as string
261 else
262 driver = driver_str as Builds
263 end
264 elseif btype == "make" then
265 pok, driver_str = pcall(require, "luarocks.build.make") as (boolean, Builds | string)
266 if not pok or not driver_str is Builds then
267 return nil, "Failed initializing build back-end for build type '"..btype.."': "..driver_str as string
268 else
269 driver = driver_str as Builds
270 end
271 elseif btype == "command" then
272 pok, driver_str = pcall(require, "luarocks.build.command") as (boolean, Builds | string)
273 if not pok or not driver_str is Builds then
274 return nil, "Failed initializing build back-end for build type '"..btype.."': "..driver_str as string
275 else
276 driver = driver_str as Builds
277 end
244 end 278 end
245 279
246 if not driver.skip_lua_inc_lib_check then 280 if not driver.skip_lua_inc_lib_check then
@@ -250,7 +284,7 @@ local function run_build_driver(rockspec: Rockspec, no_install: boolean): boolea
250 end 284 end
251 285
252 if cfg.link_lua_explicitly then 286 if cfg.link_lua_explicitly then
253 local ok, err, errcode = deps.check_lua_libdir(rockspec.variables) 287 ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
254 if not ok then 288 if not ok then
255 return nil, err, errcode 289 return nil, err, errcode
256 end 290 end
@@ -264,7 +298,7 @@ local function run_build_driver(rockspec: Rockspec, no_install: boolean): boolea
264 return true 298 return true
265end 299end
266 300
267local install_files 301local install_files: function(Rockspec, InstallDirs): boolean, string
268do 302do
269 --- Install files to a given location. 303 --- Install files to a given location.
270 -- Takes a table where the array part is a list of filenames to be copied. 304 -- Takes a table where the array part is a list of filenames to be copied.
@@ -283,16 +317,14 @@ do
283 -- Directories are always created with the default permissions. 317 -- Directories are always created with the default permissions.
284 -- @return boolean or (nil, string): True if succeeded or 318 -- @return boolean or (nil, string): True if succeeded or
285 -- nil and an error message. 319 -- nil and an error message.
286 local function install_to(files, location, is_module_path, perms) 320 local function install_to(files: {string: string}, location: string, is_module_path: boolean, perms: string): boolean, string
287 assert(type(files) == "table" or not files)
288 assert(type(location) == "string")
289 if not files then 321 if not files then
290 return true 322 return true
291 end 323 end
292 for k, file in pairs(files) do 324 for k, file in pairs(files) do
293 local dest = location 325 local dest = location
294 local filename = dir.base_name(file) 326 local filename = dir.base_name(file)
295 if type(k) == "string" then 327 if k is string then
296 local modname = k 328 local modname = k
297 if is_module_path then 329 if is_module_path then
298 dest = dir.path(location, path.module_to_path(modname)) 330 dest = dir.path(location, path.module_to_path(modname))
@@ -320,7 +352,7 @@ do
320 return true 352 return true
321 end 353 end
322 354
323 local function install_default_docs(name, version) 355 local function install_default_docs(name: string, version: string)
324 local patterns = { "readme", "license", "copying", ".*%.md" } 356 local patterns = { "readme", "license", "copying", ".*%.md" }
325 local dest = dir.path(path.install_dir(name, version), "doc") 357 local dest = dir.path(path.install_dir(name, version), "doc")
326 local has_dir = false 358 local has_dir = false
@@ -338,12 +370,12 @@ do
338 end 370 end
339 end 371 end
340 372
341 install_files = function(rockspec, dirs) 373 install_files = function(rockspec: Rockspec, dirs: InstallDirs): boolean, string
342 local name, version = rockspec.name, rockspec.version 374 local name, version = rockspec.name, rockspec.version
343 375
344 if rockspec.build.install then 376 if rockspec.build.install then
345 for k, d in pairs(dirs) do 377 for k, d in pairs(dirs as {string: InstallDir}) do
346 local ok, err = install_to(rockspec.build.install[k], d.name, d.is_module_path, d.perms) 378 local ok, err = install_to((rockspec.build.install as {string: {string: string}})[k], d.name, d.is_module_path, d.perms)
347 if not ok then return nil, err end 379 if not ok then return nil, err end
348 end 380 end
349 end 381 end
@@ -356,7 +388,7 @@ do
356 end 388 end
357 389
358 local any_docs = false 390 local any_docs = false
359 for _, copy_dir in pairs(copy_directories) do 391 for _, copy_dir in ipairs(copy_directories) do
360 if fs.is_dir(copy_dir) then 392 if fs.is_dir(copy_dir) then
361 local dest = dir.path(path.install_dir(name, version), copy_dir) 393 local dest = dir.path(path.install_dir(name, version), copy_dir)
362 fs.make_dir(dest) 394 fs.make_dir(dest)
@@ -382,8 +414,7 @@ end
382-- @param cwd string or nil: The current working directory 414-- @param cwd string or nil: The current working directory
383-- @return (string, string) or (nil, string, [string]): Name and version of 415-- @return (string, string) or (nil, string, [string]): Name and version of
384-- installed rock if succeeded or nil and an error message followed by an error code. 416-- installed rock if succeeded or nil and an error message followed by an error code.
385function build.build_rockspec(rockspec, opts, cwd) 417function build.build_rockspec(rockspec: Rockspec, opts: Op_b, cwd: string): string, string
386 assert(rockspec:type() == "rockspec")
387 418
388 cwd = cwd or dir.path(".") 419 cwd = cwd or dir.path(".")
389 420
@@ -423,8 +454,8 @@ function build.build_rockspec(rockspec, opts, cwd)
423 return name, version 454 return name, version
424 end 455 end
425 456
426 local dirs, err 457 local dirs, err: InstallDirs, string
427 local rollback 458 local rollback: util.Fn
428 if not opts.no_install then 459 if not opts.no_install then
429 if repos.is_installed(name, version) then 460 if repos.is_installed(name, version) then
430 repo_writer.delete_version(name, version, opts.deps_mode) 461 repo_writer.delete_version(name, version, opts.deps_mode)
@@ -459,7 +490,7 @@ function build.build_rockspec(rockspec, opts, cwd)
459 ok, err = install_files(rockspec, dirs) 490 ok, err = install_files(rockspec, dirs)
460 if not ok then return nil, err end 491 if not ok then return nil, err end
461 492
462 for _, d in pairs(dirs) do 493 for _, d in pairs(dirs as {string: InstallDir}) do
463 fs.remove_dir_if_empty(d.name) 494 fs.remove_dir_if_empty(d.name)
464 end 495 end
465 496
diff --git a/src/luarocks/build/builtin.tl b/src/luarocks/build/builtin.tl
index 8bd92d87..6b244024 100644
--- a/src/luarocks/build/builtin.tl
+++ b/src/luarocks/build/builtin.tl
@@ -60,9 +60,9 @@ do
60 ["tests.lua"] = true, 60 ["tests.lua"] = true,
61 } 61 }
62 62
63 function builtin.autodetect_modules(libs: {string}, incdirs: {string}, libdirs: {string}): {string : string | Module}, BuiltinBuild.Install, {string} 63 function builtin.autodetect_modules(libs: {string}, incdirs: {string}, libdirs: {string}): {string : string | Module}, Build.Install, {string}
64 local modules: {string: (string | Module)} = {} 64 local modules: {string: (string | Module)} = {}
65 local install: BuiltinBuild.Install 65 local install: Build.Install
66 local copy_directories: {string} 66 local copy_directories: {string}
67 67
68 local prefix = "" 68 local prefix = ""
diff --git a/src/luarocks/core/types/build.d.tl b/src/luarocks/core/types/build.d.tl
index e2acdda3..b67396f7 100644
--- a/src/luarocks/core/types/build.d.tl
+++ b/src/luarocks/core/types/build.d.tl
@@ -2,10 +2,10 @@ local record build
2 2
3 interface Build 3 interface Build
4 record Install 4 record Install
5 lua: {string} 5 lua: {string: string}
6 lib: {string} 6 lib: {string: string}
7 conf: {string} 7 conf: {string: string}
8 bin: {string} 8 bin: {string: string}
9 end 9 end
10 10
11 type: string 11 type: string
diff --git a/src/luarocks/fs.d.tl b/src/luarocks/fs.d.tl
index 3e099af5..f9997db1 100644
--- a/src/luarocks/fs.d.tl
+++ b/src/luarocks/fs.d.tl
@@ -62,6 +62,7 @@ local record fs
62 -- build 62 -- build
63 apply_patch: function(string, string, boolean): boolean, string 63 apply_patch: function(string, string, boolean): boolean, string
64 copy_contents: function(string, string): boolean, string 64 copy_contents: function(string, string): boolean, string
65 remove_dir_if_empty: function(string)
65 --command 66 --command
66 execute_env: function({any: any}, string, ...:string): boolean 67 execute_env: function({any: any}, string, ...:string): boolean
67end 68end
diff --git a/src/luarocks/repo_writer.tl b/src/luarocks/repo_writer.tl
index ec99ba48..22fbbfce 100644
--- a/src/luarocks/repo_writer.tl
+++ b/src/luarocks/repo_writer.tl
@@ -32,7 +32,7 @@ function repo_writer.deploy_files(name: string, version: string, wrap_bin_script
32 return ok, err 32 return ok, err
33end 33end
34 34
35function repo_writer.delete_version(name: string, version: string, deps_mode: string, quick: boolean): boolean, string 35function repo_writer.delete_version(name: string, version: string, deps_mode: string, quick?: boolean): boolean, string
36 local ok, err, op = repos.delete_local_version(name, version, deps_mode, quick) 36 local ok, err, op = repos.delete_local_version(name, version, deps_mode, quick)
37 37
38 if op == "remove" then 38 if op == "remove" then