mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-26 03:31:36 +01:00
CliFunction
This commit is contained in:
47
README.adoc
47
README.adoc
@@ -830,6 +830,14 @@ For more information on baremetal, see the section: <<baremetal>>. The following
|
||||
* <<tracing>>
|
||||
* <<baremetal-gdb-step-debug>>
|
||||
|
||||
=== User mode setup
|
||||
|
||||
Much like <<baremetal-setup>>, this is another fun setup that does not require Buildroot or the Linux kernel.
|
||||
|
||||
See: <<user-mode-simulation>>
|
||||
|
||||
TODO: test it out on a clean repo.
|
||||
|
||||
[[gdb]]
|
||||
== GDB step debug
|
||||
|
||||
@@ -960,7 +968,7 @@ This automatically clears the GDB pane, and starts a new one.
|
||||
Pass extra GDB arguments with:
|
||||
|
||||
....
|
||||
./run --wait-gdb --tmux=start_kernel
|
||||
./run --wait-gdb --tmux --tmux-args start_kernel
|
||||
....
|
||||
|
||||
See the tmux manual for further details:
|
||||
@@ -2986,7 +2994,8 @@ Or alternatively, if you are using <<tmux>>, do everything in one go with:
|
||||
./run \
|
||||
--arch aarch64 \
|
||||
--userland print_argv \
|
||||
--tmux=main \
|
||||
--tmux \
|
||||
--tmux-args main \
|
||||
--wait-gdb \
|
||||
-- \
|
||||
asdf qwer \
|
||||
@@ -4106,6 +4115,12 @@ That file contains `BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/x86_64/linux
|
||||
|
||||
`arm`, on the other hand, uses link:https://github.com/buildroot/buildroot/blob/2018.05/configs/qemu_arm_vexpress_defconfig[`buildroot/configs/qemu_arm_vexpress_defconfig`], which contains `BR2_LINUX_KERNEL_DEFCONFIG="vexpress"`, and therefore just does a `make vexpress_defconfig`, and gets its config from the Linux kernel tree itself.
|
||||
|
||||
====== Linux kernel defconfigs
|
||||
|
||||
It would be interesting to test out if `make defconfig` configs boot and work on QEMU + Buildroot: https://unix.stackexchange.com/questions/29439/compiling-the-kernel-with-default-configurations/204512#204512
|
||||
|
||||
TODO.
|
||||
|
||||
===== Notable alternate gem5 kernel configs
|
||||
|
||||
Other configs which we had previously tested at 4e0d9af81fcce2ce4e777cb82a1990d7c2ca7c1e are:
|
||||
@@ -5074,7 +5089,7 @@ If `CONFIG_KALLSYMS=n`, then addresses are shown on traces instead of symbol plu
|
||||
In v4.16 it does not seem possible to configure that at runtime. GDB step debugging with:
|
||||
|
||||
....
|
||||
./run --eval-after 'insmod /dump_stack.ko' --wait-gdb --tmux=dump_stack
|
||||
./run --eval-after 'insmod /dump_stack.ko' --wait-gdb --tmux --tmux-args dump_stack
|
||||
....
|
||||
|
||||
shows that traces are printed at `arch/x86/kernel/dumpstack.c`:
|
||||
@@ -8206,7 +8221,7 @@ And in QEMU:
|
||||
Or for a faster development loop:
|
||||
|
||||
....
|
||||
./run --debug-vm='-ex "break edu_mmio_read" -ex "run"'
|
||||
./run --debug-vm --debug-vm-args '-ex "break edu_mmio_read" -ex "run"'
|
||||
....
|
||||
|
||||
When in <<qemu-text-mode>>, using `--debug-vm` makes Ctrl-C not get passed to the QEMU guest anymore: it is instead captured by GDB itself, so allow breaking. So e.g. you won't be able to easily quit from a guest program like:
|
||||
@@ -8529,8 +8544,8 @@ List all available debug flags:
|
||||
but to understand most of them you have to look at the source code:
|
||||
|
||||
....
|
||||
less "$(./getvar gem5_src_dir)/src/cpu/SConscript"
|
||||
less "$(./getvar gem5_src_dir)/src/cpu/exetrace.cc"
|
||||
less "$(./getvar gem5_source_dir)/src/cpu/SConscript"
|
||||
less "$(./getvar gem5_source_dir)/src/cpu/exetrace.cc"
|
||||
....
|
||||
|
||||
The traces are generated from `DPRINTF(<trace-id>` calls scattered throughout the code.
|
||||
@@ -9971,7 +9986,7 @@ The `--gem5-script biglittle` option enables the alternative `configs/example/ar
|
||||
First apply:
|
||||
|
||||
....
|
||||
patch -d "$(./getvar gem5_src_dir)" -p 1 < patches/manual/gem5-biglittle.patch
|
||||
patch -d "$(./getvar gem5_source_dir)" -p 1 < patches/manual/gem5-biglittle.patch
|
||||
....
|
||||
|
||||
then:
|
||||
@@ -10327,13 +10342,13 @@ then on the second shell:
|
||||
Or if you are a <<tmux,tmux pro>>, do everything in one go with:
|
||||
|
||||
....
|
||||
./run --arch arm --baremetal interactive/prompt --wait-gdb --tmux=main
|
||||
./run --arch arm --baremetal interactive/prompt --wait-gdb --tmux --tmux-args main
|
||||
....
|
||||
|
||||
Alternatively, to start from the very first executed instruction of our tiny <<baremetal-bootloaders>>:
|
||||
|
||||
....
|
||||
./run --arch arm --baremetal interactive/prompt --wait-gdb --tmux=--no-continue
|
||||
./run --arch arm --baremetal interactive/prompt --wait-gdb --tmux --tmux-args --no-continue
|
||||
....
|
||||
|
||||
Now you can just `stepi` to when jumping into main to go to the C code in link:baremetal/interactive/prompt.c[].
|
||||
@@ -10341,7 +10356,7 @@ Now you can just `stepi` to when jumping into main to go to the C code in link:b
|
||||
This is specially interesting for the executables that don't use the bootloader from under `baremetal/arch/<arch>/no_bootloader/*.S`, e.g.:
|
||||
|
||||
....
|
||||
./run --arch arm --baremetal arch/arm/no_bootloader/semihost_exit --wait-gdb --tmux=--no-continue
|
||||
./run --arch arm --baremetal arch/arm/no_bootloader/semihost_exit --wait-gdb --tmux --tmux-args --no-continue
|
||||
....
|
||||
|
||||
The cool thing about those examples is that you start at the very first instruction of your program, which gives more control.
|
||||
@@ -10369,7 +10384,7 @@ The most important things that we setup in the bootloaders are:
|
||||
The C functions that become available as a result are:
|
||||
|
||||
* Newlib functions implemented at link:baremetal/lib/syscalls.c[]
|
||||
* non-Newlib functions implemented at link:common.c[]
|
||||
* non-Newlib functions implemented at link:kwargs['c'][]
|
||||
|
||||
It is not possible to call those C functions from the examples that don't use a bootloader.
|
||||
|
||||
@@ -10401,7 +10416,7 @@ svc 0x00123456
|
||||
|
||||
and we can see from the docs that `0x18` stands for the `SYS_EXIT` command.
|
||||
|
||||
This is also how we implement the `exit(0)` system call in C for QEMU for link:baremetal/exit.c[] through the Newlib via the function `_exit` at link:baremetal/lib/common.c[].
|
||||
This is also how we implement the `exit(0)` system call in C for QEMU for link:baremetal/exit.c[] through the Newlib via the function `_exit` at link:baremetal/lib/kwargs['c'][].
|
||||
|
||||
Other magic operations we can do with semihosting besides exiting the on the host include:
|
||||
|
||||
@@ -10441,7 +10456,7 @@ Bibliography:
|
||||
For gem5, you need:
|
||||
|
||||
....
|
||||
patch -d "$(./getvar gem5_src_dir)" -p 1 < patches/manual/gem5-semihost.patch
|
||||
patch -d "$(./getvar gem5_source_dir)" -p 1 < patches/manual/gem5-semihost.patch
|
||||
....
|
||||
|
||||
https://stackoverflow.com/questions/52475268/how-to-enable-arm-semihosting-in-gem5/52475269#52475269
|
||||
@@ -11500,17 +11515,17 @@ Analogous to the <<linux-kernel-build-variants>> but with the `--gem5-build-id`
|
||||
./build-gem5
|
||||
|
||||
# Build another branch.
|
||||
git -C "$(./getvar gem5_src_dir)" checkout some-branch
|
||||
git -C "$(./getvar gem5_source_dir)" checkout some-branch
|
||||
./build-gem5 --gem5-build-id some-branch
|
||||
|
||||
# Restore master.
|
||||
git -C "$(./getvar gem5_src_dir)" checkout -
|
||||
git -C "$(./getvar gem5_source_dir)" checkout -
|
||||
|
||||
# Run master.
|
||||
./run --gem5
|
||||
|
||||
# Run another branch.
|
||||
git -C "$(./getvar gem5_src_dir)" checkout some-branch
|
||||
git -C "$(./getvar gem5_source_dir)" checkout some-branch
|
||||
./run --gem5-build-id some-branch --gem5
|
||||
....
|
||||
|
||||
|
||||
@@ -100,9 +100,9 @@ if "$bench_gem5_build"; then
|
||||
common_arch="$default_arch"
|
||||
gem5_build_id=bench
|
||||
common_gem5_build_dir="$("$getvar" --arch "$common_arch" --gem5-build-id "$gem5_build_id" gem5_build_dir)"
|
||||
common_gem5_src_dir="$("$getvar" --arch "$common_arch" --gem5-build-id "$gem5_build_id" gem5_src_dir)"
|
||||
common_gem5_source_dir="$("$getvar" --arch "$common_arch" --gem5-build-id "$gem5_build_id" gem5_src_dir)"
|
||||
results_file="${common_gem5_build_dir}/lkmc-bench-build.txt"
|
||||
git -C "${common_gem5_src_dir}" clean -xdf
|
||||
git -C "${common_gem5_source_dir}" clean -xdf
|
||||
rm -f "$results_file"
|
||||
"${root_dir}/build-gem5" --arch "$common_arch" --clean --gem5-build-id "$gem5_build_id"
|
||||
# TODO understand better: --foreground required otherwise we cannot
|
||||
@@ -110,7 +110,7 @@ if "$bench_gem5_build"; then
|
||||
# bash -c "eval 'timeout 5 sleep 3'"
|
||||
"${root_dir}/bench-cmd" "timeout --foreground 900 ./build-gem5 --arch '$common_arch' --gem5-build-id '$gem5_build_id'" "$results_file"
|
||||
cp "$results_file" "${new_dir}/gem5-bench-build-${common_arch}.txt"
|
||||
git -C "${common_gem5_src_dir}" clean -xdf
|
||||
git -C "${common_gem5_source_dir}" clean -xdf
|
||||
"${root_dir}/build-gem5" --arch "$common_arch" --clean --gem5-build-id "$gem5_build_id"
|
||||
fi
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import shutil
|
||||
import sys
|
||||
|
||||
import common
|
||||
build_linux = imp.load_source('build-linux', os.path.join(common.root_dir, 'build_linux'))
|
||||
run = imp.load_source('run', os.path.join(common.root_dir, 'run'))
|
||||
build_linux = imp.load_source('build-linux', os.path.join(kwargs['root_dir'], 'build_linux'))
|
||||
run = imp.load_source('run', os.path.join(kwargs['root_dir'], 'run'))
|
||||
|
||||
parser = common.get_argparse(
|
||||
parser = self.get_argparse(
|
||||
argparse_args={
|
||||
'description': '''Bisect the Linux kernel on gem5 boots.
|
||||
|
||||
@@ -20,11 +20,11 @@ More information at: https://github.com/cirosantilli/linux-kernel-module-cheat#b
|
||||
'linux_build_id': 'bisect',
|
||||
},
|
||||
)
|
||||
args = common.setup(parser)
|
||||
args = self.setup(parser)
|
||||
# We need a clean rebuild because rebuilds at different revisions:
|
||||
# - may fail
|
||||
# - may not actually rebuild all files, e.g. on header changes
|
||||
common.rmrf(common.linux_build_dir)
|
||||
self.rmrf(kwargs['linux_build_dir'])
|
||||
build_linux.LinuxComponent().do_build(args)
|
||||
status = run.main(args, {
|
||||
'eval': 'm5 exit',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
import common
|
||||
parser = common.get_argparse(
|
||||
parser = self.get_argparse(
|
||||
argparse_args={'description':'Convert a BST vs heap stat file into a gnuplot input'}
|
||||
)
|
||||
args = common.setup(parser)
|
||||
stats = common.get_stats()
|
||||
args = self.setup(parser)
|
||||
stats = self.get_stats()
|
||||
it = iter(stats)
|
||||
i = 1
|
||||
for stat in it:
|
||||
|
||||
95
build
95
build
@@ -7,6 +7,7 @@ import re
|
||||
import os
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class Component:
|
||||
'''
|
||||
@@ -49,11 +50,11 @@ class Component:
|
||||
def run_cmd(cmd, arch):
|
||||
global args
|
||||
cmd_abs = cmd.copy()
|
||||
cmd_abs[0] = os.path.join(common.root_dir, cmd[0])
|
||||
cmd_abs[0] = os.path.join(kwargs['root_dir'], cmd[0])
|
||||
cmd_abs.extend(['--arch', arch])
|
||||
if args.extra_args:
|
||||
cmd_abs.append(args.extra_args)
|
||||
common.run_cmd(cmd_abs, dry_run=args.dry_run)
|
||||
if kwargs['extra_args']:
|
||||
cmd_abs.append(kwargs['extra_args'])
|
||||
self.sh.run_cmd(cmd_abs, dry_run=kwargs['dry_run'])
|
||||
|
||||
buildroot_component = Component(
|
||||
lambda arch: run_cmd(['build-buildroot'], arch),
|
||||
@@ -86,15 +87,15 @@ name_to_component_map = {
|
||||
# Leaves without dependencies.
|
||||
'baremetal-qemu': Component(
|
||||
lambda arch: run_cmd(['build-baremetal', '--qemu'], arch),
|
||||
supported_archs=common.crosstool_ng_supported_archs,
|
||||
supported_archs=kwargs['crosstool_ng_supported_archs'],
|
||||
),
|
||||
'baremetal-gem5': Component(
|
||||
lambda arch: run_cmd(['build-baremetal', '--gem5'], arch),
|
||||
supported_archs=common.crosstool_ng_supported_archs,
|
||||
supported_archs=kwargs['crosstool_ng_supported_archs'],
|
||||
),
|
||||
'baremetal-gem5-pbx': Component(
|
||||
lambda arch: run_cmd(['build-baremetal', '--gem5', '--machine', 'RealViewPBX'], arch),
|
||||
supported_archs=common.crosstool_ng_supported_archs,
|
||||
supported_archs=kwargs['crosstool_ng_supported_archs'],
|
||||
),
|
||||
'buildroot': buildroot_component,
|
||||
'buildroot-gcc': buildroot_component,
|
||||
@@ -103,7 +104,7 @@ name_to_component_map = {
|
||||
),
|
||||
'crosstool-ng': Component(
|
||||
lambda arch: run_cmd(['build-crosstool-ng'], arch),
|
||||
supported_archs=common.crosstool_ng_supported_archs,
|
||||
supported_archs=kwargs['crosstool_ng_supported_archs'],
|
||||
# http://crosstool-ng.github.io/docs/os-setup/
|
||||
apt_get_pkgs={
|
||||
'bison',
|
||||
@@ -197,7 +198,7 @@ name_to_component_map = {
|
||||
'gem5-baremetal',
|
||||
'baremetal-gem5-pbx',
|
||||
],
|
||||
supported_archs=common.crosstool_ng_supported_archs,
|
||||
supported_archs=kwargs['crosstool_ng_supported_archs'],
|
||||
),
|
||||
'all-linux': Component(dependencies=[
|
||||
'qemu-gem5-buildroot',
|
||||
@@ -296,10 +297,10 @@ group = parser.add_mutually_exclusive_group(required=False)
|
||||
group.add_argument('-A', '--all-archs', default=False, action='store_true', help='''\
|
||||
Build the selected components for all archs.
|
||||
''')
|
||||
group.add_argument('-a', '--arch', choices=common.arch_choices, default=[], action='append', help='''\
|
||||
group.add_argument('-a', '--arch', choices=kwargs['arch_choices'], default=[], action='append', help='''\
|
||||
Build the selected components for this arch. Select multiple archs by
|
||||
passing this option multiple times. Default: [{}]
|
||||
'''.format(common.default_arch))
|
||||
'''.format(kwargs['default_arch']))
|
||||
parser.add_argument('-D', '--download-dependencies', default=False, action='store_true', help='''\
|
||||
Also download all dependencies required for a given build: Ubuntu packages,
|
||||
Python packages and git submodules.
|
||||
@@ -314,27 +315,27 @@ Extra args to pass to all scripts.
|
||||
)
|
||||
parser.add_argument('components', choices=list(name_to_component_map.keys()) + [[]], default=[], nargs='*', help='''\
|
||||
Which components to build. Default: qemu-buildroot
|
||||
'''.format(common.default_arch))
|
||||
common.add_dry_run_argument(parser)
|
||||
'''.format(kwargs['default_arch']))
|
||||
self.add_dry_run_argument(parser)
|
||||
args = parser.parse_args()
|
||||
common.setup_dry_run_arguments(args)
|
||||
self.setup_dry_run_arguments(args)
|
||||
|
||||
# Decide archs.
|
||||
if args.arch == []:
|
||||
if args.all or args.all_archs:
|
||||
archs = common.all_archs.copy()
|
||||
if kwargs['arch'] == []:
|
||||
if kwargs['all'] or kwargs['all_archs']:
|
||||
archs = kwargs['all_archs'].copy()
|
||||
else:
|
||||
archs = set([common.default_arch])
|
||||
archs = set([kwargs['default_arch']])
|
||||
else:
|
||||
archs = set()
|
||||
for arch in args.arch:
|
||||
if arch in common.arch_short_to_long_dict:
|
||||
arch = common.arch_short_to_long_dict[arch]
|
||||
for arch in kwargs['arch']:
|
||||
if arch in kwargs['arch_short_to_long_dict']:
|
||||
arch = kwargs['arch_short_to_long_dict'][arch]
|
||||
archs.add(arch)
|
||||
|
||||
# Decide components.
|
||||
components = args.components
|
||||
if args.all:
|
||||
components = kwargs['components']
|
||||
if kwargs['all']:
|
||||
components = ['all']
|
||||
elif components == []:
|
||||
components = ['qemu-buildroot']
|
||||
@@ -350,7 +351,7 @@ for component_name in components:
|
||||
selected_components.append(component)
|
||||
todo.extend(component.dependencies)
|
||||
|
||||
if args.download_dependencies:
|
||||
if kwargs['download_dependencies']:
|
||||
apt_get_pkgs = {
|
||||
# Core requirements for this repo.
|
||||
'git',
|
||||
@@ -388,12 +389,12 @@ if args.download_dependencies:
|
||||
python2_pkgs.update(component.python2_pkgs)
|
||||
python3_pkgs.update(component.python3_pkgs)
|
||||
if apt_get_pkgs or apt_build_deps:
|
||||
if args.travis:
|
||||
if kwargs['travis']:
|
||||
interacive_pkgs = {
|
||||
'libsdl2-dev',
|
||||
}
|
||||
apt_get_pkgs.difference_update(interacive_pkgs)
|
||||
if common.in_docker:
|
||||
if kwargs['in_docker']:
|
||||
sudo = []
|
||||
# https://askubuntu.com/questions/909277/avoiding-user-interaction-with-tzdata-when-installing-certbot-in-a-docker-contai
|
||||
os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
|
||||
@@ -406,35 +407,35 @@ if args.download_dependencies:
|
||||
f.write(sources_txt)
|
||||
else:
|
||||
sudo = ['sudo']
|
||||
if common.in_docker or args.travis:
|
||||
if kwargs['in_docker'] or kwargs['travis']:
|
||||
y = ['-y']
|
||||
else:
|
||||
y = []
|
||||
common.run_cmd(
|
||||
sudo + ['apt-get', 'update', common.Newline]
|
||||
self.sh.run_cmd(
|
||||
sudo + ['apt-get', 'update', LF]
|
||||
)
|
||||
if apt_get_pkgs:
|
||||
common.run_cmd(
|
||||
sudo + ['apt-get', 'install'] + y + [common.Newline] +
|
||||
common.add_newlines(sorted(apt_get_pkgs))
|
||||
self.sh.run_cmd(
|
||||
sudo + ['apt-get', 'install'] + y + [LF] +
|
||||
self.sh.add_newlines(sorted(apt_get_pkgs))
|
||||
)
|
||||
if apt_build_deps:
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
sudo +
|
||||
['apt-get', 'build-dep'] + y + [common.Newline] +
|
||||
common.add_newlines(sorted(apt_build_deps))
|
||||
['apt-get', 'build-dep'] + y + [LF] +
|
||||
self.sh.add_newlines(sorted(apt_build_deps))
|
||||
)
|
||||
if python2_pkgs:
|
||||
common.run_cmd(
|
||||
['python', '-m', 'pip', 'install', '--user', common.Newline] +
|
||||
common.add_newlines(sorted(python2_pkgs))
|
||||
self.sh.run_cmd(
|
||||
['python', '-m', 'pip', 'install', '--user', LF] +
|
||||
self.sh.add_newlines(sorted(python2_pkgs))
|
||||
)
|
||||
if python3_pkgs:
|
||||
# Not with pip executable directly:
|
||||
# https://stackoverflow.com/questions/49836676/error-after-upgrading-pip-cannot-import-name-main/51846054#51846054
|
||||
common.run_cmd(
|
||||
['python3', '-m', 'pip', 'install', '--user', common.Newline] +
|
||||
common.add_newlines(sorted(python3_pkgs))
|
||||
self.sh.run_cmd(
|
||||
['python3', '-m', 'pip', 'install', '--user', LF] +
|
||||
self.sh.add_newlines(sorted(python3_pkgs))
|
||||
)
|
||||
git_cmd_common = ['git', 'submodule', 'update', '--init', '--recursive']
|
||||
if submodules:
|
||||
@@ -448,9 +449,9 @@ if args.download_dependencies:
|
||||
# * https://stackoverflow.com/questions/4640020/progress-indicator-for-git-clone
|
||||
#
|
||||
# `--jobs"`: https://stackoverflow.com/questions/26957237/how-to-make-git-clone-faster-with-multiple-threads/52327638#52327638
|
||||
common.run_cmd(
|
||||
git_cmd_common + ['--', common.Newline] +
|
||||
common.add_newlines([os.path.join(common.submodules_dir, x) for x in sorted(submodules)])
|
||||
self.sh.run_cmd(
|
||||
git_cmd_common + ['--', LF] +
|
||||
self.sh.add_newlines([os.path.join(kwargs['submodules_dir'], x) for x in sorted(submodules)])
|
||||
)
|
||||
if submodules_shallow:
|
||||
# == Shallow cloning.
|
||||
@@ -472,9 +473,9 @@ if args.download_dependencies:
|
||||
# * https://stackoverflow.com/questions/2144406/git-shallow-submodules/47374702#47374702
|
||||
# * https://unix.stackexchange.com/questions/338578/why-is-the-git-clone-of-the-linux-kernel-source-code-much-larger-than-the-extrac
|
||||
#
|
||||
common.run_cmd(
|
||||
git_cmd_common + ['--depth', '1', '--', common.Newline] +
|
||||
common.add_newlines([os.path.join(common.submodules_dir, x) for x in sorted(submodules_shallow)])
|
||||
self.sh.run_cmd(
|
||||
git_cmd_common + ['--depth', '1', '--', LF] +
|
||||
self.sh.add_newlines([os.path.join(kwargs['submodules_dir'], x) for x in sorted(submodules_shallow)])
|
||||
)
|
||||
|
||||
# Do the build.
|
||||
|
||||
148
build-baremetal
148
build-baremetal
@@ -1,73 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class BaremetalComponent(common.Component):
|
||||
def do_build(self, args):
|
||||
common.assert_crosstool_ng_supports_arch(args.arch)
|
||||
build_dir = self.get_build_dir(args)
|
||||
bootloader_obj = os.path.join(common.baremetal_build_lib_dir, 'bootloader{}'.format(common.obj_ext))
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description='''\
|
||||
Build the baremetal examples with crosstool-NG.
|
||||
''')
|
||||
|
||||
def build(self):
|
||||
self.assert_crosstool_ng_supports_arch(self.env['arch'])
|
||||
build_dir = self.get_build_dir()
|
||||
bootloader_obj = os.path.join(self.env['baremetal_build_lib_dir'], 'bootloader{}'.format(self.env['obj_ext']))
|
||||
common_basename_noext = 'common'
|
||||
common_src = os.path.join(common.root_dir, common_basename_noext + common.c_ext)
|
||||
common_obj = os.path.join(common.baremetal_build_lib_dir, common_basename_noext + common.obj_ext)
|
||||
common_src = os.path.join(self.env['root_dir'], common_basename_noext + self.env['c_ext'])
|
||||
common_obj = os.path.join(self.env['baremetal_build_lib_dir'], common_basename_noext + self.env['obj_ext'])
|
||||
syscalls_basename_noext = 'syscalls'
|
||||
syscalls_src = os.path.join(common.baremetal_src_lib_dir, syscalls_basename_noext + common.c_ext)
|
||||
syscalls_obj = os.path.join(common.baremetal_build_lib_dir, syscalls_basename_noext + common.obj_ext)
|
||||
syscalls_src = os.path.join(self.env['baremetal_src_lib_dir'], syscalls_basename_noext + self.env['c_ext'])
|
||||
syscalls_obj = os.path.join(self.env['baremetal_build_lib_dir'], syscalls_basename_noext + self.env['obj_ext'])
|
||||
common_objs = [common_obj, syscalls_obj]
|
||||
cflags = [
|
||||
'-I', common.baremetal_src_lib_dir, common.Newline,
|
||||
'-I', common.root_dir, common.Newline,
|
||||
'-O0', common.Newline,
|
||||
'-ggdb3', common.Newline,
|
||||
'-mcpu={}'.format(common.mcpu), common.Newline,
|
||||
'-nostartfiles', common.Newline,
|
||||
'-I', self.env['baremetal_src_lib_dir'], LF,
|
||||
'-I', self.env['root_dir'], LF,
|
||||
'-O0', LF,
|
||||
'-ggdb3', LF,
|
||||
'-mcpu={}'.format(self.env['mcpu']), LF,
|
||||
'-nostartfiles', LF,
|
||||
]
|
||||
if args.prebuilt:
|
||||
if self.env['prebuilt']:
|
||||
gcc = 'arm-none-eabi-gcc'
|
||||
else:
|
||||
os.environ['PATH'] = common.crosstool_ng_bin_dir + os.environ['PATH']
|
||||
gcc = common.get_toolchain_tool('gcc', allowed_toolchains=['crosstool-ng'])
|
||||
if common.emulator == 'gem5':
|
||||
if common.machine == 'VExpress_GEM5_V1':
|
||||
os.environ['PATH'] = self.env['crosstool_ng_bin_dir'] + os.environ['PATH']
|
||||
gcc = self.get_toolchain_tool('gcc', allowed_toolchains=['crosstool-ng'])
|
||||
if self.env['emulator'] == 'gem5':
|
||||
if self.env['machine'] == 'VExpress_GEM5_V1':
|
||||
entry_address = 0x80000000
|
||||
uart_address = 0x1c090000
|
||||
elif common.machine == 'RealViewPBX':
|
||||
elif self.env['machine'] == 'RealViewPBX':
|
||||
entry_address = 0x10000
|
||||
uart_address = 0x10009000
|
||||
else:
|
||||
raise Exception('unknown machine: ' + common.machine)
|
||||
cflags.extend(['-D', 'GEM5'.format(uart_address), common.Newline])
|
||||
raise Exception('unknown machine: ' + self.env['machine'])
|
||||
cflags.extend(['-D', 'GEM5'.format(uart_address), LF])
|
||||
else:
|
||||
entry_address = 0x40000000
|
||||
uart_address = 0x09000000
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
os.makedirs(common.baremetal_build_lib_dir, exist_ok=True)
|
||||
src = os.path.join(common.baremetal_src_lib_dir, '{}{}'.format(args.arch, common.asm_ext))
|
||||
if common.need_rebuild([src], bootloader_obj):
|
||||
common.run_cmd(
|
||||
[gcc, common.Newline] +
|
||||
os.makedirs(self.env['baremetal_build_lib_dir'], exist_ok=True)
|
||||
src = os.path.join(self.env['baremetal_src_lib_dir'], '{}{}'.format(self.env['arch'], self.env['asm_ext']))
|
||||
if self.need_rebuild([src], bootloader_obj):
|
||||
self.sh.run_cmd(
|
||||
[gcc, LF] +
|
||||
cflags +
|
||||
[
|
||||
'-c', common.Newline,
|
||||
'-o', bootloader_obj, common.Newline,
|
||||
src, common.Newline,
|
||||
'-c', LF,
|
||||
'-o', bootloader_obj, LF,
|
||||
src, LF,
|
||||
]
|
||||
)
|
||||
for src, obj in [
|
||||
(common_src, common_obj),
|
||||
(syscalls_src, syscalls_obj),
|
||||
]:
|
||||
if common.need_rebuild([src], obj):
|
||||
common.run_cmd(
|
||||
[gcc, common.Newline] +
|
||||
if self.need_rebuild([src], obj):
|
||||
self.sh.run_cmd(
|
||||
[gcc, LF] +
|
||||
cflags +
|
||||
[
|
||||
'-c', common.Newline,
|
||||
'-D', 'UART0_ADDR={:#x}'.format(uart_address), common.Newline,
|
||||
'-o', obj, common.Newline,
|
||||
src, common.Newline,
|
||||
'-c', LF,
|
||||
'-D', 'UART0_ADDR={:#x}'.format(uart_address), LF,
|
||||
'-o', obj, LF,
|
||||
src, LF,
|
||||
]
|
||||
)
|
||||
self._build_dir(
|
||||
@@ -86,8 +92,8 @@ class BaremetalComponent(common.Component):
|
||||
bootloader_obj=bootloader_obj,
|
||||
common_objs=common_objs,
|
||||
)
|
||||
arch_dir = os.path.join('arch', args.arch)
|
||||
if os.path.isdir(os.path.join(common.baremetal_src_dir, arch_dir)):
|
||||
arch_dir = os.path.join('arch', self.env['arch'])
|
||||
if os.path.isdir(os.path.join(self.env['baremetal_src_dir'], arch_dir)):
|
||||
self._build_dir(
|
||||
arch_dir,
|
||||
gcc=gcc,
|
||||
@@ -96,8 +102,8 @@ class BaremetalComponent(common.Component):
|
||||
bootloader_obj=bootloader_obj,
|
||||
common_objs=common_objs,
|
||||
)
|
||||
arch_dir = os.path.join('arch', args.arch, 'no_bootloader')
|
||||
if os.path.isdir(os.path.join(common.baremetal_src_dir, arch_dir)):
|
||||
arch_dir = os.path.join('arch', self.env['arch'], 'no_bootloader')
|
||||
if os.path.isdir(os.path.join(self.env['baremetal_src_dir'], arch_dir)):
|
||||
self._build_dir(
|
||||
arch_dir,
|
||||
gcc=gcc,
|
||||
@@ -108,18 +114,8 @@ class BaremetalComponent(common.Component):
|
||||
bootloader=False,
|
||||
)
|
||||
|
||||
def get_argparse_args(self):
|
||||
return {
|
||||
'description': '''\
|
||||
Build the baremetal examples with crosstool-NG.
|
||||
'''
|
||||
}
|
||||
|
||||
def get_build_dir(self, args):
|
||||
return common.baremetal_build_dir
|
||||
|
||||
def get_default_args(self):
|
||||
return {'baremetal': 'all'}
|
||||
def get_build_dir(self):
|
||||
return self.env['baremetal_build_dir']
|
||||
|
||||
def _build_dir(
|
||||
self,
|
||||
@@ -137,42 +133,42 @@ Build the baremetal examples with crosstool-NG.
|
||||
|
||||
Place outputs on the same subpath or the output directory.
|
||||
"""
|
||||
in_dir = os.path.join(common.baremetal_src_dir, subpath)
|
||||
out_dir = os.path.join(common.baremetal_build_dir, subpath)
|
||||
in_dir = os.path.join(self.env['baremetal_src_dir'], subpath)
|
||||
out_dir = os.path.join(self.env['baremetal_build_dir'], subpath)
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
common_objs = common_objs.copy()
|
||||
if bootloader:
|
||||
common_objs.append(bootloader_obj)
|
||||
for in_basename in os.listdir(in_dir):
|
||||
in_path = os.path.join(in_dir, in_basename)
|
||||
if os.path.isfile(in_path) and os.path.splitext(in_basename)[1] in (common.c_ext, common.asm_ext):
|
||||
if os.path.isfile(in_path) and os.path.splitext(in_basename)[1] in (self.env['c_ext'], self.env['asm_ext']):
|
||||
in_name = os.path.splitext(in_basename)[0]
|
||||
main_obj = os.path.join(common.baremetal_build_dir, subpath, '{}{}'.format(in_name, common.obj_ext))
|
||||
src = os.path.join(common.baremetal_src_dir, in_path)
|
||||
if common.need_rebuild([src], main_obj):
|
||||
common.run_cmd(
|
||||
[gcc, common.Newline] +
|
||||
main_obj = os.path.join(self.env['baremetal_build_dir'], subpath, '{}{}'.format(in_name, self.env['obj_ext']))
|
||||
src = os.path.join(self.env['baremetal_src_dir'], in_path)
|
||||
if self.need_rebuild([src], main_obj):
|
||||
self.sh.run_cmd(
|
||||
[gcc, LF] +
|
||||
cflags +
|
||||
[
|
||||
'-c', common.Newline,
|
||||
'-o', main_obj, common.Newline,
|
||||
src, common.Newline,
|
||||
'-c', LF,
|
||||
'-o', main_obj, LF,
|
||||
src, LF,
|
||||
]
|
||||
)
|
||||
objs = common_objs + [main_obj]
|
||||
out = os.path.join(common.baremetal_build_dir, subpath, in_name + common.baremetal_build_ext)
|
||||
link_script = os.path.join(common.baremetal_src_dir, 'link.ld')
|
||||
if common.need_rebuild(objs + [link_script], out):
|
||||
common.run_cmd(
|
||||
[gcc, common.Newline] +
|
||||
out = os.path.join(self.env['baremetal_build_dir'], subpath, in_name + self.env['baremetal_build_ext'])
|
||||
link_script = os.path.join(self.env['baremetal_src_dir'], 'link.ld')
|
||||
if self.need_rebuild(objs + [link_script], out):
|
||||
self.sh.run_cmd(
|
||||
[gcc, LF] +
|
||||
cflags +
|
||||
[
|
||||
'-Wl,--section-start=.text={:#x}'.format(entry_address), common.Newline,
|
||||
'-o', out, common.Newline,
|
||||
'-T', link_script, common.Newline,
|
||||
'-Wl,--section-start=.text={:#x}'.format(entry_address), LF,
|
||||
'-o', out, LF,
|
||||
'-T', link_script, LF,
|
||||
] +
|
||||
common.add_newlines(objs)
|
||||
self.sh.add_newlines(objs)
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
BaremetalComponent().build()
|
||||
Main().cli()
|
||||
|
||||
168
build-buildroot
168
build-buildroot
@@ -9,11 +9,16 @@ import time
|
||||
import re
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class BuildrootComponent(common.Component):
|
||||
def add_parser_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--build-linux', default=self._defaults['build_linux'], action='store_true',
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description='''\
|
||||
Run Linux on an emulator
|
||||
''')
|
||||
self.add_argument(
|
||||
'--build-linux', default=False,
|
||||
help='''\
|
||||
Enable building the Linux kernel with Buildroot. This is done mostly
|
||||
to extract Buildroot's default kernel configurations when updating Buildroot.
|
||||
@@ -21,35 +26,35 @@ This kernel will not be use by our other scripts. Configuring this kernel is
|
||||
not currently supported, juse use ./build-linux script if you want to do that.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--baseline', default=self._defaults['baseline'], action='store_true',
|
||||
self.add_argument(
|
||||
'--baseline', default=False,
|
||||
help='''Do a default-ish Buildroot defconfig build, without any of our extra options.
|
||||
Mostly to track how much slower we are than a basic build.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--config', default=self._defaults['config'], action='append',
|
||||
self.add_argument(
|
||||
'--config', default=[], action='append',
|
||||
help='''Add a single Buildroot config to the current build.
|
||||
Example value: 'BR2_TARGET_ROOTFS_EXT2_SIZE="512M"'.
|
||||
Can be used multiple times to add multiple configs.
|
||||
Takes precedence over any Buildroot config files.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--config-fragment', default=self._defaults['config_fragment'], action='append',
|
||||
self.add_argument(
|
||||
'--config-fragment', default=[], action='append',
|
||||
help='''Also use the given Buildroot configuration fragment file.
|
||||
Pass multiple times to use multiple fragment files.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-all', default=self._defaults['no_all'], action='store_true',
|
||||
self.add_argument(
|
||||
'--no-all', default=False,
|
||||
help='''\
|
||||
Don't build the all target which normally gets build by default.
|
||||
That target builds the root filesystem and all its dependencies.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-overlay', default=self._defaults['no_all'], action='store_true',
|
||||
self.add_argument(
|
||||
'--no-overlay', default=False,
|
||||
help='''\
|
||||
Don't add our overlay which contains all files we build without going through Buildroot.
|
||||
This prevents us from overwriting certain Buildroot files. Remember however that you must
|
||||
@@ -57,132 +62,117 @@ still rebuild the Buildroot package that provides those files to actually put th
|
||||
files on the root filesystem.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'extra_make_args', default=self._defaults['extra_make_args'], metavar='extra-make-args', nargs='*',
|
||||
self.add_argument(
|
||||
'extra-make-args', default=[], nargs='*',
|
||||
help='''\
|
||||
Extra arguments to be passed to the Buildroot make,
|
||||
usually extra Buildroot targets.
|
||||
'''
|
||||
)
|
||||
|
||||
def do_build(self, args):
|
||||
build_dir = self.get_build_dir(args)
|
||||
os.makedirs(common.out_dir, exist_ok=True)
|
||||
extra_make_args = common.add_newlines(args.extra_make_args)
|
||||
if args.build_linux:
|
||||
extra_make_args.extend(['linux-reconfigure', common.Newline])
|
||||
if common.emulator == 'gem5':
|
||||
extra_make_args.extend(['gem5-reconfigure', common.Newline])
|
||||
if args.arch == 'x86_64':
|
||||
def build(self):
|
||||
build_dir = self.get_build_dir()
|
||||
os.makedirs(self.env['out_dir'], exist_ok=True)
|
||||
extra_make_args = self.sh.add_newlines(self.env['extra_make_args'])
|
||||
if self.env['build_linux']:
|
||||
extra_make_args.extend(['linux-reconfigure', LF])
|
||||
if self.env['emulator'] == 'gem5':
|
||||
extra_make_args.extend(['gem5-reconfigure', LF])
|
||||
if self.env['arch'] == 'x86_64':
|
||||
defconfig = 'qemu_x86_64_defconfig'
|
||||
elif args.arch == 'arm':
|
||||
elif self.env['arch'] == 'arm':
|
||||
defconfig = 'qemu_arm_vexpress_defconfig'
|
||||
elif args.arch == 'aarch64':
|
||||
elif self.env['arch'] == 'aarch64':
|
||||
defconfig = 'qemu_aarch64_virt_defconfig'
|
||||
br2_external_dirs = []
|
||||
for package_dir in os.listdir(common.packages_dir):
|
||||
package_dir_abs = os.path.join(common.packages_dir, package_dir)
|
||||
for package_dir in os.listdir(self.env['packages_dir']):
|
||||
package_dir_abs = os.path.join(self.env['packages_dir'], package_dir)
|
||||
if os.path.isdir(package_dir_abs):
|
||||
br2_external_dirs.append(self._path_relative_to_buildroot(package_dir_abs))
|
||||
br2_external_str = ':'.join(br2_external_dirs)
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
[
|
||||
'make', common.Newline,
|
||||
'O={}'.format(common.buildroot_build_dir), common.Newline,
|
||||
'BR2_EXTERNAL={}'.format(br2_external_str), common.Newline,
|
||||
defconfig, common.Newline,
|
||||
'make', LF,
|
||||
'O={}'.format(self.env['buildroot_build_dir']), LF,
|
||||
'BR2_EXTERNAL={}'.format(br2_external_str), LF,
|
||||
defconfig, LF,
|
||||
],
|
||||
cwd=common.buildroot_src_dir,
|
||||
cwd=self.env['buildroot_src_dir'],
|
||||
)
|
||||
configs = args.config
|
||||
configs = self.env['config']
|
||||
configs.extend([
|
||||
'BR2_JLEVEL={}'.format(args.nproc),
|
||||
'BR2_DL_DIR="{}"'.format(common.buildroot_download_dir),
|
||||
'BR2_JLEVEL={}'.format(self.env['nproc']),
|
||||
'BR2_DL_DIR="{}"'.format(self.env['buildroot_download_dir']),
|
||||
])
|
||||
if not args.build_linux:
|
||||
if not self.env['build_linux']:
|
||||
configs.extend([
|
||||
'# BR2_LINUX_KERNEL is not set',
|
||||
])
|
||||
config_fragments = []
|
||||
if not args.baseline:
|
||||
if not self.env['baseline']:
|
||||
configs.extend([
|
||||
'BR2_GLOBAL_PATCH_DIR="{}"'.format(
|
||||
self._path_relative_to_buildroot(os.path.join(common.root_dir, 'patches', 'global'))
|
||||
self._path_relative_to_buildroot(os.path.join(self.env['root_dir'], 'patches', 'global'))
|
||||
),
|
||||
'BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="{}"'.format(
|
||||
self._path_relative_to_buildroot(os.path.join(common.root_dir, 'busybox_config_fragment'))
|
||||
self._path_relative_to_buildroot(os.path.join(self.env['root_dir'], 'busybox_config_fragment'))
|
||||
),
|
||||
'BR2_PACKAGE_OVERRIDE_FILE="{}"'.format(
|
||||
self._path_relative_to_buildroot(os.path.join(common.root_dir, 'buildroot_override'))
|
||||
self._path_relative_to_buildroot(os.path.join(self.env['root_dir'], 'buildroot_override'))
|
||||
),
|
||||
'BR2_ROOTFS_POST_BUILD_SCRIPT="{}"'.format(
|
||||
self._path_relative_to_buildroot(os.path.join(common.root_dir, 'rootfs-post-build-script'))
|
||||
self._path_relative_to_buildroot(os.path.join(self.env['root_dir'], 'rootfs-post-build-script'))
|
||||
),
|
||||
'BR2_ROOTFS_USERS_TABLES="{}"'.format(
|
||||
self._path_relative_to_buildroot(os.path.join(common.root_dir, 'user_table'))
|
||||
self._path_relative_to_buildroot(os.path.join(self.env['root_dir'], 'user_table'))
|
||||
),
|
||||
])
|
||||
if not args.no_overlay:
|
||||
if not self.env['no_overlay']:
|
||||
configs.append('BR2_ROOTFS_OVERLAY="{}"'.format(
|
||||
self._path_relative_to_buildroot(common.out_rootfs_overlay_dir)
|
||||
self._path_relative_to_buildroot(self.env['out_rootfs_overlay_dir'])
|
||||
))
|
||||
config_fragments = [
|
||||
os.path.join(common.root_dir, 'buildroot_config', 'default')
|
||||
] + args.config_fragment
|
||||
common.write_configs(common.buildroot_config_file, configs, config_fragments)
|
||||
common.run_cmd(
|
||||
os.path.join(self.env['root_dir'], 'buildroot_config', 'default')
|
||||
] + self.env['config_fragment']
|
||||
# TODO Can't get rid of these for now with nice fragments on Buildroot:
|
||||
# http://stackoverflow.com/questions/44078245/is-it-possible-to-use-config-fragments-with-buildroots-config
|
||||
self.sh.write_configs(self.env['buildroot_config_file'], configs, config_fragments)
|
||||
self.sh.run_cmd(
|
||||
[
|
||||
'make', common.Newline,
|
||||
'O={}'.format(common.buildroot_build_dir), common.Newline,
|
||||
'olddefconfig', common.Newline,
|
||||
'make', LF,
|
||||
'O={}'.format(self.env['buildroot_build_dir']), LF,
|
||||
'olddefconfig', LF,
|
||||
],
|
||||
cwd=common.buildroot_src_dir,
|
||||
cwd=self.env['buildroot_src_dir'],
|
||||
)
|
||||
common.make_build_dirs()
|
||||
if not args.no_all:
|
||||
extra_make_args.extend(['all', common.Newline])
|
||||
common.run_cmd(
|
||||
self.make_build_dirs()
|
||||
if not self.env['no_all']:
|
||||
extra_make_args.extend(['all', LF])
|
||||
self.sh.run_cmd(
|
||||
[
|
||||
'make', common.Newline,
|
||||
'LKMC_GEM5_SRCDIR="{}"'.format(common.gem5_src_dir), common.Newline,
|
||||
'LKMC_PARSEC_BENCHMARK_SRCDIR="{}"'.format(common.parsec_benchmark_src_dir), common.Newline,
|
||||
'O={}'.format(common.buildroot_build_dir), common.Newline,
|
||||
'V={}'.format(int(args.verbose)), common.Newline,
|
||||
'make', LF,
|
||||
'LKMC_GEM5_SRCDIR="{}"'.format(self.env['gem5_source_dir']), LF,
|
||||
'LKMC_PARSEC_BENCHMARK_SRCDIR="{}"'.format(self.env['parsec_benchmark_src_dir']), LF,
|
||||
'O={}'.format(self.env['buildroot_build_dir']), LF,
|
||||
'V={}'.format(int(self.env['verbose'])), LF,
|
||||
] +
|
||||
extra_make_args
|
||||
,
|
||||
out_file=os.path.join(common.buildroot_build_dir, 'lkmc.log'),
|
||||
out_file=os.path.join(self.env['buildroot_build_dir'], 'lkmc.log'),
|
||||
delete_env=['LD_LIBRARY_PATH'],
|
||||
cwd=common.buildroot_src_dir,
|
||||
cwd=self.env['buildroot_src_dir'],
|
||||
)
|
||||
# Create the qcow2 from ext2.
|
||||
# Skip if qemu is not present, because gem5 does not need the qcow2.
|
||||
# so we don't force a QEMU build for gem5.
|
||||
if not args.no_all and os.path.exists(common.qemu_img_executable):
|
||||
common.raw_to_qcow2()
|
||||
if not self.env['no_all'] and os.path.exists(self.env['qemu_img_executable']):
|
||||
self.raw_to_qcow2()
|
||||
|
||||
def get_argparse_args(self):
|
||||
return {
|
||||
'description': '''\
|
||||
Run Linux on an emulator
|
||||
'''
|
||||
}
|
||||
|
||||
def get_build_dir(self, args):
|
||||
return common.buildroot_build_dir
|
||||
|
||||
_defaults = {
|
||||
'baseline': False,
|
||||
'build_linux': False,
|
||||
'config': [],
|
||||
'config_fragment': [],
|
||||
'extra_make_args': [],
|
||||
'no_all': False,
|
||||
'skip_configure': False,
|
||||
}
|
||||
def get_build_dir(self):
|
||||
return self.env['buildroot_build_dir']
|
||||
|
||||
def _path_relative_to_buildroot(self, abspath):
|
||||
return os.path.relpath(abspath, common.buildroot_src_dir)
|
||||
return os.path.relpath(abspath, self.env['buildroot_src_dir'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
BuildrootComponent().build()
|
||||
Main().cli()
|
||||
|
||||
@@ -3,75 +3,70 @@
|
||||
import os
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class CrosstoolNgComponent(common.Component):
|
||||
def do_build(self, args):
|
||||
common.assert_crosstool_ng_supports_arch(args.arch)
|
||||
build_dir = self.get_build_dir(args)
|
||||
defconfig_dest = os.path.join(common.crosstool_ng_util_dir, 'defconfig')
|
||||
os.makedirs(common.crosstool_ng_util_dir, exist_ok=True)
|
||||
os.makedirs(common.crosstool_ng_download_dir, exist_ok=True)
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description='''\
|
||||
Build crosstool-NG with Newlib for bare metal compilation
|
||||
''')
|
||||
|
||||
def build(self):
|
||||
self.assert_crosstool_ng_supports_arch(self.env['arch'])
|
||||
build_dir = self.get_build_dir()
|
||||
defconfig_dest = os.path.join(self.env['crosstool_ng_util_dir'], 'defconfig')
|
||||
os.makedirs(self.env['crosstool_ng_util_dir'], exist_ok=True)
|
||||
os.makedirs(self.env['crosstool_ng_download_dir'], exist_ok=True)
|
||||
|
||||
# Bootstrap out-ot-tree WONTFIX. I've tried.
|
||||
# https://github.com/crosstool-ng/crosstool-ng/issues/1021
|
||||
os.chdir(common.crosstool_ng_src_dir)
|
||||
common.run_cmd(
|
||||
[os.path.join(common.crosstool_ng_src_dir, 'bootstrap'), common.Newline],
|
||||
os.chdir(self.env['crosstool_ng_src_dir'])
|
||||
self.sh.run_cmd(
|
||||
[os.path.join(self.env['crosstool_ng_src_dir'], 'bootstrap'), LF],
|
||||
)
|
||||
os.chdir(common.crosstool_ng_util_dir)
|
||||
common.run_cmd(
|
||||
os.chdir(self.env['crosstool_ng_util_dir'])
|
||||
self.sh.run_cmd(
|
||||
[
|
||||
os.path.join(common.crosstool_ng_src_dir, 'configure'), common.Newline,
|
||||
'--enable-local', common.Newline,
|
||||
],
|
||||
)
|
||||
common.run_cmd(
|
||||
[
|
||||
'make', common.Newline,
|
||||
'-j', str(args.nproc), common.Newline,
|
||||
os.path.join(self.env['crosstool_ng_src_dir'], 'configure'), LF,
|
||||
'--enable-local', LF,
|
||||
],
|
||||
)
|
||||
self.sh.run_cmd(['make', '-j', str(self.env['nproc']), LF])
|
||||
|
||||
# Build the toolchain.
|
||||
common.cp(
|
||||
os.path.join(common.root_dir, 'crosstool_ng_config', args.arch),
|
||||
self.sh.cp(
|
||||
os.path.join(self.env['root_dir'], 'crosstool_ng_config', self.env['arch']),
|
||||
defconfig_dest
|
||||
)
|
||||
common.write_configs(
|
||||
common.crosstool_ng_defconfig,
|
||||
self.sh.write_configs(
|
||||
self.env['crosstool_ng_defconfig'],
|
||||
[
|
||||
'CT_PREFIX_DIR="{}"'.format(common.crosstool_ng_install_dir),
|
||||
'CT_PREFIX_DIR="{}"'.format(self.env['crosstool_ng_install_dir']),
|
||||
'CT_WORK_DIR="{}"'.format(build_dir),
|
||||
'CT_LOCAL_TARBALLS_DIR="{}"'.format(common.crosstool_ng_download_dir),
|
||||
'CT_LOCAL_TARBALLS_DIR="{}"'.format(self.env['crosstool_ng_download_dir']),
|
||||
]
|
||||
)
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
[
|
||||
common.crosstool_ng_executable, common.Newline,
|
||||
'defconfig', common.Newline,
|
||||
self.env['crosstool_ng_executable'], LF,
|
||||
'defconfig', LF,
|
||||
],
|
||||
)
|
||||
os.unlink(defconfig_dest)
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
[
|
||||
common.crosstool_ng_executable, common.Newline,
|
||||
'build', common.Newline,
|
||||
'CT_JOBS={}'.format(str(args.nproc)), common.Newline,
|
||||
self.env['crosstool_ng_executable'], LF,
|
||||
'build', LF,
|
||||
'CT_JOBS={}'.format(str(self.env['nproc'])), LF,
|
||||
],
|
||||
out_file=os.path.join(build_dir, 'lkmc.log'),
|
||||
delete_env=['LD_LIBRARY_PATH'],
|
||||
extra_paths=[common.ccache_dir],
|
||||
extra_paths=[self.env['ccache_dir']],
|
||||
)
|
||||
|
||||
def get_argparse_args(self):
|
||||
return {
|
||||
'description': '''\
|
||||
Build crosstool-NG with Newlib for bare metal compilation'
|
||||
'''
|
||||
}
|
||||
|
||||
def get_build_dir(self, args):
|
||||
return common.crosstool_ng_build_dir
|
||||
def get_build_dir(self):
|
||||
return self.env['crosstool_ng_build_dir']
|
||||
|
||||
if __name__ == '__main__':
|
||||
CrosstoolNgComponent().build()
|
||||
Main().cli()
|
||||
|
||||
35
build-docker
35
build-docker
@@ -5,9 +5,10 @@ import subprocess
|
||||
import tarfile
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
|
||||
class DockerComponent(common.Component):
|
||||
class DockerComponent(self.Component):
|
||||
def get_argparse_args(self):
|
||||
return {
|
||||
'description': '''\
|
||||
@@ -17,8 +18,8 @@ See also:https://github.com/cirosantilli/linux-kernel-module-cheat#ubuntu-guest-
|
||||
'''
|
||||
}
|
||||
|
||||
def do_build(self, args):
|
||||
build_dir = self.get_build_dir(args)
|
||||
def build(self):
|
||||
build_dir = self.get_build_dir()
|
||||
container_name = 'lkmc-guest'
|
||||
target_dir = os.path.join('/root', 'linux-kernel-module-cheat')
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
@@ -29,12 +30,12 @@ See also:https://github.com/cirosantilli/linux-kernel-module-cheat#ubuntu-guest-
|
||||
'--format', '{{.Names}}',
|
||||
]).decode()
|
||||
if container_name in containers.split():
|
||||
common.run_cmd([
|
||||
self.sh.run_cmd([
|
||||
'docker',
|
||||
'rm',
|
||||
container_name,
|
||||
])
|
||||
common.run_cmd([
|
||||
self.sh.run_cmd([
|
||||
'docker',
|
||||
'create',
|
||||
'--name', container_name,
|
||||
@@ -44,36 +45,36 @@ See also:https://github.com/cirosantilli/linux-kernel-module-cheat#ubuntu-guest-
|
||||
'--privileged',
|
||||
'-t',
|
||||
'-w', target_dir,
|
||||
'-v', '{}:{}'.format(common.root_dir, target_dir),
|
||||
'-v', '{}:{}'.format(kwargs['root_dir'], target_dir),
|
||||
'ubuntu:18.04',
|
||||
'bash',
|
||||
])
|
||||
common.run_cmd([
|
||||
self.sh.run_cmd([
|
||||
'docker',
|
||||
'export',
|
||||
'-o',
|
||||
common.docker_tar_file,
|
||||
kwargs['docker_tar_file'],
|
||||
container_name,
|
||||
])
|
||||
tar = tarfile.open(common.docker_tar_file)
|
||||
tar.extractall(common.docker_tar_dir)
|
||||
tar = tarfile.open(kwargs['docker_tar_file'])
|
||||
tar.extractall(kwargs['docker_tar_dir'])
|
||||
tar.close()
|
||||
# sudo not required in theory
|
||||
# https://askubuntu.com/questions/1046828/how-to-run-libguestfs-tools-tools-such-as-virt-make-fs-without-sudo
|
||||
common.run_cmd([
|
||||
self.sh.run_cmd([
|
||||
'virt-make-fs',
|
||||
'--format', 'raw',
|
||||
'--size', '+1G',
|
||||
'--type', 'ext2',
|
||||
common.docker_tar_dir,
|
||||
common.docker_rootfs_raw_file,
|
||||
kwargs['docker_tar_dir'],
|
||||
kwargs['docker_rootfs_raw_file'],
|
||||
])
|
||||
common.raw_to_qcow2(prebuilt=True)
|
||||
self.raw_to_qcow2(prebuilt=True)
|
||||
|
||||
def get_build_dir(self, args):
|
||||
return common.docker_build_dir
|
||||
def get_build_dir(self):
|
||||
return kwargs['docker_build_dir']
|
||||
|
||||
def get_default_args(self):
|
||||
return {'docker': True}
|
||||
|
||||
DockerComponent().build()
|
||||
Main().cli()
|
||||
|
||||
111
build-gem5
111
build-gem5
@@ -5,59 +5,57 @@ import pathlib
|
||||
import subprocess
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class Gem5Component(common.Component):
|
||||
def add_parser_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.add_argument(
|
||||
'extra_scons_args',
|
||||
default=[],
|
||||
metavar='extra-scons-args',
|
||||
nargs='*'
|
||||
nargs='*',
|
||||
)
|
||||
|
||||
def do_build(self, args):
|
||||
build_dir = self.get_build_dir(args)
|
||||
binaries_dir = os.path.join(common.gem5_system_dir, 'binaries')
|
||||
disks_dir = os.path.join(common.gem5_system_dir, 'disks')
|
||||
def build(self):
|
||||
build_dir = self.get_build_dir()
|
||||
binaries_dir = os.path.join(self.env['gem5_system_dir'], 'binaries')
|
||||
disks_dir = os.path.join(self.env['gem5_system_dir'], 'disks')
|
||||
os.makedirs(binaries_dir, exist_ok=True)
|
||||
os.makedirs(disks_dir, exist_ok=True)
|
||||
if args.gem5_source_dir is None:
|
||||
if not os.path.exists(os.path.join(common.gem5_src_dir, '.git')):
|
||||
if common.gem5_src_dir == common.gem5_default_src_dir:
|
||||
if self.env['gem5_source_dir'] is None:
|
||||
if not os.path.exists(os.path.join(self.env['gem5_source_dir'], '.git')):
|
||||
if self.env['gem5_source_dir'] == self.env['gem5_default_src_dir']:
|
||||
raise Exception('gem5 submodule not checked out')
|
||||
common.run_cmd([
|
||||
'git', common.Newline,
|
||||
'-C', common.gem5_default_src_dir, common.Newline,
|
||||
'worktree', 'add', common.Newline,
|
||||
'-b', os.path.join('wt', args.gem5_build_id), common.Newline,
|
||||
common.gem5_src_dir, common.Newline,
|
||||
self.sh.run_cmd([
|
||||
'git', LF,
|
||||
'-C', self.env['gem5_default_src_dir'], LF,
|
||||
'worktree', 'add', LF,
|
||||
'-b', os.path.join('wt', self.env['gem5_build_id']), LF,
|
||||
self.env['gem5_source_dir'], LF,
|
||||
])
|
||||
if args.verbose:
|
||||
verbose = ['--verbose', common.Newline]
|
||||
if self.env['verbose']:
|
||||
verbose = ['--verbose', LF]
|
||||
else:
|
||||
verbose = []
|
||||
if args.arch == 'x86_64':
|
||||
if self.env['arch'] == 'x86_64':
|
||||
dummy_img_path = os.path.join(disks_dir, 'linux-bigswap2.img')
|
||||
with open(dummy_img_path, 'wb') as dummy_img_file:
|
||||
zeroes = b'\x00' * (2 ** 16)
|
||||
for i in range(2 ** 10):
|
||||
dummy_img_file.write(zeroes)
|
||||
common.run_cmd(['mkswap', dummy_img_path])
|
||||
self.sh.run_cmd(['mkswap', dummy_img_path, LF])
|
||||
with open(os.path.join(binaries_dir, 'x86_64-vmlinux-2.6.22.9'), 'w'):
|
||||
# This file must always be present, despite --kernel overriding that default and selecting the kernel.
|
||||
# I'm not even joking. No one has ever built x86 gem5 without the magic dist dir present.
|
||||
pass
|
||||
elif args.arch == 'arm' or args.arch == 'aarch64':
|
||||
gem5_system_src_dir = os.path.join(common.gem5_src_dir, 'system')
|
||||
elif self.env['arch'] == 'arm' or self.env['arch'] == 'aarch64':
|
||||
gem5_system_src_dir = os.path.join(self.env['gem5_source_dir'], 'system')
|
||||
|
||||
# dtb
|
||||
dt_src_dir = os.path.join(gem5_system_src_dir, 'arm', 'dt')
|
||||
dt_build_dir = os.path.join(common.gem5_system_dir, 'arm', 'dt')
|
||||
common.run_cmd([
|
||||
'make', common.Newline,
|
||||
'-C', dt_src_dir, common.Newline,
|
||||
])
|
||||
common.copy_dir_if_update_non_recursive(
|
||||
dt_build_dir = os.path.join(self.env['gem5_system_dir'], 'arm', 'dt')
|
||||
self.sh.run_cmd(['make', '-C', dt_src_dir, LF])
|
||||
self.sh.copy_dir_if_update_non_recursive(
|
||||
srcdir=dt_src_dir,
|
||||
destdir=dt_build_dir,
|
||||
filter_ext='.dtb',
|
||||
@@ -66,49 +64,46 @@ class Gem5Component(common.Component):
|
||||
# Bootloader 32.
|
||||
bootloader32_dir = os.path.join(gem5_system_src_dir, 'arm', 'simple_bootloader')
|
||||
# TODO use the buildroot cross compiler here, and remove the dependencies from configure.
|
||||
common.run_cmd([
|
||||
'make', common.Newline,
|
||||
'-C', bootloader32_dir, common.Newline,
|
||||
'CROSS_COMPILE=arm-linux-gnueabihf-', common.Newline,
|
||||
self.sh.run_cmd([
|
||||
'make', LF,
|
||||
'-C', bootloader32_dir, LF,
|
||||
'CROSS_COMPILE=arm-linux-gnueabihf-', LF,
|
||||
])
|
||||
# bootloader
|
||||
common.cp(os.path.join(bootloader32_dir, 'boot_emm.arm'), binaries_dir)
|
||||
self.sh.cp(os.path.join(bootloader32_dir, 'boot_emm.arm'), binaries_dir)
|
||||
|
||||
# Bootloader 64.
|
||||
bootloader64_dir = os.path.join(gem5_system_src_dir, 'arm', 'aarch64_bootloader')
|
||||
# TODO cross_compile is ignored because the make does not use CC...
|
||||
common.run_cmd([
|
||||
'make', common.Newline,
|
||||
'-C', bootloader64_dir, common.Newline
|
||||
])
|
||||
common.cp(os.path.join(bootloader64_dir, 'boot_emm.arm64'), binaries_dir)
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(['make', '-C', bootloader64_dir, LF])
|
||||
self.sh.cp(os.path.join(bootloader64_dir, 'boot_emm.arm64'), binaries_dir)
|
||||
self.sh.run_cmd(
|
||||
(
|
||||
[
|
||||
'scons', common.Newline,
|
||||
'-j', str(args.nproc), common.Newline,
|
||||
'--gold-linker', common.Newline,
|
||||
'--ignore-style', common.Newline,
|
||||
common.gem5_executable, common.Newline,
|
||||
'scons', LF,
|
||||
'-j', str(self.env['nproc']), LF,
|
||||
'--gold-linker', LF,
|
||||
'--ignore-style', LF,
|
||||
self.env['gem5_executable'], LF,
|
||||
] +
|
||||
verbose +
|
||||
common.add_newlines(args.extra_scons_args)
|
||||
self.sh.add_newlines(self.env['extra_scons_args'])
|
||||
),
|
||||
cwd=common.gem5_src_dir,
|
||||
extra_paths=[common.ccache_dir],
|
||||
cwd=self.env['gem5_source_dir'],
|
||||
extra_paths=[self.env['ccache_dir']],
|
||||
)
|
||||
term_src_dir = os.path.join(common.gem5_src_dir, 'util/term')
|
||||
term_src_dir = os.path.join(self.env['gem5_source_dir'], 'util/term')
|
||||
m5term_build = os.path.join(term_src_dir, 'm5term')
|
||||
common.run_cmd(['make', '-C', term_src_dir])
|
||||
if os.path.exists(common.gem5_m5term):
|
||||
# Otherwise common.cp would fail with "Text file busy" if you
|
||||
self.sh.run_cmd(['make', '-C', term_src_dir, LF])
|
||||
if os.path.exists(self.env['gem5_m5term']):
|
||||
# Otherwise self.sh.cp would fail with "Text file busy" if you
|
||||
# tried to rebuild while running m5term:
|
||||
# https://stackoverflow.com/questions/16764946/what-generates-the-text-file-busy-message-in-unix/52427512#52427512
|
||||
os.unlink(common.gem5_m5term)
|
||||
common.cp(m5term_build, common.gem5_m5term)
|
||||
os.unlink(self.env['gem5_m5term'])
|
||||
self.sh.cp(m5term_build, self.env['gem5_m5term'])
|
||||
|
||||
def get_build_dir(self, args):
|
||||
return common.gem5_build_dir
|
||||
def get_build_dir(self):
|
||||
return self.env['gem5_build_dir']
|
||||
|
||||
if __name__ == '__main__':
|
||||
Gem5Component().build()
|
||||
Main().cli()
|
||||
|
||||
123
build-linux
123
build-linux
@@ -4,10 +4,16 @@ import os
|
||||
import shutil
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class LinuxComponent(common.Component):
|
||||
def add_parser_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description='''\
|
||||
Build the Linux kernel.
|
||||
'''
|
||||
)
|
||||
self.add_argument(
|
||||
'--config', default=[], action='append',
|
||||
help='''\
|
||||
Add a single kernel config configs to the current build. Sample value:
|
||||
@@ -15,14 +21,14 @@ Add a single kernel config configs to the current build. Sample value:
|
||||
configs. Takes precedence over any config files.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
self.add_argument(
|
||||
'--config-fragment', default=[], action='append',
|
||||
help='''\
|
||||
Also use the given kernel configuration fragment file.
|
||||
Pass multiple times to use multiple fragment files.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
self.add_argument(
|
||||
'--custom-config-file',
|
||||
help='''\
|
||||
Ignore all default kernel configurations and use this file instead.
|
||||
@@ -30,137 +36,124 @@ Still uses options explicitly passed with `--config` and
|
||||
`--config-fragment` on top of it.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--custom-config-file-gem5', default=False, action='store_true',
|
||||
self.add_argument(
|
||||
'--custom-config-file-gem5', default=False,
|
||||
help='''\
|
||||
Use the gem5 Linux kernel fork config as the custom config file.
|
||||
Ignore --custom-config-file.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--config-only', default=False, action='store_true',
|
||||
self.add_argument(
|
||||
'--config-only', default=False,
|
||||
help='''\
|
||||
Configure the kernel, but don't build it.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'--initramfs', default=False, action='store_true',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--initrd', default=False, action='store_true',
|
||||
)
|
||||
parser.add_argument(
|
||||
self.add_argument(
|
||||
'extra_make_args',
|
||||
default=[],
|
||||
metavar='extra-make-args',
|
||||
nargs='*'
|
||||
)
|
||||
|
||||
def do_build(self, args):
|
||||
build_dir = self.get_build_dir(args)
|
||||
if args.initrd or args.initramfs:
|
||||
def build(self):
|
||||
build_dir = self.get_build_dir()
|
||||
if self.env['initrd'] or self.env['initramfs']:
|
||||
raise Exception('just trolling, --initrd and --initramfs are broken for now')
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
tool = 'gcc'
|
||||
gcc = common.get_toolchain_tool(tool)
|
||||
gcc = self.get_toolchain_tool(tool)
|
||||
prefix = gcc[:-len(tool)]
|
||||
common_args = {
|
||||
'cwd': common.linux_source_dir,
|
||||
'cwd': self.env['linux_source_dir'],
|
||||
}
|
||||
ccache = shutil.which('ccache')
|
||||
if ccache is not None:
|
||||
cc = '{} {}'.format(ccache, gcc)
|
||||
else:
|
||||
cc = gcc
|
||||
if args.verbose:
|
||||
if self.env['verbose']:
|
||||
verbose = ['V=1']
|
||||
else:
|
||||
verbose = []
|
||||
common_make_args = [
|
||||
'make', common.Newline,
|
||||
'-j', str(args.nproc), common.Newline,
|
||||
'ARCH={}'.format(common.linux_arch), common.Newline,
|
||||
'CROSS_COMPILE={}'.format(prefix), common.Newline,
|
||||
'CC={}'.format(cc), common.Newline,
|
||||
'O={}'.format(build_dir), common.Newline,
|
||||
'make', LF,
|
||||
'-j', str(self.env['nproc']), LF,
|
||||
'ARCH={}'.format(self.env['linux_arch']), LF,
|
||||
'CROSS_COMPILE={}'.format(prefix), LF,
|
||||
'CC={}'.format(cc), LF,
|
||||
'O={}'.format(build_dir), LF,
|
||||
] + verbose
|
||||
if args.custom_config_file_gem5:
|
||||
custom_config_file = os.path.join(common.linux_source_dir, 'arch', common.linux_arch, 'configs', 'gem5_defconfig')
|
||||
if self.env['custom_config_file_gem5']:
|
||||
custom_config_file = os.path.join(self.env['linux_source_dir'], 'arch', self.env['linux_arch'], 'configs', 'gem5_defconfig')
|
||||
else:
|
||||
custom_config_file = args.custom_config_file
|
||||
custom_config_file = self.env['custom_config_file']
|
||||
if custom_config_file is not None:
|
||||
if not os.path.exists(custom_config_file):
|
||||
raise Exception('config fragment file does not exist: {}'.format(args.custom_config_file))
|
||||
raise Exception('config fragment file does not exist: {}'.format(custom_config_file))
|
||||
base_config_file = custom_config_file
|
||||
config_fragments = []
|
||||
else:
|
||||
base_config_file = os.path.join(common.linux_config_dir, 'buildroot-{}'.format(args.arch))
|
||||
base_config_file = os.path.join(self.env['linux_config_dir'], 'buildroot-{}'.format(self.env['arch']))
|
||||
config_fragments = ['min', 'default']
|
||||
for i, config_fragment in enumerate(config_fragments):
|
||||
config_fragments[i] = os.path.join(common.linux_config_dir, config_fragment)
|
||||
config_fragments.extend(args.config_fragment)
|
||||
if args.config != []:
|
||||
config_fragments[i] = os.path.join(self.env['linux_config_dir'], config_fragment)
|
||||
config_fragments.extend(self.env['config_fragment'])
|
||||
if self.env['config'] != []:
|
||||
cli_config_fragment_path = os.path.join(build_dir, 'lkmc_cli_config_fragment')
|
||||
cli_config_str = '\n'.join(args.config)
|
||||
common.write_string_to_file(cli_config_fragment_path, cli_config_str)
|
||||
cli_config_str = '\n'.join(self.env['config'])
|
||||
self.sh.write_string_to_file(cli_config_fragment_path, cli_config_str)
|
||||
config_fragments.append(cli_config_fragment_path)
|
||||
common.cp(
|
||||
self.sh.cp(
|
||||
base_config_file,
|
||||
os.path.join(build_dir, '.config'),
|
||||
)
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
[
|
||||
os.path.join(common.linux_source_dir, 'scripts', 'kconfig', 'merge_config.sh'), common.Newline,
|
||||
'-m', common.Newline,
|
||||
'-O', build_dir, common.Newline,
|
||||
os.path.join(build_dir, '.config'), common.Newline,
|
||||
os.path.join(self.env['linux_source_dir'], 'scripts', 'kconfig', 'merge_config.sh'), LF,
|
||||
'-m', LF,
|
||||
'-O', build_dir, LF,
|
||||
os.path.join(build_dir, '.config'), LF,
|
||||
] +
|
||||
common.add_newlines(config_fragments)
|
||||
self.sh.add_newlines(config_fragments)
|
||||
)
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
(
|
||||
common_make_args +
|
||||
['olddefconfig', common.Newline]
|
||||
['olddefconfig', LF]
|
||||
),
|
||||
**common_args
|
||||
)
|
||||
if not args.config_only:
|
||||
common.run_cmd(
|
||||
if not self.env['config_only']:
|
||||
self.sh.run_cmd(
|
||||
(
|
||||
common_make_args +
|
||||
common.add_newlines(args.extra_make_args)
|
||||
self.sh.add_newlines(self.env['extra_make_args'])
|
||||
),
|
||||
extra_env={
|
||||
'KBUILD_BUILD_VERSION': '1',
|
||||
'KBUILD_BUILD_TIMESTAMP': 'Thu Jan 1 00:00:00 UTC 1970',
|
||||
'KBUILD_BUILD_USER': 'lkmc',
|
||||
'KBUILD_BUILD_HOST': common.git_sha(common.linux_source_dir),
|
||||
'KBUILD_BUILD_HOST': common.git_sha(self.env['linux_source_dir']),
|
||||
},
|
||||
**common_args
|
||||
)
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
(
|
||||
common_make_args +
|
||||
[
|
||||
'INSTALL_MOD_PATH={}'.format(common.out_rootfs_overlay_dir), common.Newline,
|
||||
'modules_install', common.Newline,
|
||||
'INSTALL_MOD_PATH={}'.format(self.env['out_rootfs_overlay_dir']), LF,
|
||||
'modules_install', LF,
|
||||
]
|
||||
),
|
||||
**common_args
|
||||
)
|
||||
# TODO: remove build and source https://stackoverflow.com/questions/13578618/what-does-build-and-source-link-do-in-lib-modules-kernel-version
|
||||
# TODO Basically all kernel modules also basically leak full host paths. Just terrible. Buildroot deals with that stuff nicely for us.
|
||||
# common.rmrf()
|
||||
# self.rmrf()
|
||||
|
||||
def get_argparse_args(self):
|
||||
return {
|
||||
'description': '''\
|
||||
Build the Linux kernel.
|
||||
'''
|
||||
}
|
||||
|
||||
def get_build_dir(self, args):
|
||||
return common.linux_build_dir
|
||||
def get_build_dir(self):
|
||||
return self.env['linux_build_dir']
|
||||
|
||||
if __name__ == '__main__':
|
||||
LinuxComponent().build()
|
||||
Main().cli()
|
||||
|
||||
55
build-m5
55
build-m5
@@ -3,42 +3,47 @@
|
||||
import os
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class M5Component(common.Component):
|
||||
def get_make_cmd(self, args):
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def _get_make_cmd(self):
|
||||
allowed_toolchains = ['buildroot']
|
||||
cc = common.get_toolchain_tool('gcc', allowed_toolchains=allowed_toolchains)
|
||||
ld = common.get_toolchain_tool('ld', allowed_toolchains=allowed_toolchains)
|
||||
if args.arch == 'x86_64':
|
||||
cc = self.get_toolchain_tool('gcc', allowed_toolchains=allowed_toolchains)
|
||||
ld = self.get_toolchain_tool('ld', allowed_toolchains=allowed_toolchains)
|
||||
if self.env['arch'] == 'x86_64':
|
||||
arch = 'x86'
|
||||
else:
|
||||
arch = args.arch
|
||||
arch = self.env['arch']
|
||||
return [
|
||||
'make', common.Newline,
|
||||
'-j', str(args.nproc), common.Newline,
|
||||
'-f', 'Makefile.{}'.format(arch), common.Newline,
|
||||
'CC={}'.format(cc), common.Newline,
|
||||
'LD={}'.format(ld), common.Newline,
|
||||
'PWD={}'.format(common.gem5_m5_src_dir), common.Newline,
|
||||
'make', LF,
|
||||
'-j', str(self.env['nproc']), LF,
|
||||
'-f', 'Makefile.{}'.format(arch), LF,
|
||||
'CC={}'.format(cc), LF,
|
||||
'LD={}'.format(ld), LF,
|
||||
'PWD={}'.format(self.env['gem5_m5_source_dir']), LF,
|
||||
]
|
||||
|
||||
def do_build(self, args):
|
||||
os.makedirs(common.gem5_m5_build_dir, exist_ok=True)
|
||||
def build(self):
|
||||
os.makedirs(self.env['gem5_m5_build_dir'], exist_ok=True)
|
||||
# We must clean first or else the build outputs of one arch can conflict with the other.
|
||||
# I should stop being lazy and go actually patch gem5 to support out of tree m5 build...
|
||||
self.clean(args)
|
||||
common.run_cmd(
|
||||
self.get_make_cmd(args),
|
||||
cwd=common.gem5_m5_src_dir,
|
||||
self.clean()
|
||||
self.sh.run_cmd(
|
||||
self._get_make_cmd(),
|
||||
cwd=self.env['gem5_m5_source_dir'],
|
||||
)
|
||||
os.makedirs(common.out_rootfs_overlay_bin_dir, exist_ok=True)
|
||||
common.cp(os.path.join(common.gem5_m5_src_dir, 'm5'), common.out_rootfs_overlay_bin_dir)
|
||||
os.makedirs(self.env['out_rootfs_overlay_bin_dir'], exist_ok=True)
|
||||
self.sh.cp(os.path.join(self.env['gem5_m5_source_dir'], 'm5'), self.env['out_rootfs_overlay_bin_dir'])
|
||||
|
||||
def clean(self, args):
|
||||
common.run_cmd(
|
||||
self.get_make_cmd(args) + ['clean', common.Newline],
|
||||
cwd=common.gem5_m5_src_dir,
|
||||
def clean(self):
|
||||
self.sh.run_cmd(
|
||||
self._get_make_cmd() + ['clean', LF],
|
||||
cwd=self.env['gem5_m5_source_dir'],
|
||||
)
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
M5Component().build()
|
||||
Main().cli()
|
||||
|
||||
110
build-modules
110
build-modules
@@ -6,32 +6,37 @@ import platform
|
||||
import shutil
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class ModulesComponent(common.Component):
|
||||
def add_parser_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description='''\
|
||||
Build our Linux kernel modules without using Buildroot.
|
||||
|
||||
See also: https://github.com/cirosantilli/linux-kernel-module-cheat#host
|
||||
''')
|
||||
self.add_argument(
|
||||
'--make-args',
|
||||
default='',
|
||||
)
|
||||
parser.add_argument(
|
||||
self.add_argument(
|
||||
'--host',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='''\
|
||||
Build the Linux kernel modules for the host instead of guest.
|
||||
Use the host packaged cross toolchain.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
'kernel_modules',
|
||||
self.add_argument(
|
||||
'kernel-modules',
|
||||
default=[],
|
||||
help='Which kernel modules to build. Default: build all',
|
||||
metavar='kernel-modules',
|
||||
nargs='*',
|
||||
)
|
||||
|
||||
def do_build(self, args):
|
||||
build_dir = self.get_build_dir(args)
|
||||
def build(self):
|
||||
build_dir = self.get_build_dir()
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
# I kid you not, out-of-tree build is not possible, O= does not work as for the kernel build:
|
||||
#
|
||||
@@ -42,87 +47,78 @@ Use the host packaged cross toolchain.
|
||||
# This copies only modified files as per:
|
||||
# https://stackoverflow.com/questions/5718899/building-an-out-of-tree-linux-kernel-module-in-a-separate-object-directory
|
||||
distutils.dir_util.copy_tree(
|
||||
common.kernel_modules_src_dir,
|
||||
os.path.join(build_dir, common.kernel_modules_subdir),
|
||||
self.env['kernel_modules_src_dir'],
|
||||
os.path.join(build_dir, self.env['kernel_modules_subdir']),
|
||||
update=1,
|
||||
)
|
||||
distutils.dir_util.copy_tree(
|
||||
common.include_src_dir,
|
||||
os.path.join(build_dir, common.include_subdir),
|
||||
self.env['include_src_dir'],
|
||||
os.path.join(build_dir, self.env['include_subdir']),
|
||||
update=1,
|
||||
)
|
||||
all_kernel_modules = []
|
||||
for basename in os.listdir(common.kernel_modules_src_dir):
|
||||
src = os.path.join(common.kernel_modules_src_dir, basename)
|
||||
for basename in os.listdir(self.env['kernel_modules_src_dir']):
|
||||
src = os.path.join(self.env['kernel_modules_src_dir'], basename)
|
||||
if os.path.isfile(src):
|
||||
noext, ext = os.path.splitext(basename)
|
||||
if ext == common.c_ext:
|
||||
if ext == self.env['c_ext']:
|
||||
all_kernel_modules.append(noext)
|
||||
if args.kernel_modules == []:
|
||||
if self.env['kernel_modules'] == []:
|
||||
kernel_modules = all_kernel_modules
|
||||
else:
|
||||
kernel_modules = map(lambda x: os.path.splitext(os.path.split(x)[1])[0], args.kernel_modules)
|
||||
object_files = map(lambda x: x + common.obj_ext, kernel_modules)
|
||||
kernel_modules = map(lambda x: os.path.splitext(os.path.split(x)[1])[0], self.env['kernel_modules'])
|
||||
object_files = map(lambda x: x + self.env['obj_ext'], kernel_modules)
|
||||
tool = 'gcc'
|
||||
if args.host:
|
||||
if self.env['host']:
|
||||
allowed_toolchains = ['host']
|
||||
build_subdir = common.kernel_modules_build_host_subdir
|
||||
build_subdir = self.env['kernel_modules_build_host_subdir']
|
||||
else:
|
||||
allowed_toolchains = None
|
||||
build_subdir = common.kernel_modules_build_subdir
|
||||
gcc = common.get_toolchain_tool(tool, allowed_toolchains=allowed_toolchains)
|
||||
build_subdir = self.env['kernel_modules_build_subdir']
|
||||
gcc = self.get_toolchain_tool(tool, allowed_toolchains=allowed_toolchains)
|
||||
prefix = gcc[:-len(tool)]
|
||||
ccache = shutil.which('ccache')
|
||||
if ccache is not None:
|
||||
cc = '{} {}'.format(ccache, gcc)
|
||||
else:
|
||||
cc = gcc
|
||||
if args.verbose:
|
||||
if self.env['verbose']:
|
||||
verbose = ['V=1']
|
||||
else:
|
||||
verbose = []
|
||||
if args.host:
|
||||
if self.env['host']:
|
||||
linux_dir = os.path.join('/lib', 'modules', platform.uname().release, 'build')
|
||||
else:
|
||||
linux_dir = common.linux_build_dir
|
||||
common.run_cmd(
|
||||
linux_dir = self.env['linux_build_dir']
|
||||
self.sh.run_cmd(
|
||||
(
|
||||
[
|
||||
'make', common.Newline,
|
||||
'-j', str(args.nproc), common.Newline,
|
||||
'ARCH={}'.format(common.linux_arch), common.Newline,
|
||||
'CC={}'.format(cc), common.Newline,
|
||||
'CROSS_COMPILE={}'.format(prefix), common.Newline,
|
||||
'LINUX_DIR={}'.format(linux_dir), common.Newline,
|
||||
'M={}'.format(build_subdir), common.Newline,
|
||||
'OBJECT_FILES={}'.format(' '.join(object_files)), common.Newline,
|
||||
'make', LF,
|
||||
'-j', str(self.env['nproc']), LF,
|
||||
'ARCH={}'.format(self.env['linux_arch']), LF,
|
||||
'CC={}'.format(cc), LF,
|
||||
'CROSS_COMPILE={}'.format(prefix), LF,
|
||||
'LINUX_DIR={}'.format(linux_dir), LF,
|
||||
'M={}'.format(build_subdir), LF,
|
||||
'OBJECT_FILES={}'.format(' '.join(object_files)), LF,
|
||||
] +
|
||||
common.shlex_split(args.make_args) +
|
||||
self.sh.shlex_split(self.env['make_args']) +
|
||||
verbose
|
||||
),
|
||||
cwd=os.path.join(common.kernel_modules_build_subdir),
|
||||
cwd=os.path.join(self.env['kernel_modules_build_subdir']),
|
||||
)
|
||||
if not args.host:
|
||||
common.copy_dir_if_update_non_recursive(
|
||||
srcdir=common.kernel_modules_build_subdir,
|
||||
destdir=common.out_rootfs_overlay_dir,
|
||||
filter_ext=common.kernel_module_ext,
|
||||
if not self.env['host']:
|
||||
self.sh.copy_dir_if_update_non_recursive(
|
||||
srcdir=self.env['kernel_modules_build_subdir'],
|
||||
destdir=self.env['out_rootfs_overlay_dir'],
|
||||
filter_ext=self.env['kernel_module_ext'],
|
||||
)
|
||||
|
||||
def get_argparse_args(self):
|
||||
return {
|
||||
'description': '''\
|
||||
Build our Linux kernel modules without using Buildroot.
|
||||
|
||||
See also: https://github.com/cirosantilli/linux-kernel-module-cheat#host
|
||||
'''
|
||||
}
|
||||
|
||||
def get_build_dir(self, args):
|
||||
if args.host:
|
||||
return os.path.join(common.kernel_modules_build_host_dir)
|
||||
def get_build_dir(self):
|
||||
if self.env['host']:
|
||||
return self.env['kernel_modules_build_host_dir']
|
||||
else:
|
||||
return os.path.join(common.kernel_modules_build_dir)
|
||||
return self.env['kernel_modules_build_dir']
|
||||
|
||||
if __name__ == '__main__':
|
||||
ModulesComponent().build()
|
||||
Main().cli()
|
||||
|
||||
55
build-qemu
55
build-qemu
@@ -3,61 +3,62 @@
|
||||
import os
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class QemuComponent(common.Component):
|
||||
def add_parser_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.add_argument(
|
||||
'--userland',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Build QEMU user mode instead of system.',
|
||||
)
|
||||
parser.add_argument(
|
||||
self.add_argument(
|
||||
'extra_config_args',
|
||||
default=[],
|
||||
metavar='extra-config-args',
|
||||
nargs='*'
|
||||
)
|
||||
|
||||
def do_build(self, args):
|
||||
build_dir = self.get_build_dir(args)
|
||||
def build(self):
|
||||
build_dir = self.get_build_dir()
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
if args.verbose:
|
||||
if self.env['verbose']:
|
||||
verbose = ['V=1']
|
||||
else:
|
||||
verbose = []
|
||||
if args.userland:
|
||||
target_list = '{}-linux-user'.format(args.arch)
|
||||
if self.env['userland']:
|
||||
target_list = '{}-linux-user'.format(self.env['arch'])
|
||||
else:
|
||||
target_list = '{}-softmmu'.format(args.arch)
|
||||
common.run_cmd(
|
||||
target_list = '{}-softmmu'.format(self.env['arch'])
|
||||
self.sh.run_cmd(
|
||||
[
|
||||
os.path.join(common.qemu_src_dir, 'configure'), common.Newline,
|
||||
'--enable-debug', common.Newline,
|
||||
'--enable-trace-backends=simple', common.Newline,
|
||||
'--target-list={}'.format(target_list), common.Newline,
|
||||
'--enable-sdl', common.Newline,
|
||||
'--with-sdlabi=2.0', common.Newline,
|
||||
os.path.join(self.env['qemu_src_dir'], 'configure'), LF,
|
||||
'--enable-debug', LF,
|
||||
'--enable-trace-backends=simple', LF,
|
||||
'--target-list={}'.format(target_list), LF,
|
||||
'--enable-sdl', LF,
|
||||
'--with-sdlabi=2.0', LF,
|
||||
] +
|
||||
common.add_newlines(args.extra_config_args),
|
||||
extra_paths=[common.ccache_dir],
|
||||
self.sh.add_newlines(self.env['extra_config_args']),
|
||||
extra_paths=[self.env['ccache_dir']],
|
||||
cwd=build_dir
|
||||
)
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
(
|
||||
[
|
||||
'make', common.Newline,
|
||||
'-j', str(args.nproc), common.Newline,
|
||||
'make', LF,
|
||||
'-j', str(self.env['nproc']), LF,
|
||||
|
||||
] +
|
||||
verbose
|
||||
),
|
||||
cwd=build_dir,
|
||||
extra_paths=[common.ccache_dir],
|
||||
extra_paths=[self.env['ccache_dir']],
|
||||
)
|
||||
|
||||
def get_build_dir(self, args):
|
||||
return common.qemu_build_dir
|
||||
def get_build_dir(self):
|
||||
return self.env['qemu_build_dir']
|
||||
|
||||
if __name__ == '__main__':
|
||||
QemuComponent().build()
|
||||
Main().cli()
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class UserlandComponent(common.Component):
|
||||
def add_parser_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description='''\
|
||||
Build our compiled userland examples.
|
||||
'''
|
||||
)
|
||||
self.add_argument(
|
||||
'--has-package',
|
||||
action='append',
|
||||
default=[],
|
||||
@@ -19,20 +22,19 @@ Indicate that a given package is present in the root filesystem, which
|
||||
allows us to build examples that rely on it.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
self.add_argument(
|
||||
'--host',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='''\
|
||||
Build the userland programs for the host instead of guest.
|
||||
Use the host packaged cross toolchain.
|
||||
''',
|
||||
)
|
||||
parser.add_argument(
|
||||
self.add_argument(
|
||||
'--make-args',
|
||||
default='',
|
||||
)
|
||||
parser.add_argument(
|
||||
self.add_argument(
|
||||
'targets',
|
||||
default=[],
|
||||
help='''\
|
||||
@@ -44,49 +46,44 @@ has the OpenBLAS libraries and headers installed.
|
||||
nargs='*',
|
||||
)
|
||||
|
||||
def do_build(self, args):
|
||||
build_dir = self.get_build_dir(args)
|
||||
def build(self):
|
||||
build_dir = self.get_build_dir()
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
if args.host:
|
||||
if self.env['host']:
|
||||
allowed_toolchains = ['host']
|
||||
else:
|
||||
allowed_toolchains = ['buildroot']
|
||||
cc = common.get_toolchain_tool('gcc', allowed_toolchains=allowed_toolchains)
|
||||
cxx = common.get_toolchain_tool('g++', allowed_toolchains=allowed_toolchains)
|
||||
common.run_cmd(
|
||||
cc = self.get_toolchain_tool('gcc', allowed_toolchains=allowed_toolchains)
|
||||
cxx = self.get_toolchain_tool('g++', allowed_toolchains=allowed_toolchains)
|
||||
self.sh.run_cmd(
|
||||
(
|
||||
[
|
||||
'make', common.Newline,
|
||||
'-j', str(args.nproc), common.Newline,
|
||||
'ARCH={}'.format(args.arch), common.Newline,
|
||||
'CCFLAGS_SCRIPT={} {}'.format('-I', common.userland_src_dir), common.Newline,
|
||||
'COMMON_DIR={}'.format(common.root_dir), common.Newline,
|
||||
'CC={}'.format(cc), common.Newline,
|
||||
'CXX={}'.format(cxx), common.Newline,
|
||||
'PKG_CONFIG={}'.format(common.buildroot_pkg_config), common.Newline,
|
||||
'STAGING_DIR={}'.format(common.buildroot_staging_dir), common.Newline,
|
||||
'OUT_DIR={}'.format(build_dir), common.Newline,
|
||||
'make', LF,
|
||||
'-j', str(self.env['nproc']), LF,
|
||||
'ARCH={}'.format(self.env['arch']), LF,
|
||||
'CCFLAGS_SCRIPT={} {}'.format('-I', self.env['userland_src_dir']), LF,
|
||||
'COMMON_DIR={}'.format(self.env['root_dir']), LF,
|
||||
'CC={}'.format(cc), LF,
|
||||
'CXX={}'.format(cxx), LF,
|
||||
'PKG_CONFIG={}'.format(self.env['buildroot_pkg_config']), LF,
|
||||
'STAGING_DIR={}'.format(self.env['buildroot_staging_dir']), LF,
|
||||
'OUT_DIR={}'.format(build_dir), LF,
|
||||
] +
|
||||
common.add_newlines(['HAS_{}=y'.format(package.upper()) for package in args.has_package]) +
|
||||
shlex.split(args.make_args) +
|
||||
common.add_newlines([os.path.join(build_dir, os.path.splitext(os.path.split(target)[1])[0]) + common.userland_build_ext for target in args.targets])
|
||||
self.sh.add_newlines(['HAS_{}=y'.format(package.upper()) for package in self.env['has_package']]) +
|
||||
shlex.split(self.env['make_args']) +
|
||||
self.sh.add_newlines([os.path.join(build_dir, os.path.splitext(os.path.split(target)[1])[0]) + self.env['userland_build_ext'] for target in self.env['targets']])
|
||||
),
|
||||
cwd=common.userland_src_dir,
|
||||
extra_paths=[common.ccache_dir],
|
||||
cwd=self.env['userland_src_dir'],
|
||||
extra_paths=[self.env['ccache_dir']],
|
||||
)
|
||||
common.copy_dir_if_update_non_recursive(
|
||||
self.sh.copy_dir_if_update_non_recursive(
|
||||
srcdir=build_dir,
|
||||
destdir=common.out_rootfs_overlay_dir,
|
||||
filter_ext=common.userland_build_ext,
|
||||
destdir=self.env['out_rootfs_overlay_dir'],
|
||||
filter_ext=self.env['userland_build_ext'],
|
||||
)
|
||||
|
||||
def get_argparse_args(self):
|
||||
return {
|
||||
'description': 'Build our compiled userland examples',
|
||||
}
|
||||
|
||||
def get_build_dir(self, args):
|
||||
return common.userland_build_dir
|
||||
def get_build_dir(self):
|
||||
return self.env['userland_build_dir']
|
||||
|
||||
if __name__ == '__main__':
|
||||
UserlandComponent().build()
|
||||
Main().cli()
|
||||
|
||||
262
cli_function.py
Executable file
262
cli_function.py
Executable file
@@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import imp
|
||||
import os
|
||||
|
||||
class Argument:
|
||||
def __init__(
|
||||
self,
|
||||
long_or_short_1,
|
||||
long_or_short_2=None,
|
||||
default=None,
|
||||
help=None,
|
||||
nargs=None,
|
||||
**kwargs
|
||||
):
|
||||
if long_or_short_2 is None:
|
||||
shortname = None
|
||||
longname = long_or_short_1
|
||||
else:
|
||||
shortname = long_or_short_1
|
||||
longname = long_or_short_2
|
||||
self.args = []
|
||||
# argparse is crappy and cannot tell us if arguments were given or not.
|
||||
# We need that information to decide if the config file should override argparse or not.
|
||||
# So we just use None as a sentinel.
|
||||
self.kwargs = {'default': None}
|
||||
if shortname is not None:
|
||||
self.args.append(shortname)
|
||||
if longname[0] == '-':
|
||||
self.args.append(longname)
|
||||
self.key = longname.lstrip('-').replace('-', '_')
|
||||
self.is_option = True
|
||||
else:
|
||||
self.key = longname.replace('-', '_')
|
||||
self.args.append(self.key)
|
||||
self.kwargs['metavar'] = longname
|
||||
self.is_option = False
|
||||
if default is not None and nargs is None:
|
||||
self.kwargs['nargs'] = '?'
|
||||
if nargs is not None:
|
||||
self.kwargs['nargs'] = nargs
|
||||
if default is True:
|
||||
bool_action = 'store_false'
|
||||
self.is_bool = True
|
||||
elif default is False:
|
||||
bool_action = 'store_true'
|
||||
self.is_bool = True
|
||||
else:
|
||||
self.is_bool = False
|
||||
if default is None and nargs in ('*', '+'):
|
||||
default = []
|
||||
if self.is_bool and not 'action' in kwargs:
|
||||
self.kwargs['action'] = bool_action
|
||||
if help is not None:
|
||||
if not self.is_bool and default:
|
||||
help += ' Default: {}'.format(default)
|
||||
self.kwargs['help'] = help
|
||||
self.optional = (
|
||||
default is not None or
|
||||
self.is_bool or
|
||||
self.is_option or
|
||||
nargs in ('?', '*', '+')
|
||||
)
|
||||
self.kwargs.update(kwargs)
|
||||
self.default = default
|
||||
self.longname = longname
|
||||
|
||||
def __str__(self):
|
||||
return str(self.args) + ' ' + str(self.kwargs)
|
||||
|
||||
class CliFunction:
|
||||
'''
|
||||
Represent a function that can be called either from Python code, or
|
||||
from the command line.
|
||||
|
||||
Features:
|
||||
|
||||
* single argument description in format very similar to argparse
|
||||
* handle default arguments transparently in both cases
|
||||
* expose a configuration file mechanism to get default parameters from a file
|
||||
* fix some argparse.ArgumentParser() annoyances:
|
||||
** allow dashes in positional arguments:
|
||||
https://stackoverflow.com/questions/12834785/having-options-in-argparse-with-a-dash
|
||||
** boolean defaults automatically use store_true or store_false, and add a --no-* CLI
|
||||
option to invert them if set from the config
|
||||
|
||||
This somewhat duplicates: https://click.palletsprojects.com but:
|
||||
|
||||
* that decorator API is insane
|
||||
* CLI + Python for single functions was wontfixed: https://github.com/pallets/click/issues/40
|
||||
'''
|
||||
def __call__(self, **args):
|
||||
'''
|
||||
Python version of the function call.
|
||||
|
||||
:type arguments: Dict
|
||||
'''
|
||||
args_with_defaults = args.copy()
|
||||
# Add missing args from config file.
|
||||
if 'config_file' in args_with_defaults and args_with_defaults['config_file'] is not None:
|
||||
config_file = args_with_defaults['config_file']
|
||||
else:
|
||||
config_file = self._config_file
|
||||
if os.path.exists(config_file):
|
||||
config_configs = {}
|
||||
config = imp.load_source('config', config_file)
|
||||
config.set_args(config_configs)
|
||||
for key in config_configs:
|
||||
if key not in self._all_keys:
|
||||
raise Exception('Unknown key in config file: ' + key)
|
||||
if (not key in args_with_defaults) or args_with_defaults[key] is None:
|
||||
args_with_defaults[key] = config_configs[key]
|
||||
# Add missing args from hard-coded defaults.
|
||||
for argument in self._arguments:
|
||||
key = argument.key
|
||||
if (not key in args_with_defaults) or args_with_defaults[key] is None:
|
||||
if argument.optional:
|
||||
args_with_defaults[key] = argument.default
|
||||
else:
|
||||
raise Exception('Value not given for mandatory argument: ' + key)
|
||||
return self.main(**args_with_defaults)
|
||||
|
||||
def __init__(self, config_file=None, description=None):
|
||||
self._all_keys = set()
|
||||
self._arguments = []
|
||||
self._config_file = config_file
|
||||
self._description = description
|
||||
if self._config_file is not None:
|
||||
self.add_argument(
|
||||
'--config-file',
|
||||
default=self._config_file,
|
||||
help='Path to the configuration file to use'
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join(str(arg) for arg in self._arguments)
|
||||
|
||||
def add_argument(
|
||||
self,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
argument = Argument(*args, **kwargs)
|
||||
self._arguments.append(argument)
|
||||
self._all_keys.add(argument.key)
|
||||
|
||||
def cli(self, cli_args=None):
|
||||
'''
|
||||
Call the function from the CLI. Parse command line arguments
|
||||
to get all arguments.
|
||||
'''
|
||||
parser = argparse.ArgumentParser(
|
||||
description=self._description,
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
)
|
||||
for argument in self._arguments:
|
||||
parser.add_argument(*argument.args, **argument.kwargs)
|
||||
if argument.is_bool:
|
||||
new_longname = '--no' + argument.longname[1:]
|
||||
kwargs = argument.kwargs.copy()
|
||||
kwargs['default'] = not argument.default
|
||||
if kwargs['action'] == 'store_false':
|
||||
kwargs['action'] = 'store_true'
|
||||
elif kwargs['action'] == 'store_true':
|
||||
kwargs['action'] = 'store_false'
|
||||
if 'help' in kwargs:
|
||||
del kwargs['help']
|
||||
parser.add_argument(new_longname, dest=argument.key, **kwargs)
|
||||
args = parser.parse_args(args=cli_args)
|
||||
return self(**vars(args))
|
||||
|
||||
def main(self, **kwargs):
|
||||
'''
|
||||
Do the main function call work.
|
||||
|
||||
:type arguments: Dict
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
if __name__ == '__main__':
|
||||
class OneCliFunction(CliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
config_file='cli_function_test_config.py',
|
||||
description = '''\
|
||||
Description of this
|
||||
amazing function!
|
||||
''',
|
||||
)
|
||||
self.add_argument('-a', '--asdf', default='A', help='Help for asdf'),
|
||||
self.add_argument('-q', '--qwer', default='Q', help='Help for qwer'),
|
||||
self.add_argument('-b', '--bool', default=True, help='Help for bool'),
|
||||
self.add_argument('--bool-cli', default=False, help='Help for bool'),
|
||||
self.add_argument('--bool-nargs', default=False, nargs='?', action='store', const='')
|
||||
self.add_argument('--no-default', help='Help for no-bool'),
|
||||
self.add_argument('pos-mandatory', help='Help for pos-mandatory', type=int),
|
||||
self.add_argument('pos-optional', default=0, help='Help for pos-optional', type=int),
|
||||
self.add_argument('args-star', help='Help for args-star', nargs='*'),
|
||||
def main(self, **kwargs):
|
||||
del kwargs['config_file']
|
||||
return kwargs
|
||||
|
||||
one_cli_function = OneCliFunction()
|
||||
|
||||
# Default code call.
|
||||
default = one_cli_function(pos_mandatory=1)
|
||||
assert default == {
|
||||
'asdf': 'A',
|
||||
'qwer': 'Q',
|
||||
'bool': True,
|
||||
'bool_nargs': False,
|
||||
'bool_cli': True,
|
||||
'no_default': None,
|
||||
'pos_mandatory': 1,
|
||||
'pos_optional': 0,
|
||||
'args_star': []
|
||||
}
|
||||
|
||||
# Default CLI call.
|
||||
out = one_cli_function.cli(['1'])
|
||||
assert out == default
|
||||
|
||||
# asdf
|
||||
out = one_cli_function(pos_mandatory=1, asdf='B')
|
||||
assert out['asdf'] == 'B'
|
||||
out['asdf'] = default['asdf']
|
||||
assert(out == default)
|
||||
|
||||
# asdf and qwer
|
||||
out = one_cli_function(pos_mandatory=1, asdf='B', qwer='R')
|
||||
assert out['asdf'] == 'B'
|
||||
assert out['qwer'] == 'R'
|
||||
out['asdf'] = default['asdf']
|
||||
out['qwer'] = default['qwer']
|
||||
assert(out == default)
|
||||
|
||||
if '--bool':
|
||||
out = one_cli_function(pos_mandatory=1, bool=False)
|
||||
cli_out = one_cli_function.cli(['--bool', '1'])
|
||||
assert out == cli_out
|
||||
assert out['bool'] == False
|
||||
out['bool'] = default['bool']
|
||||
assert(out == default)
|
||||
|
||||
if '--bool-nargs':
|
||||
|
||||
out = one_cli_function(pos_mandatory=1, bool_nargs=True)
|
||||
assert out['bool_nargs'] == True
|
||||
out['bool_nargs'] = default['bool_nargs']
|
||||
assert(out == default)
|
||||
|
||||
out = one_cli_function(pos_mandatory=1, bool_nargs='asdf')
|
||||
assert out['bool_nargs'] == 'asdf'
|
||||
out['bool_nargs'] = default['bool_nargs']
|
||||
assert(out == default)
|
||||
|
||||
# Force a boolean value set on the config to be False on CLI.
|
||||
assert one_cli_function.cli(['--no-bool-cli', '1'])['bool_cli'] is False
|
||||
|
||||
# CLI call.
|
||||
print(one_cli_function.cli())
|
||||
5
cli_function_test_config.py
Normal file
5
cli_function_test_config.py
Normal file
@@ -0,0 +1,5 @@
|
||||
def set_args(args):
|
||||
'''
|
||||
:type args: Dict[str, Any]
|
||||
'''
|
||||
args['bool_cli'] = True
|
||||
29
copy-overlay
29
copy-overlay
@@ -5,25 +5,26 @@ import os
|
||||
import shutil
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
class CopyOverlayComponent(common.Component):
|
||||
def do_build(self, args):
|
||||
distutils.dir_util.copy_tree(
|
||||
common.rootfs_overlay_dir,
|
||||
common.out_rootfs_overlay_dir,
|
||||
update=1,
|
||||
)
|
||||
|
||||
def get_argparse_args(self):
|
||||
return {
|
||||
'description': '''\
|
||||
class Main(common.BuildCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description='''\
|
||||
Copy our git tracked rootfs_overlay to the final generated rootfs_overlay
|
||||
that also contains generated build outputs. This has the following advantages
|
||||
over just adding that to BR2_ROOTFS_OVERLAY:
|
||||
- also works for non Buildroot root filesystesms
|
||||
- places everything in one place for a nice 9P mount
|
||||
''',
|
||||
}
|
||||
''')
|
||||
|
||||
def build(self):
|
||||
# TODO: print rsync equivalent, move into shell_helpers.
|
||||
distutils.dir_util.copy_tree(
|
||||
self.env['rootfs_overlay_dir'],
|
||||
self.env['out_rootfs_overlay_dir'],
|
||||
update=1,
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
CopyOverlayComponent().build()
|
||||
Main().cli()
|
||||
|
||||
13
gem5-shell
13
gem5-shell
@@ -3,14 +3,15 @@
|
||||
import sys
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
parser = common.get_argparse(
|
||||
parser = self.get_argparse(
|
||||
default_args={'gem5':True},
|
||||
argparse_args={'description':'Connect a terminal to a running gem5 instance'}
|
||||
)
|
||||
args = common.setup(parser)
|
||||
sys.exit(common.run_cmd([
|
||||
common.gem5_m5term, common.Newline,
|
||||
'localhost', common.Newline,
|
||||
str(common.gem5_telnet_port), common.Newline,
|
||||
args = self.setup(parser)
|
||||
sys.exit(self.sh.run_cmd([
|
||||
kwargs['gem5_m5term'], LF,
|
||||
'localhost', LF,
|
||||
str(kwargs['gem5_telnet_port']), LF,
|
||||
]))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import common
|
||||
parser = common.get_argparse(
|
||||
parser = self.get_argparse(
|
||||
argparse_args={'description':'Get the value of a gem5 stat from the stats.txt file.'}
|
||||
)
|
||||
parser.add_argument(
|
||||
@@ -9,6 +9,6 @@ parser.add_argument(
|
||||
help='Python regexp matching the full stat name of interest',
|
||||
nargs='?',
|
||||
)
|
||||
args = common.setup(parser)
|
||||
stats = common.get_stats(args.stat)
|
||||
args = self.setup(parser)
|
||||
stats = self.get_stats(kwargs['stat'])
|
||||
print('\n'.join(stats))
|
||||
|
||||
13
getvar
13
getvar
@@ -3,14 +3,15 @@
|
||||
import types
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
parser = common.get_argparse(argparse_args={
|
||||
'description': '''Print the value of a common.py variable.
|
||||
parser = self.get_argparse(argparse_args={
|
||||
'description': '''Print the value of a kwargs['py'] variable.
|
||||
|
||||
This is useful to:
|
||||
|
||||
* give dry commands on the README that don't change when we refactor directory structure
|
||||
* create simple bash scripts that call use common.py variables
|
||||
* create simple bash scripts that call use kwargs['py'] variables
|
||||
|
||||
For example, to get the Buildroot output directory for an ARM build, use:
|
||||
|
||||
@@ -27,9 +28,9 @@ List all available variables:
|
||||
'''
|
||||
})
|
||||
parser.add_argument('variable', nargs='?')
|
||||
args = common.setup(parser)
|
||||
if args.variable:
|
||||
print(getattr(common, args.variable))
|
||||
args = self.setup(parser)
|
||||
if kwargs['variable']:
|
||||
print(getattr(common, kwargs['variable']))
|
||||
else:
|
||||
for attr in dir(common):
|
||||
if not attr.startswith('__'):
|
||||
|
||||
11
qemu-monitor
11
qemu-monitor
@@ -5,10 +5,11 @@ import sys
|
||||
import telnetlib
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
prompt = b'\n(qemu) '
|
||||
|
||||
parser = common.get_argparse({
|
||||
parser = self.get_argparse({
|
||||
'description': '''\
|
||||
Run a command on the QEMU monitor of a running QEMU instance
|
||||
|
||||
@@ -21,24 +22,24 @@ parser.add_argument(
|
||||
help='If given, run this command and quit',
|
||||
nargs='*',
|
||||
)
|
||||
args = common.setup(parser)
|
||||
args = self.setup(parser)
|
||||
|
||||
def write_and_read(tn, cmd, prompt):
|
||||
tn.write(cmd.encode('utf-8'))
|
||||
return '\n'.join(tn.read_until(prompt).decode('utf-8').splitlines()[1:])[:-len(prompt)]
|
||||
|
||||
with telnetlib.Telnet('localhost', common.qemu_monitor_port) as tn:
|
||||
with telnetlib.Telnet('localhost', kwargs['qemu_monitor_port']) as tn:
|
||||
# Couldn't disable server echo, so just removing the write for now.
|
||||
# https://stackoverflow.com/questions/12421799/how-to-disable-telnet-echo-in-python-telnetlib
|
||||
# sock = tn.get_socket()
|
||||
# sock.send(telnetlib.IAC + telnetlib.WILL + telnetlib.ECHO)
|
||||
if os.isatty(sys.stdin.fileno()):
|
||||
if args.command == []:
|
||||
if kwargs['command'] == []:
|
||||
print(tn.read_until(prompt).decode('utf-8'), end='')
|
||||
tn.interact()
|
||||
else:
|
||||
tn.read_until(prompt)
|
||||
print(write_and_read(tn, ' '.join(args.command) + '\n', prompt))
|
||||
print(write_and_read(tn, ' '.join(kwargs['command']) + '\n', prompt))
|
||||
else:
|
||||
tn.read_until(prompt)
|
||||
print(write_and_read(tn, sys.stdin.read() + '\n', prompt))
|
||||
|
||||
@@ -5,22 +5,23 @@ import subprocess
|
||||
import sys
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
def main():
|
||||
return common.run_cmd(
|
||||
return self.sh.run_cmd(
|
||||
[
|
||||
os.path.join(common.qemu_src_dir, 'scripts/simpletrace.py'), common.Newline,
|
||||
os.path.join(common.qemu_build_dir, 'trace-events-all'), common.Newline,
|
||||
os.path.join(common.qemu_trace_file), common.Newline,
|
||||
os.path.join(kwargs['qemu_src_dir'], 'scripts/simpletrace.py'), LF,
|
||||
os.path.join(kwargs['qemu_build_dir'], 'trace-events-all'), LF,
|
||||
os.path.join(kwargs['qemu_trace_file']), LF,
|
||||
],
|
||||
cmd_file=os.path.join(common.run_dir, 'qemu-trace2txt'),
|
||||
out_file=common.qemu_trace_txt_file,
|
||||
cmd_file=os.path.join(kwargs['run_dir'], 'qemu-trace2txt'),
|
||||
out_file=kwargs['qemu_trace_txt_file'],
|
||||
show_stdout=False,
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = common.get_argparse(argparse_args={
|
||||
parser = self.get_argparse(argparse_args={
|
||||
'description': 'Convert a QEMU `-trace exec_tb` to text form.'
|
||||
})
|
||||
args = common.setup(parser)
|
||||
args = self.setup(parser)
|
||||
sys.exit(main())
|
||||
|
||||
14
release
14
release
@@ -10,23 +10,23 @@ import subprocess
|
||||
import time
|
||||
|
||||
import common
|
||||
release_zip = imp.load_source('release_zip', os.path.join(common.root_dir, 'release-zip'))
|
||||
release_upload = imp.load_source('release_upload', os.path.join(common.root_dir, 'release-upload'))
|
||||
release_zip = imp.load_source('release_zip', os.path.join(kwargs['root_dir'], 'release-zip'))
|
||||
release_upload = imp.load_source('release_upload', os.path.join(kwargs['root_dir'], 'release-upload'))
|
||||
|
||||
start_time = time.time()
|
||||
# TODO factor those out so we don't redo the same thing multiple times.
|
||||
# subprocess.check_call([os.path.join(common.root_dir, 'test')])
|
||||
# subprocess.check_call([os.path.join(common.root_dir, ''bench-all', '-A', '-u'])
|
||||
# subprocess.check_call([os.path.join(kwargs['root_dir'], 'test')])
|
||||
# subprocess.check_call([os.path.join(kwargs['root_dir'], ''bench-all', '-A', '-u'])
|
||||
|
||||
# A clean release requires a full rebuild unless we hack it :-(
|
||||
# We can't just use our current build as it contains packages we've
|
||||
# installed in random experiments. And with EXT2: we can't easily
|
||||
# know what the smallest root filesystem size is and use it either...
|
||||
# https://stackoverflow.com/questions/47320800/how-to-clean-only-target-in-buildroot
|
||||
subprocess.check_call([os.path.join(common.root_dir, 'configure'), '--all'])
|
||||
subprocess.check_call([os.path.join(common.root_dir, 'build'), '--all-archs', 'release'])
|
||||
subprocess.check_call([os.path.join(kwargs['root_dir'], 'configure'), '--all'])
|
||||
subprocess.check_call([os.path.join(kwargs['root_dir'], 'build'), '--all-archs', 'release'])
|
||||
release_zip.main()
|
||||
subprocess.check_call(['git', 'push'])
|
||||
release_upload.main()
|
||||
end_time = time.time()
|
||||
common.print_time(end_time - start_time)
|
||||
self.print_time(end_time - start_time)
|
||||
|
||||
@@ -10,7 +10,8 @@ https://stackoverflow.com/questions/24987542/is-there-a-link-to-github-for-downl
|
||||
import urllib.request
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
_json = common.github_make_request(path='/releases')
|
||||
_json = self.github_make_request(path='/releases')
|
||||
asset = _json[0]['assets'][0]
|
||||
urllib.request.urlretrieve(asset['browser_download_url'], asset['name'])
|
||||
|
||||
@@ -16,15 +16,16 @@ import sys
|
||||
import urllib.error
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
def main():
|
||||
repo = common.github_repo_id
|
||||
tag = 'sha-{}'.format(common.sha)
|
||||
upload_path = common.release_zip_file
|
||||
repo = kwargs['github_repo_id']
|
||||
tag = 'sha-{}'.format(kwargs['sha'])
|
||||
upload_path = kwargs['release_zip_file']
|
||||
|
||||
# Check the release already exists.
|
||||
try:
|
||||
_json = common.github_make_request(path='/releases/tags/' + tag)
|
||||
_json = self.github_make_request(path='/releases/tags/' + tag)
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
release_exists = False
|
||||
@@ -36,7 +37,7 @@ def main():
|
||||
|
||||
# Create release if not yet created.
|
||||
if not release_exists:
|
||||
_json = common.github_make_request(
|
||||
_json = self.github_make_request(
|
||||
authenticate=True,
|
||||
data=json.dumps({
|
||||
'tag_name': tag,
|
||||
@@ -50,12 +51,12 @@ def main():
|
||||
asset_name = os.path.split(upload_path)[1]
|
||||
|
||||
# Clear the prebuilts for a upload.
|
||||
_json = common.github_make_request(
|
||||
_json = self.github_make_request(
|
||||
path=('/releases/' + str(release_id) + '/assets'),
|
||||
)
|
||||
for asset in _json:
|
||||
if asset['name'] == asset_name:
|
||||
_json = common.github_make_request(
|
||||
_json = self.github_make_request(
|
||||
authenticate=True,
|
||||
path=('/releases/assets/' + str(asset['id'])),
|
||||
method='DELETE',
|
||||
@@ -65,7 +66,7 @@ def main():
|
||||
# Upload the prebuilt.
|
||||
with open(upload_path, 'br') as myfile:
|
||||
content = myfile.read()
|
||||
_json = common.github_make_request(
|
||||
_json = self.github_make_request(
|
||||
authenticate=True,
|
||||
data=content,
|
||||
extra_headers={'Content-Type': 'application/zip'},
|
||||
|
||||
17
release-zip
17
release-zip
@@ -9,16 +9,17 @@ import subprocess
|
||||
import zipfile
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
def main():
|
||||
os.makedirs(common.release_dir, exist_ok=True)
|
||||
if os.path.exists(common.release_zip_file):
|
||||
os.unlink(common.release_zip_file)
|
||||
zipf = zipfile.ZipFile(common.release_zip_file, 'w', zipfile.ZIP_DEFLATED)
|
||||
for arch in common.all_archs:
|
||||
common.setup(common.get_argparse(default_args={'arch': arch}))
|
||||
zipf.write(common.qcow2_file, arcname=os.path.relpath(common.qcow2_file, common.root_dir))
|
||||
zipf.write(common.linux_image, arcname=os.path.relpath(common.linux_image, common.root_dir))
|
||||
os.makedirs(kwargs['release_dir'], exist_ok=True)
|
||||
if os.path.exists(kwargs['release_zip_file']):
|
||||
os.unlink(kwargs['release_zip_file'])
|
||||
zipf = zipfile.ZipFile(kwargs['release_zip_file'], 'w', zipfile.ZIP_DEFLATED)
|
||||
for arch in kwargs['all_archs']:
|
||||
self.setup(common.get_argparse(default_args={'arch': arch}))
|
||||
zipf.write(kwargs['qcow2_file'], arcname=os.path.relpath(kwargs['qcow2_file'], kwargs['root_dir']))
|
||||
zipf.write(kwargs['linux_image'], arcname=os.path.relpath(kwargs['linux_image'], kwargs['root_dir']))
|
||||
zipf.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
49
run-docker
49
run-docker
@@ -4,50 +4,51 @@ import argparse
|
||||
import os
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
container_name = common.repo_short_id
|
||||
container_hostname = common.repo_short_id
|
||||
image_name = common.repo_short_id
|
||||
target_dir = '/root/{}'.format(common.repo_short_id)
|
||||
container_name = kwargs['repo_short_id']
|
||||
container_hostname = kwargs['repo_short_id']
|
||||
image_name = kwargs['repo_short_id']
|
||||
target_dir = '/root/{}'.format(kwargs['repo_short_id'])
|
||||
docker = ['sudo', 'docker']
|
||||
def create(args):
|
||||
common.run_cmd(docker + ['build', '-t', image_name, '.', common.Newline])
|
||||
self.sh.run_cmd(docker + ['build', '-t', image_name, '.', LF])
|
||||
# --privileged for KVM:
|
||||
# https://stackoverflow.com/questions/48422001/launching-qemu-kvm-from-inside-docker-container
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
docker +
|
||||
[
|
||||
'create', common.Newline,
|
||||
'--hostname', container_hostname, common.Newline,
|
||||
'-i', common.Newline,
|
||||
'--name', container_name, common.Newline,
|
||||
'--net', 'host', common.Newline,
|
||||
'--privileged', common.Newline,
|
||||
'-t', common.Newline,
|
||||
'-w', target_dir, common.Newline,
|
||||
'-v', '{}:{}'.format(os.getcwd(), target_dir), common.Newline,
|
||||
'create', LF,
|
||||
'--hostname', container_hostname, LF,
|
||||
'-i', LF,
|
||||
'--name', container_name, LF,
|
||||
'--net', 'host', LF,
|
||||
'--privileged', LF,
|
||||
'-t', LF,
|
||||
'-w', target_dir, LF,
|
||||
'-v', '{}:{}'.format(os.getcwd(), target_dir), LF,
|
||||
image_name,
|
||||
]
|
||||
)
|
||||
def destroy(args):
|
||||
stop(args)
|
||||
common.run_cmd(docker + ['rm', container_name, common.Newline])
|
||||
common.run_cmd(docker + ['rmi', image_name, common.Newline])
|
||||
self.sh.run_cmd(docker + ['rm', container_name, LF])
|
||||
self.sh.run_cmd(docker + ['rmi', image_name, LF])
|
||||
def sh(args):
|
||||
start(args)
|
||||
if args:
|
||||
sh_args = args
|
||||
else:
|
||||
sh_args = ['bash']
|
||||
common.run_cmd(
|
||||
self.sh.run_cmd(
|
||||
docker + ['exec', '-i', '-t', container_name] +
|
||||
sh_args +
|
||||
[common.Newline],
|
||||
[LF],
|
||||
)
|
||||
def start(args):
|
||||
common.run_cmd(docker + ['start', container_name, common.Newline])
|
||||
self.sh.run_cmd(docker + ['start', container_name, LF])
|
||||
def stop(args):
|
||||
common.run_cmd(docker + ['stop', container_name, common.Newline])
|
||||
self.sh.run_cmd(docker + ['stop', container_name, LF])
|
||||
cmd_action_map = {
|
||||
'create': lambda args: create(args),
|
||||
'DESTROY': lambda args: destroy(args),
|
||||
@@ -58,7 +59,7 @@ cmd_action_map = {
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('cmd', choices=cmd_action_map)
|
||||
parser.add_argument('args', nargs='*')
|
||||
common.add_dry_run_argument(parser)
|
||||
self.add_dry_run_argument(parser)
|
||||
args = parser.parse_args()
|
||||
common.setup_dry_run_arguments(args)
|
||||
cmd_action_map[args.cmd](args.args)
|
||||
self.setup_dry_run_arguments(args)
|
||||
cmd_action_map[kwargs['cmd']](kwargs['args'])
|
||||
|
||||
302
run-gdb
302
run-gdb
@@ -7,18 +7,7 @@ import subprocess
|
||||
import sys
|
||||
|
||||
import common
|
||||
|
||||
defaults = {
|
||||
'after': '',
|
||||
'before': '',
|
||||
'break_at': None,
|
||||
'kgdb': False,
|
||||
'no_continue': False,
|
||||
'no_lxsymbols': False,
|
||||
'test': False,
|
||||
'sim': False,
|
||||
'userland': None,
|
||||
}
|
||||
from shell_helpers import LF
|
||||
|
||||
class GdbTestcase:
|
||||
def __init__(
|
||||
@@ -34,8 +23,8 @@ class GdbTestcase:
|
||||
'''
|
||||
self.prompt = '\(gdb\) '
|
||||
self.source_path = source_path
|
||||
common.print_cmd(cmd)
|
||||
cmd = common.strip_newlines(cmd)
|
||||
self.print_cmd(cmd)
|
||||
cmd = self.strip_newlines(cmd)
|
||||
import pexpect
|
||||
self.child = pexpect.spawn(
|
||||
cmd[0],
|
||||
@@ -84,155 +73,146 @@ class GdbTestcase:
|
||||
self.child.sendline(line)
|
||||
self.child.expect(self.prompt)
|
||||
|
||||
def main(args, extra_args=None):
|
||||
'''
|
||||
:param args: argparse parse_argument() output. Must contain all the common options,
|
||||
but does not need GDB specific ones.
|
||||
:type args: argparse.Namespace
|
||||
|
||||
:param extra_args: extra arguments to be added to args
|
||||
:type extra_args: Dict[str,Any]
|
||||
|
||||
:return: GDB exit status
|
||||
:rtype: int
|
||||
'''
|
||||
global defaults
|
||||
args = common.resolve_args(defaults, args, extra_args)
|
||||
after = common.shlex_split(args.after)
|
||||
before = common.shlex_split(args.before)
|
||||
no_continue = args.no_continue
|
||||
if args.test:
|
||||
no_continue = True
|
||||
before.extend([
|
||||
'-q', common.Newline,
|
||||
'-nh', common.Newline,
|
||||
'-ex', 'set confirm off', common.Newline
|
||||
])
|
||||
elif args.verbose:
|
||||
# The output of this would affect the tests.
|
||||
# https://stackoverflow.com/questions/13496389/gdb-remote-protocol-how-to-analyse-packets
|
||||
# Also be opinionated and set remotetimeout to allow you to step debug the emulator at the same time.
|
||||
before.extend([
|
||||
'-ex', 'set debug remote 1', common.Newline,
|
||||
'-ex', 'set remotetimeout 99999', common.Newline,
|
||||
])
|
||||
if args.break_at is not None:
|
||||
break_at = ['-ex', 'break {}'.format(args.break_at), common.Newline]
|
||||
else:
|
||||
break_at = []
|
||||
linux_full_system = (common.baremetal is None and args.userland is None)
|
||||
if args.userland:
|
||||
image = common.resolve_userland(args.userland)
|
||||
elif common.baremetal:
|
||||
image = common.image
|
||||
test_script_path = os.path.splitext(common.source_path)[0] + '.py'
|
||||
else:
|
||||
image = common.vmlinux
|
||||
if common.baremetal:
|
||||
allowed_toolchains = ['crosstool-ng', 'buildroot', 'host']
|
||||
else:
|
||||
allowed_toolchains = ['buildroot', 'crosstool-ng', 'host']
|
||||
cmd = (
|
||||
[common.get_toolchain_tool('gdb', allowed_toolchains=allowed_toolchains), common.Newline] +
|
||||
before +
|
||||
['-q', common.Newline]
|
||||
)
|
||||
if linux_full_system:
|
||||
cmd.extend(['-ex', 'add-auto-load-safe-path {}'.format(common.linux_build_dir), common.Newline])
|
||||
if args.sim:
|
||||
target = 'sim'
|
||||
else:
|
||||
if args.kgdb:
|
||||
port = common.extra_serial_port
|
||||
else:
|
||||
port = common.gdb_port
|
||||
target = 'remote localhost:{}'.format(port)
|
||||
cmd.extend([
|
||||
'-ex', 'file {}'.format(image), common.Newline,
|
||||
'-ex', 'target {}'.format(target), common.Newline,
|
||||
])
|
||||
if not args.kgdb:
|
||||
cmd.extend(break_at)
|
||||
if not no_continue:
|
||||
# ## lx-symbols
|
||||
#
|
||||
# ### lx-symbols after continue
|
||||
#
|
||||
# lx symbols must be run after continue.
|
||||
#
|
||||
# running it immediately after the connect on the bootloader leads to failure,
|
||||
# likely because kernel structure on which it depends are not yet available.
|
||||
#
|
||||
# With this setup, continue runs, and lx-symbols only runs when a break happens,
|
||||
# either by hitting the breakpoint, or by entering Ctrl + C.
|
||||
#
|
||||
# Sure, if the user sets a break on a raw address of the bootloader,
|
||||
# problems will still arise, but let's think about that some other time.
|
||||
#
|
||||
# ### lx-symbols autoload
|
||||
#
|
||||
# The lx-symbols commands gets loaded through the file vmlinux-gdb.py
|
||||
# which gets put on the kernel build root when python debugging scripts are enabled.
|
||||
cmd.extend(['-ex', 'continue', common.Newline])
|
||||
if not args.no_lxsymbols and linux_full_system:
|
||||
cmd.extend(['-ex', 'lx-symbols {}'.format(common.kernel_modules_build_subdir), common.Newline])
|
||||
cmd.extend(after)
|
||||
if args.test:
|
||||
GdbTestcase(
|
||||
common.source_path,
|
||||
test_script_path,
|
||||
cmd,
|
||||
verbose=args.verbose,
|
||||
class Main(common.LkmcCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(description='''\
|
||||
Connect with GDB to an emulator to debug Linux itself
|
||||
''')
|
||||
self.add_argument(
|
||||
'-A', '--after', default='',
|
||||
help='Pass extra arguments to GDB, to be appended after all other arguments'
|
||||
)
|
||||
else:
|
||||
# I would rather have cwd be out_rootfs_overlay_dir,
|
||||
# but then lx-symbols cannot fine the vmlinux and fails with:
|
||||
# vmlinux: No such file or directory.
|
||||
return common.run_cmd(
|
||||
cmd,
|
||||
cmd_file=os.path.join(common.run_dir, 'run-gdb.sh'),
|
||||
cwd=common.linux_build_dir
|
||||
self.add_argument(
|
||||
'--before', default='',
|
||||
help='Pass extra arguments to GDB to be prepended before any of the arguments passed by this script'
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = common.get_argparse(argparse_args={'description': 'Connect with GDB to an emulator to debug Linux itself'})
|
||||
parser.add_argument(
|
||||
'-A', '--after', default=defaults['after'],
|
||||
help='Pass extra arguments to GDB, to be appended after all other arguments'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--before', default=defaults['before'],
|
||||
help='Pass extra arguments to GDB to be prepended before any of the arguments passed by this script'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-C', '--no-continue', default=defaults['no_continue'], action='store_true',
|
||||
help="Don't run continue after connecting"
|
||||
)
|
||||
parser.add_argument(
|
||||
'-k', '--kgdb', default=defaults['kgdb'], action='store_true'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sim', default=defaults['sim'], action='store_true',
|
||||
help='''Use the built-in GDB CPU simulator
|
||||
See: https://github.com/cirosantilli/linux-kernel-module-cheat#gdb-builtin-cpu-simulator
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'-X', '--no-lxsymbols', default=defaults['no_lxsymbols'], action='store_true'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--test', default=defaults['test'], action='store_true',
|
||||
help='''\
|
||||
self.add_argument(
|
||||
'break_at', nargs='?',
|
||||
help='Extra options to append at the end of the emulator command line'
|
||||
)
|
||||
self.add_argument(
|
||||
'-k', '--kgdb', default=False,
|
||||
)
|
||||
self.add_argument(
|
||||
'-C', '--no-continue', default=False,
|
||||
help="Don't run continue after connecting"
|
||||
)
|
||||
self.add_argument(
|
||||
'-X', '--no-lxsymbols', default=False,
|
||||
)
|
||||
self.add_argument(
|
||||
'--test', default=False,
|
||||
help='''\
|
||||
Run an expect test case instead of interactive usage. For baremetal and userland,
|
||||
the script is a .py file next to the source code.
|
||||
'''
|
||||
)
|
||||
parser.add_argument(
|
||||
'-u', '--userland', default=defaults['userland'],
|
||||
)
|
||||
parser.add_argument(
|
||||
'break_at', nargs='?',
|
||||
help='Extra options to append at the end of the emulator command line'
|
||||
)
|
||||
args = common.setup(parser)
|
||||
sys.exit(main(args))
|
||||
)
|
||||
self.add_argument(
|
||||
'--sim', default=False,
|
||||
help='''Use the built-in GDB CPU simulator
|
||||
See: https://github.com/cirosantilli/linux-kernel-module-cheat#gdb-builtin-cpu-simulator
|
||||
'''
|
||||
)
|
||||
self.add_argument(
|
||||
'-u', '--userland',
|
||||
)
|
||||
|
||||
def timed_main(self):
|
||||
after = self.sh.shlex_split(self.env['after'])
|
||||
before = self.sh.shlex_split(self.env['before'])
|
||||
no_continue = self.env['no_continue']
|
||||
if self.env['test']:
|
||||
no_continue = True
|
||||
before.extend([
|
||||
'-q', LF,
|
||||
'-nh', LF,
|
||||
'-ex', 'set confirm off', LF
|
||||
])
|
||||
elif self.env['verbose']:
|
||||
# The output of this would affect the tests.
|
||||
# https://stackoverflow.com/questions/13496389/gdb-remote-protocol-how-to-analyse-packets
|
||||
# Also be opinionated and set remotetimeout to allow you to step debug the emulator at the same time.
|
||||
before.extend([
|
||||
'-ex', 'set debug remote 1', LF,
|
||||
'-ex', 'set remotetimeout 99999', LF,
|
||||
])
|
||||
if self.env['break_at'] is not None:
|
||||
break_at = ['-ex', 'break {}'.format(self.env['break_at']), LF]
|
||||
else:
|
||||
break_at = []
|
||||
linux_full_system = (self.env['baremetal'] is None and self.env['userland'] is None)
|
||||
if self.env['userland']:
|
||||
image = self.resolve_userland(self.env['userland'])
|
||||
elif self.env['baremetal']:
|
||||
image = self.env['image']
|
||||
test_script_path = os.path.splitext(self.env['source_path'])[0] + '.py'
|
||||
else:
|
||||
image = self.env['vmlinux']
|
||||
if self.env['baremetal']:
|
||||
allowed_toolchains = ['crosstool-ng', 'buildroot', 'host']
|
||||
else:
|
||||
allowed_toolchains = ['buildroot', 'crosstool-ng', 'host']
|
||||
cmd = (
|
||||
[self.get_toolchain_tool('gdb', allowed_toolchains=allowed_toolchains), LF] +
|
||||
before +
|
||||
['-q', LF]
|
||||
)
|
||||
if linux_full_system:
|
||||
cmd.extend(['-ex', 'add-auto-load-safe-path {}'.format(self.env['linux_build_dir']), LF])
|
||||
if self.env['sim']:
|
||||
target = 'sim'
|
||||
else:
|
||||
if self.env['kgdb']:
|
||||
port = self.env['extra_serial_port']
|
||||
else:
|
||||
port = self.env['gdb_port']
|
||||
target = 'remote localhost:{}'.format(port)
|
||||
cmd.extend([
|
||||
'-ex', 'file {}'.format(image), LF,
|
||||
'-ex', 'target {}'.format(target), LF,
|
||||
])
|
||||
if not self.env['kgdb']:
|
||||
cmd.extend(break_at)
|
||||
if not no_continue:
|
||||
# ## lx-symbols
|
||||
#
|
||||
# ### lx-symbols after continue
|
||||
#
|
||||
# lx symbols must be run after continue.
|
||||
#
|
||||
# running it immediately after the connect on the bootloader leads to failure,
|
||||
# likely because kernel structure on which it depends are not yet available.
|
||||
#
|
||||
# With this setup, continue runs, and lx-symbols only runs when a break happens,
|
||||
# either by hitting the breakpoint, or by entering Ctrl + C.
|
||||
#
|
||||
# Sure, if the user sets a break on a raw address of the bootloader,
|
||||
# problems will still arise, but let's think about that some other time.
|
||||
#
|
||||
# ### lx-symbols autoload
|
||||
#
|
||||
# The lx-symbols commands gets loaded through the file vmlinux-gdb.py
|
||||
# which gets put on the kernel build root when python debugging scripts are enabled.
|
||||
cmd.extend(['-ex', 'continue', LF])
|
||||
if not self.env['no_lxsymbols'] and linux_full_system:
|
||||
cmd.extend(['-ex', 'lx-symbols {}'.format(self.env['kernel_modules_build_subdir']), LF])
|
||||
cmd.extend(after)
|
||||
if self.env['test']:
|
||||
GdbTestcase(
|
||||
self.env['source_path'],
|
||||
test_script_path,
|
||||
cmd,
|
||||
verbose=self.env['verbose'],
|
||||
)
|
||||
else:
|
||||
# I would rather have cwd be out_rootfs_overlay_dir,
|
||||
# but then lx-symbols cannot fine the vmlinux and fails with:
|
||||
# vmlinux: No such file or directory.
|
||||
return self.sh.run_cmd(
|
||||
cmd,
|
||||
cmd_file=os.path.join(self.env['run_dir'], 'run-gdb.sh'),
|
||||
cwd=self.env['linux_build_dir']
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
Main().cli()
|
||||
|
||||
12
run-gdb-user
12
run-gdb-user
@@ -5,9 +5,9 @@ import os
|
||||
import sys
|
||||
|
||||
import common
|
||||
rungdb = imp.load_source('rungdb', os.path.join(common.root_dir, 'run-gdb'))
|
||||
rungdb = imp.load_source('rungdb', os.path.join(kwargs['root_dir'], 'run-gdb'))
|
||||
|
||||
parser = common.get_argparse(argparse_args={
|
||||
parser = self.get_argparse(argparse_args={
|
||||
'description': '''GDB step debug guest userland processes without gdbserver.
|
||||
|
||||
More information at: https://github.com/cirosantilli/linux-kernel-module-cheat#gdb-step-debug-userland-processes
|
||||
@@ -23,9 +23,9 @@ parser.add_argument(
|
||||
help='Break at this point, e.g. main.',
|
||||
nargs='?'
|
||||
)
|
||||
args = common.setup(parser)
|
||||
executable = common.resolve_userland(args.executable)
|
||||
addr = common.get_elf_entry(os.path.join(common.buildroot_build_build_dir, executable))
|
||||
args = self.setup(parser)
|
||||
executable = self.resolve_userland(kwargs['executable'])
|
||||
addr = self.get_elf_entry(os.path.join(kwargs['buildroot_build_build_dir'], executable))
|
||||
extra_args = {}
|
||||
extra_args['before'] = '-ex \"add-symbol-file {} {}\"'.format(executable, hex(addr))
|
||||
# Or else lx-symbols throws for arm:
|
||||
@@ -33,5 +33,5 @@ extra_args['before'] = '-ex \"add-symbol-file {} {}\"'.format(executable, hex(ad
|
||||
# TODO understand better.
|
||||
# Also, lx-symbols overrides the add-symbol-file commands.
|
||||
extra_args['no_lxsymbols'] = True
|
||||
extra_args['break_at'] = args.break_at
|
||||
extra_args['break_at'] = kwargs['break_at']
|
||||
sys.exit(rungdb.main(args, extra_args))
|
||||
|
||||
@@ -5,8 +5,9 @@ import subprocess
|
||||
import sys
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
parser = common.get_argparse(argparse_args={
|
||||
parser = self.get_argparse(argparse_args={
|
||||
'description':'Connect to gdbserver running on the guest.'
|
||||
})
|
||||
parser.add_argument(
|
||||
@@ -16,13 +17,13 @@ parser.add_argument(
|
||||
parser.add_argument(
|
||||
'break_at', default='main', nargs='?'
|
||||
)
|
||||
args = common.setup(parser)
|
||||
args = self.setup(parser)
|
||||
sys.exit(subprocess.Popen([
|
||||
common.get_toolchain_tool('gdb'),
|
||||
self.get_toolchain_tool('gdb'),
|
||||
'-q',
|
||||
'-ex', 'set sysroot {}'.format(common.buildroot_staging_dir),
|
||||
'-ex', 'target remote localhost:{}'.format(common.qemu_hostfwd_generic_port),
|
||||
'-ex', 'tbreak {}'.format(args.break_at),
|
||||
'-ex', 'set sysroot {}'.format(kwargs['buildroot_staging_dir']),
|
||||
'-ex', 'target remote localhost:{}'.format(kwargs['qemu_hostfwd_generic_port']),
|
||||
'-ex', 'tbreak {}'.format(kwargs['break_at']),
|
||||
'-ex', 'continue',
|
||||
os.path.join(common.buildroot_build_build_dir, common.resolve_userland(args.executable)),
|
||||
os.path.join(kwargs['buildroot_build_build_dir'], self.resolve_userland(kwargs['executable'])),
|
||||
]).wait())
|
||||
|
||||
@@ -4,8 +4,9 @@ import os
|
||||
import sys
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
parser = common.get_argparse(argparse_args={
|
||||
parser = self.get_argparse(argparse_args={
|
||||
'description': '''Run a Buildroot ToolChain tool like readelf or objdump.
|
||||
|
||||
For example, to get some information about the arm vmlinux:
|
||||
@@ -24,7 +25,6 @@ ls "$(./getvar -a arm host_bin_dir)"
|
||||
parser.add_argument(
|
||||
'--dry',
|
||||
help='Just output the tool path to stdout but actually run it',
|
||||
action='store_true',
|
||||
)
|
||||
parser.add_argument('tool', help='Which tool to run.')
|
||||
parser.add_argument(
|
||||
@@ -34,17 +34,17 @@ parser.add_argument(
|
||||
metavar='extra-args',
|
||||
nargs='*'
|
||||
)
|
||||
args = common.setup(parser)
|
||||
if common.baremetal is None:
|
||||
image = common.vmlinux
|
||||
args = self.setup(parser)
|
||||
if kwargs['baremetal'] is None:
|
||||
image = kwargs['vmlinux']
|
||||
else:
|
||||
image = common.image
|
||||
tool= common.get_toolchain_tool(args.tool)
|
||||
if args.dry:
|
||||
image = kwargs['image']
|
||||
tool= self.get_toolchain_tool(kwargs['tool'])
|
||||
if kwargs['dry']:
|
||||
print(tool)
|
||||
else:
|
||||
sys.exit(common.run_cmd(
|
||||
[tool, common.Newline]
|
||||
+ common.add_newlines(args.extra_args),
|
||||
cmd_file=os.path.join(common.run_dir, 'run-toolchain.sh'),
|
||||
sys.exit(self.sh.run_cmd(
|
||||
[tool, LF]
|
||||
+ self.sh.add_newlines(kwargs['extra_args']),
|
||||
cmd_file=os.path.join(kwargs['run_dir'], 'run-toolchain.sh'),
|
||||
))
|
||||
|
||||
262
shell_helpers.py
Normal file
262
shell_helpers.py
Normal file
@@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import distutils.file_util
|
||||
import itertools
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import signal
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
class LF:
|
||||
'''
|
||||
LineFeed (AKA newline).
|
||||
|
||||
Singleton class. Can be used in print_cmd to print out nicer command lines
|
||||
with --key on the same line as "--key value".
|
||||
'''
|
||||
pass
|
||||
|
||||
class ShellHelpers:
|
||||
'''
|
||||
Helpers to do things which are easy from the shell,
|
||||
usually filesystem, process or pipe operations.
|
||||
|
||||
Attempt to print shell equivalents of all commands to make things
|
||||
easy to debug and understand what is going on.
|
||||
'''
|
||||
def __init__(self, dry_run=False):
|
||||
'''
|
||||
:param dry_run: don't run the commands, just potentially print them. Debug aid.
|
||||
:type dry_run: Bool
|
||||
'''
|
||||
self.dry_run = dry_run
|
||||
|
||||
def add_newlines(self, cmd):
|
||||
out = []
|
||||
for arg in cmd:
|
||||
out.extend([arg, LF])
|
||||
return out
|
||||
|
||||
def cp(self, src, dest, **kwargs):
|
||||
self.print_cmd(['cp', src, dest])
|
||||
if not self.dry_run:
|
||||
shutil.copy2(src, dest)
|
||||
|
||||
def cmd_to_string(self, cmd, cwd=None, extra_env=None, extra_paths=None):
|
||||
'''
|
||||
Format a command given as a list of strings so that it can
|
||||
be viewed nicely and executed by bash directly and print it to stdout.
|
||||
'''
|
||||
last_newline = ' \\\n'
|
||||
newline_separator = last_newline + ' '
|
||||
out = []
|
||||
if extra_env is None:
|
||||
extra_env = {}
|
||||
if cwd is not None:
|
||||
out.append('cd {} &&'.format(shlex.quote(cwd)))
|
||||
if extra_paths is not None:
|
||||
out.append('PATH="{}:${{PATH}}"'.format(':'.join(extra_paths)))
|
||||
for key in extra_env:
|
||||
out.append('{}={}'.format(shlex.quote(key), shlex.quote(extra_env[key])))
|
||||
cmd_quote = []
|
||||
newline_count = 0
|
||||
for arg in cmd:
|
||||
if arg == LF:
|
||||
cmd_quote.append(arg)
|
||||
newline_count += 1
|
||||
else:
|
||||
cmd_quote.append(shlex.quote(arg))
|
||||
if newline_count > 0:
|
||||
cmd_quote = [' '.join(list(y)) for x, y in itertools.groupby(cmd_quote, lambda z: z == LF) if not x]
|
||||
out.extend(cmd_quote)
|
||||
if newline_count == 1 and cmd[-1] == LF:
|
||||
ending = ''
|
||||
else:
|
||||
ending = last_newline + ';'
|
||||
return newline_separator.join(out) + ending
|
||||
|
||||
def copy_dir_if_update_non_recursive(self, srcdir, destdir, filter_ext=None):
|
||||
# TODO print rsync equivalent.
|
||||
os.makedirs(destdir, exist_ok=True)
|
||||
for basename in os.listdir(srcdir):
|
||||
src = os.path.join(srcdir, basename)
|
||||
if os.path.isfile(src):
|
||||
noext, ext = os.path.splitext(basename)
|
||||
if filter_ext is not None and ext == filter_ext:
|
||||
distutils.file_util.copy_file(
|
||||
src,
|
||||
os.path.join(destdir, basename),
|
||||
update=1,
|
||||
)
|
||||
|
||||
def print_cmd(self, cmd, cwd=None, cmd_file=None, extra_env=None, extra_paths=None):
|
||||
'''
|
||||
Print cmd_to_string to stdout.
|
||||
|
||||
Optionally save the command to cmd_file file, and add extra_env
|
||||
environment variables to the command generated.
|
||||
|
||||
If cmd contains at least one LF, newlines are only added on LF.
|
||||
Otherwise, newlines are added automatically after every word.
|
||||
'''
|
||||
if type(cmd) is str:
|
||||
cmd_string = cmd
|
||||
else:
|
||||
cmd_string = self.cmd_to_string(cmd, cwd=cwd, extra_env=extra_env, extra_paths=extra_paths)
|
||||
print('+ ' + cmd_string)
|
||||
if cmd_file is not None:
|
||||
with open(cmd_file, 'w') as f:
|
||||
f.write('#!/usr/bin/env bash\n')
|
||||
f.write(cmd_string)
|
||||
st = os.stat(cmd_file)
|
||||
os.chmod(cmd_file, st.st_mode | stat.S_IXUSR)
|
||||
|
||||
def run_cmd(
|
||||
self,
|
||||
cmd,
|
||||
cmd_file=None,
|
||||
out_file=None,
|
||||
show_stdout=True,
|
||||
show_cmd=True,
|
||||
extra_env=None,
|
||||
extra_paths=None,
|
||||
delete_env=None,
|
||||
raise_on_failure=True,
|
||||
**kwargs
|
||||
):
|
||||
'''
|
||||
Run a command. Write the command to stdout before running it.
|
||||
|
||||
Wait until the command finishes execution.
|
||||
|
||||
:param cmd: command to run. LF entries are magic get skipped.
|
||||
:type cmd: List[str]
|
||||
|
||||
:param cmd_file: if not None, write the command to be run to that file
|
||||
:type cmd_file: str
|
||||
|
||||
:param out_file: if not None, write the stdout and stderr of the command the file
|
||||
:type out_file: str
|
||||
|
||||
:param show_stdout: wether to show stdout and stderr on the terminal or not
|
||||
:type show_stdout: bool
|
||||
|
||||
:param extra_env: extra environment variables to add when running the command
|
||||
:type extra_env: Dict[str,str]
|
||||
'''
|
||||
if out_file is not None:
|
||||
stdout = subprocess.PIPE
|
||||
stderr = subprocess.STDOUT
|
||||
else:
|
||||
if show_stdout:
|
||||
stdout = None
|
||||
stderr = None
|
||||
else:
|
||||
stdout = subprocess.DEVNULL
|
||||
stderr = subprocess.DEVNULL
|
||||
if extra_env is None:
|
||||
extra_env = {}
|
||||
if delete_env is None:
|
||||
delete_env = []
|
||||
if 'cwd' in kwargs:
|
||||
cwd = kwargs['cwd']
|
||||
else:
|
||||
cwd = None
|
||||
env = os.environ.copy()
|
||||
env.update(extra_env)
|
||||
if extra_paths is not None:
|
||||
path = ':'.join(extra_paths)
|
||||
if 'PATH' in os.environ:
|
||||
path += ':' + os.environ['PATH']
|
||||
env['PATH'] = path
|
||||
for key in delete_env:
|
||||
if key in env:
|
||||
del env[key]
|
||||
if show_cmd:
|
||||
self.print_cmd(cmd, cwd=cwd, cmd_file=cmd_file, extra_env=extra_env, extra_paths=extra_paths)
|
||||
|
||||
# Otherwise Ctrl + C gives:
|
||||
# - ugly Python stack trace for gem5 (QEMU takes over terminal and is fine).
|
||||
# - kills Python, and that then kills GDB: https://stackoverflow.com/questions/19807134/does-python-always-raise-an-exception-if-you-do-ctrlc-when-a-subprocess-is-exec
|
||||
sigint_old = signal.getsignal(signal.SIGINT)
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
|
||||
# Otherwise BrokenPipeError when piping through | grep
|
||||
# But if I do this_module, my terminal gets broken at the end. Why, why, why.
|
||||
# https://stackoverflow.com/questions/14207708/ioerror-errno-32-broken-pipe-python
|
||||
# Ignoring the exception is not enough as it prints a warning anyways.
|
||||
#sigpipe_old = signal.getsignal(signal.SIGPIPE)
|
||||
#signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
|
||||
cmd = self.strip_newlines(cmd)
|
||||
if not self.dry_run:
|
||||
# https://stackoverflow.com/questions/15535240/python-popen-write-to-stdout-and-log-file-simultaneously/52090802#52090802
|
||||
with subprocess.Popen(cmd, stdout=stdout, stderr=stderr, env=env, **kwargs) as proc:
|
||||
if out_file is not None:
|
||||
os.makedirs(os.path.split(os.path.abspath(out_file))[0], exist_ok=True)
|
||||
with open(out_file, 'bw') as logfile:
|
||||
while True:
|
||||
byte = proc.stdout.read(1)
|
||||
if byte:
|
||||
if show_stdout:
|
||||
sys.stdout.buffer.write(byte)
|
||||
try:
|
||||
sys.stdout.flush()
|
||||
except BlockingIOError:
|
||||
# TODO understand. Why, Python, why.
|
||||
pass
|
||||
logfile.write(byte)
|
||||
else:
|
||||
break
|
||||
signal.signal(signal.SIGINT, sigint_old)
|
||||
#signal.signal(signal.SIGPIPE, sigpipe_old)
|
||||
returncode = proc.returncode
|
||||
if returncode != 0 and raise_on_failure:
|
||||
raise Exception('Command exited with status: {}'.format(returncode))
|
||||
return returncode
|
||||
else:
|
||||
return 0
|
||||
|
||||
def shlex_split(self, string):
|
||||
'''
|
||||
shlex_split, but also add Newline after every word.
|
||||
|
||||
Not perfect since it does not group arguments, but I don't see a solution.
|
||||
'''
|
||||
return self.add_newlines(shlex.split(string))
|
||||
|
||||
def strip_newlines(self, cmd):
|
||||
return [x for x in cmd if x != LF]
|
||||
|
||||
def rmrf(self, path):
|
||||
self.print_cmd(['rm', '-r', '-f', path, LF])
|
||||
if not self.dry_run and os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
def write_configs(self, config_path, configs, config_fragments=None):
|
||||
'''
|
||||
Write extra KEY=val configs into the given config file.
|
||||
'''
|
||||
if config_fragments is None:
|
||||
config_fragments = []
|
||||
with open(config_path, 'a') as config_file:
|
||||
for config_fragment in config_fragments:
|
||||
with open(config_fragment, 'r') as config_fragment_file:
|
||||
self.print_cmd(['cat', config_fragment, '>>', config_path])
|
||||
if not self.dry_run:
|
||||
for line in config_fragment_file:
|
||||
config_file.write(line)
|
||||
self.write_string_to_file(config_path, '\n'.join(configs), mode='a')
|
||||
|
||||
def write_string_to_file(self, path, string, mode='w'):
|
||||
if mode == 'a':
|
||||
redirect = '>>'
|
||||
else:
|
||||
redirect = '>'
|
||||
self.print_cmd("cat << 'EOF' {} {}\n{}\nEOF".format(redirect, path, string))
|
||||
if not self.dry_run:
|
||||
with open(path, 'a') as f:
|
||||
f.write(string)
|
||||
16
trace-boot
16
trace-boot
@@ -6,10 +6,10 @@ import subprocess
|
||||
import re
|
||||
|
||||
import common
|
||||
run = imp.load_source('run', os.path.join(common.root_dir, 'run'))
|
||||
qemu_trace2txt = imp.load_source('qemu_trace2txt', os.path.join(common.root_dir, 'qemu-trace2txt'))
|
||||
run = imp.load_source('run', os.path.join(kwargs['root_dir'], 'run'))
|
||||
qemu_trace2txt = imp.load_source('qemu_trace2txt', os.path.join(kwargs['root_dir'], 'qemu-trace2txt'))
|
||||
|
||||
parser = common.get_argparse(argparse_args={
|
||||
parser = self.get_argparse(argparse_args={
|
||||
'description': '''Trace the PIC addresses executed on a Linux kernel boot.
|
||||
|
||||
More information at: https://github.com/cirosantilli/linux-kernel-module-cheat#tracing
|
||||
@@ -19,11 +19,11 @@ parser.add_argument(
|
||||
'extra_emulator_args', nargs='*',
|
||||
help='Extra options to append at the end of the emulator command line'
|
||||
)
|
||||
args = common.setup(parser)
|
||||
args = self.setup(parser)
|
||||
extra_args = {
|
||||
'extra_emulator_args': args.extra_emulator_args,
|
||||
'extra_emulator_args': kwargs['extra_emulator_args'],
|
||||
}
|
||||
if common.emulator == 'gem5':
|
||||
if kwargs['emulator'] == 'gem5':
|
||||
extra_args.update({
|
||||
'eval': 'm5 exit',
|
||||
'trace': 'Exec,-ExecSymbol,-ExecMicro',
|
||||
@@ -39,10 +39,10 @@ else:
|
||||
# Instruction count.
|
||||
# We could put this on a separate script, but it just adds more arch boilerplate to a new script.
|
||||
# So let's just leave it here for now since it did not add a significant processing time.
|
||||
kernel_entry_addr = hex(common.get_elf_entry(common.vmlinux))
|
||||
kernel_entry_addr = hex(self.get_elf_entry(kwargs['vmlinux']))
|
||||
nlines = 0
|
||||
nlines_firmware = 0
|
||||
with open(common.qemu_trace_txt_file, 'r') as trace_file:
|
||||
with open(kwargs['qemu_trace_txt_file'], 'r') as trace_file:
|
||||
in_firmware = True
|
||||
for line in trace_file:
|
||||
line = line.rstrip()
|
||||
|
||||
29
trace2line
29
trace2line
@@ -13,23 +13,24 @@ import subprocess
|
||||
import sys
|
||||
|
||||
import common
|
||||
from shell_helpers import LF
|
||||
|
||||
parser = common.get_argparse(argparse_args={
|
||||
parser = self.get_argparse(argparse_args={
|
||||
'description': 'Convert an execution trace containing PC values into the Linux kernel linex executed'
|
||||
})
|
||||
args = common.setup(parser)
|
||||
args = self.setup(parser)
|
||||
sys.exit(subprocess.Popen([
|
||||
os.path.join(common.root_dir, 'trace2line.sh'),
|
||||
'true' if common.emulator == 'gem5' else 'false',
|
||||
common.trace_txt_file,
|
||||
common.get_toolchain_tool('addr2line'),
|
||||
common.vmlinux,
|
||||
common.run_dir,
|
||||
os.path.join(kwargs['root_dir'], 'trace2line.sh'),
|
||||
'true' if kwargs['emulator'] == 'gem5' else 'false',
|
||||
kwargs['trace_txt_file'],
|
||||
self.get_toolchain_tool('addr2line'),
|
||||
kwargs['vmlinux'],
|
||||
kwargs['run_dir'],
|
||||
]).wait())
|
||||
|
||||
# This was the full conversion attempt.
|
||||
|
||||
# if common.emulator == 'gem5':
|
||||
# if kwargs['emulator'] == 'gem5':
|
||||
# def get_pc(line):
|
||||
# # TODO
|
||||
# # stdin = sed -r 's/^.* (0x[^. ]*)[. ].*/\1/' "$common_trace_txt_file")
|
||||
@@ -40,17 +41,17 @@ sys.exit(subprocess.Popen([
|
||||
# with \
|
||||
# subprocess.Popen(
|
||||
# [
|
||||
# common.get_toolchain_tool('addr2line'),
|
||||
# self.get_toolchain_tool('addr2line'),
|
||||
# '-e',
|
||||
# common.vmlinux,
|
||||
# kwargs['vmlinux'],
|
||||
# '-f',
|
||||
# '-p',
|
||||
# ],
|
||||
# stdout=subprocess.PIPE,
|
||||
# stdin=subprocess.PIPE,
|
||||
# ) as proc, \
|
||||
# open(common.trace_txt_file, 'r') as infile, \
|
||||
# open(os.path.join(common.run_dir, 'trace-lines.txt'), 'w') as outfile \
|
||||
# open(kwargs['trace_txt_file'], 'r') as infile, \
|
||||
# open(os.path.join(kwargs['run_dir'], 'trace-lines.txt'), 'w') as outfile \
|
||||
# :
|
||||
# for in_line in infile:
|
||||
# proc.stdin.write(get_pc(in_line).encode())
|
||||
@@ -58,5 +59,5 @@ sys.exit(subprocess.Popen([
|
||||
# stdout = proc.stdout.read()
|
||||
# outfile.write(stdout.decode())
|
||||
# # TODO
|
||||
# # sed -E "s|at ${common.linux_build_dir}/(\./\|)||"
|
||||
# # sed -E "s|at ${kwargs['linux_build_dir']}/(\./\|)||"
|
||||
# # uniq -c
|
||||
|
||||
@@ -46,7 +46,9 @@ OUTS := $(addprefix $(OUT_DIR)/,$(OUTS))
|
||||
|
||||
all: mkdir $(OUTS)
|
||||
for subdir in $(SUBDIRS); do \
|
||||
$(MAKE) -C $${subdir} OUT_DIR="$(OUT_DIR)/$$subdir"; \
|
||||
if [ -d "$${subdir}" ]; then \
|
||||
$(MAKE) -C "$${subdir}" OUT_DIR="$(OUT_DIR)/$$subdir"; \
|
||||
fi \
|
||||
done
|
||||
|
||||
$(COMMON_OBJ): $(COMMON_DIR)/$(COMMON_BASENAME)$(IN_EXT_C)
|
||||
@@ -64,7 +66,9 @@ $(OUT_DIR)/%$(OUT_EXT): %$(IN_EXT_CXX) $(COMMON_OBJ)
|
||||
clean:
|
||||
rm -f *'$(OBJ_EXT)' *'$(OUT_EXT)'
|
||||
for subdir in $(SUBDIRS); do \
|
||||
$(MAKE) -C $${subdir} clean; \
|
||||
if [ -d "$${subdir}" ]; then \
|
||||
$(MAKE) -C $${subdir} clean; \
|
||||
fi \
|
||||
done
|
||||
|
||||
mkdir:
|
||||
|
||||
Reference in New Issue
Block a user