summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Theodore <tonyt@logyst.com>2016-01-20 05:43:28 (GMT)
committerTony Theodore <tonyt@logyst.com>2016-01-20 05:43:28 (GMT)
commit5618509c9822e626ecd0cf8a8330746d2832976f (patch)
tree7a240f45c73a40a4d55580d6e601f764e0f27659
parent49deddf2411096cf1d60d7d8b81c8dfedf1412ed (diff)
parentc26dbd9aec256a2ca2f619533228b5c868beb8dd (diff)
downloadmxe-5618509c9822e626ecd0cf8a8330746d2832976f.zip
mxe-5618509c9822e626ecd0cf8a8330746d2832976f.tar.gz
mxe-5618509c9822e626ecd0cf8a8330746d2832976f.tar.bz2
Merge pull request #1191 from LuaAndC/build-pkg-only-deps3
build-pkg: detect undeclared dependencies
-rwxr-xr-xtools/build-pkg.lua199
1 files changed, 154 insertions, 45 deletions
diff --git a/tools/build-pkg.lua b/tools/build-pkg.lua
index 7cd853f..40fcdc9 100755
--- a/tools/build-pkg.lua
+++ b/tools/build-pkg.lua
@@ -40,6 +40,8 @@ local TODAY = os.date("%Y%m%d")
local MXE_DIR = os.getenv('MXE_DIR') or '/usr/lib/mxe'
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$',
@@ -185,8 +187,11 @@ local function isCross(target)
return target ~= NATIVE_TARGET
end
-local cmd = "dpkg-architecture -qDEB_BUILD_ARCH 2> /dev/null"
-local ARCH = trim(shell(cmd))
+local function getArch()
+ local cmd = "dpkg-architecture -qDEB_BUILD_ARCH 2> /dev/null"
+ return trim(shell(cmd))
+end
+local ARCH = getArch()
-- return target and package from item name
local function parseItem(item)
@@ -236,6 +241,18 @@ local function getItems()
return items, item2deps, item2ver
end
+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 installed
+end
+
-- graph is a map from item to a list of destinations
local function transpose(graph)
local transposed = {}
@@ -294,18 +311,21 @@ local function sortForBuild(items, item2deps)
return build_list
end
+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 item2index
+end
+
-- return if build_list is ordered topologically
local function isTopoOrdered(build_list, items, item2deps)
if #build_list ~= #items then
return false, 'Length of build_list is wrong'
end
- local item2index = {}
- for index, item in ipairs(build_list) do
- if item2index[item] then
- return false, 'Duplicate item: ' .. item
- end
- item2index[item] = index
- 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
@@ -331,12 +351,82 @@ local function isBlacklisted(file)
return isListed(file, BLACKLIST)
end
+local GIT_INITIAL = 'initial'
+local GIT_ALL = 'first-all'
+
+local function itemToBranch(item)
+ return 'first-' .. item:gsub('~', '_')
+end
+
-- creates git repo in ./usr
local 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 conflicts
+end
+
+-- git commits changes in ./usr
+local 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)
+ local main_dep = deps[1]
+ if main_dep then
+ main_dep = itemToBranch(main_dep)
+ 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]),
+ 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)))
+ gitCommit(message)
+ end
+ end
+ if #deps > 0 then
+ -- prevent accidental rebuilds
+ -- touch usr/*/installed/* files in build order
+ -- see https://git.io/vuDJY
+ local installed = getInstalled()
+ table.sort(installed, function(x, y)
+ return item2index[x] < item2index[y]
+ end)
+ for _, item in ipairs(installed) do
+ local target, pkg = assert(parseItem(item))
+ local cmd4 = 'touch -c usr/%s/installed/%s'
+ execute(cmd4:format(target, pkg))
+ end
+ end
+end
+
local function gitAdd()
os.execute(GIT .. 'add .')
end
@@ -373,14 +463,6 @@ local function gitStatus()
return new_files, changed_files
end
--- git commits changes in ./usr
-local function gitCommit(message)
- local cmd = GIT .. '-c user.name="build-pkg" ' ..
- '-c user.email="build-pkg@mxe" ' ..
- 'commit -a -m %q --quiet'
- os.execute(cmd:format(message))
-end
-
local function isValidBinary(target, file)
local cmd = './usr/bin/%s-objdump -t %s > /dev/null 2>&1'
return execute(cmd:format(target, file))
@@ -392,9 +474,7 @@ local function checkFile(file, item)
local ext = file:sub(-4):lower()
local cmd = 'file --dereference --brief %q'
local file_type = trim(shell(cmd:format(file)))
- if ext == '.bin' then
- -- can be an executable or something else (font)
- elseif ext == '.exe' then
+ if ext == '.exe' then
if not file_type:match('PE32') then
log('File %s (%s) is %q. Remove .exe',
file, item, file_type)
@@ -404,7 +484,8 @@ local function checkFile(file, item)
log('File %s (%s) is %q. Remove .dll',
file, item, file_type)
end
- else
+ elseif ext ~= '.bin' then
+ -- .bin can be an executable or something else (font)
if file_type:match('PE32') then
log('File %s (%s) is %q. Add exe or dll',
file, item, file_type)
@@ -450,14 +531,33 @@ local function checkFileList(files, item)
end
end
+local function removeEmptyDirs(item)
+ -- removing an empty dir can reveal another one (parent)
+ local go_on = true
+ while go_on do
+ go_on = false
+ local f = io.popen('find usr/* -empty -type d', 'r')
+ for dir in f:lines() do
+ log("Remove empty directory %s created by %s",
+ dir, item)
+ os.remove(dir)
+ go_on = true
+ end
+ f:close()
+ end
+end
+
-- builds package, returns list of new files
-local function buildItem(item, item2deps, file2item)
+local function buildItem(item, item2deps, file2item, item2index)
+ gitCheckout(itemToBranch(item), item2deps[item], item2index)
local target, pkg = parseItem(item)
local cmd = '%s %s MXE_TARGETS=%s --jobs=1'
os.execute(cmd:format(tool 'make', pkg, target))
gitAdd()
local new_files, changed_files = gitStatus()
- gitCommit(("Build %s"):format(item))
+ if #new_files + #changed_files > 0 then
+ gitCommit(("Build %s"):format(item))
+ end
for _, file in ipairs(new_files) do
checkFile(file, item)
file2item[file] = item
@@ -473,6 +573,7 @@ local function buildItem(item, item2deps, file2item)
item, file, creator_item)
end
checkFileList(concatArrays(new_files, changed_files), item)
+ removeEmptyDirs(item)
return new_files
end
@@ -525,7 +626,7 @@ local function debianControl(options)
end
local function makePackage(name, files, deps, ver, d1, d2, dst)
- local dst = dst or '.'
+ dst = dst or '.'
local dirname = ('%s/%s_%s'):format(dst, name,
protectVersion(ver))
-- make .list file
@@ -533,12 +634,12 @@ local function makePackage(name, files, deps, ver, d1, d2, dst)
writeFile(list_path, table.concat(files, "\n") .. "\n")
-- make .tar.xz file
local tar_name = dirname .. '.tar.xz'
- local cmd = '%s -T %s --owner=root --group=root -cJf %s'
- os.execute(cmd:format(tool 'tar', list_path, tar_name))
+ local cmd1 = '%s -T %s --owner=root --group=root -cJf %s'
+ os.execute(cmd1:format(tool 'tar', list_path, tar_name))
-- update list of files back from .tar.xz (see #1067)
- local cmd = '%s -tf %s'
- cmd = cmd:format(tool 'tar', tar_name)
- local tar_reader = io.popen(cmd, 'r')
+ local cmd2 = '%s -tf %s'
+ cmd2 = cmd2:format(tool 'tar', tar_name)
+ local tar_reader = io.popen(cmd2, 'r')
local files_str = tar_reader:read('*all')
tar_reader:close()
writeFile(list_path, files_str)
@@ -558,16 +659,16 @@ local function makePackage(name, files, deps, ver, d1, d2, dst)
os.execute(('mkdir -p %s'):format(usr))
os.execute(('mkdir -p %s/DEBIAN'):format(dirname))
-- use tar to copy files with paths
- local cmd = '%s -C %s -xf %s'
- cmd = 'fakeroot -s deb.fakeroot ' .. cmd
- os.execute(cmd:format(tool 'tar', usr, tar_name))
+ local cmd3 = '%s -C %s -xf %s'
+ cmd3 = 'fakeroot -s deb.fakeroot ' .. cmd3
+ os.execute(cmd3:format(tool 'tar', usr, tar_name))
-- make DEBIAN/control file
local control_fname = dirname .. '/DEBIAN/control'
writeFile(control_fname, control_text)
-- make .deb file
- local cmd = 'dpkg-deb -Zxz -b %s'
- cmd = 'fakeroot -i deb.fakeroot ' .. cmd
- os.execute(cmd:format(dirname))
+ local cmd4 = 'dpkg-deb -Zxz -b %s'
+ cmd4 = 'fakeroot -i deb.fakeroot ' .. cmd4
+ os.execute(cmd4:format(dirname))
-- cleanup
os.execute(('rm -fr %s deb.fakeroot'):format(dirname))
end
@@ -604,7 +705,7 @@ local function findForeignInstalls(item, files)
for _, file in ipairs(files) do
local pattern = 'usr/([^/]+)/installed/([^/]+)'
local t, p = file:match(pattern)
- if t then
+ if t and p ~= '.gitkeep' then
local item1 = makeItem(t, p)
if item1 ~= item then
log('Item %s built item %s', item, item1)
@@ -640,7 +741,7 @@ local function progressPrinter(items)
local started_at = os.time()
local sums = {}
for i, item in ipairs(items) do
- local target, pkg = parseItem(item)
+ local _, pkg = parseItem(item)
local expected_time = pkg2time[pkg] or 1
sums[i] = (sums[i - 1] or 0) + expected_time
end
@@ -649,11 +750,11 @@ local function progressPrinter(items)
local pkgs_done = 0
local printer = {}
--
- function printer:advance(i)
+ function printer.advance(_, i)
pkgs_done = i
time_done = sums[i]
end
- function printer:status()
+ function printer.status(_)
local now = os.time()
local spent = now - started_at
local predicted_duration = spent * total_time / time_done
@@ -667,7 +768,7 @@ local function progressPrinter(items)
return printer
end
-local function isEmpty(item, files)
+local function isEmpty(files)
return #files == 1
end
@@ -685,10 +786,12 @@ local function buildPackages(items, item2deps)
end
return false
end
+ local item2index = makeItem2Index(items)
local progress_printer = progressPrinter(items)
for i, item in ipairs(items) do
if not brokenDep(item) then
- local files = buildItem(item, item2deps, file2item)
+ local files = buildItem(item, item2deps,
+ file2item, item2index)
findForeignInstalls(item, files)
if isBuilt(item, files) then
item2files[item] = files
@@ -714,7 +817,7 @@ local function makeDebs(items, item2deps, item2ver, item2files)
local to_build = {}
for _, item in ipairs(items) do
local files = assert(item2files[item], item)
- if not isEmpty(item, files) then
+ if not isEmpty(files) then
table.insert(to_build, item)
end
end
@@ -727,7 +830,7 @@ local function makeDebs(items, item2deps, item2ver, item2files)
local files = assert(item2files[item], item)
for _, dep in ipairs(deps) do
local dep_files = item2files[dep]
- if isEmpty(dep, dep_files) then
+ if isEmpty(dep_files) then
log('Item %s depends on ' ..
'empty item %s', item, dep)
missing_deps_set[dep] = true
@@ -816,19 +919,25 @@ local function makeMxeSourcePackage()
makePackage(name, files, deps, ver, d1, d2)
end
+assert(not io.open('usr/.git'), 'Remove usr/')
assert(trim(shell('pwd')) == MXE_DIR,
"Clone MXE to " .. MXE_DIR)
-assert(execute(("%s check-requirements"):format(tool 'make')))
+gitInit()
+assert(execute(("%s check-requirements MXE_TARGETS=%q"):format(
+ tool 'make', table.concat(TARGETS, ' '))))
if not max_items then
local cmd = ('%s download -j 6 -k'):format(tool 'make')
while not execute(cmd) do end
end
-gitInit()
+gitAdd()
+gitCommit('Initial commit')
+gitTag(GIT_INITIAL)
local items, item2deps, item2ver = getItems()
local build_list = sortForBuild(items, item2deps)
assert(isTopoOrdered(build_list, items, item2deps))
build_list = sliceArray(build_list, max_items)
local unbroken, item2files = buildPackages(build_list, item2deps)
+gitCheckout(GIT_ALL, unbroken, makeItem2Index(build_list))
makeDebs(unbroken, item2deps, item2ver, item2files)
if not no_debs then
makeMxeRequirementsPackage('wheezy')