--- A builtin build system: back-end to provide a portable way of building C-based Lua modules. module("luarocks.build.builtin", package.seeall) local fs = require("luarocks.fs") local path = require("luarocks.path") local util = require("luarocks.util") local cfg = require("luarocks.cfg") --- Check if platform was detected -- @param query string: The platform name to check. -- @return boolean: true if LuaRocks is currently running on queried platform. local function is_platform(query) assert(type(query) == "string") for _, platform in ipairs(cfg.platforms) do if platform == query then return true end 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 ocurred, -- nil and an error message otherwise. function run(rockspec) assert(type(rockspec) == "table") local compile_object, compile_library local build = rockspec.build local variables = rockspec.variables 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 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 = fs.base_name(library):gsub(".[^.]*$", "") local deffile = basename .. ".def" local def = io.open(fs.make_path(fs.current_dir(), deffile), "w+") def:write("EXPORTS\n") def:write("luaopen_"..name:gsub("%.", "_").."\n") def:close() local ok = execute(variables.LD, "-dll", "-def:"..deffile, "-out:"..library, fs.make_path(variables.LUA_LIBDIR, "lua5.1.lib"), unpack(extras)) local manifestfile = basename..".dll.manifest" if ok and fs.exists(manifestfile) then ok = execute(variables.MT, "-manifest", manifestfile, "-outputresource:"..basename..".dll;2") end 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) add_flags(extras, "-l%s", libraries) if is_platform("cygwin") then add_flags(extras, "-l%s", {"lua"}) end return execute(variables.LD.." "..variables.LIBFLAG, "-o", library, unpack(extras)) end end local ok = true local built_modules = {} local luadir = path.lua_dir(rockspec.name, rockspec.version) local libdir = path.lib_dir(rockspec.name, rockspec.version) local docdir = path.doc_dir(rockspec.name, rockspec.version) 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 dest = fs.make_path(luadir, moddir) built_modules[info] = dest else info = {info} end end if type(info) == "table" then local objects = {} local sources = info.sources if info[1] then sources = info end if type(sources) == "string" then sources = {sources} end for _, source in ipairs(sources) do 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) if not ok then break end table.insert(objects, object) end if not ok then break end local module_name = fs.make_path(moddir, name:match("([^.]*)$").."."..cfg.lib_extension):gsub("//", "/") if moddir ~= "" then fs.make_dir(moddir) end local dest = fs.make_path(libdir, moddir) built_modules[module_name] = dest ok = compile_library(module_name, objects, info.libraries, info.libdirs, name) if not ok then break end end end for name, dest in pairs(built_modules) do fs.make_dir(dest) ok = fs.copy(name, dest) if not ok then break end end if ok then if fs.is_dir("lua") then fs.copy_contents("lua", luadir) end end if ok then return true else return nil, "Build error" end end