Last active
April 24, 2023 14:18
-
-
Save elibroftw/36d18406d6be775e2c52af9d19571c42 to your computer and use it in GitHub Desktop.
A CLI to increase productivity for the CS 350 Operating Systems course. This should sit two levels above the os161-X.Y directory. Script is robust enough to work with the naming convention recommended by the course as well as my own.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import argparse | |
import subprocess | |
from subprocess import DEVNULL | |
import os | |
import glob | |
from pathlib import Path | |
import time | |
import shutil | |
import sys | |
# https://student.cs.uwaterloo.ca/~cs350/common/WorkingWith161.html | |
script_root = Path(os.path.dirname(__file__)).absolute() | |
try: | |
os161_dir = Path(glob.glob(glob.escape(script_root) + '/*os161*')[0]).absolute() | |
except IndexError: | |
print('ERROR: could not find a os161 project directory') | |
sys.exit(1) | |
try: | |
os161_src = Path(glob.glob(glob.escape(os161_dir) + '/os161*')[0]).absolute() | |
except IndexError: | |
print('ERROR: could not find a os161 source code directory') | |
sys.exit(2) | |
userspace_dir = script_root / 'userspace' | |
if not userspace_dir.exists(): | |
userspace_dir_bak = script_root / 'cs350-student' | |
if not userspace_dir_bak.exists(): | |
print('Could not find a userspace directory such as /userspace or /cs350-student') | |
sys.exit(3) | |
userspace_dir = userspace_dir_bak | |
parser = argparse.ArgumentParser() | |
subparsers = parser.add_subparsers(title='subcommands', description='valid subcommands', help='additional help', dest='subcommand') | |
submit_parser = subparsers.add_parser('submit', help='submit code for an assignment') | |
submit_parser.add_argument('num', help='assignment number to submit') | |
grade_parser = subparsers.add_parser('grade', help='get grade for an assignment') | |
grade_parser.add_argument('num', help='assignment number to fetch grade for') | |
new_parser = subparsers.add_parser('new', help='configure new assignment') | |
new_parser.add_argument('num', help='assignment number to configure kernel for', type=int) | |
build_parser = subparsers.add_parser('build', help='build assignment') | |
build_parser.add_argument('num', help='assignment to build and install', type=int) | |
configure_parser = subparsers.add_parser('configure', help='configure the OS on a new machine') | |
user_parser = subparsers.add_parser('user', help='build user-level programs') | |
run_parser = subparsers.add_parser('run', help='run the simulator for the default kernel or provided kernel') | |
run_parser.add_argument('num', help='assignment number for kernel to simulate', default=-1, type=int, nargs='?') | |
run_parser.add_argument('--command', '-c', help='command to run immediately on the kernel', default='') | |
debug_parser = subparsers.add_parser('debug', help="subcommands for debugging OS/161 (hopefully you won't need to use this)") | |
debug_subparsers = debug_parser.add_subparsers(title='debug subcommands', help='start and then attach', dest='debug_subcommand') | |
debug_start_parser = debug_subparsers.add_parser('start', help="1. start OS/161 default kernel in debug mode") | |
debug_start_parser.add_argument('num', help='assignment number for kernel to debug', default=-1, type=int, nargs='?') | |
debug_attach_parser = debug_subparsers.add_parser('attach', help="2. attach [break_point lines] gdb to running OS/161") | |
debug_attach_parser.add_argument('num', help='assignment number for kernel to debug', type=int) | |
debug_attach_parser.add_argument('--breakpoints', nargs='*', default=[], help='breakpoints to set in gdb (space seperated)') | |
args = parser.parse_args() | |
if args.subcommand == 'submit': | |
p_args = ['cs350_submit', os161_src, f'ASST{args.num}'] | |
print(' '.join((f'"{arg}' if ' ' in str(arg) else str(arg) for arg in p_args))) | |
p = subprocess.Popen(p_args, stdin=DEVNULL) | |
p.wait() | |
try: | |
assignment_dir = (glob.glob(glob.escape(userspace_dir) + f'/a*{args.num}') + glob.glob(glob.escape(userspace_dir) + f'/A*{args.num}'))[0] | |
except IndexError: | |
print(f'ERROR: could not find assignment directory for assignment {args.num} matching A*{args.num} or a*{args.num}') | |
sys.exit(4) | |
p_args = ['cs350_submit', assignment_dir, f'ASSTUSER{args.num}'] | |
print(' '.join((f'"{arg}' if ' ' in str(arg) else str(arg) for arg in p_args))) | |
p = subprocess.Popen(p_args, stdin=DEVNULL) | |
p.wait() | |
elif args.subcommand == 'new': | |
os.makedirs(script_root / 'userspace' / f'ASSTUSER{args.num}', exist_ok=True) | |
os.chdir(os161_src / 'kern' / 'conf') | |
p = subprocess.Popen(['./config', f'ASST{args.num}'], stdin=DEVNULL) | |
p.wait() | |
print(f'run subcommand `build {args.num}`') | |
elif args.subcommand == 'build': | |
os.chdir(os161_src / 'kern' / 'compile' / f'ASST{args.num}') | |
for command in ('bmake depend', 'bmake', 'bmake install'): | |
p = subprocess.Popen(command.split(), stdin=DEVNULL) | |
exit_code = p.wait() | |
if exit_code != 0: | |
sys.exit(exit_code) | |
print('use the `run` subcommand to run OS/161') | |
elif args.subcommand == 'user': | |
os.chdir(os161_src) | |
for command in ('bmake', 'bmake install'): | |
p = subprocess.Popen(command.split(), stdin=DEVNULL) | |
exit_code = p.wait() | |
if exit_code != 0: | |
sys.exit(exit_code) | |
elif args.subcommand == 'run': | |
kernel = 'kernel' | |
if args.num > -1: | |
kernel = f'kernel-ASST{args.num}' | |
os.chdir(os161_src / 'kern' / 'compile' / f'ASST{args.num}') | |
p = subprocess.Popen(['bmake', 'install'], stdin=DEVNULL) | |
p.wait() | |
os.chdir(os161_src.parent / 'root') | |
if args.command == 'testa2': | |
args.command = 'sy3;sy2;uw1;q' | |
p = subprocess.Popen(['sys161', kernel, args.command]) | |
p.wait() | |
elif args.subcommand == 'debug': | |
os.chdir(os161_dir / 'root') | |
if args.debug_subcommand == 'start': | |
kernel = 'kernel' if args.num < 0 else f'kernel-ASST{args.num}' | |
p = subprocess.Popen(['sys161', '-w', kernel]) | |
p.wait() | |
print('use the `debug attach` subcommand in a second terminal') | |
elif args.debug_subcommand == 'attach': | |
kernel = f'kernel-ASST{args.num}' | |
with open(os161_dir / 'root' / '.gdbinit', 'w', encoding='utf-8') as f: | |
f.write(f'dir ../{os161_src.name}/kern/compile/ASST{args.num}\n') | |
f.write('target remote unix:.sockets/gdb\n') | |
for breakpoint in args.breakpoints: | |
f.write(f'break {breakpoint}\n') | |
# f.write('c\n') | |
p = subprocess.Popen(['cs350-gdb', kernel]) | |
p.wait() | |
else: | |
debug_parser.print_help() | |
debug_start_parser.print_usage() | |
debug_attach_parser.print_usage() | |
elif args.subcommand == 'configure': | |
os.chdir(os161_src) | |
p = subprocess.Popen(['./configure', f'--ostree={os161_dir}/root', '--toolprefix=cs350-'], stdin=DEVNULL) | |
p.wait() | |
print('use subcommand new to create new assignments') | |
os.makedirs(os161_dir / 'root', exist_ok=True) | |
shutil.copyfile('/u/cs350/sys161/sys161.conf', os161_dir / 'root' / 'sys161.conf') | |
elif args.subcommand == 'grade': | |
p_args = ['cs350-grade', f'ASST{args.num}'] | |
print(' '.join((f'"{arg}' if ' ' in str(arg) else str(arg) for arg in p_args))) | |
p = subprocess.Popen(p_args, stdin=DEVNULL) | |
p.wait() | |
p_args = ['cs350-grade', f'ASSTUSER{args.num}'] | |
print(' '.join((f'"{arg}' if ' ' in str(arg) else str(arg) for arg in p_args))) | |
p = subprocess.Popen(p_args, stdin=DEVNULL) | |
p.wait() | |
else: | |
parser.print_help() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
File hierarchy
setup new machine
./cs350_cli.py configure
Calls configure script and copies over the sys161.conf file. You should duplicate the cpu and memory line in the config and set CPUs to 4 and virtual memory to 4MB (commented out). Use this line when testing beefier programs.
configure new assignment
./cs350_cli.py new 1
build and run the kernel for an assignment
./cs350_cli.py build 1
./cs350_cli.py run [uint]
gdb debug requires running in two different terminals
./cs350_cli.py debug 2
./cs350_cli.py debug attach 2 --breakpoints file.c:30 file2.c:50
submit and see grade later
./cs350_cli.py submit 0
internally calls:
cs350_submit os161/os161-1.99/kern/compile/ASST0 ASST0
cs350_submit userspace/ASSTUSER0 ASSTUSER0
./cs350_cli.py grade 0