123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- --- Module implementing the LuaRocks "buildroot" command.
- local buildroot = {}
- local dir = require("luarocks.dir")
- local fs = require("luarocks.fs")
- local util = require("luarocks.util")
- local queries = require("luarocks.queries")
- local search = require("luarocks.search")
- local download = require("luarocks.download")
- local fetch = require("luarocks.fetch")
- function buildroot.add_to_parser(parser)
- local cmd = parser:command("buildroot", [[
- This addon generates Buildroot package files of a rock.
- First argument is the name of a rock, the second argument is optional
- and needed when Buildroot uses another name (usually prefixed by lua-).
- Files are generated with the source content of the rock and more
- especially the rockspec. So, the rock is downloaded and unpacked.
- ]], util.see_also())
- :summary("generate buildroot package files of a rock.")
- cmd:argument("rockname", "the name of a rock to be fetched and unpacked.")
- cmd:argument("brname", "the name used by Buildroot.")
- :args("?")
- end
- local function brname (name)
- return name:upper():gsub('-', '_')
- end
- local function brlicense (license)
- if license:match('MIT/X') then
- return 'MIT'
- end
- return license
- end
- local function wrap (txt, max)
- local lines = {}
- local line = ''
- for word in txt:gmatch('(%S+)') do
- if line:len() + word:len() > max - 1 then
- lines[#lines+1] = line
- line = ''
- end
- if line == '' then
- line = word
- else
- line = line .. ' ' .. word
- end
- end
- lines[#lines+1] = line
- return lines
- end
- local function has_c_files (rockspec)
- for _, mod in pairs(rockspec.build.modules or {}) do
- if type(mod) == 'string' then
- if mod:match'%.c$' then
- return true
- end
- elseif type(mod) == 'table' then
- local sources = mod.sources
- if type(sources) == 'string' and sources:match'%.c$' then
- return true
- end
- for _, src in ipairs(sources or mod) do
- if src:match'%.c$' then
- return true
- end
- end
- end
- end
- return false
- end
- local function get_main_modules (rockspec)
- local t = {}
- for name in pairs(rockspec.build.modules or {}) do
- if not name:match('%.') then
- t[#t+1] = name
- end
- end
- if #t == 0 then
- for name in pairs(rockspec.build.modules or {}) do
- t[#t+1] = name
- end
- end
- if #t == 0 then
- t[#t+1] = rockspec.package:gsub('%-', '')
- end
- table.sort(t)
- return t
- end
- local function get_external_dependencies (rockspec)
- local t = {}
- for k in pairs(rockspec.external_dependencies or {}) do
- k = k:lower()
- if fs.is_dir('package/' .. k) then
- t[#t+1] = k
- else
- t[#t+1] = 'lib' .. k
- if not fs.is_dir('package/lib' .. k) then
- util.printout('unkwown external dependency: ' .. k)
- end
- end
- end
- table.sort(t)
- return t
- end
- local function get_dependencies (rockspec)
- local t = {}
- for i = 1, #rockspec.dependencies do
- local dep = tostring(rockspec.dependencies[i]):match('^(%S+)')
- if dep ~= 'lua' then
- dep = dep:gsub('_', '-')
- if fs.is_dir('package/lua-' .. dep) then
- t[#t+1] = 'lua-' .. dep
- else
- t[#t+1] = dep
- if not fs.is_dir('package/' .. dep) then
- util.printout('unkwown dependency: ' .. dep)
- end
- end
- end
- end
- table.sort(t)
- return t
- end
- function get_digest (file)
- local absname = fs.absolute_name(file)
- local pipe = io.popen('sha256sum ' .. fs.Q(absname))
- local line = pipe:read('*l')
- pipe:close()
- local computed = line and line:match('(' .. ('%x'):rep(64) .. ')')
- if computed then
- return computed
- else
- return nil, "Failed to compute SHA256 hash for file " .. absname
- end
- end
- local function generate_config (rockspec, lcname)
- local ucname = brname(lcname)
- local only_luajit = rockspec.package:match('^lj')
- local summary = rockspec.description.summary
- if not summary then
- summary = '???'
- elseif not summary:match('%.%s*$') then
- summary = summary:gsub('%s*$', '.')
- end
- local homepage = rockspec.description.homepage or '???'
- local external_dependencies = get_external_dependencies(rockspec)
- local dependencies = get_dependencies(rockspec)
- local fname = 'package/' .. lcname .. '/Config.in'
- local f = assert(io.open(fname, 'w'))
- util.printout('write ' .. fname)
- f:write('config BR2_PACKAGE_' .. ucname .. '\n')
- f:write('\tbool "' .. lcname .. '"\n')
- if only_luajit then
- f:write('\tdepends on BR2_PACKAGE_LUAJIT\n')
- end
- for i = 1, #external_dependencies do
- f:write('\tselect BR2_PACKAGE_' .. brname(external_dependencies[i]) .. '\n')
- end
- for i = 1, #dependencies do
- f:write('\tselect BR2_PACKAGE_' .. brname(dependencies[i]) .. ' # runtime\n')
- end
- f:write('\thelp\n')
- f:write('\t ' .. table.concat(wrap(summary, 62), '\n\t ') .. '\n')
- f:write('\n\t ' .. homepage .. '\n')
- if only_luajit then
- f:write('\ncomment "' .. lcname .. ' needs LuaJIT"\n')
- f:write('\tdepends on !BR2_PACKAGE_LUAJIT\n')
- end
- f:close()
- end
- local function generate_mk (rockspec, lcname, licenses)
- local function escape (s)
- return s:gsub('-', '%%-'):gsub('%.', '%%.')
- end
- local ucname = brname(lcname)
- local need_name_upstream = false
- local need_version_upstream = false
- local name_upstream = rockspec.package
- local version = rockspec.version
- local version_upstream = version:match('^([^-]+)-')
- local revision = version:match('-(%d+)$')
- local license = rockspec.description.license
- local subdir = rockspec.source.dir
- if subdir then
- local root = subdir:match('^(.-)-' .. escape(version) .. '$')
- if root then
- subdir = root .. '-$(' .. ucname .. '_VERSION)'
- end
- root = subdir:match('^(.--[Vv])' .. escape(version_upstream) .. '$')
- if root then
- need_version_upstream = true
- subdir = root .. '$(' .. ucname .. '_VERSION_UPSTREAM)'
- end
- root = subdir:match('^(.-)-' .. escape(version_upstream) .. '$')
- if root then
- if root == lcname then
- subdir = nil
- elseif root == name_upstream then
- subdir = nil
- need_name_upstream = true
- else
- need_version_upstream = true
- subdir = root .. '-$(' .. ucname .. '_VERSION_UPSTREAM)'
- end
- end
- end
- local external_dependencies = get_external_dependencies(rockspec)
- local fname = 'package/' .. lcname .. '/' .. lcname .. '.mk'
- local f = assert(io.open(fname, 'w'))
- util.printout('write ' .. fname)
- f:write('################################################################################\n')
- f:write('#\n')
- f:write('# ' .. lcname .. '\n')
- f:write('#\n')
- f:write('################################################################################\n')
- f:write('\n')
- if need_version_upstream then
- f:write(ucname .. '_VERSION_UPSTREAM = ' .. version_upstream .. '\n')
- f:write(ucname .. '_VERSION = $(' .. ucname .. '_VERSION_UPSTREAM)-' .. revision .. '\n')
- else
- f:write(ucname .. '_VERSION = ' .. version .. '\n')
- end
- if lcname ~= name_upstream:lower() or need_name_upstream then
- f:write(ucname .. '_NAME_UPSTREAM = ' .. name_upstream .. '\n')
- end
- if subdir then
- f:write(ucname .. '_SUBDIR = ' .. subdir .. '\n')
- end
- if license then
- f:write(ucname .. '_LICENSE = ' .. brlicense(license) .. '\n')
- end
- if #licenses == 1 then
- f:write(ucname .. '_LICENSE_FILES = $(' .. ucname .. '_SUBDIR)/' .. licenses[1] .. '\n')
- elseif #licenses > 1 then
- f:write(ucname .. '_LICENSE_FILES =')
- for i = 1, #licenses do
- local file = licenses[i]
- f:write(' \\\n\t$(' .. ucname .. '_SUBDIR)/' .. file)
- end
- f:write('\n')
- end
- if #external_dependencies > 0 then
- f:write(ucname .. '_DEPENDENCIES = ' .. table.concat(external_dependencies, ' ') .. '\n')
- end
- f:write('\n$(eval $(luarocks-package))\n')
- f:close()
- end
- local function generate_hash (rockspec, lcname, rock_file, licenses, digest)
- local subdir = rockspec.source.dir
- local fname = 'package/' .. lcname .. '/' .. lcname .. '.hash'
- local f = assert(io.open(fname, 'w'))
- util.printout('write ' .. fname)
- f:write('# computed by luarocks/buildroot\n')
- f:write('sha256 ' .. digest[rock_file] .. ' ' .. rock_file .. '\n')
- for i = 1, #licenses do
- local file = licenses[i]
- f:write('sha256 ' .. digest[file] .. ' ' .. subdir .. '/' .. file .. '\n')
- end
- f:close()
- end
- local function generate_test (rockspec, lcname)
- local ucname = brname(lcname)
- local classname = rockspec.package:gsub('%-', ''):gsub('%.', '')
- classname = classname:sub(1, 1):upper() .. classname:sub(2)
- local modnames = get_main_modules(rockspec)
- local fname = 'support/testing/tests/package/test_' .. ucname:lower() .. '.py'
- local f = assert(io.open(fname, 'w'))
- util.printout('write ' .. fname)
- f:write('from tests.package.test_lua import TestLuaBase\n')
- f:write('\n')
- f:write('\n')
- f:write('class TestLua' .. classname .. '(TestLuaBase):\n')
- f:write(' config = TestLuaBase.config + \\\n')
- f:write(' """\n')
- f:write(' BR2_PACKAGE_LUA=y\n')
- f:write(' BR2_PACKAGE_' .. ucname .. '=y\n')
- f:write(' """\n')
- f:write('\n')
- f:write(' def test_run(self):\n')
- f:write(' self.login()\n')
- for i = 1, #modnames do
- f:write(' self.module_test("' .. modnames[i] .. '")\n')
- end
- f:write('\n')
- f:write('\n')
- f:write('class TestLuajit' .. classname .. '(TestLuaBase):\n')
- f:write(' config = TestLuaBase.config + \\\n')
- f:write(' """\n')
- f:write(' BR2_PACKAGE_LUAJIT=y\n')
- f:write(' BR2_PACKAGE_' .. ucname .. '=y\n')
- f:write(' """\n')
- f:write('\n')
- f:write(' def test_run(self):\n')
- f:write(' self.login()\n')
- for i = 1, #modnames do
- f:write(' self.module_test("' .. modnames[i] .. '")\n')
- end
- f:close()
- end
- --- Driver function for the "buildroot" command.
- -- @return boolean: true if successful
- function buildroot.command(args)
- local rockname = assert(args.rockname)
- local fsname = args.brname or rockname
- local query = queries.new(rockname:lower(), nil, nil, false, 'src')
- local url, err = search.find_suitable_rock(query)
- if not url then
- return nil, "Could not find a result named " .. tostring(query) .. ": " .. err
- end
- local rock_file = dir.base_name(url)
- local temp_dir, err = fs.make_temp_dir(rockname)
- if not temp_dir then
- return nil, "Failed creating temporary dir: " .. err
- end
- local ok, err = fs.change_dir(temp_dir)
- if not ok then return nil, err end
- ok = fs.download(url, rock_file, true)
- if not ok then
- return nil, "Failed downloading " .. url
- end
- local digest = {}
- digest[rock_file], err = get_digest(rock_file)
- if not digest[rock_file] then return nil, err end
- ok, err = fs.unzip(rock_file)
- if not ok then return nil, err end
- local rockspec_file = rock_file:gsub('%.src%.rock$', '.rockspec')
- local rockspec, err = fetch.load_rockspec(rockspec_file)
- if not rockspec then
- return nil, "Error loading rockspec: " .. err
- end
- if rockspec.source.file then
- ok, err = fs.unpack_archive(rockspec.source.file)
- if not ok then return nil, err end
- end
- if rockspec.source.dir ~= '.' then
- fs.copy(rockspec.local_abs_filename, rockspec.source.dir, 'read')
- end
- local build_type = rockspec.build.type
- if build_type ~= 'none' and build_type ~= 'builtin' and build_type ~= 'module' then
- util.printout('[' .. rockspec.package .. "] build_type '" .. build_type .. "' not supported")
- end
- local licenses = {}
- ok, err = fs.change_dir(rockspec.source.dir)
- if not ok then return nil, err end
- local files = fs.find()
- for i = 1, #files do
- local v = files[i]
- if v == 'COPYING'
- or v == 'COPYRIGHT'
- or v:match('^LICENSE') then
- licenses[#licenses+1] = v
- digest[v], err = get_digest(v)
- if not digest[v] then return nil, err end
- end
- end
- if #licenses == 0 then
- for i = 1, #files do
- local v = files[i]
- if v:match('^doc/LICENSE')
- or v:match('^doc/license')
- or v:match('^doc/us/license') then
- licenses[#licenses+1] = v
- digest[v], err = get_digest(v)
- if not digest[v] then return nil, err end
- end
- end
- end
- fs.pop_dir()
- table.sort(licenses)
- fs.pop_dir()
- ok, err = fs.make_dir('package/' .. fsname:lower())
- if not ok then return nil, err end
- generate_config(rockspec, fsname:lower())
- generate_mk(rockspec, fsname:lower(), licenses)
- generate_hash(rockspec, fsname:lower(), rock_file, licenses, digest)
- if has_c_files(rockspec) then
- ok, err = fs.make_dir('support/testing/tests/package')
- if not ok then return nil, err end
- generate_test(rockspec, fsname:lower())
- end
- return true
- end
- return buildroot
|