Hello, there!
This commit is contained in:
parent
bfde354b22
commit
ae950fb9f7
|
@ -0,0 +1,6 @@
|
||||||
|
All Alpine Linux keys are stored here, so we can verify the downloaded files with pmbootstrap before APK itself is verified.
|
||||||
|
|
||||||
|
Sources for the keys (must be identical, there's a testcase that verifies this):
|
||||||
|
https://github.com/alpinelinux/aports/tree/master/main/alpine-keys
|
||||||
|
http://git.alpinelinux.org/cgit/aports/tree/main/alpine-keys?h=master
|
||||||
|
alpine-keys package
|
|
@ -0,0 +1,9 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1yHJxQgsHQREclQu4Ohe
|
||||||
|
qxTxd1tHcNnvnQTu/UrTky8wWvgXT+jpveroeWWnzmsYlDI93eLI2ORakxb3gA2O
|
||||||
|
Q0Ry4ws8vhaxLQGC74uQR5+/yYrLuTKydFzuPaS1dK19qJPXB8GMdmFOijnXX4SA
|
||||||
|
jixuHLe1WW7kZVtjL7nufvpXkWBGjsfrvskdNA/5MfxAeBbqPgaq0QMEfxMAn6/R
|
||||||
|
L5kNepi/Vr4S39Xvf2DzWkTLEK8pcnjNkt9/aafhWqFVW7m3HCAII6h/qlQNQKSo
|
||||||
|
GuH34Q8GsFG30izUENV9avY7hSLq7nggsvknlNBZtFUcmGoQrtx3FmyYsIC8/R+B
|
||||||
|
ywIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,9 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNijDxJ8kloskKQpJdx+
|
||||||
|
mTMVFFUGDoDCbulnhZMJoKNkSuZOzBoFC94omYPtxnIcBdWBGnrm6ncbKRlR+6oy
|
||||||
|
DO0W7c44uHKCFGFqBhDasdI4RCYP+fcIX/lyMh6MLbOxqS22TwSLhCVjTyJeeH7K
|
||||||
|
aA7vqk+QSsF4TGbYzQDDpg7+6aAcNzg6InNePaywA6hbT0JXbxnDWsB+2/LLSF2G
|
||||||
|
mnhJlJrWB1WGjkz23ONIWk85W4S0XB/ewDefd4Ly/zyIciastA7Zqnh7p3Ody6Q0
|
||||||
|
sS2MJzo7p3os1smGjUF158s6m/JbVh4DN6YIsxwl2OjDOz9R0OycfJSDaBVIGZzg
|
||||||
|
cQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,9 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr8s1q88XpuJWLCZALdKj
|
||||||
|
lN8wg2ePB2T9aIcaxryYE/Jkmtu+ZQ5zKq6BT3y/udt5jAsMrhHTwroOjIsF9DeG
|
||||||
|
e8Y3vjz+Hh4L8a7hZDaw8jy3CPag47L7nsZFwQOIo2Cl1SnzUc6/owoyjRU7ab0p
|
||||||
|
iWG5HK8IfiybRbZxnEbNAfT4R53hyI6z5FhyXGS2Ld8zCoU/R4E1P0CUuXKEN4p0
|
||||||
|
64dyeUoOLXEWHjgKiU1mElIQj3k/IF02W89gDj285YgwqA49deLUM7QOd53QLnx+
|
||||||
|
xrIrPv3A+eyXMFgexNwCKQU9ZdmWa00MjjHlegSGK8Y2NPnRoXhzqSP9T9i2HiXL
|
||||||
|
VQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,9 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwlzMkl7b5PBdfMzGdCT0
|
||||||
|
cGloRr5xGgVmsdq5EtJvFkFAiN8Ac9MCFy/vAFmS8/7ZaGOXoCDWbYVLTLOO2qtX
|
||||||
|
yHRl+7fJVh2N6qrDDFPmdgCi8NaE+3rITWXGrrQ1spJ0B6HIzTDNEjRKnD4xyg4j
|
||||||
|
g01FMcJTU6E+V2JBY45CKN9dWr1JDM/nei/Pf0byBJlMp/mSSfjodykmz4Oe13xB
|
||||||
|
Ca1WTwgFykKYthoLGYrmo+LKIGpMoeEbY1kuUe04UiDe47l6Oggwnl+8XD1MeRWY
|
||||||
|
sWgj8sF4dTcSfCMavK4zHRFFQbGp/YFJ/Ww6U9lA3Vq0wyEI6MCMQnoSMFwrbgZw
|
||||||
|
wwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,9 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3v8/ye/V/t5xf4JiXLXa
|
||||||
|
hWFRozsnmn3hobON20GdmkrzKzO/eUqPOKTpg2GtvBhK30fu5oY5uN2ORiv2Y2ht
|
||||||
|
eLiZ9HVz3XP8Fm9frha60B7KNu66FO5P2o3i+E+DWTPqqPcCG6t4Znk2BypILcit
|
||||||
|
wiPKTsgbBQR2qo/cO01eLLdt6oOzAaF94NH0656kvRewdo6HG4urbO46tCAizvCR
|
||||||
|
CA7KGFMyad8WdKkTjxh8YLDLoOCtoZmXmQAiwfRe9pKXRH/XXGop8SYptLqyVVQ+
|
||||||
|
tegOD9wRs2tOlgcLx4F/uMzHN7uoho6okBPiifRX+Pf38Vx+ozXh056tjmdZkCaV
|
||||||
|
aQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,9 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoSPnuAGKtRIS5fEgYPXD
|
||||||
|
8pSGvKAmIv3A08LBViDUe+YwhilSHbYXUEAcSH1KZvOo1WT1x2FNEPBEFEFU1Eyc
|
||||||
|
+qGzbA03UFgBNvArurHQ5Z/GngGqE7IarSQFSoqewYRtFSfp+TL9CUNBvM0rT7vz
|
||||||
|
2eMu3/wWG+CBmb92lkmyWwC1WSWFKO3x8w+Br2IFWvAZqHRt8oiG5QtYvcZL6jym
|
||||||
|
Y8T6sgdDlj+Y+wWaLHs9Fc+7vBuyK9C4O1ORdMPW15qVSl4Lc2Wu1QVwRiKnmA+c
|
||||||
|
DsH/m7kDNRHM7TjWnuj+nrBOKAHzYquiu5iB3Qmx+0gwnrSVf27Arc3ozUmmJbLj
|
||||||
|
zQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,9 @@
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvBxJN9ErBgdRcPr5g4hV
|
||||||
|
qyUSGZEKuvQliq2Z9SRHLh2J43+EdB6A+yzVvLnzcHVpBJ+BZ9RV30EM9guck9sh
|
||||||
|
r+bryZcRHyjG2wiIEoduxF2a8KeWeQH7QlpwGhuobo1+gA8L0AGImiA6UP3LOirl
|
||||||
|
I0G2+iaKZowME8/tydww4jx5vG132JCOScMjTalRsYZYJcjFbebQQolpqRaGB4iG
|
||||||
|
WqhytWQGWuKiB1A22wjmIYf3t96l1Mp+FmM2URPxD1gk/BIBnX7ew+2gWppXOK9j
|
||||||
|
1BJpo0/HaX5XoZ/uMqISAAtgHZAqq+g3IUPouxTphgYQRTRYpz2COw3NF43VYQrR
|
||||||
|
bQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,18 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
|
@ -0,0 +1,49 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import pmb.aportgen.binutils
|
||||||
|
import pmb.aportgen.musl
|
||||||
|
import pmb.aportgen.gcc
|
||||||
|
import pmb.helpers.git
|
||||||
|
|
||||||
|
|
||||||
|
def generate(args, pkgname):
|
||||||
|
# Prepare git repo and temp folder
|
||||||
|
pmb.helpers.git.clone(args, "aports_upstream")
|
||||||
|
logging.info("(native) generate " + pkgname + " aport")
|
||||||
|
if os.path.exists(args.work + "/aportgen"):
|
||||||
|
pmb.helpers.run.user(args, ["rm", "-r", args.work + "/aportgen"])
|
||||||
|
|
||||||
|
# Choose generator based on the name
|
||||||
|
if pkgname.startswith("binutils-"):
|
||||||
|
pmb.aportgen.binutils.generate(args, pkgname)
|
||||||
|
elif pkgname.startswith("musl-"):
|
||||||
|
pmb.aportgen.musl.generate(args, pkgname)
|
||||||
|
elif pkgname.startswith("gcc-"):
|
||||||
|
pmb.aportgen.gcc.generate(args, pkgname)
|
||||||
|
else:
|
||||||
|
raise ValueError("No generator available for " + pkgname + "!")
|
||||||
|
|
||||||
|
# Move to the aports folder
|
||||||
|
path_target = args.aports + "/" + pkgname
|
||||||
|
if os.path.exists(path_target):
|
||||||
|
pmb.helpers.run.user(args, ["rm", "-r", path_target])
|
||||||
|
pmb.helpers.run.user(
|
||||||
|
args, ["mv", args.work + "/aportgen", path_target])
|
|
@ -0,0 +1,71 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.aportgen.core
|
||||||
|
|
||||||
|
|
||||||
|
def generate(args, pkgname):
|
||||||
|
# Copy original aport
|
||||||
|
arch = pkgname.split("-")[1]
|
||||||
|
path_original = "main/binutils"
|
||||||
|
upstream = (args.work + "/cache_git/aports_upstream/" + path_original)
|
||||||
|
pmb.helpers.run.user(args, ["cp", "-r", upstream, args.work + "/aportgen"])
|
||||||
|
|
||||||
|
# Rewrite APKBUILD
|
||||||
|
fields = {
|
||||||
|
"pkgname": pkgname,
|
||||||
|
"pkgdesc": "Tools necessary to build programs for " + arch + " targets",
|
||||||
|
"makedepends_build": "",
|
||||||
|
"makedepends_host": "",
|
||||||
|
"makedepends": "gettext libtool autoconf automake bison",
|
||||||
|
"subpackages": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_functions = {
|
||||||
|
"build": """
|
||||||
|
_target="$(arch_to_hostspec armhf)"
|
||||||
|
cd "$builddir"
|
||||||
|
"$builddir"/configure \\
|
||||||
|
--build="$CBUILD" \\
|
||||||
|
--target=$_target \\
|
||||||
|
--with-lib-path=/usr/lib \\
|
||||||
|
--prefix=/usr \\
|
||||||
|
--with-sysroot=/usr/$_target \\
|
||||||
|
--enable-ld=default \\
|
||||||
|
--enable-gold=yes \\
|
||||||
|
--enable-plugins \\
|
||||||
|
--disable-multilib \\
|
||||||
|
--disable-werror \\
|
||||||
|
--disable-nls \\
|
||||||
|
|| return 1
|
||||||
|
make
|
||||||
|
""",
|
||||||
|
"package": """
|
||||||
|
cd "$builddir"
|
||||||
|
make install DESTDIR="$pkgdir" || return 1
|
||||||
|
|
||||||
|
# remove man, info folders
|
||||||
|
rm -rf "$pkgdir"/usr/share
|
||||||
|
""",
|
||||||
|
"libs": None,
|
||||||
|
"gold": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
pmb.aportgen.core.rewrite(args, pkgname, path_original, fields, "binutils",
|
||||||
|
replace_functions)
|
|
@ -0,0 +1,117 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import fnmatch
|
||||||
|
|
||||||
|
|
||||||
|
def format_function(name, body, remove_indent=4):
|
||||||
|
"""
|
||||||
|
Format the body of a shell function passed to rewrite() below, so it fits
|
||||||
|
the format of the original APKBUILD.
|
||||||
|
"""
|
||||||
|
ret = ""
|
||||||
|
lines = body.split("\n")
|
||||||
|
for i in range(len(lines)):
|
||||||
|
line = lines[i]
|
||||||
|
if not line.strip():
|
||||||
|
if not ret or i == len(lines) - 1:
|
||||||
|
continue
|
||||||
|
ret += line[remove_indent:] + "\n"
|
||||||
|
return name + "() {\n" + ret + "}\n"
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite(args, pkgname, path_original, fields={}, replace_pkgname=None,
|
||||||
|
replace_functions={}, replace_simple={}, below_header=""):
|
||||||
|
"""
|
||||||
|
Append a header to $WORK/aportgen/APKBUILD, delete maintainer/contributor
|
||||||
|
lines (so they won't be bugged with issues regarding our generated aports),
|
||||||
|
and add reference to the original aport.
|
||||||
|
|
||||||
|
:param fields: key-value pairs of fields, that shall be changed in the
|
||||||
|
APKBUILD. For example: {"pkgdesc": "my new package", "subpkgs": ""}
|
||||||
|
:param replace_pkgname: When set, $pkgname gets replaced with that string in
|
||||||
|
every line.
|
||||||
|
:param replace_functions: Function names and new bodies, for example:
|
||||||
|
{"build": "return 0"}
|
||||||
|
The body can also be None (deletes the function)
|
||||||
|
:param replace_simple: Lines, that fnmatch the pattern, get
|
||||||
|
replaced/deleted. Example: {"*test*": "# test", "*mv test.bin*": None}
|
||||||
|
:param below_header: String, that gets directly placed below the header.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Header
|
||||||
|
lines_new = [
|
||||||
|
"# Automatically generated aport, do not edit!\n",
|
||||||
|
"# Generator: pmbootstrap aportgen " + pkgname + "\n",
|
||||||
|
"# Based on: " + path_original + "\n",
|
||||||
|
"\n",
|
||||||
|
]
|
||||||
|
for line in below_header.split("\n"):
|
||||||
|
lines_new += line.strip() + "\n"
|
||||||
|
|
||||||
|
# Copy/modify lines, skip Maintainer/Contributor
|
||||||
|
path = args.work + "/aportgen/APKBUILD"
|
||||||
|
with open(path, "r+", encoding="utf-8") as handle:
|
||||||
|
skip_in_func = False
|
||||||
|
for line in handle.readlines():
|
||||||
|
# Skip maintainer/contributor
|
||||||
|
if line.startswith("# Maintainer") or line.startswith(
|
||||||
|
"# Contributor"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Replace functions
|
||||||
|
if skip_in_func:
|
||||||
|
if line.startswith("}"):
|
||||||
|
skip_in_func = False
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
for func, body in replace_functions.items():
|
||||||
|
if line.startswith(func + "() {"):
|
||||||
|
skip_in_func = True
|
||||||
|
if body:
|
||||||
|
lines_new += format_function(func, body)
|
||||||
|
break
|
||||||
|
if skip_in_func:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Replace fields
|
||||||
|
for key, value in fields.items():
|
||||||
|
if line.startswith(key + "="):
|
||||||
|
line = key + "=\"" + value + "\"\n"
|
||||||
|
break
|
||||||
|
|
||||||
|
# Replace $pkgname
|
||||||
|
if replace_pkgname and "$pkgname" in line:
|
||||||
|
line = line.replace("$pkgname", replace_pkgname)
|
||||||
|
|
||||||
|
# Replace simple
|
||||||
|
for pattern, replacement in replace_simple.items():
|
||||||
|
if fnmatch.fnmatch(line, pattern):
|
||||||
|
line = replacement
|
||||||
|
if replacement:
|
||||||
|
line += "\n"
|
||||||
|
break
|
||||||
|
if line is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
lines_new.append(line)
|
||||||
|
|
||||||
|
# Write back
|
||||||
|
handle.seek(0)
|
||||||
|
handle.write("".join(lines_new))
|
||||||
|
handle.truncate()
|
|
@ -0,0 +1,72 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.aportgen.core
|
||||||
|
|
||||||
|
|
||||||
|
def generate(args, pkgname):
|
||||||
|
# Copy original aport
|
||||||
|
arch = pkgname.split("-")[1]
|
||||||
|
path_original = "main/gcc"
|
||||||
|
upstream = (args.work + "/cache_git/aports_upstream/" + path_original)
|
||||||
|
pmb.helpers.run.user(args, ["cp", "-r", upstream, args.work + "/aportgen"])
|
||||||
|
|
||||||
|
# Rewrite APKBUILD
|
||||||
|
fields = {
|
||||||
|
"pkgname": pkgname,
|
||||||
|
"pkgdesc": "Stage2 cross-compiler for " + arch,
|
||||||
|
"depends": "isl binutils-" + arch,
|
||||||
|
"makedepends_build": "gcc g++ paxmark bison flex texinfo gawk zip gmp-dev mpfr-dev mpc1-dev zlib-dev",
|
||||||
|
"makedepends_host": "linux-headers gmp-dev mpfr-dev mpc1-dev isl-dev zlib-dev musl-dev-" + arch + " binutils-" + arch,
|
||||||
|
"subpackages": "",
|
||||||
|
|
||||||
|
"LIBGOMP": "false",
|
||||||
|
"LIBGCC": "false",
|
||||||
|
"LIBATOMIC": "false",
|
||||||
|
"LIBITM": "false",
|
||||||
|
}
|
||||||
|
|
||||||
|
below_header = "CTARGET_ARCH=" + arch + """
|
||||||
|
CTARGET="$(arch_to_hostspec ${CTARGET_ARCH})"
|
||||||
|
CBUILDROOT="/usr/$CTARGET"
|
||||||
|
LANG_OBJC=false
|
||||||
|
LANG_JAVA=false
|
||||||
|
LANG_GO=false
|
||||||
|
LANG_FORTRAN=false
|
||||||
|
LANG_ADA=false
|
||||||
|
options="!strip !tracedeps"
|
||||||
|
"""
|
||||||
|
|
||||||
|
replace_simple = {
|
||||||
|
# Do not package libstdc++
|
||||||
|
'*subpackages="$subpackages libstdc++:libcxx:*':
|
||||||
|
' subpackages="$subpackages g++$_target:gpp"',
|
||||||
|
|
||||||
|
# Do not move gdb.py
|
||||||
|
'*-gdb.py*': None,
|
||||||
|
'*/usr/share/gdb/python/auto-load/usr/lib/*': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
pmb.aportgen.core.rewrite(
|
||||||
|
args,
|
||||||
|
pkgname,
|
||||||
|
path_original,
|
||||||
|
fields,
|
||||||
|
replace_simple=replace_simple,
|
||||||
|
below_header=below_header)
|
|
@ -0,0 +1,113 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.aportgen.core
|
||||||
|
import pmb.parse.apkindex
|
||||||
|
import pmb.chroot.apk
|
||||||
|
import pmb.chroot.apk_static
|
||||||
|
|
||||||
|
|
||||||
|
def generate(args, pkgname):
|
||||||
|
# Install musl in chroot (so we have the APKINDEX and verified musl apks)
|
||||||
|
arch = pkgname.split("-")[1]
|
||||||
|
apkindex = pmb.chroot.apk_static.download(args, "APKINDEX.tar.gz")
|
||||||
|
pmb.chroot.apk.install(args, ["musl-dev"], "buildroot_" + arch)
|
||||||
|
|
||||||
|
# Parse musl version from APKINDEX
|
||||||
|
package_data = pmb.parse.apkindex.read(args, "musl", apkindex)
|
||||||
|
version = package_data["version"]
|
||||||
|
pkgver = version.split("-r")[0]
|
||||||
|
pkgrel = version.split("-r")[1]
|
||||||
|
|
||||||
|
# Copy the apk files to the distfiles cache
|
||||||
|
for subpkgname in ["musl", "musl-dev"]:
|
||||||
|
path = glob.glob(args.work + "/cache_apk_" + arch + "/" + subpkgname +
|
||||||
|
"-" + version + ".*.apk")[0]
|
||||||
|
path_target = (args.work + "/cache_distfiles/" + subpkgname + "-" +
|
||||||
|
version + "-" + arch + ".apk")
|
||||||
|
if not os.path.exists(path_target):
|
||||||
|
pmb.helpers.run.user(args, ["cp", path, path_target])
|
||||||
|
|
||||||
|
# Hash the distfiles
|
||||||
|
hashes = pmb.chroot.user(args, ["sha512sum",
|
||||||
|
"musl-" + version + "-" + arch + ".apk",
|
||||||
|
"musl-dev-" + version + "-" + arch + ".apk"], "buildroot_" + arch,
|
||||||
|
working_dir="/var/cache/distfiles", return_stdout=True)
|
||||||
|
|
||||||
|
# Write the APKBUILD
|
||||||
|
pmb.helpers.run.user(args, ["mkdir", "-p", args.work + "/aportgen"])
|
||||||
|
with open(args.work + "/aportgen/APKBUILD", "w", encoding="utf-8") as handle:
|
||||||
|
# Variables
|
||||||
|
handle.write("# Automatically generated aport, do not edit!\n"
|
||||||
|
"# Generator: pmbootstrap aportgen " + pkgname + "\n"
|
||||||
|
"\n"
|
||||||
|
"pkgname=" + pkgname + "\n"
|
||||||
|
"pkgver=" + pkgver + "\n"
|
||||||
|
"pkgrel=" + pkgrel + "\n"
|
||||||
|
"subpackages=\"musl-dev-" + arch + ":package_dev\"\n"
|
||||||
|
"\n"
|
||||||
|
"_arch=\"" + arch + "\"\n"
|
||||||
|
"_mirror=\"" + args.mirror_alpine + "\"\n"
|
||||||
|
)
|
||||||
|
# Static part
|
||||||
|
static = """
|
||||||
|
url="https://musl-libc.org"
|
||||||
|
license="MIT"
|
||||||
|
arch="all"
|
||||||
|
options="!check !strip"
|
||||||
|
pkgdesc="the musl library (lib c) implementation for $_arch"
|
||||||
|
|
||||||
|
_target="$(arch_to_hostspec $_arch)"
|
||||||
|
|
||||||
|
source="
|
||||||
|
musl-$pkgver-r$pkgrel-$_arch.apk::$_mirror/edge/main/$_arch/musl-$pkgver-r$pkgrel.apk
|
||||||
|
musl-dev-$pkgver-r$pkgrel-$_arch.apk::$_mirror/edge/main/$_arch/musl-dev-$pkgver-r$pkgrel.apk
|
||||||
|
"
|
||||||
|
|
||||||
|
package() {
|
||||||
|
mkdir -p "$pkgdir/usr/$_target"
|
||||||
|
cd "$pkgdir/usr/$_target"
|
||||||
|
tar -xf $srcdir/musl-$pkgver-r$pkgrel-$_arch.apk
|
||||||
|
rm .PKGINFO .SIGN.*
|
||||||
|
}
|
||||||
|
package_dev() {
|
||||||
|
mkdir -p "$subpkgdir/usr/$_target"
|
||||||
|
cd "$subpkgdir/usr/$_target"
|
||||||
|
tar -xf $srcdir/musl-dev-$pkgver-r$pkgrel-$_arch.apk
|
||||||
|
rm .PKGINFO .SIGN.*
|
||||||
|
|
||||||
|
# symlink everything from /usr/$_target/usr/* to /usr/$_target/*
|
||||||
|
# so the cross-compiler gcc does not fail to build.
|
||||||
|
for _dir in include lib; do
|
||||||
|
mkdir -p "$subpkgdir/usr/$_target/$_dir"
|
||||||
|
cd "$subpkgdir/usr/$_target/usr/$_dir"
|
||||||
|
for i in *; do
|
||||||
|
cd "$subpkgdir/usr/$_target/$_dir"
|
||||||
|
ln -s /usr/$_target/usr/$_dir/$i $i
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
for line in static.split("\n"):
|
||||||
|
handle.write(line[12:] + "\n")
|
||||||
|
|
||||||
|
# Hashes
|
||||||
|
handle.write("sha512sums=\"" + hashes.rstrip() + "\"")
|
|
@ -0,0 +1,24 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from pmb.chroot.init import init
|
||||||
|
from pmb.chroot.mount import mount
|
||||||
|
from pmb.chroot.root import root
|
||||||
|
from pmb.chroot.user import user
|
||||||
|
from pmb.chroot.shutdown import shutdown
|
||||||
|
from pmb.chroot.zap import zap
|
|
@ -0,0 +1,76 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.parse.apkindex
|
||||||
|
|
||||||
|
|
||||||
|
def install(args, packages, suffix="native", build=True):
|
||||||
|
"""
|
||||||
|
:param build: automatically build the package, when it does not exist yet
|
||||||
|
and it is inside the pm-aports folder. Checking this is expensive - if
|
||||||
|
you know, that all packages are provides by upstream repos, set this to
|
||||||
|
False!
|
||||||
|
"""
|
||||||
|
# Initialize chroot
|
||||||
|
pmb.chroot.init(args, suffix)
|
||||||
|
|
||||||
|
# Filter already installed packages
|
||||||
|
packages_installed = installed(args, suffix)
|
||||||
|
packages_todo = []
|
||||||
|
for package in packages:
|
||||||
|
if package not in packages_installed:
|
||||||
|
packages_todo.append(package)
|
||||||
|
if not len(packages_todo):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Build packages if necessary
|
||||||
|
arch = pmb.parse.arch.from_chroot_suffix(args, suffix)
|
||||||
|
if build:
|
||||||
|
for package in packages_todo:
|
||||||
|
pmb.build.package(args, package, arch)
|
||||||
|
|
||||||
|
# Sanitize packages: don't allow '--allow-untrusted' and other options
|
||||||
|
# to be passed to apk!
|
||||||
|
for package in packages_todo:
|
||||||
|
if package.startswith("-"):
|
||||||
|
raise ValueError("Invalid package name: " + package)
|
||||||
|
|
||||||
|
# Install everything
|
||||||
|
logging.info("(" + suffix + ") install " + " ".join(packages_todo))
|
||||||
|
pmb.chroot.root(args, ["apk", "--no-progress", "add"] + packages_todo,
|
||||||
|
suffix)
|
||||||
|
|
||||||
|
# Update all packages installed in a chroot
|
||||||
|
|
||||||
|
|
||||||
|
def update(args, suffix="native"):
|
||||||
|
pmb.chroot.init(args, suffix)
|
||||||
|
pmb.chroot.root(args, ["apk", "update"], suffix)
|
||||||
|
|
||||||
|
# Get all explicitly installed packages
|
||||||
|
|
||||||
|
|
||||||
|
def installed(args, suffix="native"):
|
||||||
|
world = args.work + "/chroot_" + suffix + "/etc/apk/world"
|
||||||
|
if not os.path.exists(world):
|
||||||
|
return []
|
||||||
|
with open(world, encoding="utf-8") as handle:
|
||||||
|
return handle.read().splitlines()
|
|
@ -0,0 +1,179 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import urllib.request
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import shutil
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
import stat
|
||||||
|
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.config
|
||||||
|
import pmb.config.load
|
||||||
|
import pmb.parse.apkindex
|
||||||
|
import pmb.helpers.http
|
||||||
|
|
||||||
|
|
||||||
|
def read_signature_info(tar):
|
||||||
|
"""
|
||||||
|
Find various information about the signature, that was used to sign
|
||||||
|
/sbin/apk.static inside the archive (not to be confused with the normal apk
|
||||||
|
archive signature!)
|
||||||
|
|
||||||
|
:returns: (sigfilename, sigkey_path)
|
||||||
|
"""
|
||||||
|
# Get signature filename and key
|
||||||
|
prefix = "sbin/apk.static.SIGN.RSA."
|
||||||
|
sigfilename = None
|
||||||
|
for filename in tar.getnames():
|
||||||
|
if filename.startswith(prefix):
|
||||||
|
sigfilename = filename
|
||||||
|
break
|
||||||
|
if not sigfilename:
|
||||||
|
raise RuntimeError("Could not find signature filename in apk." +
|
||||||
|
" This means, that your apk file is damaged. Delete it" +
|
||||||
|
" and try again. If the problem persists, fill out a bug" +
|
||||||
|
" report.")
|
||||||
|
sigkey = sigfilename[len(prefix):]
|
||||||
|
logging.debug("sigfilename: " + sigfilename)
|
||||||
|
logging.debug("sigkey: " + sigkey)
|
||||||
|
|
||||||
|
# Get path to keyfile on disk
|
||||||
|
sigkey_path = pmb.config.pmb_src + "/keys/" + sigkey
|
||||||
|
if "/" in sigkey or not os.path.exists(sigkey_path):
|
||||||
|
raise RuntimeError("Invalid signature key: " + sigkey)
|
||||||
|
|
||||||
|
return (sigfilename, sigkey_path)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_temp(tar, sigfilename):
|
||||||
|
"""
|
||||||
|
Extract apk.static and signature as temporary files.
|
||||||
|
"""
|
||||||
|
ret = {
|
||||||
|
"apk": {
|
||||||
|
"filename": "sbin/apk.static",
|
||||||
|
"temp_path": None
|
||||||
|
},
|
||||||
|
"sig": {
|
||||||
|
"filename": sigfilename,
|
||||||
|
"temp_path": None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ftype in ret.keys():
|
||||||
|
member = tar.getmember(ret[ftype]["filename"])
|
||||||
|
|
||||||
|
handle, path = tempfile.mkstemp(ftype, "pmbootstrap")
|
||||||
|
handle = open(handle, "wb")
|
||||||
|
ret[ftype]["temp_path"] = path
|
||||||
|
shutil.copyfileobj(tar.extractfile(member), handle)
|
||||||
|
|
||||||
|
logging.debug("extracted: " + path)
|
||||||
|
handle.close()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def verify_signature(args, files, sigkey_path):
|
||||||
|
"""
|
||||||
|
Verify the signature with openssl.
|
||||||
|
|
||||||
|
:param files: return value from extract_temp()
|
||||||
|
:raises RuntimeError: when verification failed and removes temp files
|
||||||
|
"""
|
||||||
|
logging.debug("Verify apk.static signature with " + sigkey_path)
|
||||||
|
try:
|
||||||
|
pmb.helpers.run.user(args, ["openssl", "dgst", "-sha1", "-verify",
|
||||||
|
sigkey_path, "-signature", files[
|
||||||
|
"sig"]["temp_path"],
|
||||||
|
files["apk"]["temp_path"]], check=True)
|
||||||
|
except:
|
||||||
|
os.unlink(files["sig"]["temp_path"])
|
||||||
|
os.unlink(files["apk"]["temp_path"])
|
||||||
|
raise RuntimeError("Failed to validate signature of apk.static."
|
||||||
|
" There's something wrong with the archive - run 'pmbootstrap"
|
||||||
|
" zap -a' and try again!")
|
||||||
|
|
||||||
|
|
||||||
|
def extract(args, version, apk_path):
|
||||||
|
"""
|
||||||
|
Extract everything to temporary locations, verify signatures and reported
|
||||||
|
versions. When everything is right, move the extracted apk.static to the
|
||||||
|
final location.
|
||||||
|
"""
|
||||||
|
# Extract to a temporary path
|
||||||
|
with tarfile.open(apk_path, "r:gz") as tar:
|
||||||
|
sigfilename, sigkey_path = read_signature_info(tar)
|
||||||
|
files = extract_temp(tar, sigfilename)
|
||||||
|
|
||||||
|
# Verify signature
|
||||||
|
verify_signature(args, files, sigkey_path)
|
||||||
|
os.unlink(files["sig"]["temp_path"])
|
||||||
|
temp_path = files["apk"]["temp_path"]
|
||||||
|
|
||||||
|
# Verify the version, that the extracted binary reports
|
||||||
|
logging.debug("Verify the version reported by the apk.static binary" +
|
||||||
|
" (must match the package version " + version + ")")
|
||||||
|
os.chmod(temp_path, os.stat(temp_path).st_mode | stat.S_IEXEC)
|
||||||
|
version_bin = pmb.helpers.run.user(args, [temp_path, "--version"],
|
||||||
|
check=True, return_stdout=True)
|
||||||
|
version_bin = version_bin.split(" ")[1].split(",")[0]
|
||||||
|
if not version.startswith(version_bin + "-r"):
|
||||||
|
os.unlink(temp_path)
|
||||||
|
raise RuntimeError("Downloaded apk-tools-static-" + version + ".apk,"
|
||||||
|
" but the apk binary inside that package reports to be"
|
||||||
|
" version: " + version_bin + "! Looks like a downgrade attack"
|
||||||
|
" from a malicious server! Switch the server (-m) and try again.")
|
||||||
|
|
||||||
|
# Move it to the right path
|
||||||
|
target_path = args.work + "/apk.static"
|
||||||
|
shutil.move(temp_path, target_path)
|
||||||
|
|
||||||
|
|
||||||
|
def download(args, file):
|
||||||
|
"""
|
||||||
|
Download a single file from an Alpine mirror.
|
||||||
|
"""
|
||||||
|
base_url = args.mirror_alpine + "edge/main/" + args.arch_native
|
||||||
|
return pmb.helpers.http.download(args, base_url + "/" + file, file)
|
||||||
|
|
||||||
|
|
||||||
|
def init(args):
|
||||||
|
"""
|
||||||
|
Download, verify, extract $WORK/apk.static.
|
||||||
|
"""
|
||||||
|
base_url = args.mirror_alpine + "edge/main/" + args.arch_native
|
||||||
|
apkindex = download(args, "APKINDEX.tar.gz")
|
||||||
|
index_data = pmb.parse.apkindex.read(args, "apk-tools-static", apkindex)
|
||||||
|
version = index_data["version"]
|
||||||
|
version_min = pmb.config.apk_tools_static_min_version
|
||||||
|
apk_name = "apk-tools-static-" + version + ".apk"
|
||||||
|
if pmb.parse.apkindex.compare_version(version, version_min) == -1:
|
||||||
|
raise RuntimeError("Server provides an outdated version of"
|
||||||
|
" apk-tools-static: " + version +
|
||||||
|
" (expected at least " + version_min +
|
||||||
|
"). Looks like a downgrade attack from a"
|
||||||
|
" malicious server! Switch the server (-m) and try again!")
|
||||||
|
apk_static = download(args, apk_name)
|
||||||
|
extract(args, version, apk_static)
|
||||||
|
|
||||||
|
|
||||||
|
def run(args, parameters, check):
|
||||||
|
pmb.helpers.run.root(
|
||||||
|
args, [args.work + "/apk.static"] + parameters, check=check)
|
|
@ -0,0 +1,68 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.parse
|
||||||
|
import pmb.parse.arch
|
||||||
|
|
||||||
|
|
||||||
|
def is_registered(arch_debian):
|
||||||
|
return os.path.exists("/proc/sys/fs/binfmt_misc/qemu-" + arch_debian)
|
||||||
|
|
||||||
|
|
||||||
|
def register(args, arch):
|
||||||
|
"""
|
||||||
|
Get arch, magic, mask.
|
||||||
|
"""
|
||||||
|
arch_debian = pmb.parse.arch.alpine_to_debian(arch)
|
||||||
|
if is_registered(arch_debian):
|
||||||
|
return
|
||||||
|
pmb.chroot.apk.install(args, ["qemu-user-static-repack",
|
||||||
|
"qemu-user-static-repack-binfmt"])
|
||||||
|
info = pmb.parse.binfmt_info(args, arch_debian)
|
||||||
|
|
||||||
|
# Build registration string
|
||||||
|
# https://en.wikipedia.org/wiki/Binfmt_misc
|
||||||
|
# :name:type:offset:magic:mask:interpreter:flags
|
||||||
|
name = "qemu-" + arch_debian
|
||||||
|
type = "M"
|
||||||
|
offset = ""
|
||||||
|
magic = info["magic"]
|
||||||
|
mask = info["mask"]
|
||||||
|
interpreter = "/usr/bin/qemu-" + arch_debian + "-static"
|
||||||
|
flags = "C"
|
||||||
|
code = ":".join(["", name, type, offset, magic, mask, interpreter,
|
||||||
|
flags])
|
||||||
|
|
||||||
|
# Register in binfmt_misc
|
||||||
|
logging.info("Register qemu binfmt (" + arch_debian + ")")
|
||||||
|
register = "/proc/sys/fs/binfmt_misc/register"
|
||||||
|
pmb.helpers.run.root(
|
||||||
|
args, ["sh", "-c", 'echo "' + code + '" > ' + register])
|
||||||
|
|
||||||
|
|
||||||
|
def unregister(args, arch):
|
||||||
|
arch_debian = pmb.parse.arch.alpine_to_debian(arch)
|
||||||
|
binfmt_file = "/proc/sys/fs/binfmt_misc/qemu-" + arch_debian
|
||||||
|
if not os.path.exists(binfmt_file):
|
||||||
|
return
|
||||||
|
logging.info("Unregister qemu binfmt (" + arch_debian + ")")
|
||||||
|
pmb.helpers.run.root(args, ["sh", "-c", "echo -1 > " + binfmt_file])
|
|
@ -0,0 +1,81 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import errno
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.config
|
||||||
|
import pmb.chroot.apk
|
||||||
|
|
||||||
|
|
||||||
|
def get_pid(args):
|
||||||
|
pidfile = args.work + "/chroot_native/home/user/distccd.pid"
|
||||||
|
if not os.path.exists(pidfile):
|
||||||
|
return None
|
||||||
|
with open(pidfile, "r") as handle:
|
||||||
|
lines = handle.readlines()
|
||||||
|
return int(lines[0][:-1])
|
||||||
|
|
||||||
|
|
||||||
|
def is_running(args):
|
||||||
|
# Get the PID
|
||||||
|
pid = get_pid(args)
|
||||||
|
if not pid:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Verify, if it still exists by sending a kill signal
|
||||||
|
try:
|
||||||
|
os.kill(pid, 0)
|
||||||
|
except OSError as err:
|
||||||
|
if err.errno == errno.ESRCH: # no such process
|
||||||
|
pmb.chroot.root(args, ["rm", "/home/user/distccd.pid"])
|
||||||
|
return False
|
||||||
|
elif err.errno == errno.EPERM: # access denied
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def start(args):
|
||||||
|
if is_running(args):
|
||||||
|
return
|
||||||
|
pmb.chroot.apk.install(args, ["distcc", "gcc-cross-wrappers"])
|
||||||
|
|
||||||
|
# Start daemon with cross-compiler in path
|
||||||
|
arch = args.deviceinfo["arch"]
|
||||||
|
path = "/usr/lib/gcc-cross-wrappers/" + arch + "/bin:" + pmb.config.chroot_path
|
||||||
|
daemon = ["PATH=" + path,
|
||||||
|
"distccd",
|
||||||
|
"--pid-file", "/home/user/distccd.pid",
|
||||||
|
"--listen", "127.0.0.1",
|
||||||
|
"--allow", "127.0.0.1",
|
||||||
|
"--port", args.port_distccd,
|
||||||
|
"--log-file", "/home/user/distccd.log",
|
||||||
|
"--jobs", args.jobs,
|
||||||
|
"--nice", "19",
|
||||||
|
"--job-lifetime", "60",
|
||||||
|
"--daemon"
|
||||||
|
]
|
||||||
|
logging.info("(native) start distccd (listen on 127.0.0.1:" +
|
||||||
|
args.port_distccd + ")")
|
||||||
|
pmb.chroot.user(args, daemon)
|
||||||
|
|
||||||
|
|
||||||
|
def stop(args):
|
||||||
|
if is_running(args):
|
||||||
|
logging.info("(native) stop distccd")
|
||||||
|
pmb.chroot.user(args, ["kill", str(get_pid(args))])
|
|
@ -0,0 +1,123 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import glob
|
||||||
|
import filecmp
|
||||||
|
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.chroot.apk_static
|
||||||
|
import pmb.config
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.parse.arch
|
||||||
|
|
||||||
|
|
||||||
|
def copy_resolv_conf(args, suffix="native"):
|
||||||
|
"""
|
||||||
|
Use pythons super fast file compare function (due to caching)
|
||||||
|
and copy the /etc/resolv.conf to the chroot, in case it is
|
||||||
|
different from the host.
|
||||||
|
"""
|
||||||
|
host = "/etc/resolv.conf"
|
||||||
|
chroot = args.work + "/chroot_" + suffix + host
|
||||||
|
if not os.path.exists(chroot) or not filecmp.cmp(host, chroot):
|
||||||
|
pmb.helpers.run.root(args, ["cp", host, chroot])
|
||||||
|
|
||||||
|
|
||||||
|
def init(args, suffix="native"):
|
||||||
|
# When already initialized: just prepare the chroot
|
||||||
|
chroot = args.work + "/chroot_" + suffix
|
||||||
|
arch = pmb.parse.arch.from_chroot_suffix(args, suffix)
|
||||||
|
pmb.chroot.mount(args, suffix)
|
||||||
|
if os.path.islink(chroot + "/bin/sh"):
|
||||||
|
if suffix != "native":
|
||||||
|
pmb.chroot.binfmt.register(args, arch)
|
||||||
|
copy_resolv_conf(args, suffix)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Require apk-tools-static
|
||||||
|
pmb.chroot.apk_static.init(args)
|
||||||
|
|
||||||
|
# Non-native chroot: require qemu-user-static
|
||||||
|
if suffix != "native":
|
||||||
|
pmb.chroot.apk.install(args, ["qemu-user-static-repack",
|
||||||
|
"qemu-user-static-repack-binfmt"])
|
||||||
|
pmb.chroot.binfmt.register(args, arch)
|
||||||
|
|
||||||
|
logging.info("(" + suffix + ") install alpine-base")
|
||||||
|
|
||||||
|
# Initialize cache
|
||||||
|
apk_cache = args.work + "/cache_apk_" + arch
|
||||||
|
pmb.helpers.run.root(args, ["ln", "-s", "/var/cache/apk", chroot +
|
||||||
|
"/etc/apk/cache"])
|
||||||
|
|
||||||
|
# Copy /etc/apk/keys/ and resolv.conf
|
||||||
|
logging.debug(pmb.config.apk_keys_path)
|
||||||
|
for key in glob.glob(pmb.config.apk_keys_path + "/*.pub"):
|
||||||
|
pmb.helpers.run.root(args, ["cp", key, args.work +
|
||||||
|
"/config_apk_keys/"])
|
||||||
|
copy_resolv_conf(args, suffix)
|
||||||
|
|
||||||
|
# Write /etc/apk/repositories
|
||||||
|
repos_path = chroot + "/etc/apk/repositories"
|
||||||
|
if not os.path.exists(repos_path):
|
||||||
|
lines = ["/home/user/packages/user"]
|
||||||
|
directories = ["main", "community"]
|
||||||
|
if args.alpine_version == "edge":
|
||||||
|
directories.append("testing")
|
||||||
|
for dir in directories:
|
||||||
|
lines.append(args.mirror_alpine + args.alpine_version +
|
||||||
|
"/" + dir)
|
||||||
|
for line in lines:
|
||||||
|
pmb.helpers.run.root(args, ["sh", "-c",
|
||||||
|
"echo " + shlex.quote(line) + " >> " + repos_path])
|
||||||
|
|
||||||
|
# Install alpine-base (no clean exit for non-native chroot!)
|
||||||
|
pmb.chroot.apk_static.run(args, ["-U", "--root", chroot,
|
||||||
|
"--cache-dir", apk_cache, "--initdb", "--arch", arch,
|
||||||
|
"add", "alpine-base"], check=(suffix == "native"))
|
||||||
|
|
||||||
|
# Create device nodes
|
||||||
|
for dev in pmb.config.chroot_device_nodes:
|
||||||
|
path = chroot + "/dev/" + str(dev[4])
|
||||||
|
if not os.path.exists(path):
|
||||||
|
pmb.helpers.run.root(args, ["mknod",
|
||||||
|
"-m", str(dev[0]), # permissions
|
||||||
|
path, # name
|
||||||
|
str(dev[1]), # type
|
||||||
|
str(dev[2]), # major
|
||||||
|
str(dev[3]), # minor
|
||||||
|
])
|
||||||
|
|
||||||
|
# Non-native chroot: install qemu-user-binary, run apk fix
|
||||||
|
if suffix != "native":
|
||||||
|
arch_debian = pmb.parse.arch.alpine_to_debian(arch)
|
||||||
|
pmb.helpers.run.root(args, ["cp", args.work +
|
||||||
|
"/chroot_native/usr/bin/qemu-" + arch_debian + "-static",
|
||||||
|
chroot + "/usr/bin/qemu-" + arch_debian + "-static"])
|
||||||
|
pmb.chroot.root(args, ["apk", "fix"], suffix,
|
||||||
|
auto_init=False)
|
||||||
|
|
||||||
|
# Add user (-D: don't assign password)
|
||||||
|
logging.debug("Add user")
|
||||||
|
pmb.chroot.root(args, ["adduser", "-D", "user", "-u", pmb.config.chroot_uid_user],
|
||||||
|
suffix, auto_init=False)
|
||||||
|
pmb.chroot.root(args, ["chown", "-R", "user:user", "/home/user"],
|
||||||
|
suffix)
|
|
@ -0,0 +1,37 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import pmb.config
|
||||||
|
import pmb.parse
|
||||||
|
import pmb.helpers.mount
|
||||||
|
|
||||||
|
|
||||||
|
def mount(args, suffix="native"):
|
||||||
|
arch = pmb.parse.arch.from_chroot_suffix(args, suffix)
|
||||||
|
|
||||||
|
# get all mountpoints
|
||||||
|
mountpoints = {}
|
||||||
|
for source, target in pmb.config.chroot_mount_bind.items():
|
||||||
|
source = source.replace("$WORK", args.work)
|
||||||
|
source = source.replace("$ARCH", arch)
|
||||||
|
mountpoints[source] = target
|
||||||
|
|
||||||
|
# mount if necessary
|
||||||
|
for source, target in mountpoints.items():
|
||||||
|
target_full = args.work + "/chroot_" + suffix + target
|
||||||
|
pmb.helpers.mount.bind(args, source, target_full)
|
|
@ -0,0 +1,30 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
|
||||||
|
def installed_kernel_flavors(args, suffix):
|
||||||
|
prefix = "vmlinuz-"
|
||||||
|
prefix_len = len(prefix)
|
||||||
|
pattern = args.work + "/chroot_" + suffix + "/boot/" + prefix + "*"
|
||||||
|
ret = []
|
||||||
|
for file in glob.glob(pattern):
|
||||||
|
ret.append(os.path.basename(file)[prefix_len:])
|
||||||
|
return ret
|
|
@ -0,0 +1,72 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
import pmb.config
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.chroot.binfmt
|
||||||
|
import pmb.helpers.run
|
||||||
|
|
||||||
|
|
||||||
|
def root(args, cmd, suffix="native", working_dir="/", log=True,
|
||||||
|
auto_init=True, return_stdout=False, check=True):
|
||||||
|
"""
|
||||||
|
Run a command inside a chroot as root.
|
||||||
|
|
||||||
|
:param log: When set to true, redirect all output to the logfile
|
||||||
|
:param auto_init: Automatically initialize the chroot
|
||||||
|
"""
|
||||||
|
# Get and verify chroot folder
|
||||||
|
chroot = args.work + "/chroot_" + suffix
|
||||||
|
if not auto_init and not os.path.islink(chroot + "/bin/sh"):
|
||||||
|
raise RuntimeError("Chroot does not exist: " + chroot)
|
||||||
|
|
||||||
|
pmb.chroot.init(args, suffix)
|
||||||
|
|
||||||
|
# Run the args with sudo chroot, and with cleaned environment
|
||||||
|
# variables
|
||||||
|
sh_bin = shutil.which("sh")
|
||||||
|
chroot_bin = shutil.which("chroot")
|
||||||
|
for i in range(len(cmd)):
|
||||||
|
cmd[i] = shlex.quote(cmd[i])
|
||||||
|
|
||||||
|
cmd_inner_shell = ("cd " + shlex.quote(working_dir) + ";" +
|
||||||
|
" ".join(cmd))
|
||||||
|
cmd_full = ["sudo", sh_bin, "-c",
|
||||||
|
"unset $(env | cut -d= -f1);" + # unset all
|
||||||
|
" CHARSET=UTF-8" +
|
||||||
|
" PATH=" + pmb.config.chroot_path +
|
||||||
|
" SHELL=/bin/ash" +
|
||||||
|
" HISTFILE=~/.ash_history" +
|
||||||
|
" " + chroot_bin +
|
||||||
|
" " + chroot +
|
||||||
|
" sh -c " + shlex.quote(cmd_inner_shell)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Generate log message
|
||||||
|
log_message = "(" + suffix + ") % "
|
||||||
|
if working_dir != "/":
|
||||||
|
log_message += "cd " + working_dir + " && "
|
||||||
|
log_message += " ".join(cmd)
|
||||||
|
|
||||||
|
# Run the command
|
||||||
|
return pmb.helpers.run.core(args, cmd_full, log_message, log,
|
||||||
|
return_stdout, check)
|
|
@ -0,0 +1,53 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pmb.install.losetup
|
||||||
|
import pmb.helpers.mount
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.chroot.distccd
|
||||||
|
|
||||||
|
|
||||||
|
def shutdown(args, only_install_related=False):
|
||||||
|
pmb.chroot.distccd.stop(args)
|
||||||
|
|
||||||
|
# Umount installation-related paths (order is important!)
|
||||||
|
pmb.helpers.mount.umount_all(args, args.work +
|
||||||
|
"/chroot_native/mnt/install/boot")
|
||||||
|
pmb.helpers.mount.umount_all(args, args.work +
|
||||||
|
"/chroot_native/mnt/install")
|
||||||
|
if os.path.exists(args.work + "/chroot_native/dev/mapper/pm_crypt"):
|
||||||
|
pmb.chroot.root(args, ["cryptsetup", "luksClose", "pm_crypt"])
|
||||||
|
|
||||||
|
# Umount all losetup mounted images
|
||||||
|
chroot = args.work + "/chroot_native"
|
||||||
|
if pmb.helpers.mount.ismount(chroot + "/dev/loop-control"):
|
||||||
|
pattern = chroot + "/home/user/rootfs/*.img"
|
||||||
|
for path_outside in glob.glob(pattern):
|
||||||
|
path = path_outside[len(chroot):]
|
||||||
|
pmb.install.losetup.umount(args, path)
|
||||||
|
|
||||||
|
if not only_install_related:
|
||||||
|
# Clean up the rest
|
||||||
|
pmb.helpers.mount.umount_all(args, args.work)
|
||||||
|
pmb.helpers.mount.umount_all(args, args.work)
|
||||||
|
pmb.chroot.binfmt.unregister(args, args.deviceinfo["arch"])
|
||||||
|
logging.info("Shutdown complete")
|
|
@ -0,0 +1,32 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import pmb.chroot.root
|
||||||
|
|
||||||
|
|
||||||
|
def user(args, cmd, suffix="native", working_dir="/", log=True,
|
||||||
|
auto_init=True, return_stdout=False, check=True):
|
||||||
|
"""
|
||||||
|
Run a command inside a chroot as "user"
|
||||||
|
|
||||||
|
:param log: When set to true, redirect all output to the logfile
|
||||||
|
:param auto_init: Automatically initialize the chroot
|
||||||
|
"""
|
||||||
|
cmd = ["su", "user", "-c", " ".join(cmd)]
|
||||||
|
return pmb.chroot.root(args, cmd, suffix, working_dir, log,
|
||||||
|
auto_init, return_stdout, check)
|
|
@ -0,0 +1,46 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.helpers.run
|
||||||
|
|
||||||
|
|
||||||
|
def zap(args):
|
||||||
|
pmb.chroot.shutdown(args)
|
||||||
|
patterns = [
|
||||||
|
"chroot_native",
|
||||||
|
"chroot_buildroot_" + args.deviceinfo["arch"],
|
||||||
|
"chroot_rootfs_" + args.device,
|
||||||
|
]
|
||||||
|
|
||||||
|
# Only ask for removal, if the user specificed the extra '-p' switch.
|
||||||
|
# Deleting the packages by accident is really annoying.
|
||||||
|
if args.packages:
|
||||||
|
patterns += ["packages"]
|
||||||
|
if args.http:
|
||||||
|
patterns += ["cache_http"]
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
pattern = os.path.abspath(args.work + "/" + pattern)
|
||||||
|
matches = glob.glob(pattern)
|
||||||
|
for match in matches:
|
||||||
|
if pmb.helpers.cli.ask(args, "Remove " + match + "?") == "y":
|
||||||
|
pmb.helpers.run.root(args, ["rm", "-rf", match])
|
|
@ -0,0 +1,225 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
#
|
||||||
|
# Exported functions
|
||||||
|
#
|
||||||
|
from pmb.config.init import init
|
||||||
|
from pmb.config.load import load
|
||||||
|
from pmb.config.save import save
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Exported variables (internal configuration)
|
||||||
|
#
|
||||||
|
version = "0.1.0"
|
||||||
|
pmb_src = os.path.normpath(os.path.realpath(__file__) + "/../../..")
|
||||||
|
apk_keys_path = pmb_src + "/keys"
|
||||||
|
|
||||||
|
# Update this frequently to prevent a MITM attack with an outdated version
|
||||||
|
# (which may contain a vulnerable apk/libressl, and allows and attacker to
|
||||||
|
# exploit the system!)
|
||||||
|
apk_tools_static_min_version = "2.7.1-r0"
|
||||||
|
|
||||||
|
# Config file/commandline default values
|
||||||
|
# $WORK gets replaced with the actual value for args.work (which may be
|
||||||
|
# overriden on the commandline)
|
||||||
|
defaults = {
|
||||||
|
"alpine_version": "edge", # alternatively: latest-stable
|
||||||
|
"aports": os.path.normpath(pmb_src + "/../aports"),
|
||||||
|
"config": os.path.expanduser("~") + "/.config/pmbootstrap.cfg",
|
||||||
|
"device": "samsung-i9100",
|
||||||
|
"log": "$WORK/log.txt",
|
||||||
|
"mirror_alpine": "https://nl.alpinelinux.org/alpine/",
|
||||||
|
"work": os.path.expanduser("~") + "/.local/var/pmbootstrap",
|
||||||
|
"port_distccd": "33632",
|
||||||
|
|
||||||
|
# aes-xts-plain64 would be better, but this is not supported on LineageOS
|
||||||
|
# kernel configs
|
||||||
|
"cipher": "aes-cbc-plain64"
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# CHROOT
|
||||||
|
#
|
||||||
|
|
||||||
|
# Usually the ID for the first user created is 1000. However, we want
|
||||||
|
# pmbootstrap to work even if the 'user' account inside the chroots has
|
||||||
|
# another UID, so we force it to be different.
|
||||||
|
chroot_uid_user = "12345"
|
||||||
|
|
||||||
|
# The PATH variable used inside all chroots
|
||||||
|
chroot_path = ":".join([
|
||||||
|
"/usr/lib/ccache/bin",
|
||||||
|
"/usr/local/sbin",
|
||||||
|
"/usr/local/bin",
|
||||||
|
"/usr/sbin:/usr/bin",
|
||||||
|
"/sbin",
|
||||||
|
"/bin"
|
||||||
|
])
|
||||||
|
|
||||||
|
# Folders, that get mounted inside the chroot
|
||||||
|
# $WORK gets replaced with args.work
|
||||||
|
# $ARCH gets replaced with the chroot architecture (eg. x86_64, armhf)
|
||||||
|
chroot_mount_bind = {
|
||||||
|
"/proc": "/proc",
|
||||||
|
"$WORK/cache_apk_$ARCH": "/var/cache/apk",
|
||||||
|
"$WORK/cache_ccache_$ARCH": "/home/user/.ccache",
|
||||||
|
"$WORK/cache_distfiles": "/var/cache/distfiles",
|
||||||
|
"$WORK/cache_git": "/home/user/git",
|
||||||
|
"$WORK/config_abuild": "/home/user/.abuild",
|
||||||
|
"$WORK/config_apk_keys": "/etc/apk/keys",
|
||||||
|
"$WORK/packages": "/home/user/packages/user",
|
||||||
|
}
|
||||||
|
|
||||||
|
# The package alpine-base only creates some device nodes. Specify here, which
|
||||||
|
# additional nodes will get created during initialization of the chroot.
|
||||||
|
# Syntax for each entry: [permissions, type, major, minor, name]
|
||||||
|
chroot_device_nodes = [
|
||||||
|
[666, "c", 1, 5, "zero"],
|
||||||
|
[666, "c", 1, 7, "full"],
|
||||||
|
[644, "c", 1, 8, "random"],
|
||||||
|
[644, "c", 1, 9, "urandom"],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUILD
|
||||||
|
#
|
||||||
|
|
||||||
|
# Packages, that will be installed in a chroot before it build packages
|
||||||
|
# for the first time
|
||||||
|
build_packages = ["abuild", "build-base", "ccache"]
|
||||||
|
|
||||||
|
# fnmatch for supported pkgnames, that can be directly compiled inside
|
||||||
|
# the native chroot and a cross-compiler, without using distcc
|
||||||
|
build_cross_native = ["linux-*"]
|
||||||
|
|
||||||
|
# Variables in APKBUILD files, that get parsed
|
||||||
|
apkbuild_attributes = {
|
||||||
|
"arch": {"array": True},
|
||||||
|
"depends": {"array": True},
|
||||||
|
"makedepends": {"array": True},
|
||||||
|
"options": {"array": True},
|
||||||
|
"pkgname": {"array": False},
|
||||||
|
"pkgrel": {"array": False},
|
||||||
|
"pkgver": {"array": False},
|
||||||
|
"subpackages": {"array": True},
|
||||||
|
|
||||||
|
# cross-compilers
|
||||||
|
"makedepends_build": {"array": True},
|
||||||
|
"makedepends_host": {"array": True},
|
||||||
|
|
||||||
|
# kernels
|
||||||
|
"_flavor": {"array": False},
|
||||||
|
"_device": {"array": False},
|
||||||
|
"_kernver": {"array": False},
|
||||||
|
"_pmb_build_in_native_chroot": {"array": False},
|
||||||
|
|
||||||
|
# mesa
|
||||||
|
"_llvmver": {"array": False},
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# INSTALL
|
||||||
|
#
|
||||||
|
|
||||||
|
# Packages, that will be installed inside the native chroot to perform
|
||||||
|
# the installation to the device.
|
||||||
|
# util-linux: losetup, fallocate
|
||||||
|
install_native_packages = ["cryptsetup", "util-linux", "e2fsprogs", "parted"]
|
||||||
|
install_device_packages = [
|
||||||
|
|
||||||
|
# postmarketos
|
||||||
|
"postmarketos-base", "postmarketos-demos",
|
||||||
|
|
||||||
|
# weston
|
||||||
|
"weston", "weston-shell-desktop", "weston-backend-fbdev", "weston-backend-drm",
|
||||||
|
"weston-backend-x11", "weston-clients", "weston-terminal",
|
||||||
|
"weston-xwayland", "xorg-server-xwayland",
|
||||||
|
|
||||||
|
# other
|
||||||
|
"ttf-droid"
|
||||||
|
]
|
||||||
|
install_size_image = "835M"
|
||||||
|
install_size_boot = "100M"
|
||||||
|
|
||||||
|
# fnmatch-patterns, that the sdcard patch must match. Otherwise the
|
||||||
|
# installer will refuse to format the device.
|
||||||
|
install_valid_sdcard_devices = ["/dev/mmcblk*", "/dev/loop*"]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# FLASH
|
||||||
|
#
|
||||||
|
|
||||||
|
# These folders will be mounted at the same location into the native
|
||||||
|
# chroot, before the flash programs get started.
|
||||||
|
flash_mount_bind = [
|
||||||
|
"/sys/bus/usb/devices/",
|
||||||
|
"/sys/devices/",
|
||||||
|
"/dev/bus/usb/"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Allowed variables:
|
||||||
|
# $KERNEL, $RAMDISK, $IMAGE (system partition image), $BOOTPARAM
|
||||||
|
flashers = {
|
||||||
|
"fastboot": {
|
||||||
|
"depends": ["android-tools"],
|
||||||
|
"actions":
|
||||||
|
{
|
||||||
|
"list_devices": [["fastboot", "devices", "-l"]],
|
||||||
|
"flash_system": [["fastboot", "flash", "system", "$IMAGE"]],
|
||||||
|
"flash_kernel": [["fastboot",
|
||||||
|
"--base", "$OFFSET_BASE",
|
||||||
|
"--kernel-offset", "$OFFSET_KERNEL",
|
||||||
|
"--ramdisk-offset", "$OFFSET_RAMDISK",
|
||||||
|
"--tags-offset", "$OFFSET_TAGS",
|
||||||
|
"--page-size", "$PAGE_SIZE",
|
||||||
|
"flash:raw", "$KERNEL", "$RAMDISK"]],
|
||||||
|
"boot": [["fastboot",
|
||||||
|
"--base", "$OFFSET_BASE",
|
||||||
|
"--kernel-offset", "$OFFSET_KERNEL",
|
||||||
|
"--ramdisk-offset", "$OFFSET_RAMDISK",
|
||||||
|
"--tags-offset", "$OFFSET_TAGS",
|
||||||
|
"--page-size", "$PAGE_SIZE",
|
||||||
|
"boot", "$KERNEL", "$RAMDISK"]],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"heimdall": {
|
||||||
|
"depends": ["heimdall"],
|
||||||
|
"actions":
|
||||||
|
{
|
||||||
|
"list_devices": [["heimdall", "detect"]],
|
||||||
|
"flash_system": [
|
||||||
|
["heimdall_wait_for_device.sh"],
|
||||||
|
["heimdall", "flash", "--SYSTEM", "$IMAGE"]],
|
||||||
|
"flash_kernel": [["heimdall_flash_kernel.sh", "$RAMDISK", "$KERNEL"]]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# GIT
|
||||||
|
#
|
||||||
|
git_repos = {
|
||||||
|
"aports_upstream": "https://github.com/alpinelinux/aports",
|
||||||
|
"apk-tools": "https://github.com/alpinelinux/apk-tools",
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import multiprocessing
|
||||||
|
|
||||||
|
import pmb.config
|
||||||
|
import pmb.helpers.cli
|
||||||
|
import pmb.helpers.devices
|
||||||
|
|
||||||
|
|
||||||
|
def init(args):
|
||||||
|
cfg = pmb.config.load(args)
|
||||||
|
|
||||||
|
# Device
|
||||||
|
devices = sorted(pmb.helpers.devices.list(args))
|
||||||
|
logging.info("Target device (either an existing one, or a new one for"
|
||||||
|
" porting). Available: " + ", ".join(devices))
|
||||||
|
cfg["pmbootstrap"]["device"] = pmb.helpers.cli.ask(args, "Device",
|
||||||
|
None, args.device)
|
||||||
|
|
||||||
|
# Work folder
|
||||||
|
logging.info("Location of the 'work' path. Multiple chroots (native,"
|
||||||
|
" device arch, device rootfs) will be created in there.")
|
||||||
|
cfg["pmbootstrap"]["work"] = pmb.helpers.cli.ask(args, "Work path",
|
||||||
|
None, args.work)
|
||||||
|
os.makedirs(cfg["pmbootstrap"]["work"], 0o700, True)
|
||||||
|
|
||||||
|
# Parallel job count
|
||||||
|
default = args.jobs
|
||||||
|
if not default:
|
||||||
|
default = multiprocessing.cpu_count() + 1
|
||||||
|
logging.info("How many jobs should run parallel on this machine, when"
|
||||||
|
" compiling?")
|
||||||
|
cfg["pmbootstrap"]["jobs"] = pmb.helpers.cli.ask(args, "Jobs",
|
||||||
|
None, default)
|
||||||
|
|
||||||
|
# Save config
|
||||||
|
pmb.config.save(args, cfg)
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
"WARNING: The applications in the chroots do not get updated automatically.")
|
||||||
|
logging.info("Run 'pmbootstrap zap' to delete all chroots once a day before"
|
||||||
|
" working with pmbootstrap!")
|
||||||
|
logging.info("It only takes a few seconds, and all packages are cached.")
|
||||||
|
|
||||||
|
logging.info("Done!")
|
|
@ -0,0 +1,36 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import configparser
|
||||||
|
import os
|
||||||
|
import pmb.config
|
||||||
|
|
||||||
|
|
||||||
|
def load(args):
|
||||||
|
cfg = configparser.ConfigParser()
|
||||||
|
if os.path.isfile(args.config):
|
||||||
|
cfg.read(args.config)
|
||||||
|
|
||||||
|
if "pmbootstrap" not in cfg:
|
||||||
|
cfg["pmbootstrap"] = {}
|
||||||
|
|
||||||
|
for key in pmb.config.defaults:
|
||||||
|
if key not in cfg["pmbootstrap"]:
|
||||||
|
cfg["pmbootstrap"][key] = pmb.config.defaults[key]
|
||||||
|
|
||||||
|
return cfg
|
|
@ -0,0 +1,27 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def save(args, cfg):
|
||||||
|
logging.debug("save config: " + args.config)
|
||||||
|
os.makedirs(os.path.dirname(args.config), 0o700, True)
|
||||||
|
with open(args.config, "w") as handle:
|
||||||
|
cfg.write(handle)
|
|
@ -0,0 +1,21 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from pmb.flasher.init import init
|
||||||
|
from pmb.flasher.run import run
|
||||||
|
from pmb.flasher.frontend import frontend
|
|
@ -0,0 +1,88 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pmb.flasher
|
||||||
|
import pmb.install
|
||||||
|
import pmb.chroot.other
|
||||||
|
|
||||||
|
|
||||||
|
def kernel(args):
|
||||||
|
# Parse the kernel flavor
|
||||||
|
suffix = "rootfs_" + args.device
|
||||||
|
flavor = args.flavor
|
||||||
|
flavors = pmb.chroot.other.installed_kernel_flavors(args, suffix)
|
||||||
|
if flavor:
|
||||||
|
if flavor not in flavors:
|
||||||
|
raise RuntimeError("No kernel installed with flavor " + flavor + "!" +
|
||||||
|
" Run 'pmbootstrap flasher list_flavors' to get a list.")
|
||||||
|
elif not len(flavors):
|
||||||
|
raise RuntimeError(
|
||||||
|
"No kernel flavors installed in chroot " + suffix + "!")
|
||||||
|
else:
|
||||||
|
flavor = flavors[0]
|
||||||
|
|
||||||
|
# Generate the paths and run the flasher
|
||||||
|
pmb.flasher.init(args)
|
||||||
|
mnt = "/mnt/rootfs_" + args.device
|
||||||
|
kernel = mnt + "/boot/vmlinuz-" + flavor
|
||||||
|
ramdisk = mnt + "/boot/initramfs-" + flavor
|
||||||
|
if args.action_flasher == "boot":
|
||||||
|
logging.info("(native) boot " + flavor + " kernel")
|
||||||
|
pmb.flasher.run(args, "boot", kernel, ramdisk)
|
||||||
|
else:
|
||||||
|
logging.info("(native) flash kernel '" + flavor + "'")
|
||||||
|
pmb.flasher.run(args, "flash_kernel", kernel, ramdisk)
|
||||||
|
|
||||||
|
|
||||||
|
def list_flavors(args):
|
||||||
|
suffix = "rootfs_" + args.device
|
||||||
|
logging.info("(" + suffix + ") installed kernel flavors:")
|
||||||
|
for flavor in pmb.chroot.other.installed_kernel_flavors(args, suffix):
|
||||||
|
logging.info("* " + flavor)
|
||||||
|
|
||||||
|
|
||||||
|
def system(args):
|
||||||
|
# Generate system image, install flasher
|
||||||
|
img_path = "/home/user/rootfs/" + args.device + ".img"
|
||||||
|
if not os.path.exists(args.work + "/chroot_native" + img_path):
|
||||||
|
setattr(args, "sdcard", None)
|
||||||
|
pmb.install.install(args, False)
|
||||||
|
pmb.flasher.init(args)
|
||||||
|
|
||||||
|
# Run the flasher
|
||||||
|
logging.info("(native) flash system image")
|
||||||
|
pmb.flasher.run(args, "flash_system", image=img_path)
|
||||||
|
|
||||||
|
|
||||||
|
def list_devices(args):
|
||||||
|
pmb.flasher.run(args, "list_devices")
|
||||||
|
|
||||||
|
|
||||||
|
def frontend(args):
|
||||||
|
action = args.action_flasher
|
||||||
|
if action in ["boot", "flash_kernel"]:
|
||||||
|
kernel(args)
|
||||||
|
if action == "flash_system":
|
||||||
|
system(args)
|
||||||
|
if action == "list_flavors":
|
||||||
|
list_flavors(args)
|
||||||
|
if action == "list_devices":
|
||||||
|
list_devices(args)
|
|
@ -0,0 +1,46 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import pmb.config
|
||||||
|
import pmb.chroot.apk
|
||||||
|
import pmb.helpers.mount
|
||||||
|
|
||||||
|
|
||||||
|
def init(args):
|
||||||
|
# Validate method
|
||||||
|
method = args.deviceinfo["flash_methods"]
|
||||||
|
if method not in pmb.config.flashers:
|
||||||
|
raise RuntimeError("Flash method " + method + " is not supported by the"
|
||||||
|
" current configuration. However, adding a new flash method is "
|
||||||
|
" not that hard, when the flashing application already exists.\n"
|
||||||
|
"Make sure, it is packaged for Alpine Linux, or package it "
|
||||||
|
" yourself, and then add it to pmb/config/__init__.py.")
|
||||||
|
cfg = pmb.config.flashers[method]
|
||||||
|
|
||||||
|
# Install depends
|
||||||
|
pmb.chroot.apk.install(args, cfg["depends"])
|
||||||
|
|
||||||
|
# Mount folders from host system
|
||||||
|
for folder in pmb.config.flash_mount_bind:
|
||||||
|
pmb.helpers.mount.bind(args, folder, args.work +
|
||||||
|
"/chroot_native" + folder)
|
||||||
|
|
||||||
|
# Mount device chroot inside native chroot (required for kernel/ramdisk)
|
||||||
|
mountpoint = "/mnt/rootfs_" + args.device
|
||||||
|
pmb.helpers.mount.bind(args, args.work + "/chroot_rootfs_" + args.device,
|
||||||
|
args.work + "/chroot_native" + mountpoint)
|
|
@ -0,0 +1,58 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import pmb.flasher
|
||||||
|
|
||||||
|
|
||||||
|
def run(args, action, kernel=None, ramdisk=None, image=None):
|
||||||
|
pmb.flasher.init(args)
|
||||||
|
|
||||||
|
# Verify action
|
||||||
|
method = args.deviceinfo["flash_methods"]
|
||||||
|
cfg = pmb.config.flashers[method]
|
||||||
|
if action not in cfg["actions"]:
|
||||||
|
raise RuntimeError("action " + action + " is not"
|
||||||
|
" configured for method " + method + "!")
|
||||||
|
|
||||||
|
# Variable setup
|
||||||
|
vars = {
|
||||||
|
"$KERNEL": kernel,
|
||||||
|
"$RAMDISK": ramdisk,
|
||||||
|
"$IMAGE": image,
|
||||||
|
"$OFFSET_BASE": args.deviceinfo["flash_offset_base"],
|
||||||
|
"$OFFSET_KERNEL": args.deviceinfo["flash_offset_kernel"],
|
||||||
|
"$OFFSET_RAMDISK": args.deviceinfo["flash_offset_ramdisk"],
|
||||||
|
"$OFFSET_SECOND": args.deviceinfo["flash_offset_second"],
|
||||||
|
"$OFFSET_TAGS": args.deviceinfo["flash_offset_tags"],
|
||||||
|
"$PAGE_SIZE": args.deviceinfo["flash_pagesize"],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Each action has multiple commands
|
||||||
|
for command in cfg["actions"][action]:
|
||||||
|
# Variable replacement
|
||||||
|
for key, value in vars.items():
|
||||||
|
for i in range(len(command)):
|
||||||
|
if key in command[i]:
|
||||||
|
if not value:
|
||||||
|
raise RuntimeError("Variable " + key + " found in"
|
||||||
|
" action " + action + " for method " + method + ","
|
||||||
|
" but the value for this variable is None!")
|
||||||
|
command[i] = command[i].replace(key, value)
|
||||||
|
|
||||||
|
# Run the action
|
||||||
|
pmb.chroot.root(args, command, log=False)
|
|
@ -0,0 +1,18 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
|
@ -0,0 +1,39 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def ask(args, question="Continue?", choices=['y', 'n'], default='n',
|
||||||
|
lowercase_answer=True):
|
||||||
|
date = datetime.datetime.now().strftime("%H:%M:%S")
|
||||||
|
question = "[" + date + "] " + question
|
||||||
|
if choices:
|
||||||
|
question += " (" + str.join("/", choices) + ")"
|
||||||
|
if default:
|
||||||
|
question += " [" + str(default) + "]"
|
||||||
|
|
||||||
|
ret = input(question + ": ")
|
||||||
|
if lowercase_answer:
|
||||||
|
ret = ret.lower()
|
||||||
|
if ret == "":
|
||||||
|
ret = str(default)
|
||||||
|
|
||||||
|
args.logfd.write(question + " " + ret + "\n")
|
||||||
|
args.logfd.flush()
|
||||||
|
return ret
|
|
@ -0,0 +1,44 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import pmb.parse
|
||||||
|
|
||||||
|
|
||||||
|
def list(args):
|
||||||
|
"""
|
||||||
|
Get all devices, for which aports are available
|
||||||
|
:returns: ["first-device", "second-device", ...]
|
||||||
|
"""
|
||||||
|
ret = []
|
||||||
|
for path in glob.glob(args.aports + "/device-*"):
|
||||||
|
device = os.path.basename(path).split("-", 1)[1]
|
||||||
|
ret += [device]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def list_apkbuilds(args):
|
||||||
|
"""
|
||||||
|
:returns: { "first-device": {"pkgname": ..., "pkgver": ...}, ... }
|
||||||
|
"""
|
||||||
|
ret = {}
|
||||||
|
for device in list(args):
|
||||||
|
apkbuild_path = args.aports + "/device-" + device + "/APKBUILD"
|
||||||
|
ret[device] = pmb.parse.apkbuild(apkbuild_path)
|
||||||
|
return ret
|
|
@ -0,0 +1,27 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
def replace(path, old, new):
|
||||||
|
text = ""
|
||||||
|
with open(path, 'r') as handle:
|
||||||
|
text = handle.read()
|
||||||
|
|
||||||
|
text = text.replace(old, new)
|
||||||
|
|
||||||
|
with open(path, 'w') as handle:
|
||||||
|
handle.write(text)
|
|
@ -0,0 +1,35 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pmb.build
|
||||||
|
import pmb.chroot.apk
|
||||||
|
import pmb.config
|
||||||
|
|
||||||
|
|
||||||
|
def clone(args, repo_name):
|
||||||
|
if repo_name not in pmb.config.git_repos:
|
||||||
|
raise ValueError("No git repository configured for " + repo_name)
|
||||||
|
|
||||||
|
if not os.path.exists(args.work + "/cache_git/" + repo_name):
|
||||||
|
pmb.chroot.apk.install(args, ["git"])
|
||||||
|
logging.info("(native) git clone " + pmb.config.git_repos[repo_name])
|
||||||
|
pmb.chroot.user(args, ["git", "clone", "--depth=1",
|
||||||
|
pmb.config.git_repos[repo_name], repo_name], working_dir="/home/user/git/")
|
|
@ -0,0 +1,49 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import hashlib
|
||||||
|
import shutil
|
||||||
|
import logging
|
||||||
|
import urllib.request
|
||||||
|
import pmb.helpers.run
|
||||||
|
|
||||||
|
|
||||||
|
def download(args, url, prefix, cache=True):
|
||||||
|
"""
|
||||||
|
Download a file to disk.
|
||||||
|
"""
|
||||||
|
# Create cache folder
|
||||||
|
if not os.path.exists(args.work + "/cache_http"):
|
||||||
|
pmb.helpers.run.user(args, ["mkdir", "-p", args.work + "/cache_http"])
|
||||||
|
|
||||||
|
# Check if file exists in cache
|
||||||
|
prefix = prefix.replace("/", "_")
|
||||||
|
path = (args.work + "/cache_http/" + prefix + "_" +
|
||||||
|
hashlib.sha512(url.encode("utf-8")).hexdigest())
|
||||||
|
if os.path.exists(path):
|
||||||
|
if cache:
|
||||||
|
return path
|
||||||
|
pmb.helpers.run.user(args, ["rm", path])
|
||||||
|
|
||||||
|
# Download the file
|
||||||
|
logging.info("Download " + url)
|
||||||
|
with urllib.request.urlopen(url) as response:
|
||||||
|
with open(path, "wb") as handle:
|
||||||
|
shutil.copyfileobj(response, handle)
|
||||||
|
return path
|
|
@ -0,0 +1,76 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class log_handler(logging.StreamHandler):
|
||||||
|
"""
|
||||||
|
Write to stdout and to the already opened log file.
|
||||||
|
"""
|
||||||
|
_args = None
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
try:
|
||||||
|
msg = self.format(record)
|
||||||
|
|
||||||
|
# INFO or higher: Write to stdout
|
||||||
|
if not self._args.quiet and record.levelno >= logging.INFO:
|
||||||
|
stream = self.stream
|
||||||
|
stream.write(msg)
|
||||||
|
stream.write(self.terminator)
|
||||||
|
self.flush()
|
||||||
|
|
||||||
|
# Everything: Write to logfd
|
||||||
|
msg = "(" + str(os.getpid()).zfill(6) + ") " + msg
|
||||||
|
self._args.logfd.write(msg + "\n")
|
||||||
|
self._args.logfd.flush()
|
||||||
|
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
|
||||||
|
def init(args):
|
||||||
|
"""
|
||||||
|
Set log format and add the log file descriptor to args.logfd.
|
||||||
|
"""
|
||||||
|
if not os.path.exists(args.work):
|
||||||
|
os.makedirs(args.work)
|
||||||
|
|
||||||
|
date_format = "%H:%M:%S"
|
||||||
|
setattr(args, "logfd", open(args.log, "a+"))
|
||||||
|
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
root_logger.handlers = []
|
||||||
|
|
||||||
|
formatter = None
|
||||||
|
root_logger.setLevel(logging.DEBUG)
|
||||||
|
if args.verbose:
|
||||||
|
formatter = logging.Formatter("[%(asctime)s %(module)s]"
|
||||||
|
" %(message)s", datefmt=date_format)
|
||||||
|
else:
|
||||||
|
formatter = logging.Formatter("[%(asctime)s] %(message)s",
|
||||||
|
datefmt=date_format)
|
||||||
|
|
||||||
|
handler = log_handler()
|
||||||
|
log_handler._args = args
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
root_logger.addHandler(handler)
|
|
@ -0,0 +1,90 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import pmb.helpers.run
|
||||||
|
|
||||||
|
|
||||||
|
def ismount(folder):
|
||||||
|
"""
|
||||||
|
Ismount() implementation, that works for mount --bind.
|
||||||
|
Workaround for: https://bugs.python.org/issue29707
|
||||||
|
"""
|
||||||
|
folder = os.path.abspath(folder)
|
||||||
|
with open("/proc/mounts", "r") as handle:
|
||||||
|
for line in handle:
|
||||||
|
words = line.split()
|
||||||
|
if len(words) >= 2 and words[1] == folder:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def bind(args, source, destination, create_folders=True):
|
||||||
|
"""
|
||||||
|
Mount --bind a folder and create necessary directory structure.
|
||||||
|
"""
|
||||||
|
if ismount(destination):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check/create folders
|
||||||
|
for path in [source, destination]:
|
||||||
|
if os.path.exists(path):
|
||||||
|
continue
|
||||||
|
if create_folders:
|
||||||
|
pmb.helpers.run.root(args, ["mkdir", "-p", path])
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Mount failed, folder does not exist: " +
|
||||||
|
path)
|
||||||
|
|
||||||
|
# Actually mount the folder
|
||||||
|
pmb.helpers.run.root(args, ["mount", "--bind", source, destination])
|
||||||
|
|
||||||
|
# Verify, that it has worked
|
||||||
|
if not ismount(destination):
|
||||||
|
raise RuntimeError("Mount failed: " + source + " -> " + destination)
|
||||||
|
|
||||||
|
# Mount a blockdevice
|
||||||
|
|
||||||
|
|
||||||
|
def bind_blockdevice(args, source, destination):
|
||||||
|
# Skip existing mountpoint
|
||||||
|
if ismount(destination):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create empty file
|
||||||
|
if not os.path.exists(destination):
|
||||||
|
pmb.helpers.run.root(args, ["touch", destination])
|
||||||
|
|
||||||
|
# Mount
|
||||||
|
pmb.helpers.run.root(args, ["mount", "--bind", source,
|
||||||
|
destination])
|
||||||
|
|
||||||
|
|
||||||
|
def umount_all(args, folder):
|
||||||
|
"""
|
||||||
|
Umount all folders, that are mounted inside a given folder.
|
||||||
|
"""
|
||||||
|
folder = os.path.abspath(folder)
|
||||||
|
with open("/proc/mounts", "r") as handle:
|
||||||
|
for line in handle:
|
||||||
|
words = line.split()
|
||||||
|
if len(words) < 2 or not words[1].startswith(folder):
|
||||||
|
continue
|
||||||
|
pmb.helpers.run.root(args, ["umount", words[1]])
|
||||||
|
if ismount(words[1]):
|
||||||
|
raise RuntimeError("Failed to umount: " + words[1])
|
|
@ -0,0 +1,70 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def core(args, cmd, log_message, log, return_stdout, check=True):
|
||||||
|
logging.debug(log_message)
|
||||||
|
"""
|
||||||
|
Run the command and write the output to the log.
|
||||||
|
|
||||||
|
:param check: raise an exception, when the command fails
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
ret = None
|
||||||
|
if log:
|
||||||
|
if return_stdout:
|
||||||
|
ret = subprocess.run(cmd, stdout=subprocess.PIPE,
|
||||||
|
check=check).stdout.decode('utf-8')
|
||||||
|
args.logfd.write(ret)
|
||||||
|
else:
|
||||||
|
subprocess.run(cmd, stdout=args.logfd, stderr=args.logfd,
|
||||||
|
check=check)
|
||||||
|
args.logfd.flush()
|
||||||
|
else:
|
||||||
|
logging.debug("*** output passed to pmbootstrap stdout, not" +
|
||||||
|
" to this log ***")
|
||||||
|
subprocess.run(cmd, check=check)
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as exc:
|
||||||
|
raise RuntimeError("Command failed: " + log_message) from exc
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def user(args, cmd, log=True, working_dir=None, return_stdout=False,
|
||||||
|
check=True):
|
||||||
|
"""
|
||||||
|
:param working_dir: defaults to args.work
|
||||||
|
"""
|
||||||
|
if not working_dir:
|
||||||
|
working_dir = args.work
|
||||||
|
|
||||||
|
# TODO: maintain and check against a whitelist
|
||||||
|
return core(args, cmd, "% " + " ".join(cmd), log, return_stdout, check)
|
||||||
|
|
||||||
|
|
||||||
|
def root(args, cmd, log=True, working_dir=None, return_stdout=False,
|
||||||
|
check=True):
|
||||||
|
"""
|
||||||
|
:param working_dir: defaults to args.work
|
||||||
|
"""
|
||||||
|
cmd = ["sudo"] + cmd
|
||||||
|
return user(args, cmd, log, working_dir, return_stdout, check)
|
|
@ -0,0 +1,21 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from pmb.install.install import install
|
||||||
|
from pmb.install.partition import partition
|
||||||
|
from pmb.install.format import format
|
|
@ -0,0 +1,98 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pmb.helpers.mount
|
||||||
|
import pmb.install.losetup
|
||||||
|
import pmb.helpers.cli
|
||||||
|
import pmb.config
|
||||||
|
import fnmatch
|
||||||
|
|
||||||
|
|
||||||
|
def sdcard_validate_path(args):
|
||||||
|
for pattern in pmb.config.install_valid_sdcard_devices:
|
||||||
|
if fnmatch.fnmatch(args.sdcard, pattern):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def mount_sdcard(args):
|
||||||
|
# Sanity checks
|
||||||
|
if args.deviceinfo["external_disk_install"] != "true":
|
||||||
|
raise RuntimeError("According to the deviceinfo, this device does"
|
||||||
|
" not support a sdcard installation.")
|
||||||
|
if not os.path.exists(args.sdcard):
|
||||||
|
raise RuntimeError("The sdcard device does not exist: " +
|
||||||
|
args.sdcard)
|
||||||
|
if not sdcard_validate_path(args):
|
||||||
|
raise RuntimeError("The sdcard path does not look valid. We will"
|
||||||
|
" not attempt to format this!")
|
||||||
|
if pmb.helpers.cli.ask(args, "EVERYTHING ON " + args.sdcard + " WILL BE"
|
||||||
|
" ERASED! CONTINUE?") != "y":
|
||||||
|
raise RuntimeError("Aborted.")
|
||||||
|
|
||||||
|
logging.info("(native) mount /dev/install (host: " + args.sdcard + ")")
|
||||||
|
pmb.helpers.mount.bind_blockdevice(args, args.sdcard,
|
||||||
|
args.work + "/chroot_native/dev/install")
|
||||||
|
|
||||||
|
|
||||||
|
def create_and_mount_image(args):
|
||||||
|
# Short variables for paths
|
||||||
|
chroot = args.work + "/chroot_native"
|
||||||
|
img_path = "/home/user/rootfs/" + args.device + ".img"
|
||||||
|
img_path_outside = chroot + img_path
|
||||||
|
|
||||||
|
# Umount and delete existing image
|
||||||
|
if os.path.exists(img_path_outside):
|
||||||
|
pmb.helpers.mount.umount_all(args, chroot + "/mnt")
|
||||||
|
pmb.install.losetup.umount(args, img_path)
|
||||||
|
pmb.chroot.root(args, ["rm", img_path])
|
||||||
|
if os.path.exists(img_path_outside):
|
||||||
|
raise RuntimeError("Failed to remove old image file: " +
|
||||||
|
img_path_outside)
|
||||||
|
|
||||||
|
# Create empty image file
|
||||||
|
size = pmb.config.install_size_image
|
||||||
|
logging.info("(native) create " + args.device + ".img (" + size + ")")
|
||||||
|
logging.info("WARNING: Make sure, that your target device's partition"
|
||||||
|
" table has allocated at least " + size + " as system partition!")
|
||||||
|
if pmb.helpers.cli.ask(args) != "y":
|
||||||
|
raise RuntimeError("Aborted.")
|
||||||
|
|
||||||
|
pmb.chroot.user(args, ["mkdir", "-p", "/home/user/rootfs"])
|
||||||
|
pmb.chroot.root(args, ["fallocate", "-l", size, img_path])
|
||||||
|
|
||||||
|
# Mount to /dev/install
|
||||||
|
logging.info("(native) mount /dev/install (" + args.device + ".img)")
|
||||||
|
pmb.install.losetup.mount(args, img_path)
|
||||||
|
device = pmb.install.losetup.device_by_back_file(args, img_path)
|
||||||
|
pmb.helpers.mount.bind_blockdevice(args, device, args.work +
|
||||||
|
"/chroot_native/dev/install")
|
||||||
|
|
||||||
|
|
||||||
|
def create(args):
|
||||||
|
"""
|
||||||
|
Create /dev/install (the "install blockdevice").
|
||||||
|
"""
|
||||||
|
pmb.helpers.mount.umount_all(
|
||||||
|
args, args.work + "/chroot_native/dev/install")
|
||||||
|
if args.sdcard:
|
||||||
|
mount_sdcard(args)
|
||||||
|
else:
|
||||||
|
create_and_mount_image(args)
|
|
@ -0,0 +1,58 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import pmb.chroot
|
||||||
|
|
||||||
|
|
||||||
|
def format_and_mount_boot(args):
|
||||||
|
mountpoint = "/mnt/install/boot"
|
||||||
|
logging.info("(native) format /dev/installp1 (boot, ext2), mount to " +
|
||||||
|
mountpoint)
|
||||||
|
pmb.chroot.root(args, ["mkfs.ext2", "-F", "-q", "/dev/installp1"])
|
||||||
|
pmb.chroot.root(args, ["mkdir", "-p", mountpoint])
|
||||||
|
pmb.chroot.root(args, ["mount", "/dev/installp1", mountpoint])
|
||||||
|
|
||||||
|
|
||||||
|
def format_and_mount_root(args):
|
||||||
|
mountpoint = "/dev/mapper/pm_crypt"
|
||||||
|
logging.info("(native) format /dev/installp2 (root, luks), mount to " +
|
||||||
|
mountpoint)
|
||||||
|
pmb.chroot.root(args, ["cryptsetup", "luksFormat", "--use-urandom",
|
||||||
|
"--cipher", args.cipher, "-q", "/dev/installp2"], log=False)
|
||||||
|
pmb.chroot.root(args, ["cryptsetup", "luksOpen", "/dev/installp2",
|
||||||
|
"pm_crypt"], log=False)
|
||||||
|
if not os.path.exists(args.work + "/chroot_native" + mountpoint):
|
||||||
|
raise RuntimeError("Failed to open cryptdevice!")
|
||||||
|
|
||||||
|
|
||||||
|
def format_and_mount_pm_crypt(args):
|
||||||
|
cryptdevice = "/dev/mapper/pm_crypt"
|
||||||
|
mountpoint = "/mnt/install"
|
||||||
|
logging.info("(native) format " + cryptdevice + " (ext4), mount to " +
|
||||||
|
mountpoint)
|
||||||
|
pmb.chroot.root(args, ["mkfs.ext4", "-F", "-q", cryptdevice])
|
||||||
|
pmb.chroot.root(args, ["mkdir", "-p", mountpoint])
|
||||||
|
pmb.chroot.root(args, ["mount", cryptdevice, mountpoint])
|
||||||
|
|
||||||
|
|
||||||
|
def format(args):
|
||||||
|
format_and_mount_root(args)
|
||||||
|
format_and_mount_pm_crypt(args)
|
||||||
|
format_and_mount_boot(args)
|
|
@ -0,0 +1,113 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.chroot.apk
|
||||||
|
import pmb.config
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.install.blockdevice
|
||||||
|
import pmb.install
|
||||||
|
|
||||||
|
|
||||||
|
def copy_files(args):
|
||||||
|
# Mount the device rootfs
|
||||||
|
logging.info("(native) copy rootfs_" + args.device + " to" +
|
||||||
|
" /mnt/install/")
|
||||||
|
mountpoint = "/mnt/rootfs_" + args.device
|
||||||
|
pmb.helpers.mount.bind(args, args.work + "/chroot_rootfs_" + args.device,
|
||||||
|
args.work + "/chroot_native" + mountpoint)
|
||||||
|
|
||||||
|
# Get all folders inside the device rootfs
|
||||||
|
folders = []
|
||||||
|
for path in glob.glob(args.work + "/chroot_native" + mountpoint + "/*"):
|
||||||
|
folders += [os.path.basename(path)]
|
||||||
|
|
||||||
|
# Run the copy command
|
||||||
|
pmb.chroot.root(args, ["cp", "-a"] + folders + ["/mnt/install/"],
|
||||||
|
working_dir=mountpoint)
|
||||||
|
|
||||||
|
# copy over keys and delete unneded mount folders
|
||||||
|
|
||||||
|
|
||||||
|
def fix_mount_folders(args):
|
||||||
|
# copy over keys
|
||||||
|
rootfs = args.work + "/chroot_native/mnt/install/"
|
||||||
|
for key in glob.glob(args.work + "/config_apk_keys/*.pub"):
|
||||||
|
pmb.helpers.run.root(args, ["cp", key, rootfs + "/etc/apk/keys/"])
|
||||||
|
|
||||||
|
# delete everything (-> empty mount folders) in /home/user
|
||||||
|
pmb.helpers.run.root(args, ["rm", "-r", rootfs + "/home/user"])
|
||||||
|
pmb.helpers.run.root(args, ["mkdir", rootfs + "/home/user"])
|
||||||
|
pmb.helpers.run.root(args, ["chown", pmb.config.chroot_uid_user,
|
||||||
|
rootfs + "/home/user"])
|
||||||
|
|
||||||
|
|
||||||
|
def set_user_password(args):
|
||||||
|
"""
|
||||||
|
Loop until the passwords for user and root have been changed successfully.
|
||||||
|
"""
|
||||||
|
suffix = "rootfs_" + args.device
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
pmb.chroot.root(args, ["passwd", "user"], suffix, log=False)
|
||||||
|
break
|
||||||
|
except RuntimeError:
|
||||||
|
logging.info("WARNING: Failed to set the password. Try it"
|
||||||
|
" one more time.")
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def install(args, show_flash_msg=True):
|
||||||
|
# Install required programs in native chroot
|
||||||
|
logging.info("*** (1/5) PREPARE NATIVE CHROOT ***")
|
||||||
|
pmb.chroot.apk.install(args, pmb.config.install_native_packages,
|
||||||
|
build=False)
|
||||||
|
|
||||||
|
# Install all packages to device rootfs chroot
|
||||||
|
logging.info("*** (2/5) CREATE DEVICE ROOTFS (" + args.device + ") ***")
|
||||||
|
suffix = "rootfs_" + args.device
|
||||||
|
pmb.chroot.apk.install(args, pmb.config.install_device_packages +
|
||||||
|
["device-" + args.device], suffix)
|
||||||
|
pmb.chroot.apk.update(args, suffix)
|
||||||
|
set_user_password(args)
|
||||||
|
|
||||||
|
# Partition and fill image/sdcard
|
||||||
|
logging.info("*** (3/5) PREPARE INSTALL BLOCKDEVICE ***")
|
||||||
|
pmb.chroot.shutdown(args, True)
|
||||||
|
pmb.install.blockdevice.create(args)
|
||||||
|
pmb.install.partition(args)
|
||||||
|
pmb.install.format(args)
|
||||||
|
|
||||||
|
# Just copy all the files
|
||||||
|
logging.info("*** (4/5) FILL INSTALL BLOCKDEVICE ***")
|
||||||
|
copy_files(args)
|
||||||
|
fix_mount_folders(args)
|
||||||
|
pmb.chroot.shutdown(args, True)
|
||||||
|
|
||||||
|
# Flash to target device
|
||||||
|
logging.info("*** (5/5) FLASHING TO DEVICE ***")
|
||||||
|
if show_flash_msg:
|
||||||
|
logging.info("Run the following to flash your installation to the"
|
||||||
|
" target device:")
|
||||||
|
logging.info("* pmbootstrap flasher flash_kernel")
|
||||||
|
if not args.sdcard:
|
||||||
|
logging.info("* pmbootstrap flasher flash_system")
|
|
@ -0,0 +1,71 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import pmb.helpers.mount
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.chroot
|
||||||
|
|
||||||
|
|
||||||
|
def init(args):
|
||||||
|
pmb.helpers.run.root(args, ["modprobe", "loop"])
|
||||||
|
for loopdev in glob.glob("/dev/loop*"):
|
||||||
|
pmb.helpers.mount.bind_blockdevice(args, loopdev,
|
||||||
|
args.work + "/chroot_native/" + loopdev)
|
||||||
|
|
||||||
|
|
||||||
|
def mount(args, img_path):
|
||||||
|
"""
|
||||||
|
:param img_path: Path to the img file inside native chroot.
|
||||||
|
"""
|
||||||
|
logging.debug("(native) mount " + img_path + " (loop)")
|
||||||
|
init(args)
|
||||||
|
pmb.chroot.root(args, ["losetup", "-f", img_path])
|
||||||
|
|
||||||
|
|
||||||
|
def device_by_back_file(args, back_file):
|
||||||
|
"""
|
||||||
|
Get the /dev/loopX device, that points to a specific image file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get list from losetup
|
||||||
|
losetup_output = pmb.chroot.root(args, ["losetup", "--json",
|
||||||
|
"--list"], return_stdout=True)
|
||||||
|
if not losetup_output:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Find the back_file
|
||||||
|
losetup = json.loads(losetup_output)
|
||||||
|
for loopdevice in losetup["loopdevices"]:
|
||||||
|
if loopdevice["back-file"] == back_file:
|
||||||
|
return loopdevice["name"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def umount(args, img_path):
|
||||||
|
"""
|
||||||
|
:param img_path: Path to the img file inside native chroot.
|
||||||
|
"""
|
||||||
|
device = device_by_back_file(args, img_path)
|
||||||
|
if not device:
|
||||||
|
return
|
||||||
|
logging.debug("(native) umount " + device)
|
||||||
|
pmb.chroot.root(args, ["losetup", "-d", device])
|
|
@ -0,0 +1,57 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.config
|
||||||
|
import pmb.install.losetup
|
||||||
|
|
||||||
|
|
||||||
|
def partitions_mount(args):
|
||||||
|
"""
|
||||||
|
Mount blockdevices of partitions inside native chroot
|
||||||
|
"""
|
||||||
|
prefix = args.sdcard
|
||||||
|
if not args.sdcard:
|
||||||
|
img_path = "/home/user/rootfs/" + args.device + ".img"
|
||||||
|
prefix = pmb.install.losetup.device_by_back_file(args, img_path)
|
||||||
|
for suffix in ["p1", "p2"]:
|
||||||
|
pmb.helpers.mount.bind_blockdevice(args, prefix + suffix,
|
||||||
|
args.work + "/chroot_native/dev/install" + suffix)
|
||||||
|
|
||||||
|
|
||||||
|
def partition(args):
|
||||||
|
"""
|
||||||
|
Partition /dev/install and create /dev/install{p1,p2}
|
||||||
|
"""
|
||||||
|
|
||||||
|
size_boot = pmb.config.install_size_boot
|
||||||
|
logging.info("(native) partition /dev/install (boot: " + size_boot +
|
||||||
|
", root: the rest)")
|
||||||
|
commands = [
|
||||||
|
["mktable", "msdos"],
|
||||||
|
["mkpart", "primary", "ext2", "2048s", size_boot],
|
||||||
|
["mkpart", "primary", size_boot, "100%"],
|
||||||
|
["set", "1", "boot", "on"]
|
||||||
|
]
|
||||||
|
for command in commands:
|
||||||
|
pmb.chroot.root(args, ["parted", "-s", "/dev/install"] +
|
||||||
|
command)
|
||||||
|
|
||||||
|
# Mount new partitions
|
||||||
|
partitions_mount(args)
|
|
@ -0,0 +1,23 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from pmb.parse.arguments import arguments
|
||||||
|
from pmb.parse.apkbuild import apkbuild
|
||||||
|
from pmb.parse.deviceinfo import deviceinfo
|
||||||
|
from pmb.parse.binfmt_info import binfmt_info
|
||||||
|
import pmb.parse.arch
|
|
@ -0,0 +1,125 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import pmb.config
|
||||||
|
|
||||||
|
|
||||||
|
def replace_variables(apkbuild):
|
||||||
|
"""
|
||||||
|
Replace a hardcoded list of variables inside the APKBUILD.
|
||||||
|
"""
|
||||||
|
ret = apkbuild
|
||||||
|
# _flavor: ${_device} (lineageos kernel packages)
|
||||||
|
ret["_flavor"] = ret["_flavor"].replace("${_device}",
|
||||||
|
ret["_device"])
|
||||||
|
|
||||||
|
# pkgname: $_flavor
|
||||||
|
ret["pkgname"] = ret["pkgname"].replace("${_flavor}", ret["_flavor"])
|
||||||
|
|
||||||
|
# subpackages: $pkgname
|
||||||
|
replaced = []
|
||||||
|
for subpackage in ret["subpackages"]:
|
||||||
|
replaced.append(subpackage.replace("$pkgname", ret["pkgname"]))
|
||||||
|
ret["subpackages"] = replaced
|
||||||
|
|
||||||
|
# makedepend: $makedepends_host, $makedepends_build, $_llvmver
|
||||||
|
replaced = []
|
||||||
|
for makedepend in ret["makedepends"]:
|
||||||
|
if makedepend.startswith("$"):
|
||||||
|
key = makedepend[1:]
|
||||||
|
if key in ret:
|
||||||
|
replaced += ret[key]
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Could not resolve variable " +
|
||||||
|
makedepend + " in APKBUILD of " +
|
||||||
|
apkbuild["pkgname"])
|
||||||
|
else:
|
||||||
|
# replace in the middle of the string
|
||||||
|
for var in ["_llvmver"]:
|
||||||
|
makedepend = makedepend.replace("$" + var, ret[var])
|
||||||
|
replaced += [makedepend]
|
||||||
|
ret["makedepends"] = replaced
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def cut_off_function_names(apkbuild):
|
||||||
|
"""
|
||||||
|
For subpackages: only keep the subpackage name, without the internal
|
||||||
|
function name, that tells how to build the subpackage.
|
||||||
|
"""
|
||||||
|
sub = apkbuild["subpackages"]
|
||||||
|
for i in range(len(sub)):
|
||||||
|
sub[i] = sub[i].split(":", 1)[0]
|
||||||
|
apkbuild["subpackages"] = sub
|
||||||
|
return apkbuild
|
||||||
|
|
||||||
|
|
||||||
|
def apkbuild(path):
|
||||||
|
"""
|
||||||
|
Parse relevant information out of the APKBUILD file. This is not meant
|
||||||
|
to be perfect and catch every edge case (for that, a full shell parser
|
||||||
|
would be necessary!). Instead, it should just work with the use-cases
|
||||||
|
covered by pmbootstrap and not take too long.
|
||||||
|
|
||||||
|
:param path: Full path to the APKBUILD
|
||||||
|
:returns: Relevant variables from the APKBUILD. Arrays get returned as
|
||||||
|
arrays.
|
||||||
|
"""
|
||||||
|
with open(path, encoding="utf-8") as handle:
|
||||||
|
lines = handle.readlines()
|
||||||
|
|
||||||
|
# Parse all attributes from the config
|
||||||
|
ret = {}
|
||||||
|
for i in range(len(lines)):
|
||||||
|
for attribute, options in pmb.config.apkbuild_attributes.items():
|
||||||
|
if not lines[i].startswith(attribute + "="):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Extend the line value until we reach the ending quote sign
|
||||||
|
line_value = lines[i][len(attribute + "="):-1]
|
||||||
|
end_char = None
|
||||||
|
if line_value.startswith("\""):
|
||||||
|
end_char = "\""
|
||||||
|
value = ""
|
||||||
|
while i < len(lines) - 1:
|
||||||
|
value += line_value.replace("\"", "").strip()
|
||||||
|
if not end_char or line_value.endswith(end_char):
|
||||||
|
break
|
||||||
|
value += " "
|
||||||
|
i += 1
|
||||||
|
line_value = lines[i][:-1]
|
||||||
|
|
||||||
|
# Split up arrays, delete empty strings inside the list
|
||||||
|
if options["array"]:
|
||||||
|
if value:
|
||||||
|
value = list(filter(None, value.split(" ")))
|
||||||
|
else:
|
||||||
|
value = []
|
||||||
|
ret[attribute] = value
|
||||||
|
|
||||||
|
# Add missing entries
|
||||||
|
for attribute, options in pmb.config.apkbuild_attributes.items():
|
||||||
|
if attribute not in ret:
|
||||||
|
if options["array"]:
|
||||||
|
ret[attribute] = []
|
||||||
|
else:
|
||||||
|
ret[attribute] = ""
|
||||||
|
|
||||||
|
ret = replace_variables(ret)
|
||||||
|
ret = cut_off_function_names(ret)
|
||||||
|
return ret
|
|
@ -0,0 +1,109 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import distutils.version
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import tarfile
|
||||||
|
|
||||||
|
|
||||||
|
def compare_version(a_str, b_str):
|
||||||
|
"""
|
||||||
|
http://stackoverflow.com/a/11887885
|
||||||
|
LooseVersion behaves just like apk's version check, at least
|
||||||
|
for all package versions, that have "-r".
|
||||||
|
|
||||||
|
:returns:
|
||||||
|
(a < b): -1
|
||||||
|
(a == b): 0
|
||||||
|
(a > b): 1
|
||||||
|
"""
|
||||||
|
if a_str is None:
|
||||||
|
a_str = "0"
|
||||||
|
if b_str is None:
|
||||||
|
b_str = "0"
|
||||||
|
a = distutils.version.LooseVersion(a_str)
|
||||||
|
b = distutils.version.LooseVersion(b_str)
|
||||||
|
if a < b:
|
||||||
|
return -1
|
||||||
|
if a == b:
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def read(args, package, path, must_exist=True):
|
||||||
|
"""
|
||||||
|
:param path: Path to APKINDEX.tar.gz, defaults to $WORK/APKINDEX.tar.gz
|
||||||
|
:param package: The package of which you want to read the properties.
|
||||||
|
:param must_exist: When set to true, raise an exception when the package is
|
||||||
|
missing in the index, or the index file was not found.
|
||||||
|
:returns: {"pkgname": ..., "version": ..., "depends": [...]}
|
||||||
|
When the package appears multiple times in the APKINDEX, this
|
||||||
|
function returns the attributes of the latest version.
|
||||||
|
"""
|
||||||
|
# Verify APKINDEX path
|
||||||
|
if not os.path.exists(path):
|
||||||
|
if not must_exist:
|
||||||
|
return None
|
||||||
|
raise RuntimeError("File not found: " + path)
|
||||||
|
|
||||||
|
# Read the tarfile
|
||||||
|
ret = None
|
||||||
|
with tarfile.open(path, "r:gz") as tar:
|
||||||
|
with tar.extractfile(tar.getmember("APKINDEX")) as handle:
|
||||||
|
current = {}
|
||||||
|
for line in handle:
|
||||||
|
line = line.decode()
|
||||||
|
if line == "\n": # end of package
|
||||||
|
if current["pkgname"] == package:
|
||||||
|
if not ret or compare_version(current["version"],
|
||||||
|
ret["version"]) == 1:
|
||||||
|
ret = current
|
||||||
|
current = {}
|
||||||
|
if line.startswith("P:"): # package
|
||||||
|
current["pkgname"] = line[2:-1]
|
||||||
|
if line.startswith("V:"): # version
|
||||||
|
current["version"] = line[2:-1]
|
||||||
|
if line.startswith("D:"): # depends
|
||||||
|
depends = line[2:-1]
|
||||||
|
if depends:
|
||||||
|
current["depends"] = depends.split(" ")
|
||||||
|
else:
|
||||||
|
current["depends"] = []
|
||||||
|
if not ret and must_exist:
|
||||||
|
raise RuntimeError("Package " + package + " not found in " + path)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def read_any_index(args, package, arch=None):
|
||||||
|
"""
|
||||||
|
Check if *any* APKINDEX has a specific package.
|
||||||
|
|
||||||
|
:param arch: defaults to native architecture
|
||||||
|
"""
|
||||||
|
if not arch:
|
||||||
|
arch = args.arch_native
|
||||||
|
indexes = [args.work + "/packages/" + arch + "/APKINDEX.tar.gz"]
|
||||||
|
pattern = args.work + "/cache_apk_" + arch + "/APKINDEX.*.tar.gz"
|
||||||
|
indexes += glob.glob(pattern)
|
||||||
|
|
||||||
|
for index in indexes:
|
||||||
|
index_data = read(args, package, index, False)
|
||||||
|
if index_data:
|
||||||
|
return index_data
|
||||||
|
return None
|
|
@ -0,0 +1,103 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import platform
|
||||||
|
import logging
|
||||||
|
import fnmatch
|
||||||
|
|
||||||
|
|
||||||
|
def alpine_native():
|
||||||
|
machine = platform.machine()
|
||||||
|
ret = ""
|
||||||
|
|
||||||
|
if machine == "x86_64":
|
||||||
|
ret = "x86_64"
|
||||||
|
else:
|
||||||
|
raise ValueError("Can not map platform.machine " + machine +
|
||||||
|
" to the right Alpine Linux architecture")
|
||||||
|
|
||||||
|
logging.debug("(native) Alpine architecture: " + ret)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def from_chroot_suffix(args, suffix):
|
||||||
|
if suffix == "native":
|
||||||
|
return args.arch_native
|
||||||
|
if suffix == "rootfs_" + args.device:
|
||||||
|
return args.deviceinfo["arch"]
|
||||||
|
if suffix.startswith("buildroot_"):
|
||||||
|
return suffix.split("_", 2)[1]
|
||||||
|
|
||||||
|
raise ValueError("Invalid chroot suffix: " + suffix +
|
||||||
|
" (wrong device chosen in 'init' step?)")
|
||||||
|
|
||||||
|
|
||||||
|
def alpine_to_debian(arch):
|
||||||
|
"""
|
||||||
|
Convert the architecture to the string used in the binfmt info
|
||||||
|
(aka. the Debian architecture format).
|
||||||
|
"""
|
||||||
|
|
||||||
|
mapping = {
|
||||||
|
"x86_64": "amd64",
|
||||||
|
"armhf": "arm",
|
||||||
|
}
|
||||||
|
for pattern, arch_debian in mapping.items():
|
||||||
|
if fnmatch.fnmatch(arch, pattern):
|
||||||
|
return arch_debian
|
||||||
|
raise ValueError("Can not map Alpine architecture " + arch +
|
||||||
|
" to the right Debian architecture.")
|
||||||
|
|
||||||
|
|
||||||
|
def alpine_to_kernel(arch):
|
||||||
|
"""
|
||||||
|
Convert the architecture to the string used inside the kernel sources.
|
||||||
|
You can read the mapping from the linux-vanilla APKBUILD for example.
|
||||||
|
"""
|
||||||
|
mapping = {
|
||||||
|
"aarch64*": "arm64",
|
||||||
|
"arm*": "arm",
|
||||||
|
"ppc*": "powerpc",
|
||||||
|
"s390*": "s390"
|
||||||
|
}
|
||||||
|
for pattern, arch_kernel in mapping.items():
|
||||||
|
if fnmatch.fnmatch(arch, pattern):
|
||||||
|
return arch_kernel
|
||||||
|
return arch
|
||||||
|
|
||||||
|
|
||||||
|
def alpine_to_hostspec(arch):
|
||||||
|
"""
|
||||||
|
See: abuild source code/functions.sh.in: arch_to_hostspec()
|
||||||
|
"""
|
||||||
|
mapping = {
|
||||||
|
"aarch64": "aarch64-alpine-linux-musl",
|
||||||
|
"armhf": "armv6-alpine-linux-muslgnueabihf",
|
||||||
|
"armv7": "armv7-alpine-linux-musleabihf",
|
||||||
|
"ppc": "powerpc-alpine-linux-musl",
|
||||||
|
"ppc64": "powerpc64-alpine-linux-musl",
|
||||||
|
"ppc64le": "powerpc64le-alpine-linux-musl",
|
||||||
|
"s390x": "s390x-alpine-linux-musl",
|
||||||
|
"x86": "i586-alpine-linux-musl",
|
||||||
|
"x86_66": "x86_64-alpine-linux-musl",
|
||||||
|
}
|
||||||
|
if arch in mapping:
|
||||||
|
return mapping[arch]
|
||||||
|
|
||||||
|
raise ValueError("Can not map Alpine architecture " + arch +
|
||||||
|
" to the right hostspec value")
|
|
@ -0,0 +1,145 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import pmb.config
|
||||||
|
import pmb.parse.arch
|
||||||
|
|
||||||
|
|
||||||
|
def arguments_flasher(subparser):
|
||||||
|
ret = subparser.add_parser("flasher", help="flash something to the"
|
||||||
|
" target device")
|
||||||
|
sub = ret.add_subparsers(dest="action_flasher")
|
||||||
|
|
||||||
|
# Other
|
||||||
|
sub.add_parser("flash_system", help="flash the system partition")
|
||||||
|
sub.add_parser("list_flavors", help="list installed kernel flavors" +
|
||||||
|
" inside the device rootfs chroot on this computer")
|
||||||
|
sub.add_parser("list_devices", help="show connected devices")
|
||||||
|
|
||||||
|
# Boot, flash kernel
|
||||||
|
boot = sub.add_parser("boot", help="boot a kernel once")
|
||||||
|
flash_kernel = sub.add_parser("flash_kernel", help="flash a kernel")
|
||||||
|
for action in [boot, flash_kernel]:
|
||||||
|
action.add_argument("--flavor", default=None)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def arguments():
|
||||||
|
parser = argparse.ArgumentParser(prog="pmbootstrap")
|
||||||
|
|
||||||
|
# Other
|
||||||
|
parser.add_argument("-V", "--version", action="version",
|
||||||
|
version=pmb.config.version)
|
||||||
|
parser.add_argument("--no-cross", action="store_false", dest="cross",
|
||||||
|
help="disable crosscompiler, build only with qemu + gcc (slower!)")
|
||||||
|
|
||||||
|
parser.add_argument("-a", "--alpine-version", dest="alpine_version",
|
||||||
|
help="examples: edge, latest-stable, v3.5")
|
||||||
|
parser.add_argument("-c", "--config", dest="config",
|
||||||
|
default=pmb.config.defaults["config"])
|
||||||
|
parser.add_argument("-d", "--port-distccd", dest="port_distccd")
|
||||||
|
parser.add_argument("-m", "--mirror-alpine", dest="mirror_alpine")
|
||||||
|
parser.add_argument("-j", "--jobs", help="parallel jobs when compiling")
|
||||||
|
parser.add_argument("-p", "--aports",
|
||||||
|
help="postmarketos aports paths")
|
||||||
|
parser.add_argument("-w", "--work", help="folder where all data"
|
||||||
|
" gets stored (chroots, caches, built packages)")
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
parser.add_argument("-l", "--log", dest="log", default=None)
|
||||||
|
parser.add_argument("-v", "--verbose", dest="verbose",
|
||||||
|
action="store_true", help="output the source file, where the log"
|
||||||
|
" message originated from with each log message")
|
||||||
|
parser.add_argument("-q", "--quiet", dest="quiet",
|
||||||
|
action="store_true", help="do not output any log messages")
|
||||||
|
|
||||||
|
# Actions
|
||||||
|
sub = parser.add_subparsers(title="action", dest="action")
|
||||||
|
sub.add_parser("init", help="initialize config file")
|
||||||
|
sub.add_parser("log", help="follow the pmbootstrap logfile")
|
||||||
|
sub.add_parser("log_distccd", help="follow the distccd logfile")
|
||||||
|
sub.add_parser("shutdown", help="umount, unregister binfmt")
|
||||||
|
sub.add_parser("index", help="re-index all repositories with custom built"
|
||||||
|
" packages (do this after manually removing package files)")
|
||||||
|
arguments_flasher(sub)
|
||||||
|
|
||||||
|
# Action: zap
|
||||||
|
zap = sub.add_parser("zap", help="safely delete chroot"
|
||||||
|
"folders")
|
||||||
|
zap.add_argument("-p", "--packages", action="store_true", help="also delete"
|
||||||
|
" the precious, self-compiled packages")
|
||||||
|
zap.add_argument("-hc", "--http", action="store_true", help="also delete http"
|
||||||
|
"cache")
|
||||||
|
|
||||||
|
# Action: stats
|
||||||
|
stats = sub.add_parser("stats", help="show ccache stats")
|
||||||
|
stats.add_argument("--arch")
|
||||||
|
|
||||||
|
# Action: chroot / build_init / kernel
|
||||||
|
build_init = sub.add_parser("build_init", help="initialize build"
|
||||||
|
" environment (usually you do not need to call this)")
|
||||||
|
chroot = sub.add_parser("chroot", help="start shell in chroot")
|
||||||
|
chroot.add_argument("command", default=["sh"], help="command"
|
||||||
|
" to execute inside the chroot. default: sh", nargs='*')
|
||||||
|
for action in [build_init, chroot]:
|
||||||
|
action.add_argument("--suffix", default="native")
|
||||||
|
|
||||||
|
# Action: install
|
||||||
|
install = sub.add_parser("install", help="set up device specific" +
|
||||||
|
" chroot and install to sdcard or image file")
|
||||||
|
install.add_argument("--sdcard", help="path to the sdcard device,"
|
||||||
|
" eg. /dev/mmcblk0")
|
||||||
|
install.add_argument("--cipher", help="cryptsetup cipher used to"
|
||||||
|
" encrypt the system partition, eg. aes-xts-plain64")
|
||||||
|
|
||||||
|
# Action: build / checksum / menuconfig / parse_apkbuild / aportgen
|
||||||
|
menuconfig = sub.add_parser("menuconfig", help="run menuconfig on"
|
||||||
|
" a kernel aport")
|
||||||
|
checksum = sub.add_parser("checksum", help="update aport checksums")
|
||||||
|
parse_apkbuild = sub.add_parser("parse_apkbuild")
|
||||||
|
aportgen = sub.add_parser("aportgen", help="generate a package build recipe"
|
||||||
|
" (aport/APKBUILD) based on an upstream aport from Alpine")
|
||||||
|
build = sub.add_parser("build", help="create a package for a"
|
||||||
|
" specific architecture")
|
||||||
|
build.add_argument("--arch")
|
||||||
|
build.add_argument("--force", action="store_true")
|
||||||
|
for action in [checksum, build, menuconfig, parse_apkbuild, aportgen]:
|
||||||
|
action.add_argument("package")
|
||||||
|
|
||||||
|
# Use defaults from the user's config file
|
||||||
|
args = parser.parse_args()
|
||||||
|
cfg = pmb.config.load(args)
|
||||||
|
for varname in cfg["pmbootstrap"]:
|
||||||
|
if varname not in args or not getattr(args, varname):
|
||||||
|
setattr(args, varname, cfg["pmbootstrap"][varname])
|
||||||
|
|
||||||
|
# Replace $WORK in variables from user's config
|
||||||
|
for varname in cfg["pmbootstrap"]:
|
||||||
|
old = getattr(args, varname)
|
||||||
|
setattr(args, varname, old.replace("$WORK", args.work))
|
||||||
|
|
||||||
|
# Add convinience shortcuts
|
||||||
|
setattr(args, "arch_native", pmb.parse.arch.alpine_native())
|
||||||
|
|
||||||
|
# Add the deviceinfo (only after initialization)
|
||||||
|
if args.action != "init":
|
||||||
|
setattr(args, "deviceinfo", pmb.parse.deviceinfo(args))
|
||||||
|
|
||||||
|
return args
|
|
@ -0,0 +1,48 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Get magic and mask from binfmt info file
|
||||||
|
# Return: {magic: ..., mask: ...}
|
||||||
|
|
||||||
|
|
||||||
|
def binfmt_info(args, arch_debian):
|
||||||
|
# Parse the info file
|
||||||
|
full = {}
|
||||||
|
info = args.work + "/chroot_native/usr/share/qemu-user-binfmt.txt"
|
||||||
|
logging.debug("parsing: " + info)
|
||||||
|
with open(info, "r") as handle:
|
||||||
|
for line in handle:
|
||||||
|
if line.startswith('#') or "=" not in line:
|
||||||
|
continue
|
||||||
|
splitted = line.split("=")
|
||||||
|
key = splitted[0].strip()
|
||||||
|
value = splitted[1]
|
||||||
|
full[key] = value[1:-2]
|
||||||
|
|
||||||
|
ret = {}
|
||||||
|
logging.debug("filtering by architecture: " + arch_debian)
|
||||||
|
for type in ["mask", "magic"]:
|
||||||
|
key = arch_debian + "_" + type
|
||||||
|
if key not in full:
|
||||||
|
raise RuntimeError("Could not find key " + key + " in binfmt info file:" +
|
||||||
|
info)
|
||||||
|
ret[type] = full[key]
|
||||||
|
logging.debug("=> " + str(ret))
|
||||||
|
return ret
|
|
@ -0,0 +1,51 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def deviceinfo(args, device=None):
|
||||||
|
"""
|
||||||
|
:param device: defaults to args.device
|
||||||
|
"""
|
||||||
|
if not device:
|
||||||
|
device = args.device
|
||||||
|
|
||||||
|
aport = args.aports + "/device-" + device
|
||||||
|
if not os.path.exists(aport) or not os.path.exists(aport + "/deviceinfo"):
|
||||||
|
logging.fatal("You will need to create a device-specific package")
|
||||||
|
logging.fatal("before you can continue. Please create at least the")
|
||||||
|
logging.fatal("following files:")
|
||||||
|
logging.fatal(aport + "/APKBUILD")
|
||||||
|
logging.fatal(aport + "/deviceinfo")
|
||||||
|
raise RuntimeError("Incomplete device information")
|
||||||
|
|
||||||
|
ret = {}
|
||||||
|
path = aport + "/deviceinfo"
|
||||||
|
with open(path) as handle:
|
||||||
|
for line in handle:
|
||||||
|
if not line.startswith("deviceinfo_"):
|
||||||
|
continue
|
||||||
|
if "=" not in line:
|
||||||
|
raise SyntaxError(path + ": No '=' found:\n\t" + line)
|
||||||
|
split = line.split("=", 1)
|
||||||
|
key = split[0][len("deviceinfo_"):]
|
||||||
|
value = split[1].replace("\"", "").replace("\n", "")
|
||||||
|
ret[key] = value
|
||||||
|
return ret
|
|
@ -0,0 +1,101 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
import pmb.aportgen
|
||||||
|
import pmb.build
|
||||||
|
import pmb.config
|
||||||
|
import pmb.chroot
|
||||||
|
import pmb.chroot.other
|
||||||
|
import pmb.flasher
|
||||||
|
import pmb.helpers.logging
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.parse
|
||||||
|
import pmb.install
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
# Parse arguments
|
||||||
|
args = pmb.parse.arguments()
|
||||||
|
pmb.helpers.logging.init(args)
|
||||||
|
|
||||||
|
# Initialize or require config
|
||||||
|
if args.action == "init":
|
||||||
|
return pmb.config.init(args)
|
||||||
|
if not os.path.exists(args.config):
|
||||||
|
logging.critical("Please specify a config file, or run"
|
||||||
|
" 'pmbootstrap init' to generate one.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# All other actions
|
||||||
|
if args.action == "aportgen":
|
||||||
|
pmb.aportgen.generate(args, args.package)
|
||||||
|
elif args.action == "build":
|
||||||
|
pmb.build.package(args, args.package, args.arch, args.force, False)
|
||||||
|
elif args.action == "build_init":
|
||||||
|
pmb.build.init(args, args.suffix)
|
||||||
|
elif args.action == "checksum":
|
||||||
|
pmb.build.checksum(args, args.package)
|
||||||
|
elif args.action == "chroot":
|
||||||
|
pmb.chroot.root(args, args.command, args.suffix, log=False)
|
||||||
|
elif args.action == "index":
|
||||||
|
pmb.build.index_repo(args)
|
||||||
|
elif args.action == "install":
|
||||||
|
pmb.install.install(args)
|
||||||
|
elif args.action == "flasher":
|
||||||
|
pmb.flasher.frontend(args)
|
||||||
|
elif args.action == "menuconfig":
|
||||||
|
pmb.build.menuconfig(args, args.package, args.deviceinfo["arch"])
|
||||||
|
elif args.action == "parse_apkbuild":
|
||||||
|
print(json.dumps(pmb.parse.apkbuild(args.aports + "/" +
|
||||||
|
args.package + "/APKBUILD"), indent=4))
|
||||||
|
elif args.action == "shutdown":
|
||||||
|
pmb.chroot.shutdown(args)
|
||||||
|
elif args.action == "stats":
|
||||||
|
pmb.build.ccache_stats(args, args.arch)
|
||||||
|
elif args.action == "log":
|
||||||
|
pmb.helpers.run.user(args, ["tail", "-f", args.log], log=False)
|
||||||
|
elif args.action == "log_distccd":
|
||||||
|
pmb.chroot.user(args, ["tail", "-f", "/home/user/distccd.log"],
|
||||||
|
log=False)
|
||||||
|
elif args.action == "zap":
|
||||||
|
pmb.chroot.zap(args)
|
||||||
|
else:
|
||||||
|
logging.info("Run pmbootstrap -h for usage information.")
|
||||||
|
|
||||||
|
# Print finish timestamp
|
||||||
|
logging.info("Done")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.info("ERROR: " + str(e))
|
||||||
|
logging.info("Run 'pmbootstrap log' for details.")
|
||||||
|
logging.debug(traceback.format_exc())
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
|
@ -0,0 +1,126 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import glob
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Import from parent directory
|
||||||
|
pmb_src = os.path.abspath(os.path.join(os.path.dirname(__file__) + "/.."))
|
||||||
|
sys.path.append(pmb_src)
|
||||||
|
import pmb.chroot.apk_static
|
||||||
|
import pmb.parse.apkindex
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def args():
|
||||||
|
import pmb.parse
|
||||||
|
sys.argv = ["pmbootstrap.py", "chroot"]
|
||||||
|
args = pmb.parse.arguments()
|
||||||
|
setattr(args, "logfd", open("/dev/null", "a+"))
|
||||||
|
yield args
|
||||||
|
args.logfd.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_signature_info(tmpdir):
|
||||||
|
with tarfile.open(tmpdir + "/test.apk", "w:gz") as tar:
|
||||||
|
# No signature found
|
||||||
|
with pytest.raises(RuntimeError) as e:
|
||||||
|
pmb.chroot.apk_static.read_signature_info(tar)
|
||||||
|
assert "Could not find signature" in str(e.value)
|
||||||
|
|
||||||
|
# Add signature file with invalid name
|
||||||
|
tar.add(__file__, "sbin/apk.static.SIGN.RSA.invalid.pub")
|
||||||
|
with pytest.raises(RuntimeError) as e:
|
||||||
|
pmb.chroot.apk_static.read_signature_info(tar)
|
||||||
|
assert "Invalid signature key" in str(e.value)
|
||||||
|
|
||||||
|
# Add signature file with realistic name
|
||||||
|
path = glob.glob(pmb_src + "/keys/*.pub")[0]
|
||||||
|
name = os.path.basename(path)
|
||||||
|
path_archive = "sbin/apk.static.SIGN.RSA." + name
|
||||||
|
with tarfile.open(tmpdir + "/test2.apk", "w:gz") as tar:
|
||||||
|
tar.add(__file__, path_archive)
|
||||||
|
sigfilename, sigkey_path = pmb.chroot.apk_static.read_signature_info(
|
||||||
|
tar)
|
||||||
|
assert sigfilename == path_archive
|
||||||
|
assert sigkey_path == path
|
||||||
|
|
||||||
|
|
||||||
|
def test_successful_extraction(args, tmpdir):
|
||||||
|
if os.path.exists(args.work + "/apk.static"):
|
||||||
|
os.remove(args.work + "/apk.static")
|
||||||
|
|
||||||
|
pmb.chroot.apk_static.init(args)
|
||||||
|
assert os.path.exists(args.work + "/apk.static")
|
||||||
|
os.remove(args.work + "/apk.static")
|
||||||
|
|
||||||
|
|
||||||
|
def test_signature_verification(args, tmpdir):
|
||||||
|
if os.path.exists(args.work + "/apk.static"):
|
||||||
|
os.remove(args.work + "/apk.static")
|
||||||
|
|
||||||
|
apk_index = pmb.chroot.apk_static.download(args, "APKINDEX.tar.gz")
|
||||||
|
version = pmb.parse.apkindex.read(args, "apk-tools-static",
|
||||||
|
apk_index)["version"]
|
||||||
|
apk_path = pmb.chroot.apk_static.download(args,
|
||||||
|
"apk-tools-static-" + version + ".apk")
|
||||||
|
|
||||||
|
# Extract to temporary folder
|
||||||
|
with tarfile.open(apk_path, "r:gz") as tar:
|
||||||
|
sigfilename, sigkey_path = pmb.chroot.apk_static.read_signature_info(
|
||||||
|
tar)
|
||||||
|
files = pmb.chroot.apk_static.extract_temp(tar, sigfilename)
|
||||||
|
|
||||||
|
# Verify signature (successful)
|
||||||
|
pmb.chroot.apk_static.verify_signature(args, files, sigkey_path)
|
||||||
|
|
||||||
|
# Append data to extracted apk.static
|
||||||
|
with open(files["apk"]["temp_path"], "ab") as handle:
|
||||||
|
handle.write("appended something".encode())
|
||||||
|
|
||||||
|
# Verify signature again (fail) (this deletes the tempfiles)
|
||||||
|
with pytest.raises(RuntimeError) as e:
|
||||||
|
pmb.chroot.apk_static.verify_signature(args, files, sigkey_path)
|
||||||
|
assert "Failed to validate signature" in str(e.value)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test "apk.static --version" check
|
||||||
|
#
|
||||||
|
with pytest.raises(RuntimeError) as e:
|
||||||
|
pmb.chroot.apk_static.extract(args, "99.1.2-r1", apk_path)
|
||||||
|
assert "downgrade attack" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_outdated_version(args):
|
||||||
|
if os.path.exists(args.work + "/apk.static"):
|
||||||
|
os.remove(args.work + "/apk.static")
|
||||||
|
|
||||||
|
# change min version
|
||||||
|
min = pmb.config.apk_tools_static_min_version
|
||||||
|
pmb.config.apk_tools_static_min_version = "99.1.2-r1"
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError) as e:
|
||||||
|
pmb.chroot.apk_static.init(args)
|
||||||
|
assert "outdated version" in str(e.value)
|
||||||
|
|
||||||
|
# reset min version
|
||||||
|
pmb.config.apk_tools_static_min_version = min
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
import filecmp
|
||||||
|
|
||||||
|
# Import from parent directory
|
||||||
|
sys.path.append(os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__) + "/..")))
|
||||||
|
import pmb.aportgen
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def args(tmpdir):
|
||||||
|
import pmb.parse
|
||||||
|
sys.argv = ["pmbootstrap.py", "chroot"]
|
||||||
|
args = pmb.parse.arguments()
|
||||||
|
setattr(args, "logfd", open("/dev/null", "a+"))
|
||||||
|
setattr(args, "_aports_real", args.aports)
|
||||||
|
args.aports = str(tmpdir)
|
||||||
|
yield args
|
||||||
|
args.logfd.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_aportgen(args):
|
||||||
|
# Create aportgen folder -> code path where it still exists
|
||||||
|
pmb.helpers.run.user(args, ["mkdir", "-p", args.work + "/aportgen"])
|
||||||
|
|
||||||
|
# Generate all valid packages (gcc-armhf twice, so the output folder
|
||||||
|
# exists)
|
||||||
|
for pkgname in ["binutils-armhf", "musl-armhf", "gcc-armhf", "gcc-armhf"]:
|
||||||
|
pmb.aportgen.generate(args, pkgname)
|
||||||
|
path_new = args.aports + "/" + pkgname + "/APKBUILD"
|
||||||
|
path_old = args._aports_real + "/" + pkgname + "/APKBUILD"
|
||||||
|
assert os.path.exists(path_new)
|
||||||
|
assert filecmp.cmp(path_new, path_old, False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_aportgen_invalid_generator(args):
|
||||||
|
with pytest.raises(ValueError) as e:
|
||||||
|
pmb.aportgen.generate(args, "pkgname-with-no-generator")
|
||||||
|
assert "No generator available" in str(e.value)
|
|
@ -0,0 +1,48 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Import from parent directory
|
||||||
|
sys.path.append(os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__) + "/..")))
|
||||||
|
import pmb.aportgen
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def args(tmpdir):
|
||||||
|
import pmb.parse
|
||||||
|
sys.argv = ["pmbootstrap.py", "chroot"]
|
||||||
|
args = pmb.parse.arguments()
|
||||||
|
setattr(args, "logfd", open("/dev/null", "a+"))
|
||||||
|
yield args
|
||||||
|
args.logfd.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_build(args):
|
||||||
|
pmb.build.package(args, "hello-world", args.arch_native, True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_armhf(args):
|
||||||
|
"""
|
||||||
|
Build in armhf chroot, with cross-compiler through distcc.
|
||||||
|
"""
|
||||||
|
pmb.build.package(args, "hello-world", "armhf", True)
|
|
@ -0,0 +1,57 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
import glob
|
||||||
|
import filecmp
|
||||||
|
|
||||||
|
# Import from parent directory
|
||||||
|
sys.path.append(os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__) + "/..")))
|
||||||
|
import pmb.parse.apkindex
|
||||||
|
import pmb.helpers.git
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def args():
|
||||||
|
import pmb.parse
|
||||||
|
sys.argv = ["pmbootstrap.py", "chroot"]
|
||||||
|
args = pmb.parse.arguments()
|
||||||
|
setattr(args, "logfd", open("/dev/null", "a+"))
|
||||||
|
yield args
|
||||||
|
args.logfd.close()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def test_keys(args):
|
||||||
|
mirror_path = os.path.join(os.path.dirname(__file__) + "../keys")
|
||||||
|
original_path = args.work + "/cache_git/aports_upstream/main/alpine-keys"
|
||||||
|
pmb.helpers.git.clone(args, "aports_upstream")
|
||||||
|
|
||||||
|
# Check if original keys are mirrored correctly
|
||||||
|
for path in glob.glob(original_path + "/*.key"):
|
||||||
|
key = os.path.basename(path)
|
||||||
|
assert filecmp.cmp(original_path + "/" + key, mirror_path + "/" + key,
|
||||||
|
False)
|
||||||
|
|
||||||
|
# Find outdated keys, which need to be removed
|
||||||
|
for path in glob.glob(mirror_path + "/*.key"):
|
||||||
|
assert os.path.exists(original_path + "/" + os.path.basename(path))
|
|
@ -0,0 +1,65 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Import from parent directory
|
||||||
|
pmb_src = os.path.abspath(os.path.join(os.path.dirname(__file__) + "/.."))
|
||||||
|
sys.path.append(pmb_src)
|
||||||
|
import pmb.helpers.run
|
||||||
|
import pmb.chroot.root
|
||||||
|
import pmb.chroot.user
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def args():
|
||||||
|
import pmb.parse
|
||||||
|
sys.argv = ["pmbootstrap.py", "chroot"]
|
||||||
|
args = pmb.parse.arguments()
|
||||||
|
setattr(args, "logfd", open("/dev/null", "a+"))
|
||||||
|
yield args
|
||||||
|
args.logfd.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_shell_escape(args):
|
||||||
|
cmds = {
|
||||||
|
"test\n": ["echo", "test"],
|
||||||
|
"test && test\n": ["echo", "test", "&&", "test"],
|
||||||
|
"test ; test\n": ["echo", "test", ";", "test"],
|
||||||
|
"'test\"test\\'\n": ["echo", "'test\"test\\'"],
|
||||||
|
"*\n": ["echo", "*"],
|
||||||
|
"$PWD\n": ["echo", "$PWD"],
|
||||||
|
}
|
||||||
|
for expected, cmd in cmds.items():
|
||||||
|
core = pmb.helpers.run.core(args, cmd, "test", True, True)
|
||||||
|
assert expected == core
|
||||||
|
|
||||||
|
user = pmb.helpers.run.user(args, cmd, return_stdout=True)
|
||||||
|
assert expected == user
|
||||||
|
|
||||||
|
root = pmb.helpers.run.root(args, cmd, return_stdout=True)
|
||||||
|
assert expected == root
|
||||||
|
|
||||||
|
chroot_root = pmb.chroot.root(args, cmd, return_stdout=True)
|
||||||
|
assert expected == chroot_root
|
||||||
|
|
||||||
|
chroot_user = pmb.chroot.user(args, cmd, return_stdout=True)
|
||||||
|
assert expected == chroot_user
|
|
@ -0,0 +1,33 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
|
||||||
|
def test_use_pmb_helpers_run_instead_of_subprocess_run():
|
||||||
|
src = os.path.abspath(os.path.dirname(__file__) + "/..")
|
||||||
|
files = glob.glob(src + "/pmb/**/*.py",
|
||||||
|
recursive=True) + glob.glob(src + "*.py")
|
||||||
|
okay = os.path.abspath(src + "/pmb/helpers/run.py")
|
||||||
|
for file in files:
|
||||||
|
with open(file, "r") as handle:
|
||||||
|
source = handle.read()
|
||||||
|
if file != okay and "subprocess.run" in source:
|
||||||
|
raise RuntimeError("File " + file + " use pmb.helpers.run.user()"
|
||||||
|
" instead of subprocess.run()!")
|
|
@ -0,0 +1,69 @@
|
||||||
|
"""
|
||||||
|
Copyright 2017 Oliver Smith
|
||||||
|
|
||||||
|
This file is part of pmbootstrap.
|
||||||
|
|
||||||
|
pmbootstrap is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
pmbootstrap is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Import from parent directory
|
||||||
|
sys.path.append(os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__) + "/..")))
|
||||||
|
import pmb.parse.apkindex
|
||||||
|
import pmb.helpers.git
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def args():
|
||||||
|
import pmb.parse
|
||||||
|
sys.argv = ["pmbootstrap.py", "chroot"]
|
||||||
|
args = pmb.parse.arguments()
|
||||||
|
setattr(args, "logfd", open("/dev/null", "a+"))
|
||||||
|
yield args
|
||||||
|
args.logfd.close()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def test_version(args):
|
||||||
|
# clone official test file from apk-tools
|
||||||
|
pmb.helpers.git.clone(args, "apk-tools")
|
||||||
|
path = args.work + "/cache_git/apk-tools/test/version.data"
|
||||||
|
|
||||||
|
mapping = {-1: "<", 0: "=", 1: ">"}
|
||||||
|
with open(path) as handle:
|
||||||
|
for line in handle:
|
||||||
|
split = line.split(" ")
|
||||||
|
a = split[0]
|
||||||
|
b = split[2].rstrip()
|
||||||
|
expected = split[1]
|
||||||
|
|
||||||
|
# Alpine packages nowadays always have '-r' in their version
|
||||||
|
if "-r" not in a or "-r" not in b:
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(line.rstrip())
|
||||||
|
try:
|
||||||
|
result = pmb.parse.apkindex.compare_version(a, b)
|
||||||
|
real = mapping[result]
|
||||||
|
except TypeError:
|
||||||
|
# FIXME: Bug in Python:
|
||||||
|
# https://bugs.python.org/issue14894
|
||||||
|
# When this happens in pmbootstrap, it will also raise the
|
||||||
|
# TypeError exception.
|
||||||
|
continue
|
||||||
|
assert(real == expected)
|
Loading…
Reference in New Issue