Skip to content

Commit 8512665

Browse files
committed
feat(build): frontend structure
1 parent 2af6af4 commit 8512665

File tree

7 files changed

+187
-261
lines changed

7 files changed

+187
-261
lines changed

aero-ref.py

Lines changed: 84 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,55 @@
1-
def download_userland_host_rust():
2-
out_file = os.path.join(BUNDLED_DIR, "host-rust-prebuilt.tar.gz")
3-
4-
# we have already cloned the toolchain
5-
if os.path.exists(out_file):
6-
return
7-
8-
log_info("downloading prebuilt userland host rust toolchain")
9-
10-
cmd = r"""
11-
wget --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=$(wget --quiet --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate "https://docs.google.com/uc?export=download&id=FILE_HASH" -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=FILE_HASH" -O OUTPUT_FILE && rm -rf /tmp/cookies.txt
12-
""".replace("FILE_HASH", "1TTC9qa1z-KdLaQkhgMCYxLE5nuKg4gcx").replace("OUTPUT_FILE", out_file)
13-
14-
subprocess.run(cmd, shell=True)
15-
16-
log_info("extracting prebuilt userland host rust toolchain")
17-
18-
# the toolchain is compressed, so we need to extract it
19-
file = tarfile.open(out_file)
20-
file.extractall(os.path.join(BUNDLED_DIR, "host-rust-prebuilt"))
21-
file.close()
22-
23-
24-
def get_userland_tool():
25-
toolchain = os.path.join(SYSROOT_DIR, "tools")
26-
27-
if os.path.exists(toolchain):
28-
return toolchain
29-
30-
return os.path.join(BUNDLED_DIR, "host-rust-prebuilt/aero")
31-
32-
33-
def get_userland_package():
34-
toolchain = os.path.join(SYSROOT_DIR, "packages")
35-
36-
if os.path.exists(toolchain):
37-
return toolchain
38-
39-
return os.path.join(BUNDLED_DIR, "host-rust-prebuilt/aero")
40-
1+
# Slowly deleting code from this file as things are implemented
2+
# in Rust—the goal is to see what's left to write
3+
4+
#!/usr/bin/env python3
5+
6+
# Copyright (C) 2021-2022 The Aero Project Developers.
7+
#
8+
# This file is part of The Aero Project.
9+
#
10+
# Aero is free software: you can redistribute it and/or modify
11+
# it under the terms of the GNU General Public License as published by
12+
# the Free Software Foundation, either version 3 of the License, or
13+
# (at your option) any later version.
14+
#
15+
# Aero is distributed in the hope that it will be useful,
16+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
# GNU General Public License for more details.
19+
#
20+
# You should have received a copy of the GNU General Public License
21+
# along with Aero. If not, see <https://www.gnu.org/licenses/>.
22+
23+
import argparse
24+
import json
25+
import os
26+
import platform
27+
import shutil
28+
import subprocess
29+
import sys
30+
import tarfile
31+
import time
32+
33+
from typing import List
34+
35+
class BuildInfo:
36+
args: argparse.Namespace
37+
target_arch: str
38+
39+
def __init__(self, target_arch: str, args: argparse.Namespace):
40+
self.target_arch = target_arch
41+
self.args = args
42+
43+
44+
def get_userland_tool(): return os.path.join(SYSROOT_DIR, "tools")
45+
def get_userland_package(): return os.path.join(SYSROOT_DIR, "packages")
4146

4247
def remove_prefix(string: str, prefix: str):
4348
if string.startswith(prefix):
4449
return string[len(prefix):]
4550
else:
4651
return string[:]
4752

48-
49-
def parse_args():
50-
parser = argparse.ArgumentParser(
51-
description="utility used to build aero kernel and userland")
52-
53-
check_test = parser.add_mutually_exclusive_group()
54-
55-
check_test.add_argument('--clean',
56-
default=False,
57-
action='store_true',
58-
help='removes the build artifacts')
59-
60-
check_test.add_argument('--check',
61-
default=False,
62-
action='store_true',
63-
help='checks if aero builds correctly without packaging and running it')
64-
65-
check_test.add_argument('--test',
66-
default=False,
67-
action='store_true',
68-
help='runs the aero test suite')
69-
70-
check_test.add_argument('--document',
71-
default=False,
72-
action='store_true',
73-
help='generates the documentation for the aero kernel')
74-
75-
parser.add_argument('--debug',
76-
default=False,
77-
action='store_true',
78-
help='builds the kernel and userland in debug mode')
79-
80-
parser.add_argument('--no-run',
81-
default=False,
82-
action='store_true',
83-
help='doesn\'t run the built image in emulator when applicable')
84-
85-
parser.add_argument('--only-run',
86-
default=False,
87-
action='store_true',
88-
help='runs aero without rebuilding. ignores any build-related flags')
89-
90-
parser.add_argument('--bios',
91-
type=str,
92-
default='legacy',
93-
choices=['legacy', 'uefi'],
94-
help='run aero using the selected BIOS')
95-
96-
parser.add_argument('--features',
97-
type=lambda x: x.split(','),
98-
default=[],
99-
help='additional features to build the kernel with')
100-
101-
parser.add_argument('--target',
102-
default='x86_64-aero_os',
103-
help='override the target triple the kernel will be built for')
104-
105-
parser.add_argument('--la57',
106-
default=False,
107-
action='store_true',
108-
help='run emulator with 5 level paging support')
109-
110-
parser.add_argument('--sysroot',
111-
default=False,
112-
action='store_true',
113-
help='build the full userland sysroot. If disabled, then the sysroot will only contain the aero_shell and the init binaries')
114-
115-
parser.add_argument('--disable-kvm',
116-
default=False,
117-
action='store_true',
118-
help='disable KVM acceleration even if its available')
119-
120-
parser.add_argument('remaining',
121-
nargs=argparse.REMAINDER,
122-
help='additional arguments to pass as the emulator')
123-
124-
parser.add_argument('--memory',
125-
default='9800M',
126-
help='amount of memory to allocate to QEMU')
127-
128-
return parser.parse_args()
129-
130-
13153
def run_command(args, **kwargs):
13254
output = subprocess.run(args, **kwargs)
13355

@@ -149,7 +71,8 @@ def download_bundled():
14971
'--depth', '1', LIMINE_URL, limine_path])
15072

15173
if not os.path.exists(SYSROOT_DIR):
152-
download_userland_host_rust()
74+
log_info("building minimal sysroot")
75+
build_userland_sysroot(True)
15376

15477

15578
def extract_artifacts(stdout):
@@ -209,7 +132,7 @@ def symlink_rel(src, dst):
209132
os.symlink(rel_path_src, dst)
210133

211134

212-
def build_userland_sysroot(args):
135+
def build_userland_sysroot(minimal):
213136
if not os.path.exists(SYSROOT_DIR):
214137
os.mkdir(SYSROOT_DIR)
215138

@@ -238,31 +161,28 @@ def build_userland_sysroot(args):
238161
if not os.path.islink(blink):
239162
# symlink the bootstrap.yml file in the src root to sysroot/bootstrap.link
240163
symlink_rel('bootstrap.yml', blink)
164+
165+
def run_xbstrap(args):
166+
try:
167+
run_command(['xbstrap', *args], cwd=SYSROOT_DIR)
168+
except FileNotFoundError:
169+
run_command([f'{os.environ["HOME"]}/.local/bin/xbstrap', *args], cwd=SYSROOT_DIR)
170+
171+
if minimal:
172+
run_xbstrap(['install', '-u', 'bash', 'coreutils'])
173+
else:
174+
run_xbstrap(['install', '-u', '--all'])
241175

242-
os.chdir(SYSROOT_DIR)
243-
244-
args = {
245-
"update": True,
246-
"all": True,
247-
"dry_run": False,
248-
"check": False,
249-
"recursive": False,
250-
"paranoid": False,
251-
"reset": False,
252-
"hard_reset": False,
253-
"only_wanted": False,
254-
"keep_going": False,
255-
256-
"progress_file": None, # file that receives machine-ready progress notifications
257-
"reconfigure": False,
258-
"rebuild": False
259-
}
260176

261-
namespace = argparse.Namespace(**args)
262-
xbstrap.do_install(namespace)
177+
def build_userland(args):
178+
# We need to check if we have host-cargo in-order for us to build
179+
# our rust userland applications in `userland/`.
180+
host_cargo = os.path.join(SYSROOT_DIR, "tools/host-cargo")
263181

182+
if not os.path.exists(host_cargo):
183+
log_error("host-cargo not built as a part of the sysroot, skipping compilation of `userland/`")
184+
return []
264185

265-
def build_userland(args):
266186
HOST_CARGO = "host-cargo/bin/cargo"
267187
HOST_RUST = "host-rust/bin/rustc"
268188
HOST_GCC = "host-gcc/bin/x86_64-aero-gcc"
@@ -345,45 +265,11 @@ def prepare_iso(args, kernel_bin, user_bins):
345265
shutil.copy(os.path.join(limine_path, 'BOOTX64.EFI'), efi_boot)
346266

347267
sysroot_dir = os.path.join(SYSROOT_DIR, 'system-root')
348-
shutil.copytree(BASE_FILES_DIR, sysroot_dir, dirs_exist_ok=True)
349-
350-
# dynamic linker (ld.so)
351-
mlibc = os.path.join(get_userland_package(), "mlibc")
352-
# gcc libraries required for rust programs
353-
gcc = os.path.join(get_userland_package(), "gcc")
354-
355-
# FIXME
356-
if "host-rust-prebuilt" in str(mlibc):
357-
shutil.copytree(mlibc, sysroot_dir, dirs_exist_ok=True)
358-
shutil.copytree(gcc, sysroot_dir, dirs_exist_ok=True)
359-
360268
for file in user_bins:
361269
bin_name = os.path.basename(file)
362-
shutil.copy(file, os.path.join(sysroot_dir, "usr", "bin", bin_name))
363-
364-
def find(path) -> List[str]:
365-
_, find_output, _ = run_command(['find', '.', '-type', 'f'],
366-
cwd=path,
367-
stdout=subprocess.PIPE)
368-
369-
files_without_dot = filter(
370-
lambda x: x != '.', find_output.decode('utf-8').splitlines())
371-
files_without_prefix = map(
372-
lambda x: remove_prefix(x, './'), files_without_dot)
373-
files = list(files_without_prefix)
374-
375-
files.append("usr/lib/libiconv.so.2")
376-
return files
377-
378-
files = find(sysroot_dir)
379-
380-
with open(os.path.join(iso_root, 'initramfs.cpio'), 'wb') as initramfs:
381-
cpio_input = '\n'.join(files)
382-
code, _, _ = run_command(['cpio', '-o', '-v'],
383-
cwd=sysroot_dir,
384-
stdout=initramfs,
385-
stderr=subprocess.PIPE,
386-
input=cpio_input.encode('utf-8'))
270+
dest_dir = os.path.join(sysroot_dir, "usr", "bin")
271+
os.makedirs(dest_dir, exist_ok=True)
272+
shutil.copy(file, os.path.join(dest_dir, bin_name))
387273

388274
with open(os.path.join(iso_root, 'limine.cfg'), 'w') as limine_cfg:
389275
limine_cfg.write(LIMINE_TEMPLATE)
@@ -421,6 +307,13 @@ def find(path) -> List[str]:
421307

422308
return None
423309

310+
# create the disk image
311+
disk_path = os.path.join(BUILD_DIR, 'disk.img')
312+
313+
if not os.path.exists(disk_path):
314+
log_info('creating disk image')
315+
os.system('bash ./tools/mkimage.sh')
316+
424317
return iso_path
425318

426319

@@ -431,7 +324,10 @@ def run_in_emulator(build_info: BuildInfo, iso_path):
431324
qemu_args = ['-cdrom', iso_path,
432325
'-m', args.memory,
433326
'-smp', '1',
434-
'-serial', 'stdio']
327+
'-serial', 'stdio',
328+
'-drive', 'file=build/disk.img,if=none,id=NVME1,format=raw', '-device', 'nvme,drive=NVME1,serial=nvme',
329+
# Specify the boot order (where `d` is the first CD-ROM drive)
330+
'--boot', 'd']
435331

436332
if args.bios == 'uefi':
437333
qemu_args += ['-bios',
@@ -548,6 +444,7 @@ def is_kvm_supported() -> bool:
548444

549445

550446
def main():
447+
t0 = time.time()
551448
args = parse_args()
552449

553450
# arch-aero_os
@@ -565,10 +462,6 @@ def main():
565462

566463
if not os.path.exists(iso_path):
567464
user_bins = build_userland(args)
568-
569-
if not user_bins:
570-
return
571-
572465
kernel_bin = build_kernel(args)
573466

574467
if not kernel_bin or args.check:
@@ -587,17 +480,13 @@ def main():
587480
if os.path.exists(userland_target):
588481
shutil.rmtree(userland_target)
589482
elif args.sysroot:
590-
build_userland_sysroot(args)
483+
build_userland_sysroot(False)
591484
elif args.document:
592485
build_kernel(args)
593486

594487
generate_docs(args)
595488
else:
596489
user_bins = build_userland(args)
597-
598-
if not user_bins:
599-
return
600-
601490
kernel_bin = build_kernel(args)
602491

603492
if not kernel_bin or args.check:
@@ -606,6 +495,8 @@ def main():
606495
kernel_bin = kernel_bin[0]
607496
iso_path = prepare_iso(args, kernel_bin, user_bins)
608497

498+
t1 = time.time()
499+
log_info(f"build completed in {t1 - t0:.2f} seconds")
609500
if not args.no_run:
610501
run_in_emulator(build_info, iso_path)
611502

0 commit comments

Comments
 (0)