1166 lines · 35.2 KB · Lua
RawDownload
#!/usr/bin/env lua --[[This file is part of MXE. See LICENSE.md for licensing information. build-pkg, Build binary packages from MXE packagesInstructions: https://pkg.mxe.cc/ Requirements (see bootstrapped build below for non-debian systems):    MXE (https://mxe.cc/#requirements-debian)    apt-get install lua5.1 fakeroot dpkg dpkg-devUsage: lua tools/build-pkg.luaPackages are written to `<codename>/*.tar.xz` files.Debian packages are written to `<codename>/*.deb` files. Build in directory /usr/lib/mxeThis directory can not be changed in .deb packages. To prevent build-pkg from creating deb packages,set environment variable MXE_BUILD_PKG_NO_DEBS to 1In this case fakeroot and dpkg-deb are not needed. To do a dry run without actually building any packages,set environment variable MXE_BUILD_DRY_RUN to any valuePackages will be downloaded, but builds will be skipped. To override the codename detection of `lsb_release -sc`, setMXE_BUILD_PKG_CODENAME. This sets the output directory and namemangling for the pool directory in the apt repo. Could be used tocreate lowest-common-glibc based versions. Set MXE_BUILD_PKG_VERSION_ID to add a git tag to each pkg versionstring - defaults to YYYYMMDD build datestamp. To switch off the second pass, setMXE_BUILD_PKG_NO_SECOND_PASS to 1.See https://github.com/mxe/mxe/issues/1111 To limit number of packages being built to x,set environment variable MXE_BUILD_PKG_MAX_ITEMS to x. To set list of MXE targets to build,set environment variable MXE_BUILD_PKG_TARGETS tothe list of targets separated by space.By default, all 4 major targets are built. To set list of MXE packages to build,set environment variable MXE_BUILD_PKG_PKGS tothe list of packages separated by space. This is similarto a normal `make` invocation in that all dependencieswill be built, so list just the packages you require.By default, all packages are built. The following error:> fakeroot, while creating message channels: Invalid argument> This may be due to a lack of SYSV IPC support.> fakeroot: error while starting the `faked' daemon.can be caused by leaked ipc resources originating in fakeroot.How to remove them: https://stackoverflow.com/a/4262545Alternatively, to switch off using fakeroot (e.g. inside docker),set MXE_BUILD_PKG_NO_FAKEROOT to 1. Bootstrapped build (non-debian systems building withoutdeb pkgs to test print-deps-for-build-pkg, control files etc.): export MXE_DIR=`pwd` && \export BUILD=`$MXE_DIR/ext/config.guess` && \rm -rf $MXE_DIR/usr $MXE_DIR/log $MXE_DIR/mxe-* && \make -C $MXE_DIR lua \    MXE_TARGETS=$BUILD \    lua_TARGETS=$BUILD \    MXE_PREFIX=$MXE_DIR/usr.lua && \MXE_BUILD_PKG_CODENAME=focal \MXE_BUILD_PKG_TARGETS="i686-w64-mingw32.static" \MXE_BUILD_PKG_PKGS=qt5 \MXE_BUILD_DRY_RUN=1 \MXE_BUILD_PKG_MAX_ITEMS= \MXE_BUILD_PKG_NO_DEBS=1 \MXE_BUILD_PKG_NO_SECOND_PASS=1 \$MXE_DIR/usr.lua/$BUILD/bin/lua $MXE_DIR/tools/build-pkg.lua]] local max_items = tonumber(os.getenv('MXE_BUILD_PKG_MAX_ITEMS'))local no_debs = os.getenv('MXE_BUILD_PKG_NO_DEBS')local no_fakeroot = os.getenv('MXE_BUILD_PKG_NO_FAKEROOT')local no_second_pass = os.getenv('MXE_BUILD_PKG_NO_SECOND_PASS')local build_targets = os.getenv('MXE_BUILD_PKG_TARGETS') local VERSION_ID = os.getenv('MXE_BUILD_PKG_VERSION_ID') or os.date("%Y%m%d") local MAX_TRIES = 10 local GIT = 'git --work-tree=./usr/ --git-dir=./usr/.git 'local GIT_USER = '-c user.name="build-pkg" ' ..    '-c user.email="build-pkg@mxe" ' local BLACKLIST = {    '^usr/installed/check%-requirements$',    -- usr/share/cmake is useful    '^usr/share/doc/',    '^usr/share/info/',    '^usr/share/man/',    '^usr/share/gcc',    '^usr/share/gtk-doc',    '^usr/[^/]+/share/doc/',    '^usr/[^/]+/share/info/',     -- usr/lib/nonetwork.so and    -- usr/x86_64-unknown-linux-gnu/lib/nonetwork.so    'lib/nonetwork.so',     -- https://github.com/mxe/mxe/issues/1886#issuecomment-331719282    'installed/.gitkeep',    'lib/.gitkeep',} local TARGETS = {    'i686-w64-mingw32.static',    'x86_64-w64-mingw32.static',    'i686-w64-mingw32.shared',    'x86_64-w64-mingw32.shared',}if build_targets then    TARGETS = {}    for target in build_targets:gmatch('(%S+)') do        table.insert(TARGETS, target)    endend local function echo(fmt, ...)    print(fmt:format(...))    io.stdout:flush()end local function log(fmt, ...)    echo('[build-pkg]\t' .. fmt, ...)end -- based on http://lua-users.org/wiki/SplitJoinlocal 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 aRecordend local function trim(str)    local text = str:gsub("%s+$", "")    text = text:gsub("^%s+", "")    return textend local function isInArray(element, array)    for _, member in ipairs(array) do        if member == element then            return true        end    end    return falseend local function sliceArray(list, nelements)    nelements = nelements or #list    local new_list = {}    for i = 1, nelements do        new_list[i] = list[i]    end    return new_listend local function concatArrays(...)    local result = {}    for _, array in ipairs({...}) do        for _, elem in ipairs(array) do            table.insert(result, elem)        end    end    return resultend local function isInString(substring, string)    return string:find(substring, 1, true)end local function shell(cmd)    local f = io.popen(cmd, 'r')    local text = f:read('*all')    f:close()    return textend local function execute(cmd)    if _VERSION == 'Lua 5.1' then        return os.execute(cmd) == 0    else        -- Lua >= 5.2        return os.execute(cmd)    endend local MXE_DIR = trim(shell('pwd')) -- for tar, try gtar and gnutar firstlocal tools = {}local function tool(name)    if tools[name] then        return tools[name]    end    if execute(("g%s --help > /dev/null 2>&1"):format(name)) then        tools[name] = 'g' .. name    elseif execute(("gnu%s --help > /dev/null 2>&1"):format(name)) then        tools[name] = 'gnu' .. name    else        tools[name] = name    end    return tools[name]end local function fileExists(name)    local f = io.open(name, "r")    if f ~= nil then        io.close(f)        return true    else        return false    endend local function fileSize(name)    local f = io.open(name, "r")    local size = f:seek("end")    io.close(f)    return sizeend local function isSymlink(name)    return shell(("ls -l %q"):format(name)):sub(1, 1) == "l"end local function writeFile(filename, data)    local file = io.open(filename, 'w')    file:write(data)    file:close()end local NATIVE_TARGET = trim(shell("ext/config.guess"))local function isCross(target)    return target ~= NATIVE_TARGETend local function getArch()    local cmd = "dpkg-architecture -qDEB_BUILD_ARCH 2> /dev/null"    return trim(shell(cmd))endlocal ARCH = getArch() -- return target and package from item namelocal function parseItem(item)    return item:match("([^~]+)~([^~]+)")end -- return item name from target and packagelocal function makeItem(target, package)    return target .. '~' .. packageend -- return several tables describing packages for all targets-- * list of items-- * map from item to list of deps (which are also items)-- * map from item to version-- Item is a string like "target~pkg"local function getItems()    local items = {}    local item2deps = {}    local item2ver = {}    local cmd = '%s print-deps-for-build-pkg MXE_TARGETS=%q'    cmd = cmd:format(tool 'make', table.concat(TARGETS, ' '))    local make = io.popen(cmd)    for line in make:lines() do        local deps = split(trim(line))        if deps[1] == 'for-build-pkg' then            -- first value is marker 'for-build-pkg'            table.remove(deps, 1)            -- first value is name of package which depends on            local item = table.remove(deps, 1)            -- second value is version of package            local ver = table.remove(deps, 1)            table.insert(items, item)            item2deps[item] = deps            item2ver[item] = ver            local target, _ = parseItem(item)            for _, dep_item in ipairs(deps) do                local target2, _ = parseItem(dep_item)                if isCross(target2) and target2 ~= target then                    log("Cross-target dependency %s -> %s",                        target2, target)                end            end        end    end    make:close()    return items, item2deps, item2verend local function getInstalled()    local installed = {}    local f = io.popen('ls usr/*/installed/*')    local pattern = '/([^/]+)/installed/([^/]+)'    for file in f:lines() do        local target, pkg = assert(file:match(pattern))        table.insert(installed, makeItem(target, pkg))    end    f:close()    return installedend -- graph is a map from item to a list of destinationslocal function transpose(graph)    local transposed = {}    for item, destinations in pairs(graph) do        for _, dest in ipairs(destinations) do            if not transposed[dest] then                transposed[dest] = {}            end            table.insert(transposed[dest], item)        end    end    return transposedend local function reverse(list)    local n = #list    local reversed = {}    for i = 1, n do        reversed[i] = list[n - i + 1]    end    return reversedend -- return items ordered in build order-- this means, if item depends on item2, then-- item2 precedes item1 in the listlocal function sortForBuild(items, item2deps)    local n = #items    local item2followers = transpose(item2deps)    -- Tarjan's algorithm    -- https://en.wikipedia.org/wiki/Topological_sorting    local build_list_reversed = {}    local marked_permanently = {}    local marked_temporarily = {}    local function visit(item1)        assert(not marked_temporarily[item1], 'not a DAG')        if not marked_permanently[item1] then            marked_temporarily[item1] = true            local followers = item2followers[item1] or {}            for _, item2 in ipairs(followers) do                visit(item2)            end            marked_permanently[item1] = true            marked_temporarily[item1] = false            table.insert(build_list_reversed, item1)        end    end    for _, item in ipairs(items) do        if not marked_permanently[item] then            visit(item)        end    end    assert(#build_list_reversed == n)    local build_list = reverse(build_list_reversed)    assert(#build_list == n)    return build_listend local function isDependency(item, dependency, item2deps)    for _, dep in ipairs(item2deps[item]) do        if dep == dependency then            return true        end        if isDependency(dep, dependency, item2deps) then            return true        end    end    return falseend local function makeItem2Index(build_list)    local item2index = {}    for index, item in ipairs(build_list) do        assert(not item2index[item], 'Duplicate item')        item2index[item] = index    end    return item2indexend -- return if build_list is ordered topologicallylocal function isTopoOrdered(build_list, items, item2deps)    if #build_list ~= #items then        return false, 'Length of build_list is wrong'    end    local item2index = makeItem2Index(build_list)    for item, deps in pairs(item2deps) do        for _, dep in ipairs(deps) do            if item2index[item] < item2index[dep] then                return false, 'Item ' .. item ..                    'is built before its dependency ' ..                    dep            end        end    end    return trueend local function isListed(file, list)    for _, pattern in ipairs(list) do        if file:match(pattern) then            return true        end    end    return falseend local function isBlacklisted(file)    return isListed(file, BLACKLIST)end local GIT_INITIAL = 'initial'local GIT_ALL_PSEUDOITEM = 'all' local function itemToBranch(item, pass)    return pass .. '-' .. item:gsub('~', '_')end -- creates git repo in ./usrlocal function gitInit()    os.execute('mkdir -p ./usr')    os.execute(GIT .. 'init --quiet')end local function gitTag(name)    os.execute(GIT .. 'tag ' .. name)end local function gitConflicts()    local cmd = GIT .. 'diff --name-only --diff-filter=U'    local f = io.popen(cmd, 'r')    local conflicts = {}    for conflict in f:lines() do        table.insert(conflicts, conflict)    end    f:close()    return conflictsend -- git commits changes in ./usrlocal function gitCommit(message)    local cmd = GIT .. GIT_USER .. 'commit -a -m %q --quiet'    assert(execute(cmd:format(message)))end local function gitCheckout(new_branch, deps, item2index, pass_of_deps)    local main_dep = deps[1]    if main_dep then        main_dep = itemToBranch(main_dep, pass_of_deps)    else        main_dep = GIT_INITIAL    end    local cmd = '%s checkout -q -b %s %s'    assert(execute(cmd:format(GIT, new_branch, main_dep)))    -- merge with other dependencies    for i = 2, #deps do        local message = 'Merge with ' .. deps[i]        local cmd2 = '%s %s merge -q %s -m %q'        if not execute(cmd2:format(GIT,            GIT_USER,            itemToBranch(deps[i], pass_of_deps),            message))        then            -- probably merge conflict            local conflicts = table.concat(gitConflicts(), ' ')            log('Merge conflicts: %s', conflicts)            local cmd3 = '%s checkout --ours %s'            assert(execute(cmd3:format(GIT, conflicts)))

Lines 1–500 of 1166.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t