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)