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" )
41
46
42
47
def remove_prefix (string : str , prefix : str ):
43
48
if string .startswith (prefix ):
44
49
return string [len (prefix ):]
45
50
else :
46
51
return string [:]
47
52
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
-
131
53
def run_command (args , ** kwargs ):
132
54
output = subprocess .run (args , ** kwargs )
133
55
@@ -149,7 +71,8 @@ def download_bundled():
149
71
'--depth' , '1' , LIMINE_URL , limine_path ])
150
72
151
73
if not os .path .exists (SYSROOT_DIR ):
152
- download_userland_host_rust ()
74
+ log_info ("building minimal sysroot" )
75
+ build_userland_sysroot (True )
153
76
154
77
155
78
def extract_artifacts (stdout ):
@@ -209,7 +132,7 @@ def symlink_rel(src, dst):
209
132
os .symlink (rel_path_src , dst )
210
133
211
134
212
- def build_userland_sysroot (args ):
135
+ def build_userland_sysroot (minimal ):
213
136
if not os .path .exists (SYSROOT_DIR ):
214
137
os .mkdir (SYSROOT_DIR )
215
138
@@ -238,31 +161,28 @@ def build_userland_sysroot(args):
238
161
if not os .path .islink (blink ):
239
162
# symlink the bootstrap.yml file in the src root to sysroot/bootstrap.link
240
163
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' ])
241
175
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
- }
260
176
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" )
263
181
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 []
264
185
265
- def build_userland (args ):
266
186
HOST_CARGO = "host-cargo/bin/cargo"
267
187
HOST_RUST = "host-rust/bin/rustc"
268
188
HOST_GCC = "host-gcc/bin/x86_64-aero-gcc"
@@ -345,45 +265,11 @@ def prepare_iso(args, kernel_bin, user_bins):
345
265
shutil .copy (os .path .join (limine_path , 'BOOTX64.EFI' ), efi_boot )
346
266
347
267
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
-
360
268
for file in user_bins :
361
269
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 ))
387
273
388
274
with open (os .path .join (iso_root , 'limine.cfg' ), 'w' ) as limine_cfg :
389
275
limine_cfg .write (LIMINE_TEMPLATE )
@@ -421,6 +307,13 @@ def find(path) -> List[str]:
421
307
422
308
return None
423
309
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
+
424
317
return iso_path
425
318
426
319
@@ -431,7 +324,10 @@ def run_in_emulator(build_info: BuildInfo, iso_path):
431
324
qemu_args = ['-cdrom' , iso_path ,
432
325
'-m' , args .memory ,
433
326
'-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' ]
435
331
436
332
if args .bios == 'uefi' :
437
333
qemu_args += ['-bios' ,
@@ -548,6 +444,7 @@ def is_kvm_supported() -> bool:
548
444
549
445
550
446
def main ():
447
+ t0 = time .time ()
551
448
args = parse_args ()
552
449
553
450
# arch-aero_os
@@ -565,10 +462,6 @@ def main():
565
462
566
463
if not os .path .exists (iso_path ):
567
464
user_bins = build_userland (args )
568
-
569
- if not user_bins :
570
- return
571
-
572
465
kernel_bin = build_kernel (args )
573
466
574
467
if not kernel_bin or args .check :
@@ -587,17 +480,13 @@ def main():
587
480
if os .path .exists (userland_target ):
588
481
shutil .rmtree (userland_target )
589
482
elif args .sysroot :
590
- build_userland_sysroot (args )
483
+ build_userland_sysroot (False )
591
484
elif args .document :
592
485
build_kernel (args )
593
486
594
487
generate_docs (args )
595
488
else :
596
489
user_bins = build_userland (args )
597
-
598
- if not user_bins :
599
- return
600
-
601
490
kernel_bin = build_kernel (args )
602
491
603
492
if not kernel_bin or args .check :
@@ -606,6 +495,8 @@ def main():
606
495
kernel_bin = kernel_bin [0 ]
607
496
iso_path = prepare_iso (args , kernel_bin , user_bins )
608
497
498
+ t1 = time .time ()
499
+ log_info (f"build completed in { t1 - t0 :.2f} seconds" )
609
500
if not args .no_run :
610
501
run_in_emulator (build_info , iso_path )
611
502
0 commit comments