mirror of https://git.wownero.com/dsc/mxe.git
add tool build-pkg.lua
This tool builds all packages for all targets and generates *.tar.xz and *.deb files. The latter ones can be uploaded to APT repository and installed to Debian machines. build-pkg.lua was previously known as mxedeb.lua See also: http://mxe.redjohn.tk/ https://github.com/starius/mxedeb https://lists.nongnu.org/archive/html/mingw-cross-env-list/2015-02/msg00010.html https://lists.nongnu.org/archive/html/mingw-cross-env-list/2015-03/msg00001.html
This commit is contained in:
parent
5080308e17
commit
effa84377c
|
@ -0,0 +1,356 @@
|
|||
#!/usr/bin/env lua
|
||||
|
||||
-- This file is part of MXE.
|
||||
-- See index.html for further information.
|
||||
|
||||
-- mxedeb, Build DEB packages from MXE packages
|
||||
-- Instructions: http://mxe.redjohn.tk
|
||||
|
||||
-- Requirements: MXE, lua, tsort, fakeroot, dpkg-deb.
|
||||
-- Usage: lua tools/build-pkg.lua
|
||||
-- Packages are written to `*.tar.xz` files.
|
||||
-- Debian packages are written to `*.deb` files.
|
||||
|
||||
local max_packages = tonumber(os.getenv('MXE_MAX_PACKAGES'))
|
||||
|
||||
local MXE_DIR = '/usr/lib/mxe'
|
||||
|
||||
local target -- used by many functions
|
||||
|
||||
-- based on http://lua-users.org/wiki/SplitJoin
|
||||
local function split(self, sep, nMax, plain)
|
||||
if not sep then
|
||||
sep = '%s+'
|
||||
end
|
||||
assert(sep ~= '')
|
||||
assert(nMax == nil or nMax >= 1)
|
||||
local aRecord = {}
|
||||
if self:len() > 0 then
|
||||
nMax = nMax or -1
|
||||
local nField = 1
|
||||
local nStart = 1
|
||||
local nFirst, nLast = self:find(sep, nStart, plain)
|
||||
while nFirst and nMax ~= 0 do
|
||||
aRecord[nField] = self:sub(nStart, nFirst - 1)
|
||||
nField = nField + 1
|
||||
nStart = nLast + 1
|
||||
nFirst, nLast = self:find(sep, nStart, plain)
|
||||
nMax = nMax - 1
|
||||
end
|
||||
aRecord[nField] = self:sub(nStart)
|
||||
end
|
||||
return aRecord
|
||||
end
|
||||
|
||||
local function trim(str)
|
||||
local text = str:gsub("%s+$", "")
|
||||
text = text:gsub("^%s+", "")
|
||||
return text
|
||||
end
|
||||
|
||||
local function shell(cmd)
|
||||
local f = io.popen(cmd, 'r')
|
||||
local text = f:read('*all')
|
||||
f:close()
|
||||
return text
|
||||
end
|
||||
|
||||
-- return several tables describing packages
|
||||
-- * list of packages
|
||||
-- * map from package to list of deps
|
||||
-- * map from package to version of package
|
||||
local function getPkgs()
|
||||
-- create file deps.mk showing deps
|
||||
-- (make show-upstream-deps-% does not present in
|
||||
-- stable MXE)
|
||||
local deps_mk_content = [[
|
||||
include Makefile
|
||||
NOTHING:=
|
||||
SPACE:=$(NOTHING) $(NOTHING)
|
||||
NAME_WITH_UNDERSCORES:=$(subst $(SPACE),_,$(NAME))
|
||||
print-deps:
|
||||
@$(foreach pkg,$(PKGS),echo \
|
||||
for-mxedeb $(pkg) \
|
||||
$(subst $(SPACE),-,$($(pkg)_VERSION)) \
|
||||
$($(pkg)_DEPS);)]]
|
||||
local deps_mk_file = io.open('deps.mk', 'w')
|
||||
deps_mk_file:write(deps_mk_content)
|
||||
deps_mk_file:close()
|
||||
local pkgs = {}
|
||||
local pkg2deps = {}
|
||||
local pkg2ver = {}
|
||||
local cmd = 'make -f deps.mk print-deps MXE_TARGETS=%s'
|
||||
cmd = cmd:format(target)
|
||||
local make = io.popen(cmd)
|
||||
for line in make:lines() do
|
||||
local deps = split(trim(line))
|
||||
if deps[1] == 'for-mxedeb' then
|
||||
-- first value is marker 'for-mxedeb'
|
||||
table.remove(deps, 1)
|
||||
-- first value is name of package which depends on
|
||||
local pkg = table.remove(deps, 1)
|
||||
-- second value is version of package
|
||||
local ver = table.remove(deps, 1)
|
||||
table.insert(pkgs, pkg)
|
||||
pkg2deps[pkg] = deps
|
||||
pkg2ver[pkg] = ver
|
||||
end
|
||||
end
|
||||
make:close()
|
||||
os.remove('deps.mk')
|
||||
return pkgs, pkg2deps, pkg2ver
|
||||
end
|
||||
|
||||
-- return packages ordered in build order
|
||||
-- this means, if pkg1 depends on pkg2, then
|
||||
-- pkg2 preceeds pkg1 in the list
|
||||
local function sortForBuild(pkgs, pkg2deps)
|
||||
-- use sommand tsort
|
||||
local tsort_input_fname = os.tmpname()
|
||||
local tsort_input = io.open(tsort_input_fname, 'w')
|
||||
for _, pkg1 in ipairs(pkgs) do
|
||||
for _, pkg2 in ipairs(pkg2deps[pkg1]) do
|
||||
tsort_input:write(pkg2 .. ' ' .. pkg1 .. '\n')
|
||||
end
|
||||
end
|
||||
tsort_input:close()
|
||||
--
|
||||
local build_list = {}
|
||||
local tsort = io.popen('tsort ' .. tsort_input_fname, 'r')
|
||||
for line in tsort:lines() do
|
||||
local pkg = trim(line)
|
||||
table.insert(build_list, pkg)
|
||||
end
|
||||
tsort:close()
|
||||
os.remove(tsort_input_fname)
|
||||
return build_list
|
||||
end
|
||||
|
||||
-- return set of all filepaths under ./usr/
|
||||
local function findFiles()
|
||||
local files = {}
|
||||
local find = io.popen('find usr -type f -or -type l', 'r')
|
||||
for line in find:lines() do
|
||||
local file = trim(line)
|
||||
files[file] = true
|
||||
end
|
||||
find:close()
|
||||
return files
|
||||
end
|
||||
|
||||
-- builds package, returns list of new files
|
||||
local function buildPackage(pkg)
|
||||
local files_before = findFiles()
|
||||
local cmd = 'make %s MXE_TARGETS=%s --jobs=1'
|
||||
os.execute(cmd:format(pkg, target))
|
||||
local files_after = findFiles()
|
||||
local new_files = {}
|
||||
for file in pairs(files_after) do
|
||||
if not files_before[file] then
|
||||
table.insert(new_files, file)
|
||||
end
|
||||
end
|
||||
return new_files
|
||||
end
|
||||
|
||||
local function nameToDebian(pkg)
|
||||
local name = 'mxe-%s-%s'
|
||||
name = name:format(target, pkg)
|
||||
name = name:gsub('_', '-')
|
||||
return name
|
||||
end
|
||||
|
||||
local function protectVersion(ver)
|
||||
ver = ver:gsub('_', '-')
|
||||
if ver:sub(1, 1):match('%d') then
|
||||
return ver
|
||||
else
|
||||
-- version number does not start with digit
|
||||
return '0.' .. ver
|
||||
end
|
||||
end
|
||||
|
||||
local CONTROL = [[Package: %s
|
||||
Version: %s
|
||||
Section: devel
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: %s
|
||||
Maintainer: Boris Nagaev <bnagaev@gmail.com>
|
||||
Homepage: http://mxe.cc
|
||||
Description: MXE package %s for %s
|
||||
MXE (M cross environment) is a Makefile that compiles
|
||||
a cross compiler and cross compiles many free libraries
|
||||
such as SDL and Qt for various target platforms (MinGW).
|
||||
.
|
||||
This package contains the files for MXE package %s.
|
||||
]]
|
||||
|
||||
local function makeDeb(pkg, list_path, deps, ver)
|
||||
local deb_pkg = nameToDebian(pkg)
|
||||
local dirname = ('%s_%s'):format(deb_pkg,
|
||||
protectVersion(ver))
|
||||
-- make .tar.xz file
|
||||
local tar_name = dirname .. '.tar.xz'
|
||||
local cmd = 'tar -T %s --owner=0 --group=0 -cJf %s'
|
||||
os.execute(cmd:format(list_path, tar_name))
|
||||
-- unpack .tar.xz to the path for Debian
|
||||
local usr = dirname .. MXE_DIR
|
||||
os.execute(('mkdir -p %s'):format(usr))
|
||||
-- use tar to copy files with paths
|
||||
local cmd = 'fakeroot -s deb.fakeroot tar -C %s -xf %s'
|
||||
os.execute(cmd:format(usr, tar_name))
|
||||
-- prepare dependencies
|
||||
local deb_deps = {'mxe-requirements'}
|
||||
for _, dep in ipairs(deps) do
|
||||
table.insert(deb_deps, nameToDebian(dep))
|
||||
end
|
||||
local deb_deps_str = table.concat(deb_deps, ', ')
|
||||
-- make DEBIAN/control file
|
||||
os.execute(('mkdir -p %s/DEBIAN'):format(dirname))
|
||||
local control_fname = dirname .. '/DEBIAN/control'
|
||||
local control = io.open(control_fname, 'w')
|
||||
control:write(CONTROL:format(deb_pkg, protectVersion(ver),
|
||||
deb_deps_str, pkg, target, pkg))
|
||||
control:close()
|
||||
-- make .deb file
|
||||
local cmd = 'fakeroot -i deb.fakeroot dpkg-deb -b %s'
|
||||
os.execute(cmd:format(dirname))
|
||||
-- cleanup
|
||||
os.execute(('rm -fr %s deb.fakeroot'):format(dirname))
|
||||
end
|
||||
|
||||
local function saveFileList(pkg, list)
|
||||
local list_file = pkg .. '.list'
|
||||
local file = io.open(list_file, 'w')
|
||||
for _, installed_file in ipairs(list) do
|
||||
file:write(installed_file .. '\n')
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
|
||||
-- build all packages, save filelist to file #pkg.list
|
||||
local function buildPackages(pkgs, pkg2deps)
|
||||
local broken = {}
|
||||
local unbroken = {}
|
||||
local function brokenDep(pkg)
|
||||
for _, dep in ipairs(pkg2deps[pkg]) do
|
||||
if broken[dep] then
|
||||
return dep
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
for _, pkg in ipairs(pkgs) do
|
||||
if not brokenDep(pkg) then
|
||||
local files = buildPackage(pkg)
|
||||
if #files > 0 then
|
||||
saveFileList(pkg, files)
|
||||
table.insert(unbroken, pkg)
|
||||
else
|
||||
-- broken package
|
||||
broken[pkg] = true
|
||||
print('The package is broken: ' .. pkg)
|
||||
end
|
||||
else
|
||||
local msg = 'Package %s depends on broken %s'
|
||||
print(msg:format(pkg, brokenDep(pkg)))
|
||||
end
|
||||
end
|
||||
return unbroken
|
||||
end
|
||||
|
||||
local function makeDebs(pkgs, pkg2deps, pkg2ver)
|
||||
for _, pkg in ipairs(pkgs) do
|
||||
local deps = assert(pkg2deps[pkg], pkg)
|
||||
local ver = assert(pkg2ver[pkg], pkg)
|
||||
makeDeb(pkg, pkg .. '.list', deps, ver)
|
||||
end
|
||||
end
|
||||
|
||||
local function clean()
|
||||
local cmd = 'make clean MXE_TARGETS=%s'
|
||||
os.execute(cmd:format(target))
|
||||
end
|
||||
|
||||
local function buildForTarget(mxe_target)
|
||||
target = mxe_target
|
||||
local pkgs, pkg2deps, pkg2ver = getPkgs()
|
||||
local build_list = sortForBuild(pkgs, pkg2deps)
|
||||
if max_packages then
|
||||
while #build_list > max_packages do
|
||||
table.remove(build_list)
|
||||
end
|
||||
end
|
||||
clean()
|
||||
local unbroken = buildPackages(build_list, pkg2deps)
|
||||
makeDebs(unbroken, pkg2deps, pkg2ver)
|
||||
clean()
|
||||
end
|
||||
|
||||
local function getMxeVersion()
|
||||
local index_html = io.open 'index.html'
|
||||
local text = index_html:read('*all')
|
||||
index_html:close()
|
||||
return text:match('Release ([^<]+)')
|
||||
end
|
||||
|
||||
local MXE_REQUIREMENTS_CONTROL = [[Package: %s
|
||||
Version: %s
|
||||
Section: devel
|
||||
Priority: optional
|
||||
Architecture: %s
|
||||
Depends: %s
|
||||
Maintainer: Boris Nagaev <bnagaev@gmail.com>
|
||||
Homepage: http://mxe.cc
|
||||
Description: MXE requirements package
|
||||
MXE (M cross environment) is a Makefile that compiles
|
||||
a cross compiler and cross compiles many free libraries
|
||||
such as SDL and Qt for various target platforms (MinGW).
|
||||
.
|
||||
This package depends on all Debian dependencies of MXE.
|
||||
Other MXE packages depend on this package.
|
||||
]]
|
||||
|
||||
local function makeMxeRequirementsDeb(arch)
|
||||
local name = 'mxe-requirements'
|
||||
local ver = getMxeVersion()
|
||||
-- dependencies
|
||||
local deps = {
|
||||
'autoconf', 'automake', 'autopoint', 'bash', 'bison',
|
||||
'bzip2', 'cmake', 'flex', 'gettext', 'git', 'g++',
|
||||
'gperf', 'intltool', 'libffi-dev', 'libtool',
|
||||
'libltdl-dev', 'libssl-dev', 'libxml-parser-perl',
|
||||
'make', 'openssl', 'patch', 'perl', 'pkg-config',
|
||||
'python', 'ruby', 'scons', 'sed', 'unzip', 'wget',
|
||||
'xz-utils',
|
||||
}
|
||||
if arch == 'amd64' then
|
||||
table.insert(deps, 'g++-multilib')
|
||||
table.insert(deps, 'libc6-dev-i386')
|
||||
end
|
||||
local deps_str = table.concat(deps, ', ')
|
||||
-- directory
|
||||
local dirname = ('%s_%s_%s'):format(name, ver, arch)
|
||||
-- make DEBIAN/control file
|
||||
os.execute(('mkdir -p %s/DEBIAN'):format(dirname))
|
||||
local control_fname = dirname .. '/DEBIAN/control'
|
||||
local control = io.open(control_fname, 'w')
|
||||
control:write(MXE_REQUIREMENTS_CONTROL:format(name,
|
||||
ver, arch, deps_str))
|
||||
control:close()
|
||||
-- make .deb file
|
||||
local cmd = 'fakeroot -i deb.fakeroot dpkg-deb -b %s'
|
||||
os.execute(cmd:format(dirname))
|
||||
-- cleanup
|
||||
os.execute(('rm -fr %s deb.fakeroot'):format(dirname))
|
||||
end
|
||||
|
||||
assert(trim(shell('pwd')) == MXE_DIR,
|
||||
"Clone MXE to " .. MXE_DIR)
|
||||
buildForTarget('i686-w64-mingw32.static')
|
||||
buildForTarget('x86_64-w64-mingw32.static')
|
||||
buildForTarget('i686-w64-mingw32.shared')
|
||||
buildForTarget('x86_64-w64-mingw32.shared')
|
||||
makeMxeRequirementsDeb('i386')
|
||||
makeMxeRequirementsDeb('amd64')
|
Loading…
Reference in New Issue