From ca858c676ec8147dcae59a1d383ca4bfb912aea5 Mon Sep 17 00:00:00 2001
From: V1K1NGbg <victor@ilchev.com>
Date: Thu, 22 Aug 2024 17:48:59 -0300
Subject: Teal: convert luarocks.build.builtin

---
 src/luarocks/build/builtin.lua | 395 ----------------------------------------
 src/luarocks/build/builtin.tl  | 403 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 403 insertions(+), 395 deletions(-)
 delete mode 100644 src/luarocks/build/builtin.lua
 create mode 100644 src/luarocks/build/builtin.tl

diff --git a/src/luarocks/build/builtin.lua b/src/luarocks/build/builtin.lua
deleted file mode 100644
index 4c15d2bf..00000000
--- a/src/luarocks/build/builtin.lua
+++ /dev/null
@@ -1,395 +0,0 @@
-
---- A builtin build system: back-end to provide a portable way of building C-based Lua modules.
-local builtin = {}
-
--- This build driver checks LUA_INCDIR and LUA_LIBDIR on demand,
--- so that pure-Lua rocks don't need to have development headers
--- installed.
-builtin.skip_lua_inc_lib_check = true
-
-local unpack = unpack or table.unpack
-local dir_sep = package.config:sub(1, 1)
-
-local fs = require("luarocks.fs")
-local path = require("luarocks.path")
-local util = require("luarocks.util")
-local cfg = require("luarocks.core.cfg")
-local dir = require("luarocks.dir")
-local deps = require("luarocks.deps")
-
-local function autoextract_libs(external_dependencies, variables)
-   if not external_dependencies then
-      return nil, nil, nil
-   end
-   local libs = {}
-   local incdirs = {}
-   local libdirs = {}
-   for name, data in pairs(external_dependencies) do
-      if data.library then
-         table.insert(libs, data.library)
-         table.insert(incdirs, variables[name .. "_INCDIR"])
-         table.insert(libdirs, variables[name .. "_LIBDIR"])
-      end
-   end
-   return libs, incdirs, libdirs
-end
-
-do
-   local function get_cmod_name(file)
-      local fd = io.open(dir.path(fs.current_dir(), file), "r")
-      if not fd then return nil end
-      local data = fd:read("*a")
-      fd:close()
-      return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)"))
-   end
-
-   local skiplist = {
-      ["spec"] = true,
-      [".luarocks"] = true,
-      ["lua_modules"] = true,
-      ["test.lua"] = true,
-      ["tests.lua"] = true,
-   }
-
-   function builtin.autodetect_modules(libs, incdirs, libdirs)
-      local modules = {}
-      local install
-      local copy_directories
-
-      local prefix = ""
-      for _, parent in ipairs({"src", "lua", "lib"}) do
-         if fs.is_dir(parent) then
-            fs.change_dir(parent)
-            prefix = parent .. dir_sep
-            break
-         end
-      end
-
-      for _, file in ipairs(fs.find()) do
-         local base = file:match("^([^\\/]*)")
-         if not skiplist[base] then
-            local luamod = file:match("(.*)%.lua$")
-            if luamod then
-               modules[path.path_to_module(file)] = prefix .. file
-            else
-               local cmod = file:match("(.*)%.c$")
-               if cmod then
-                  local modname = get_cmod_name(file) or path.path_to_module(file:gsub("%.c$", ".lua"))
-                  modules[modname] = {
-                     sources = prefix..file,
-                     libraries = libs,
-                     incdirs = incdirs,
-                     libdirs = libdirs,
-                  }
-               end
-            end
-         end
-      end
-
-      if prefix ~= "" then
-         fs.pop_dir()
-      end
-
-      local bindir = (fs.is_dir(dir.path("src", "bin")) and dir.path("src", "bin"))
-                  or (fs.is_dir("bin") and "bin")
-      if bindir then
-         install = { bin = {} }
-         for _, file in ipairs(fs.list_dir(bindir)) do
-            table.insert(install.bin, dir.path(bindir, file))
-         end
-      end
-
-      for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do
-         if fs.is_dir(directory) then
-            if not copy_directories then
-               copy_directories = {}
-            end
-            table.insert(copy_directories, directory)
-         end
-      end
-
-      return modules, install, copy_directories
-   end
-end
-
---- Run a command displaying its execution on standard output.
--- @return boolean: true if command succeeds (status code 0), false
--- otherwise.
-local function execute(...)
-   io.stdout:write(table.concat({...}, " ").."\n")
-   return fs.execute(...)
-end
-
---- Driver function for the builtin build back-end.
--- @param rockspec table: the loaded rockspec.
--- @return boolean or (nil, string): true if no errors occurred,
--- nil and an error message otherwise.
-function builtin.run(rockspec, no_install)
-   assert(rockspec:type() == "rockspec")
-   local compile_object, compile_library, compile_static_library
-
-   local build = rockspec.build
-   local variables = rockspec.variables
-   local checked_lua_h = false
-
-   for _, var in ipairs{ "CC", "CFLAGS", "LDFLAGS" } do
-      variables[var] = variables[var] or os.getenv(var) or ""
-   end
-
-   local function add_flags(extras, flag, flags)
-      if flags then
-         if type(flags) ~= "table" then
-            flags = { tostring(flags) }
-         end
-         util.variable_substitutions(flags, variables)
-         for _, v in ipairs(flags) do
-            table.insert(extras, flag:format(v))
-         end
-      end
-   end
-
-   if cfg.is_platform("mingw32") then
-      compile_object = function(object, source, defines, incdirs)
-         local extras = {}
-         add_flags(extras, "-D%s", defines)
-         add_flags(extras, "-I%s", incdirs)
-         return execute(variables.CC.." "..variables.CFLAGS, "-c", "-o", object, "-I"..variables.LUA_INCDIR, source, unpack(extras))
-      end
-      compile_library = function(library, objects, libraries, libdirs, name)
-         local extras = { unpack(objects) }
-         add_flags(extras, "-L%s", libdirs)
-         add_flags(extras, "-l%s", libraries)
-         extras[#extras+1] = dir.path(variables.LUA_LIBDIR, variables.LUALIB)
-
-         if variables.CC == "clang" or variables.CC == "clang-cl" then
-            local exported_name = name:gsub("%.", "_")
-            exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
-            extras[#extras+1] = string.format("-Wl,-export:luaopen_%s", exported_name)
-         else
-            extras[#extras+1] = "-l" .. (variables.MSVCRT or "m")
-         end
-
-         local ok = execute(variables.LD.." "..variables.LDFLAGS.." "..variables.LIBFLAG, "-o", library, unpack(extras))
-         return ok
-      end
-      --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
-      compile_static_library = function(library, objects, libraries, libdirs, name)
-         local ok = execute(variables.AR, "rc", library, unpack(objects))
-         if ok then
-            ok = execute(variables.RANLIB, library)
-         end
-         return ok
-      end
-      ]]
-   elseif cfg.is_platform("win32") then
-      compile_object = function(object, source, defines, incdirs)
-         local extras = {}
-         add_flags(extras, "-D%s", defines)
-         add_flags(extras, "-I%s", incdirs)
-         return execute(variables.CC.." "..variables.CFLAGS, "-c", "-Fo"..object, "-I"..variables.LUA_INCDIR, source, unpack(extras))
-      end
-      compile_library = function(library, objects, libraries, libdirs, name)
-         local extras = { unpack(objects) }
-         add_flags(extras, "-libpath:%s", libdirs)
-         add_flags(extras, "%s.lib", libraries)
-         local basename = dir.base_name(library):gsub(".[^.]*$", "")
-         local deffile = basename .. ".def"
-         local def = io.open(dir.path(fs.current_dir(), deffile), "w+")
-         local exported_name = name:gsub("%.", "_")
-         exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
-         def:write("EXPORTS\n")
-         def:write("luaopen_"..exported_name.."\n")
-         def:close()
-         local ok = execute(variables.LD, "-dll", "-def:"..deffile, "-out:"..library, dir.path(variables.LUA_LIBDIR, variables.LUALIB), unpack(extras))
-         local basedir = ""
-         if name:find("%.") ~= nil then
-            basedir = name:gsub("%.%w+$", "\\")
-            basedir = basedir:gsub("%.", "\\")
-         end
-         local manifestfile = basedir .. basename..".dll.manifest"
-
-         if ok and fs.exists(manifestfile) then
-            ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basedir..basename..".dll;2")
-         end
-         return ok
-      end
-      --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
-      compile_static_library = function(library, objects, libraries, libdirs, name)
-         local ok = execute(variables.AR, "-out:"..library, unpack(objects))
-         return ok
-      end
-      ]]
-   else
-      compile_object = function(object, source, defines, incdirs)
-         local extras = {}
-         add_flags(extras, "-D%s", defines)
-         add_flags(extras, "-I%s", incdirs)
-         return execute(variables.CC.." "..variables.CFLAGS, "-I"..variables.LUA_INCDIR, "-c", source, "-o", object, unpack(extras))
-      end
-      compile_library = function (library, objects, libraries, libdirs)
-         local extras = { unpack(objects) }
-         add_flags(extras, "-L%s", libdirs)
-         if cfg.gcc_rpath then
-            add_flags(extras, "-Wl,-rpath,%s", libdirs)
-         end
-         add_flags(extras, "-l%s", libraries)
-         if cfg.link_lua_explicitly then
-            extras[#extras+1] = "-L"..variables.LUA_LIBDIR
-            extras[#extras+1] = "-llua"
-         end
-         return execute(variables.LD.." "..variables.LDFLAGS.." "..variables.LIBFLAG, "-o", library, unpack(extras))
-      end
-      compile_static_library = function(library, objects, libraries, libdirs, name)  -- luacheck: ignore 211
-         local ok = execute(variables.AR, "rc", library, unpack(objects))
-         if ok then
-            ok = execute(variables.RANLIB, library)
-         end
-         return ok
-      end
-   end
-
-   local ok, err
-   local lua_modules = {}
-   local lib_modules = {}
-   local luadir = path.lua_dir(rockspec.name, rockspec.version)
-   local libdir = path.lib_dir(rockspec.name, rockspec.version)
-
-   local autolibs, autoincdirs, autolibdirs = autoextract_libs(rockspec.external_dependencies, rockspec.variables)
-
-   if not build.modules then
-      if rockspec:format_is_at_least("3.0") then
-         local install, copy_directories
-         build.modules, install, copy_directories = builtin.autodetect_modules(autolibs, autoincdirs, autolibdirs)
-         build.install = build.install or install
-         build.copy_directories = build.copy_directories or copy_directories
-      else
-         return nil, "Missing build.modules table"
-      end
-   end
-
-   local compile_temp_dir
-
-   local mkdir_cache = {}
-   local function cached_make_dir(name)
-      if name == "" or mkdir_cache[name] then
-         return true
-      end
-      mkdir_cache[name] = true
-      return fs.make_dir(name)
-   end
-
-   for name, info in pairs(build.modules) do
-      local moddir = path.module_to_path(name)
-      if type(info) == "string" then
-         local ext = info:match("%.([^.]+)$")
-         if ext == "lua" then
-            local filename = dir.base_name(info)
-            if filename == "init.lua" and not name:match("%.init$") then
-               moddir = path.module_to_path(name..".init")
-            else
-               local basename = name:match("([^.]+)$")
-               filename = basename..".lua"
-            end
-            local dest = dir.path(luadir, moddir, filename)
-            lua_modules[info] = dest
-         else
-            info = {info}
-         end
-      end
-      if type(info) == "table" then
-         if not checked_lua_h then
-            local ok, err, errcode = deps.check_lua_incdir(rockspec.variables)
-            if not ok then
-               return nil, err, errcode
-            end
-
-            if cfg.link_lua_explicitly then
-               local ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
-               if not ok then
-                  return nil, err, errcode
-               end
-            end
-            checked_lua_h = true
-         end
-         local objects = {}
-         local sources = info.sources
-         if info[1] then sources = info end
-         if type(sources) == "string" then sources = {sources} end
-         if type(sources) ~= "table" then
-            return nil, "error in rockspec: module '" .. name .. "' entry has no 'sources' list"
-         end
-         for _, source in ipairs(sources) do
-            if type(source) ~= "string" then
-               return nil, "error in rockspec: module '" .. name .. "' does not specify source correctly."
-            end
-            local object = source:gsub("%.[^.]*$", "."..cfg.obj_extension)
-            if not object then
-               object = source.."."..cfg.obj_extension
-            end
-            ok = compile_object(object, source, info.defines, info.incdirs or autoincdirs)
-            if not ok then
-               return nil, "Failed compiling object "..object
-            end
-            table.insert(objects, object)
-         end
-
-         if not compile_temp_dir then
-            compile_temp_dir = fs.make_temp_dir("build-" .. rockspec.package .. "-" .. rockspec.version)
-            util.schedule_function(fs.delete, compile_temp_dir)
-         end
-
-         local module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.lib_extension)
-         if moddir ~= "" then
-            module_name = dir.path(moddir, module_name)
-         end
-
-         local build_name = dir.path(compile_temp_dir, module_name)
-         local build_dir = dir.dir_name(build_name)
-         cached_make_dir(build_dir)
-
-         lib_modules[build_name] = dir.path(libdir, module_name)
-         ok = compile_library(build_name, objects, info.libraries, info.libdirs or autolibdirs, name)
-         if not ok then
-            return nil, "Failed compiling module "..module_name
-         end
-
-         -- for backwards compatibility, try keeping a copy of the module
-         -- in the old location (luasec-1.3.2-1 rockspec breaks otherwise)
-         if cached_make_dir(dir.dir_name(module_name)) then
-            fs.copy(build_name, module_name)
-         end
-
-         --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
-         module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.static_lib_extension)
-         if moddir ~= "" then
-            module_name = dir.path(moddir, module_name)
-         end
-         lib_modules[module_name] = dir.path(libdir, module_name)
-         ok = compile_static_library(module_name, objects, info.libraries, info.libdirs, name)
-         if not ok then
-            return nil, "Failed compiling static library "..module_name
-         end
-         ]]
-      end
-   end
-   if not no_install then
-      for _, mods in ipairs({{ tbl = lua_modules, perms = "read" }, { tbl = lib_modules, perms = "exec" }}) do
-         for name, dest in pairs(mods.tbl) do
-            cached_make_dir(dir.dir_name(dest))
-            ok, err = fs.copy(name, dest, mods.perms)
-            if not ok then
-               return nil, "Failed installing "..name.." in "..dest..": "..err
-            end
-         end
-      end
-      if fs.is_dir("lua") then
-         ok, err = fs.copy_contents("lua", luadir)
-         if not ok then
-            return nil, "Failed copying contents of 'lua' directory: "..err
-         end
-      end
-   end
-   return true
-end
-
-return builtin
diff --git a/src/luarocks/build/builtin.tl b/src/luarocks/build/builtin.tl
new file mode 100644
index 00000000..51d75900
--- /dev/null
+++ b/src/luarocks/build/builtin.tl
@@ -0,0 +1,403 @@
+
+--- A builtin build system: back-end to provide a portable way of building C-based Lua modules.
+local record builtin
+   skip_lua_inc_lib_check: boolean
+end
+
+local type Rockspec = require("luarocks.core.types.rockspec").Rockspec
+
+local type BuiltinBuild = require("luarocks.core.types.build").BuiltinBuild
+local type Build = require("luarocks.core.types.build").Build
+local type Module = BuiltinBuild.Module
+
+-- This build driver checks LUA_INCDIR and LUA_LIBDIR on demand,
+-- so that pure-Lua rocks don't need to have development headers
+-- installed.
+builtin.skip_lua_inc_lib_check = true
+
+local dir_sep = package.config:sub(1, 1)
+
+local fs = require("luarocks.fs")
+local path = require("luarocks.path")
+local util = require("luarocks.util")
+local cfg = require("luarocks.core.cfg")
+local dir = require("luarocks.dir")
+local deps = require("luarocks.deps")
+
+local function autoextract_libs(external_dependencies: {string: {string: string}}, variables: {string: string}): {string}, {string}, {string}
+   if not external_dependencies then
+      return nil, nil, nil
+   end
+   local libs: {string} = {}
+   local incdirs: {string} = {}
+   local libdirs: {string} = {}
+   for name, data in pairs(external_dependencies) do
+      if data.library then
+         table.insert(libs, data.library)
+         table.insert(incdirs, variables[name .. "_INCDIR"])
+         table.insert(libdirs, variables[name .. "_LIBDIR"])
+      end
+   end
+   return libs, incdirs, libdirs
+end
+
+do
+   local function get_cmod_name(file: string): string
+      local fd = io.open(dir.path(fs.current_dir(), file), "r")
+      if not fd then return nil end
+      local data = fd:read("*a")
+      fd:close()
+      return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)"))
+   end
+
+   local skiplist: {string: boolean} = {
+      ["spec"] = true,
+      [".luarocks"] = true,
+      ["lua_modules"] = true,
+      ["test.lua"] = true,
+      ["tests.lua"] = true,
+   }
+
+   function builtin.autodetect_modules(libs: {string}, incdirs: {string}, libdirs: {string}):  {string : string | Module}, Build.Install, {string}
+      local modules: {string: (string | Module)} = {}
+      local install: Build.Install
+      local copy_directories: {string}
+
+      local prefix = ""
+      for _, parent in ipairs({"src", "lua", "lib"}) do
+         if fs.is_dir(parent) then
+            fs.change_dir(parent)
+            prefix = parent .. dir_sep
+            break
+         end
+      end
+
+      for _, file in ipairs(fs.find()) do
+         local base = file:match("^([^\\/]*)")
+         if not skiplist[base] then
+            local luamod = file:match("(.*)%.lua$")
+            if luamod then
+               modules[path.path_to_module(file)] = prefix .. file
+            else
+               local cmod = file:match("(.*)%.c$")
+               if cmod then
+                  local modname = get_cmod_name(file) or path.path_to_module((file:gsub("%.c$", ".lua")))
+                  modules[modname] = {
+                     sources = prefix..file,
+                     libraries = libs,
+                     incdirs = incdirs,
+                     libdirs = libdirs,
+                  }
+               end
+            end
+         end
+      end
+
+      if prefix ~= "" then
+         fs.pop_dir()
+      end
+
+      local bindir = (fs.is_dir(dir.path("src", "bin")) and dir.path("src", "bin"))
+                  or (fs.is_dir("bin") and "bin")
+      if bindir then
+         install = { bin = {} }
+         for _, file in ipairs(fs.list_dir(bindir)) do
+            table.insert((install.bin as {string}), dir.path(bindir, file))
+         end
+      end
+
+      for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do
+         if fs.is_dir(directory) then
+            if not copy_directories then
+               copy_directories = {}
+            end
+            table.insert(copy_directories, directory)
+         end
+      end
+
+      return modules, install, copy_directories
+   end
+end
+
+--- Run a command displaying its execution on standard output.
+-- @return boolean: true if command succeeds (status code 0), false
+-- otherwise.
+local function execute(...: string): boolean, string, string
+   io.stdout:write(table.concat({...}, " ").."\n")
+   return fs.execute(...)
+end
+
+--- Driver function for the builtin build back-end.
+-- @param rockspec table: the loaded rockspec.
+-- @return boolean or (nil, string): true if no errors occurred,
+-- nil and an error message otherwise.
+function builtin.run(rockspec: Rockspec, no_install: boolean): boolean, string, string
+   local compile_object: function(string, string, {string}, {string}): boolean, string, string
+   local compile_library: function(string, {string}, {string}, {string}, string): boolean, string, string
+   local  compile_static_library: function(string, {string}, {string}, {string}, string): boolean, string, string
+
+   local build = rockspec.build as BuiltinBuild
+   local variables = rockspec.variables
+   local checked_lua_h = false
+
+   for _, var in ipairs{ "CC", "CFLAGS", "LDFLAGS" } do
+      variables[var] = variables[var] or os.getenv(var) or ""
+   end
+
+   local function add_flags(extras: {string}, flag: string, flags: {string})
+      if flags then
+         if not flags is {string} then
+            flags = { tostring(flags) }
+         end
+         util.variable_substitutions(flags, variables)
+         for _, v in ipairs(flags) do
+            table.insert(extras, flag:format(v))
+         end
+      end
+   end
+
+   if cfg.is_platform("mingw32") then
+      compile_object = function(object: string, source: string, defines: {string}, incdirs: {string}): boolean, string, string
+         local extras = {}
+         add_flags(extras, "-D%s", defines)
+         add_flags(extras, "-I%s", incdirs)
+         return execute(variables.CC.." "..variables.CFLAGS, "-c", "-o", object, "-I"..variables.LUA_INCDIR, source, table.unpack(extras))
+      end
+      compile_library = function(library: string, objects: {string}, libraries: {string}, libdirs: {string}, name: string): boolean, string, string
+         local extras = { table.unpack(objects) }
+         add_flags(extras, "-L%s", libdirs)
+         add_flags(extras, "-l%s", libraries)
+         extras[#extras+1] = dir.path(variables.LUA_LIBDIR, variables.LUALIB)
+
+         if variables.CC == "clang" or variables.CC == "clang-cl" then
+            local exported_name = name:gsub("%.", "_")
+            exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
+            extras[#extras+1] = string.format("-Wl,-export:luaopen_%s", exported_name)
+         else
+            extras[#extras+1] = "-l" .. (variables.MSVCRT or "m")
+         end
+
+         local ok = execute(variables.LD.." "..variables.LDFLAGS.." "..variables.LIBFLAG, "-o", library, table.unpack(extras))
+         return ok
+      end
+      --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
+      compile_static_library = function(library, objects, libraries, libdirs, name)
+         local ok = execute(variables.AR, "rc", library, unpack(objects))
+         if ok then
+            ok = execute(variables.RANLIB, library)
+         end
+         return ok
+      end
+      ]]
+   elseif cfg.is_platform("win32") then
+      compile_object = function(object: string, source: string, defines: {string}, incdirs: {string}): boolean, string, string
+         local extras = {}
+         add_flags(extras, "-D%s", defines)
+         add_flags(extras, "-I%s", incdirs)
+         return execute(variables.CC.." "..variables.CFLAGS, "-c", "-Fo"..object, "-I"..variables.LUA_INCDIR, source, table.unpack(extras))
+      end
+      compile_library = function(library: string, objects: {string}, libraries: {string}, libdirs: {string}, name: string): boolean, string, string
+         local extras = { table.unpack(objects) }
+         add_flags(extras, "-libpath:%s", libdirs)
+         add_flags(extras, "%s.lib", libraries)
+         local basename = dir.base_name(library):gsub(".[^.]*$", "")
+         local deffile = basename .. ".def"
+         local def = io.open(dir.path(fs.current_dir(), deffile), "w+")
+         local exported_name = name:gsub("%.", "_")
+         exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
+         def:write("EXPORTS\n")
+         def:write("luaopen_"..exported_name.."\n")
+         def:close()
+         local ok = execute(variables.LD, "-dll", "-def:"..deffile, "-out:"..library, dir.path(variables.LUA_LIBDIR, variables.LUALIB), table.unpack(extras))
+         local basedir = ""
+         if name:find("%.") then
+            basedir = name:gsub("%.%w+$", "\\")
+            basedir = basedir:gsub("%.", "\\")
+         end
+         local manifestfile = basedir .. basename..".dll.manifest"
+
+         if ok and fs.exists(manifestfile) then
+            ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basedir..basename..".dll;2")
+         end
+         return ok
+      end
+      --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
+      compile_static_library = function(library, objects, libraries, libdirs, name)
+         local ok = execute(variables.AR, "-out:"..library, unpack(objects))
+         return ok
+      end
+      ]]
+   else
+      compile_object = function(object: string, source: string, defines: {string}, incdirs: {string}): boolean, string, string
+         local extras = {}
+         add_flags(extras, "-D%s", defines)
+         add_flags(extras, "-I%s", incdirs)
+         return execute(variables.CC.." "..variables.CFLAGS, "-I"..variables.LUA_INCDIR, "-c", source, "-o", object, table.unpack(extras))
+      end
+      compile_library = function (library: string, objects: {string}, libraries: {string}, libdirs: {string}): boolean, string, string
+         local extras = { table.unpack(objects) }
+         add_flags(extras, "-L%s", libdirs)
+         if cfg.gcc_rpath then
+            add_flags(extras, "-Wl,-rpath,%s", libdirs)
+         end
+         add_flags(extras, "-l%s", libraries)
+         if cfg.link_lua_explicitly then
+            extras[#extras+1] = "-L"..variables.LUA_LIBDIR
+            extras[#extras+1] = "-llua"
+         end
+         return execute(variables.LD.." "..variables.LDFLAGS.." "..variables.LIBFLAG, "-o", library, table.unpack(extras))
+      end
+      compile_static_library = function(library: string, objects: {string}, libraries: {string}, libdirs: {string}, name: string): boolean, string, string  -- luacheck: ignore 211
+         local ok = execute(variables.AR, "rc", library, table.unpack(objects))
+         if ok then
+            ok = execute(variables.RANLIB, library)
+         end
+         return ok
+      end
+   end
+
+   local ok, err: boolean, string
+   local lua_modules = {}
+   local lib_modules = {}
+   local luadir = path.lua_dir(rockspec.name, rockspec.version)
+   local libdir = path.lib_dir(rockspec.name, rockspec.version)
+
+   local autolibs, autoincdirs, autolibdirs = autoextract_libs(rockspec.external_dependencies, rockspec.variables)
+
+   if not build.modules then
+      if rockspec:format_is_at_least("3.0") then
+         local install, copy_directories: Build.Install, {string}
+         build.modules, install, copy_directories = builtin.autodetect_modules(autolibs, autoincdirs, autolibdirs)
+         build.install = build.install or install
+         build.copy_directories = build.copy_directories or copy_directories
+      else
+         return nil, "Missing build.modules table"
+      end
+   end
+
+   local compile_temp_dir: string
+
+   local mkdir_cache = {}
+   local function cached_make_dir(name: string): boolean, string
+      if name == "" or mkdir_cache[name] then
+         return true
+      end
+      mkdir_cache[name] = true
+      return fs.make_dir(name)
+   end
+
+   for name, info in pairs(build.modules) do
+      local moddir = path.module_to_path(name)
+      if info is string then
+         local ext = info:match("%.([^.]+)$")
+         if ext == "lua" then
+            local filename = dir.base_name(info)
+            if filename == "init.lua" and not name:match("%.init$") then
+               moddir = path.module_to_path(name..".init")
+            else
+               local basename = name:match("([^.]+)$")
+               filename = basename..".lua"
+            end
+            local dest = dir.path(luadir, moddir, filename)
+            lua_modules[info] = dest
+         else
+            info = {info}
+         end
+      end
+      if info is Module then
+         if not checked_lua_h then
+            local ok, err, errcode = deps.check_lua_incdir(rockspec.variables)
+            if not ok then
+               return nil, err, errcode
+            end
+
+            if cfg.link_lua_explicitly then
+               local ok, err, errcode = deps.check_lua_libdir(rockspec.variables)
+               if not ok then
+                  return nil, err, errcode
+               end
+            end
+            checked_lua_h = true
+         end
+         local objects = {}
+         local sources = info.sources
+         if info[1] then sources = info end
+         if sources is string then sources = {sources} end
+         if not sources is {string} then
+            return nil, "error in rockspec: module '" .. name .. "' entry has no 'sources' list"
+         end
+         for _, source in ipairs(sources) do
+            if not source is string then
+               return nil, "error in rockspec: module '" .. name .. "' does not specify source correctly."
+            end
+            local object = source:gsub("%.[^.]*$", "."..cfg.obj_extension)
+            if not object then
+               object = source.."."..cfg.obj_extension
+            end
+            ok = compile_object(object, source, info.defines, info.incdirs or autoincdirs)
+            if not ok then
+               return nil, "Failed compiling object "..object
+            end
+            table.insert(objects, object)
+         end
+
+         if not compile_temp_dir then
+            compile_temp_dir = fs.make_temp_dir("build-" .. rockspec.package .. "-" .. rockspec.version)
+            util.schedule_function(fs.delete, compile_temp_dir)
+         end
+
+         local module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.lib_extension)
+         if moddir ~= "" then
+            module_name = dir.path(moddir, module_name)
+         end
+
+         local build_name = dir.path(compile_temp_dir, module_name)
+         local build_dir = dir.dir_name(build_name)
+         cached_make_dir(build_dir)
+
+         lib_modules[build_name] = dir.path(libdir, module_name)
+         ok = compile_library(build_name, objects, info.libraries as {string}, info.libdirs or autolibdirs, name)
+         if not ok then
+            return nil, "Failed compiling module "..module_name
+         end
+
+         -- for backwards compatibility, try keeping a copy of the module
+         -- in the old location (luasec-1.3.2-1 rockspec breaks otherwise)
+         if cached_make_dir(dir.dir_name(module_name)) then
+            fs.copy(build_name, module_name)
+         end
+
+         --[[ TODO disable static libs until we fix the conflict in the manifest, which will take extending the manifest format.
+         module_name = name:match("([^.]*)$").."."..util.matchquote(cfg.static_lib_extension)
+         if moddir ~= "" then
+            module_name = dir.path(moddir, module_name)
+         end
+         lib_modules[module_name] = dir.path(libdir, module_name)
+         ok = compile_static_library(module_name, objects, info.libraries, info.libdirs, name)
+         if not ok then
+            return nil, "Failed compiling static library "..module_name
+         end
+         ]]
+      end
+   end
+   if not no_install then
+      for _, mods in ipairs({{ tbl = lua_modules, perms = "read" }, { tbl = lib_modules, perms = "exec" }}) do
+         for name, dest in pairs(mods.tbl) do
+            cached_make_dir(dir.dir_name(dest))
+            ok, err = fs.copy(name, dest, mods.perms)
+            if not ok then
+               return nil, "Failed installing "..name.." in "..dest..": "..err
+            end
+         end
+      end
+      if fs.is_dir("lua") then
+         ok, err = fs.copy_contents("lua", luadir)
+         if not ok then
+            return nil, "Failed copying contents of 'lua' directory: "..err
+         end
+      end
+   end
+   return true
+end
+
+return builtin
-- 
cgit v1.2.3-55-g6feb