aboutsummaryrefslogtreecommitdiff
path: root/run
blob: 8d647ebe79b395ab2ecee2e438b8e80eeea0bfd3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
#!/usr/bin/env sh
# MIT License, Copyright (c) 2020 Marvin Borner

set -e

cd "$(dirname "$0")"

MAKE=make
NPROC=nproc
SUDO=sudo
TAGS=ctags
if [ "$(uname -s)" = "OpenBSD" ]; then
    NPROC="sysctl -n hw.ncpuonline"
    SUDO="doas"
    TAGS="ectags"
    export MAKE=gmake
    export CC="egcc"
    export CXX="eg++"
    export LDFLAGS=-Wl,-z,notext
fi

mode="${1}"
no_ask="${2}"

# TODO: Support q35 chipset ('-machine q35')
# TODO: Support -enable-kvm: GPF?!
qemu_with_flags() {
    network="rtl8139"
    qemu-system-i386 -d guest_errors,unimp,pcall -cpu max -no-reboot -vga std -rtc base=localtime -m 256M -netdev user,id=net0,hostfwd=tcp:127.0.0.1:8000-10.0.2.15:8000 -device $network,netdev=net0 -object filter-dump,id=dump,netdev=net0,file=dump.pcap "$@"
}

make_cross() {
    if [ ! -d "./cross/" ]; then
        if [ "$no_ask" != "-y" ]; then
            echo -n "Do you want to compile a cross compiler (this can take up to 20 minutes)? [yn] "
            read -r answer
            if ! [ "$answer" != "${answer#[Yy]}" ]; then
                echo "The compilation of melvix requires a cross compiler!"
                exit 1
            fi
        fi

        # Create directory
        mkdir -p cross
        cd cross
        DIR=$(pwd)

        # Get sources
        mkdir "${DIR}/src" && cd "${DIR}/src"
        echo "Downloading..."
        curl "https://ftp.gnu.org/gnu/binutils/binutils-2.36.1.tar.gz" >binutils.tar.gz
        tar xzf binutils.tar.gz
        curl "https://ftp.gnu.org/gnu/gcc/gcc-11.1.0/gcc-11.1.0.tar.gz" >gcc.tar.gz
        tar xzf gcc.tar.gz

        # Prepare compiling
        mkdir -p "${DIR}/opt/bin"
        export CFLAGS="-g0 -O2"
        export PREFIX="${DIR}/opt"
        export TARGET=i686-elf
        export PATH="$PREFIX/bin:$PATH"

        if [ "$(uname -s)" = "OpenBSD" ]; then
            export with_gmp=/usr/local
            sed -i 's/-no-pie/-nopie/g' "${DIR}/src/gcc-11.1.0/gcc/configure"
        fi

        # Compile binutils
        mkdir "${DIR}/src/build-binutils" && cd "${DIR}/src/build-binutils"
        ../binutils-2.36.1/configure --target="$TARGET" --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror
        $MAKE -j $($NPROC)
        $MAKE install

        # Compile GCC
        mkdir "${DIR}/src/build-gcc" && cd "${DIR}/src/build-gcc"
        ../gcc-11.1.0/configure --target="$TARGET" --prefix="$PREFIX" --disable-nls --enable-languages=c --without-headers
        $MAKE -j $($NPROC) all-gcc all-target-libgcc
        $MAKE install-gcc install-target-libgcc

        # Fix things
        if [ "$(uname -s)" = "OpenBSD" ]; then
            cd "${DIR}/opt/libexec/gcc/i686-elf/11.1.0/" && ln -sf liblto_plugin.so.0.0 liblto_plugin.so
        fi

        cd "${DIR}/.."
    fi
}

make_disk() {
    rm -rf disk && mkdir -p disk/font/ && mkdir -p disk/icons/ && mkdir -p disk/conf/ && mkdir -p disk/boot/
    echo "Hallo" >disk/conf/test

    cp -r res/ disk/

    echo "Getting font"
    cd disk/font/
    VERSION="1.8.2"
    wget -q "https://github.com/fcambus/spleen/releases/download/$VERSION/spleen-$VERSION.tar.gz"
    tar xzf "spleen-$VERSION.tar.gz"
    mv spleen-"$VERSION"/*.psfu .
    rm -rf "spleen-$VERSION"*
    cd ../../

    icons="cursor-default
    cursor-default-outline
    chess-bishop
    chess-king
    chess-knight
    chess-pawn
    chess-queen
    chess-rook"

    [ -d icons/ ] || git clone --depth=1 https://github.com/Templarian/MaterialDesign.git icons/
    inkscape_version="$(inkscape --version | awk '{print $2}' | cut -c1)"
    echo "$icons" | while read icon; do
        echo "Converting $icon"
        if [ "$inkscape_version" = "0" ]; then
            inkscape -z -w 18 -h 18 icons/svg/"$icon".svg -e disk/icons/"$icon"-18.png
            inkscape -z -w 24 -h 24 icons/svg/"$icon".svg -e disk/icons/"$icon"-24.png
            inkscape -z -w 36 -h 36 icons/svg/"$icon".svg -e disk/icons/"$icon"-36.png
            inkscape -z -w 48 -h 48 icons/svg/"$icon".svg -e disk/icons/"$icon"-48.png
        else
            inkscape -w 18 -h 18 icons/svg/"$icon".svg -o disk/icons/"$icon"-18.png
            inkscape -w 24 -h 24 icons/svg/"$icon".svg -o disk/icons/"$icon"-24.png
            inkscape -w 36 -h 36 icons/svg/"$icon".svg -o disk/icons/"$icon"-36.png
            inkscape -w 48 -h 48 icons/svg/"$icon".svg -o disk/icons/"$icon"-48.png
        fi
    done

    echo "Done!"
}

make_build() {
    if ! [ -d "disk/" ]; then
        echo "Creating disk..."
        make_disk
    fi

    mkdir -p build/
    rm -rf build/*

    printf "\nBuilding...\n"
    if [ "$mode" = "debug" ] || [ "$MELVIX_DEBUG" = "1" ]; then
        $MAKE -j $($NPROC) DEBUG=1
    else
        $MAKE -j $($NPROC) DEBUG=0
    fi

    # Prepare Grub
    mkdir -p disk/boot/grub/
    cp build/apps/kernel/exec disk/boot/melvix
    cp boot/grub.cfg disk/boot/grub/

    # Create disk image # TODO: Fix build for OpenBSD
    dd if=/dev/zero of=build/disk.img bs=1k count=32k status=none
    DEV=$($SUDO losetup --find --partscan --show build/disk.img)
    PART="p1"
    $SUDO parted -s "$DEV" mklabel msdos mkpart primary ext2 32k 100% -a minimal set 1 boot on
    if [ "$(uname -s)" = "OpenBSD" ]; then
        VND=$($SUDO vnconfig build/disk.img)
        (
            echo "e 0"
            echo 83
            echo n
            echo 0
            echo "*"
            echo "quit"
        ) | $SUDO fdisk -e $VND >/dev/null
        $SUDO mkfs.ext2 -F /dev/${VND}i >/dev/null
    else
        $SUDO mke2fs -b 1024 -q "$DEV$PART"
    fi

    # Set test app as init
    if [ "$mode" = "test" ]; then
        cp build/apps/test/exec build/apps/init/exec
    fi

    mkdir -p mnt/
    if [ "$(uname -s)" = "OpenBSD" ]; then
        $SUDO mount -t ext2fs /dev/${VND}i mnt/
    else
        $SUDO mount "$DEV$PART" mnt/
    fi
    $SUDO cp -r disk/* mnt/
    $SUDO chmod -R 0 mnt/conf/
    $SUDO cp -r build/apps/ mnt/apps/
    $SUDO grub-install --boot-directory=mnt/boot --target=i386-pc --modules="ext2" "$DEV"
    $SUDO umount mnt/ || (sleep 1 && sync && sudo umount mnt)
    $SUDO rm -rf mnt/

    if [ "$(uname -s)" = "OpenBSD" ]; then
        $SUDO vnconfig -u $VND
    else
        $SUDO losetup -d "$DEV"
    fi

    printf "Build finshed successfully!\n\n"
}

make_test() {
    if [ "$mode" = "test" ]; then
        qemu_with_flags -serial file:test.log -nographic -drive file=build/disk.img,format=raw,index=1,media=disk
        echo
        grep -E 'PASS|FAIL' test.log
        if grep -q "All tests passed" test.log; then exit 0; else exit 1; fi
    else
        qemu_with_flags -serial stdio -drive file=build/disk.img,format=raw,index=1,media=disk
    fi
}

make_debug() {
    qemu_with_flags -serial stdio -drive file=build/disk.img,format=raw,index=1,media=disk -s -S
}

make_disasm() {
    if [ -z "$1" ]; then
        echo "Usage: './run disasm {kernel, wm, ...} [-S]'"
        exit 1
    fi
    objdump -drwC "$2" -Mintel build/apps/"$1"/exec | less -R
    #hexdump -C build/kernel.bin | less -R
}

make_addr() {
    printf "Info: Make sure that you've turned the debug build on (e.g. with MELVIX_DEBUG=1)\n\n"
    if [ -z "$2" ]; then
        echo "Usage: './run addr kernel 0x50042'"
        exit 1
    fi
    addr2line -e build/apps/"$1"/exec -f -p "$2"
}

make_cloc() {
    cloc . --exclude-dir=build,iso,disk,res,cross,icons
}

make_append_commands() {
    s=""
    while read -r data; do
        s="${s}
${data}"
    done

    echo "$s" |
        sed -n "/Compiled $1/,/Compiled $2/p" |
        grep -wE 'gcc' |
        grep -w '\-c' |
        jq -nR '[inputs|{directory:"'"$(pwd)/$3"'/", command:., file: match(" [^ ]+$").string[1:]}]' \
            >>compile_commands.json
}

make_sync() {
    $TAGS -R --exclude=.git --exclude=build --exclude=disk --exclude=cross --exclude=boot .

    rm -f compile_commands.json
    output=$($MAKE --always-make --dry-run)
    echo "$output" | make_append_commands libc libk libs/libc
    echo "$output" | make_append_commands libk libgui libs/libgui
    echo "$output" | make_append_commands libgui libtxt libs/libtxt
    echo "$output" | make_append_commands libtxt kernel kernel
    echo "$output" | make_append_commands kernel apps apps
    tr <compile_commands.json '\n' '\r' | sed -e 's/\r]\r\[/,/g' | tr '\r' '\n' >tmp
    mv tmp compile_commands.json
}

make_clean() {
    #rm -rf build/
    $MAKE clean
}

if [ "${mode}" = "cross" ]; then
    make_cross
elif [ "${mode}" = "build" ]; then
    make_cross
    make_clean
    make_build
elif [ "${mode}" = "clean" ]; then
    make_clean
elif [ "${mode}" = "again" ]; then
    make_test
elif [ "${mode}" = "disasm" ]; then
    make_disasm "$2" "$3"
elif [ "${mode}" = "addr" ]; then
    make_addr "$2" "$3"
elif [ "${mode}" = "cloc" ]; then
    make_cloc
elif [ "${mode}" = "sync" ]; then
    make_sync
elif [ "${mode}" = "disk" ]; then
    make_disk
elif [ "${mode}" = "debug" ]; then
    make_cross
    make_clean
    make_build
    make_sync &
    make_debug
elif [ "${mode}" = "test" ] || [ "${mode}" = "" ]; then
    make_cross
    make_clean
    make_build
    make_sync &
    make_test
    make_clean
else
    echo "Usage: ./run {cross | clean | build | test | debug | again | disasm | addr | sync | disk} [-y]"
    printf "\nDescription of options:\n"
    printf "cross\t\tBuilds the cross compiler\n"
    printf "clean\t\tRemoves the compiled files\n"
    printf "build\t\tBuilds the whole project (cross+clean)\n"
    printf "test\t\tRuns the Melvix unit tests with QEMU (cross+clean+build)\n"
    printf "debug\t\tEmulates Melvix with QEMU and debug options (cross+clean+build)\n"
    printf "again\t\tOpens QEMU again using the previous build\n"
    printf "disasm\t\tDisassembles a given part of Melvix\n"
    printf "addr\t\tResolves an address to a line of code\n"
    printf "cloc\t\tCount the total lines of code\n"
    printf "sync\t\tSyncs the 'tags' and 'compile_commands.json' file\n"
    printf "disk\t\tPrepares the userspace disk (e.g. fonts)\n"
    printf "*\t\tAnything else prints this help\n"
    printf "\t\tWhen no option is set, Melvix gets built and emulated using QEMU (cross+clean+build)\n\n"
    echo "Set the environment variable 'MELVIX_DEBUG=1' for persistent debug builds"
fi