diff --git a/.github/workflows/netbsd.yml b/.github/workflows/netbsd.yml new file mode 100644 index 00000000..38be0e60 --- /dev/null +++ b/.github/workflows/netbsd.yml @@ -0,0 +1,26 @@ +on: + push: + branches: + - main + pull_request: + +jobs: + test: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - uses: cross-platform-actions/action@v0.22.0 + env: + PKG_PATH: 'https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/amd64/9.3/All' + with: + operating_system: netbsd + version: '9.3' #TODO: bump to 10.0 + environment_variables: PKG_PATH + run: | + sudo pkgin -y install clang libLLVM gmake bash diffutils && \ + gmake && \ + ./runtests.sh --verbose && \ + ./runtests.sh --verbose --jou-flags "--verbose" && \ + gmake clean && \ + ./doctest.sh && \ + ./compare_compilers.sh diff --git a/Makefile.posix b/Makefile.posix index 37a66f91..fc63e37c 100644 --- a/Makefile.posix +++ b/Makefile.posix @@ -7,6 +7,7 @@ LLVM_CONFIG ?= $(shell \ || which /usr/local/opt/llvm@13/bin/llvm-config \ || which llvm-config-11 \ || which /usr/local/opt/llvm@11/bin/llvm-config \ + || which /usr/pkg/libexec/libLLVM/llvm-config \ ) CFLAGS += $(shell $(LLVM_CONFIG) --cflags) CFLAGS += -gdwarf-4 # https://github.com/llvm/llvm-project/issues/56550 @@ -14,7 +15,10 @@ LDFLAGS ?= $(shell $(LLVM_CONFIG) --ldflags --libs) ifeq ($(CC),cc) # default c compiler --> use clang - CC := $(shell $(LLVM_CONFIG) --bindir)/clang + CC := $(shell \ + which `$(LLVM_CONFIG) --bindir`/clang \ + || which clang \ + ) endif all: jou compile_flags.txt @@ -26,10 +30,10 @@ compile_flags.txt: config.h: @v=`$(LLVM_CONFIG) --version`; case "$$v" in 11.*|13.*|14.*) ;; *) echo "Error: Found unsupported LLVM version $$v. Only LLVM 11, 13 and 14 are supported."; exit 1; esac echo "// auto-generated by Makefile" > config.h - echo "#define JOU_CLANG_PATH \"$(shell $(LLVM_CONFIG) --bindir)/clang\"" >> config.h + echo "#define JOU_CLANG_PATH \"$(CC)\"" >> config.h obj/%.o: src/%.c $(wildcard src/*.h) config.h - mkdir -vp obj && $(CC) -c $(CFLAGS) $< -o $@ + mkdir -p obj && $(CC) -c $(CFLAGS) $< -o $@ jou: $(SRC:src/%.c=obj/%.o) $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) @@ -37,7 +41,7 @@ jou: $(SRC:src/%.c=obj/%.o) config.jou: echo "# auto-generated by Makefile" > config.jou echo "def get_jou_clang_path() -> byte*:" >> config.jou - echo " return \"$(shell $(LLVM_CONFIG) --bindir)/clang\"" >> config.jou + echo " return \"$(CC)\"" >> config.jou self_hosted_compiler: jou config.jou $(wildcard self_hosted/*.jou) ./jou -o $@ --linker-flags "$(LDFLAGS)" self_hosted/main.jou diff --git a/README.md b/README.md index 1d205bf4..21f59799 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,38 @@ MacOS support is new. Please create an issue if something doesn't work. +
NetBSD +Support for NetBSD is still experimental. Please report bugs and +shortcomings. + +1. Install the dependencies: + ``` + # pkgin install bash clang gmake libLLVM + ``` + Optionally `diffutils` can be installed for coloured diff outputs. +2. Download and compile Jou. + ``` + $ git clone https://github.com/Akuli/jou + $ cd jou + $ gmake + ``` +3. Run the hello world program to make sure that Jou works: + ``` + $ ./jou examples/hello.jou + Hello World + ``` + You can now run other Jou programs in the same way. +4. (Optional) If you want to run Jou programs with simply `jou + filename` instead of something like `./jou filename` or + `/full/path/to/jou filename`, you can add the `jou` directory to + your PATH. Refer to the manual page of your login shell for exact + syntax. + +NB: Using Clang and LLVM libraries built as a part of the base system +is not currently supported. + +
+
64-bit Windows 1. Go to releases on GitHub. It's in the sidebar at right. diff --git a/compare_compilers.sh b/compare_compilers.sh index 575ce24a..4d96b808 100755 --- a/compare_compilers.sh +++ b/compare_compilers.sh @@ -5,6 +5,7 @@ # If tokenizing/parsing a Jou file fails, both compilers should fail with the same error message. set -e +ulimit -c unlimited files=() actions=() @@ -37,13 +38,24 @@ if [ ${#actions[@]} = 0 ]; then fi rm -rf tmp/compare_compilers -mkdir -vp tmp/compare_compilers +mkdir -p tmp/compare_compilers + +if ls | grep -q core$; then + for core in *core; do + mv -v "$core" "$core"~ + done +fi YELLOW="\x1b[33m" GREEN="\x1b[32m" RED="\x1b[31m" RESET="\x1b[0m" +DIFF=$(which gdiff || which diff) +if $DIFF --help | grep -q -- --color; then + diff_color="--color=always" +fi + function append_line() { local file="$1" @@ -84,8 +96,10 @@ for error_list_file in self_hosted/*s_wrong.txt; do done echo "Compiling the self-hosted compiler..." -if [[ "$OS" =~ Windows ]]; then +if [[ "${OS:=$(uname)}" =~ Windows ]]; then mingw32-make self_hosted_compiler.exe +elif [[ "$OS" =~ NetBSD ]]; then + gmake self_hosted_compiler else make self_hosted_compiler fi @@ -124,10 +138,18 @@ for action in ${actions[@]}; do fi else echo " Compilers behave differently as expected (listed in $error_list_file)" + echo -en ${YELLOW} + rm -vf *core | xargs echo Core dumped and ignored: + echo -en ${RESET} fi else - if diff -u --color=always tmp/compare_compilers/compiler_written_in_c.txt tmp/compare_compilers/self_hosted.txt; then - echo " Compilers behave the same as expected" + if $DIFF -u $diff_color tmp/compare_compilers/compiler_written_in_c.txt tmp/compare_compilers/self_hosted.txt; then + if ls | grep -q core$; then + echo -e " ${RED}Error: Core dumped.${RESET}" + exit 1 + else + echo " Compilers behave the same as expected" + fi else if [ $fix = yes ]; then append_line $error_list_file $file diff --git a/doctest.sh b/doctest.sh index 32e45a53..5e9da232 100755 --- a/doctest.sh +++ b/doctest.sh @@ -17,15 +17,24 @@ else files=("$@") fi -if [[ "$OS" =~ Windows ]]; then +if [[ "${OS:=$(uname)}" =~ Windows ]]; then source activate mingw32-make jou="$PWD/jou.exe" +elif [[ "$OS" =~ NetBSD ]]; then + gmake + jou="$PWD/jou" else make jou="$PWD/jou" fi + +DIFF=$(which gdiff || which diff) +if $DIFF --help | grep -q -- --color; then + diff_color="--color=always" +fi + function slice() { local first_lineno="$1" @@ -71,7 +80,7 @@ for file in */*.jou; do echo -n "$(basename "$(dirname "$file")" | base64 -d):$(basename "$file" | cut -d'.' -f1): " cp "$file" test.jou - if diff --text -u --color=always <(generate_expected_output test.jou | tr -d '\r') <( ("$jou" test.jou 2>&1 || true) | tr -d '\r'); then + if $DIFF --text -u $diff_color <(generate_expected_output test.jou | tr -d '\r') <( ("$jou" test.jou 2>&1 || true) | tr -d '\r'); then echo "ok" else ((nfail++)) || true diff --git a/examples/x11_window.jou b/examples/x11_window.jou index c79937ec..74280e4b 100644 --- a/examples/x11_window.jou +++ b/examples/x11_window.jou @@ -1,7 +1,9 @@ -# To run this program, you need linux: +# To run this program, you need a system with X11: # +# Linux: # $ ./jou --linker-flags "-lX11" examples/x11_window.jou -# +# NetBSD: +# $ ./jou --linker-flags "-L/usr/X11R7/lib -Wl,-R/usr/X11R7/lib -lX11" examples/x11_window.jou declare usleep(x: int) -> int diff --git a/runtests.sh b/runtests.sh index 996e3964..2da0e59b 100755 --- a/runtests.sh +++ b/runtests.sh @@ -58,18 +58,25 @@ while [ $# != 0 ]; do esac done -if [ $valgrind = yes ] && [[ "$OS" =~ Windows ]]; then - echo "valgrind doesn't work on Windows." >&2 - exit 2 +if [ $valgrind = yes ]; then + case "${OS:=$(uname)}" in + Windows*|NetBSD*) + echo "valgrind doesn't work on $OS." >&2 + exit 2 + ;; + esac fi if [ $run_make = yes ]; then - if [[ "$OS" =~ Windows ]]; then - source activate - mingw32-make + if [[ "${OS:=$(uname)}" =~ Windows ]]; then + source activate + mingw32-make + elif [[ "$OS" =~ NetBSD ]]; then + gmake else - make + make fi + fi rm -rf tmp/tests @@ -206,7 +213,12 @@ function run_test() printf "\n\n\x1b[33m*** Command: %s ***\x1b[0m\n\n" "$command" > $diffpath - if diff --text -u --color=always <( + DIFF=$(which gdiff || which diff) + if $DIFF --help | grep -q -- --color; then + diff_color="--color=always" + fi + + if $DIFF --text -u $diff_color <( generate_expected_output $joufile $correct_exit_code | tr -d '\r' ) <( export PATH="$PWD:$PATH" diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index 9ab42a77..1ecf2545 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -800,6 +800,8 @@ class AstToIR: name = "_jou_windows_startup" elif MACOS: name = "_jou_macos_startup" + elif NETBSD: + name = "_jou_netbsd_startup" else: return @@ -817,7 +819,7 @@ class AstToIR: self->llvm_function = self->declare_function(sig) self->new_block("start") - if (WINDOWS or MACOS) and strcmp(sig->name, "main") == 0 and not sig->is_method(): + if (WINDOWS or MACOS or NETBSD) and strcmp(sig->name, "main") == 0 and not sig->is_method(): self->call_the_special_startup_function() # Allocate all local variables at the start of the function. diff --git a/self_hosted/main.jou b/self_hosted/main.jou index 3e29b028..c5d75170 100644 --- a/self_hosted/main.jou +++ b/self_hosted/main.jou @@ -178,6 +178,9 @@ class Compiler: if MACOS: self->automagic_files[1] = malloc(strlen(self->stdlib_path) + 40) sprintf(self->automagic_files[1], "%s/_macos_startup.jou", self->stdlib_path) + if NETBSD: + self->automagic_files[1] = malloc(strlen(self->stdlib_path) + 40) + sprintf(self->automagic_files[1], "%s/_netbsd_startup.jou", self->stdlib_path) def parse_all_files(self) -> None: queue: ParseQueueItem* = malloc(50 * sizeof queue[0]) diff --git a/src/codegen.c b/src/codegen.c index 470cf29b..a596d70a 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -414,14 +414,16 @@ static int find_block(const CfGraph *cfg, const CfBlock *b) assert(0); } -#if defined(_WIN32) || defined(__APPLE__) +#if defined(_WIN32) || defined(__APPLE__) || defined(__NetBSD__) static void codegen_call_to_the_special_startup_function(const struct State *st) { const char *name; -#ifdef _WIN32 +#if defined _WIN32 name = "_jou_windows_startup"; -#else +#elif defined __APPLE__ name = "_jou_macos_startup"; +#else + name = "_jou_netbsd_startup"; #endif LLVMTypeRef functype = LLVMFunctionType(LLVMVoidType(), NULL, 0, false); @@ -448,7 +450,7 @@ static void codegen_function_or_method_def(struct State *st, const CfGraph *cfg) assert(cfg->all_blocks.ptr[0] == &cfg->start_block); LLVMPositionBuilderAtEnd(st->builder, blocks[0]); -#if defined(_WIN32) || defined(__APPLE__) +#if defined(_WIN32) || defined(__APPLE__) || defined(__NetBSD__) if (!get_self_class(&cfg->signature) && !strcmp(cfg->signature.name, "main")) codegen_call_to_the_special_startup_function(st); #endif diff --git a/src/evaluate.c b/src/evaluate.c index f598d39b..dccf373a 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -17,6 +17,13 @@ int get_special_constant(const char *name) return 0; #endif } + if (!strcmp(name, "NETBSD")) { + #ifdef __NetBSD__ + return 1; + #else + return 0; + #endif + } return -1; } diff --git a/src/main.c b/src/main.c index 8589a776..d7298b19 100644 --- a/src/main.c +++ b/src/main.c @@ -461,6 +461,10 @@ int main(int argc, char **argv) #ifdef __APPLE__ include_special_stdlib_file(&compst, "_macos_startup.jou"); #endif +#ifdef __NetBSD__ + assert(sizeof(FILE) == 152); + include_special_stdlib_file(&compst, "_netbsd_startup.jou"); +#endif parse_file(&compst, command_line_args.infile, NULL); parse_all_pending_files(&compst); diff --git a/src/update.c b/src/update.c index bd44e896..b88ff84b 100644 --- a/src/update.c +++ b/src/update.c @@ -17,6 +17,12 @@ #include #include "util.h" +#ifdef __NetBSD__ + #define MAKE "gmake" +#else + #define MAKE "make" +#endif + static noreturn void fail() { char *s = @@ -60,8 +66,8 @@ void update_jou_compiler() if (system("powershell -ExecutionPolicy bypass -File update.ps1") != 0) fail(); #else - confirm("Run \"git pull && make\"?"); - if (system("git pull && make")) + confirm("Run \"git pull && "MAKE" \"?"); + if (system("git pull && " MAKE)) fail(); #endif diff --git a/stdlib/_netbsd_startup.jou b/stdlib/_netbsd_startup.jou new file mode 100644 index 00000000..e22d4bc9 --- /dev/null +++ b/stdlib/_netbsd_startup.jou @@ -0,0 +1,23 @@ +# A call to the _jou_netbsd_startup() function is inserted to the +# start of every Jou program when compiling for NetBSD. + +# On NetBSD, these std* things are macros +# expanding to elements of the __sF array +# of FILE structs. + +declare global __sF: byte[152][3] # sizeof(FILE) == 152 +global stdin: void* +global stdout: void* +global stderr: void* + +def _jou_netbsd_startup() -> None: + stdin = &__sF[0] + stdout = &__sF[1] + stderr = &__sF[2] + +# Jou uses __errno_location, +# NetBSD has __ernno() + +declare __errno() -> int* +def __errno_location() -> int*: + return __errno() diff --git a/tests/should_succeed/compiler_cli.jou b/tests/should_succeed/compiler_cli.jou index abf56f03..7cda3192 100644 --- a/tests/should_succeed/compiler_cli.jou +++ b/tests/should_succeed/compiler_cli.jou @@ -88,7 +88,10 @@ def main() -> int: # Compile a GUI program if not (WINDOWS or MACOS): - ret = system("./jou -o /dev/null --linker-flags \"-lX11\" examples/x11_window.jou") + if NETBSD: + ret = system("./jou -o /dev/null --linker-flags \"-L/usr/X11R7/lib -lX11\" examples/x11_window.jou") + else: + ret = system("./jou -o /dev/null --linker-flags \"-lX11\" examples/x11_window.jou") assert ret == 0 # Compile a program with a memory leak. diff --git a/tests/should_succeed/file.jou b/tests/should_succeed/file.jou index 99925608..67267e38 100644 --- a/tests/should_succeed/file.jou +++ b/tests/should_succeed/file.jou @@ -5,7 +5,7 @@ def write_hello_123() -> None: f = fopen("tmp/tests/hello.txt", "w") if f == NULL: printf("can't write tmp/tests/hello.txt\n") - abort() + exit(1) # abort() would make a core dump when comparing compilers fputc('h', f) fputs("ell", f)