diff options
author | Volker Grabsch <v@njh.eu> | 2015-06-28 09:50:01 (GMT) |
---|---|---|
committer | Volker Grabsch <v@njh.eu> | 2015-06-28 09:50:01 (GMT) |
commit | f71a35ec3d4179c809e9ad6ea465c060b4d6a110 (patch) | |
tree | 53540978e927a56d2b0e90fb662e3bdc161b6eaa | |
parent | f91180675b067a45ccbfeaa45ce413da88054138 (diff) | |
parent | effa84377cf326bc3eaa3c2ba2bc2fca5736f15d (diff) | |
download | mxe-f71a35ec3d4179c809e9ad6ea465c060b4d6a110.zip mxe-f71a35ec3d4179c809e9ad6ea465c060b4d6a110.tar.gz mxe-f71a35ec3d4179c809e9ad6ea465c060b4d6a110.tar.bz2 |
Merge pull request #737 from LuaAndC/master
add tool build-pkg.lua
-rwxr-xr-x | tools/build-pkg.lua | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/tools/build-pkg.lua b/tools/build-pkg.lua new file mode 100755 index 0000000..a2b4bb8 --- /dev/null +++ b/tools/build-pkg.lua @@ -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') |