-
Notifications
You must be signed in to change notification settings - Fork 0
/
create_dkms_debs.py
executable file
·188 lines (163 loc) · 7.4 KB
/
create_dkms_debs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env python3
'''
SPDX-License-Identifier: GPL-3.0-or-later
Copyright 2021 Thore Sommer
'''
import argparse
import subprocess
import yaml
import shutil
import glob
import logging
from tempfile import TemporaryDirectory
from collections import namedtuple
from string import Template
from datetime import datetime
from email import utils
import os
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
Config = namedtuple("Config", ["k_ver", "k_arch","k_arch_cpu", "kbuild_version", "package_version", "local_repo", "output_dir", "distribution", "packages"])
Package = namedtuple("Package", ["debian_name", "dkms_name", "result_name", "template_dir", "deb_version"])
def parse_config():
parser = argparse.ArgumentParser()
parser.add_argument("config_file")
args = parser.parse_args()
with open(args.config_file, 'r') as f:
config = yaml.safe_load(f)
packages = list()
for debian_name in config["packages"].keys():
packages.append(Package(debian_name=debian_name,
dkms_name=config["packages"][debian_name]["dkms"],
result_name=config["packages"][debian_name]["result"],
template_dir=config["packages"][debian_name]["template-dir"],
deb_version=config["packages"][debian_name].get("deb-version"))
)
return Config(k_ver=config["kernel"]["version"],
k_arch=config["kernel"]["arch"],
k_arch_cpu=config["kernel"]["arch-cpu"],
kbuild_version=config["kernel"]["kbuild-version"],
package_version=config["kernel"]["package-version"],
output_dir=config["output-dir"],
local_repo=config["local-repo"],
distribution=config["distribution"],
packages=packages)
def install_kernel(config: Config):
p = subprocess.run(["apt-get", "update"])
if p.returncode != 0:
raise Exception("Package update failed")
p = subprocess.run(["apt-get", "upgrade", "-y"])
if p.returncode != 0:
raise Exception("Package upgrade failed")
p = subprocess.run(["apt-get", "remove", "-y", "linux-image-*"])
if p.returncode != 0:
raise Exception("Removal of old kernels failed")
p = subprocess.run(["apt-get", "autoremove","-y"])
if p.returncode != 0:
raise Exception("Apt autoremove failes")
header_arch_name = f'linux-headers-{config.k_ver}-{config.k_arch}'
header_common_name = f'linux-headers-{config.k_ver}-common'
kbuild_name = f'linux-kbuild-{config.kbuild_version}'
# Install exact version
if config.package_version:
header_arch_name = f'{header_arch_name}={config.package_version}'
header_common_name = f'{header_common_name}={config.package_version}'
kbuild_name = f'{kbuild_name}={config.package_version}'
p = subprocess.run(["apt-get", "install","--no-install-recommends", "-y", header_arch_name, header_common_name, kbuild_name])
if p.returncode != 0:
raise Exception("Kernel installation failed")
def install_package(package: str):
p = subprocess.run(["apt-get", "install", "--no-install-recommends", "-y", package])
if p.returncode != 0:
raise Exception(f"Installation of {package} failed")
def get_package_version(package: str):
p = subprocess.run(["dpkg-query", "--showformat='${Version}'", "--show", package], capture_output=True)
if p.returncode != 0:
raise Exception(f"Couldn't get package version of {package}")
return p.stdout.decode().strip("\'")
def dkms_get_version(package: Package):
p = subprocess.run(["dkms", "status", package.dkms_name], capture_output=True)
if p.returncode != 0:
raise Exception("Running dkms status failed")
values = p.stdout.decode().rstrip('\n').replace(": ", ", ").replace("/", ", ").split(", ")
if len(values) != 5:
raise Exception("Package is not correctly installed")
return values[1]
def create_dkms_tarball(config: Config, package: Package, dkms_version, tmp_dir):
kernel_name = f"{config.k_ver}-{config.k_arch}"
archive = f"{tmp_dir}/{package.dkms_name}.dkms.tar.gz"
p = subprocess.run(["dkms", "mktarball", "-m", package.dkms_name, "-v", dkms_version, "-k", kernel_name, "--archive", archive])
if p.returncode != 0:
raise Exception("Creation of tarball failed")
def subst_variables(config: Config, package: Package, dkms_version: str, tmp_dir):
package_name = Template(package.result_name).substitute(MODULE_VERSION=dkms_version)
deb_version = f".lernstick.{package.deb_version}" if package.deb_version else ""
values = {
"DEBIAN_PACKAGE": package.debian_name,
"MODULE_NAME": package.dkms_name,
"PACKAGE_NAME": package_name,
"PACKAGE_VERSION": get_package_version(package.debian_name),
"MODULE_VERSION": f"{config.package_version.replace('-','+')}+{dkms_version}",
"TIME_STAMP": utils.format_datetime(datetime.now()),
"KERNEL_VERSION": f'{config.k_ver}-{config.k_arch}',
"KERNEL_PACKAGE_VERSION": config.package_version,
"KERNEL_ARCH_CPU": config.k_arch_cpu,
"KBUILD_VERSION": config.kbuild_version,
"DEBIAN_BUILD_ARCH": config.k_arch,
"DISTRIBUTION": config.distribution,
"DEB_VERSION": deb_version
}
debian_dir = os.path.join(tmp_dir, "debian")
for debian_file in glob.glob(f"{debian_dir}/**", recursive=True):
name, ext = os.path.splitext(debian_file)
if os.path.isdir(debian_file) or ext != ".in":
continue
logging.debug("Substitute variables in: %s", debian_file)
with open(debian_file, 'r') as f:
subst = Template(f.read())
os.remove(debian_file)
out = subst.substitute(values)
with open(name, 'w') as f:
f.write(out)
def build_package(dir):
logging.info("Building package")
p = subprocess.run(["dpkg-buildpackage", "-uc", "-us"], cwd=dir)
if p.returncode != 0:
raise Exception("Building the package failed")
def create_debian_package(config: Config, package: Package, dkms_version):
logging.info("Build package for: %s", package.debian_name)
with TemporaryDirectory(prefix="dkms") as tmp_dir:
content = os.path.join(tmp_dir, "content")
logging.debug("Copy template: %s", package.template_dir)
shutil.copytree(package.template_dir, content)
logging.debug("Subst vars")
subst_variables(config, package, dkms_version, content)
logging.debug("Create Tarball")
create_dkms_tarball(config, package, dkms_version, content)
build_package(content)
logging.debug("Remove build dir")
shutil.rmtree(content)
for f in os.listdir(tmp_dir):
f_src = os.path.join(tmp_dir, f)
f_dst = os.path.join(config.output_dir, f)
shutil.copy(f_src, f_dst)
def create_packages(config):
for package in config.packages:
install_package(package.debian_name)
dkms_version = dkms_get_version(package)
create_debian_package(config, package, dkms_version)
def setup_local_repo(config):
if not config.local_repo:
return
shutil.copytree(config.local_repo, "/etc/apt/", dirs_exist_ok=True)
subprocess.run(["apt-get", "update"])
def main():
try:
config = parse_config()
setup_local_repo(config)
install_kernel(config)
create_packages(config)
except Exception as e:
logging.exception(e)
exit(1)
if __name__ == "__main__":
main()