#!/bin/sh set -u root=$(cd "$(dirname "$0")" && pwd) mkproj=${MKPROJ:-$root/mkproj} work=$root/tests rm -rf "$work" mkdir -p "$work" trap 'rm -rf "$work"' EXIT pass=0 fail=0 ok() { pass=$((pass + 1)); printf ' ok %s\n' "$1"; } bad() { fail=$((fail + 1)); printf ' FAIL %s\n' "$1"; } skip() { printf ' skip %s\n' "$1"; } have() { command -v "$1" >/dev/null 2>&1; } build_run() { bs=$1 dir=$2 name=$3 case $bs in make) ( cd "$dir" && make ) >/dev/null 2>&1 "$dir/$name" 2>&1 ;; premake) ( cd "$dir" && premake5 gmake2 && make ) >/dev/null 2>&1 "$dir/$name" 2>&1 ;; cmake) ( cd "$dir" && cmake -B build && cmake --build build ) >/dev/null 2>&1 "$dir/build/$name" 2>&1 ;; meson) ( cd "$dir" && meson setup build && meson compile -C build ) >/dev/null 2>&1 "$dir/build/$name" 2>&1 ;; xmake) ( cd "$dir" && xmake -y ) >/dev/null 2>&1 ( cd "$dir" && xmake run ) 2>&1 ;; esac } check() { label=$1 bs=$2 name=$3 shift 3 if ! ( cd "$work" && "$mkproj" -b "$bs" "$@" "$name" ) >/dev/null 2>&1 then bad "$label (generate)" return fi if build_run "$bs" "$work/$name" "$name" | grep -q 'type shi' then ok "$label" else bad "$label (build/run)" fi } lib_build() { bs=$1 dir=$2 case $bs in make) ( cd "$dir" && make ) >/dev/null 2>&1 ;; premake) ( cd "$dir" && premake5 gmake2 && make ) >/dev/null 2>&1 ;; cmake) ( cd "$dir" && cmake -B build && cmake --build build ) >/dev/null 2>&1 ;; meson) ( cd "$dir" && meson setup build && meson compile -C build ) >/dev/null 2>&1 ;; xmake) ( cd "$dir" && xmake -y ) >/dev/null 2>&1 ;; esac } check_lib() { bs=$1 name=$2 if ! ( cd "$work" && "$mkproj" -b "$bs" -k lib "$name" ) >/dev/null 2>&1 then bad "$bs / lib (generate)" return fi if lib_build "$bs" "$work/$name" then ok "$bs / lib" else bad "$bs / lib (build)" fi } check_shared() { bs=$1 name=$2 if ! ( cd "$work" && "$mkproj" -b "$bs" -k shared "$name" ) >/dev/null 2>&1 then bad "$bs / shared (generate)" return fi lib_build "$bs" "$work/$name" if find "$work/$name" \( -name "lib$name.so" -o -name "lib$name.dylib" \) | grep -q . then ok "$bs / shared" else bad "$bs / shared (build)" fi } echo "mkproj test harness ($mkproj)" echo echo "cli:" "$mkproj" --version >/dev/null 2>&1 && ok "--version" || bad "--version" "$mkproj" --help >/dev/null 2>&1 && ok "--help" || bad "--help" "$mkproj" --completions bash | bash -n 2>/dev/null && ok "--completions bash" || bad "--completions bash" "$mkproj" --completions zsh | zsh -n 2>/dev/null && ok "--completions zsh" || bad "--completions zsh" "$mkproj" --completions fish >/dev/null 2>&1 && ok "--completions fish" || bad "--completions fish" "$mkproj" --completions tcsh >/dev/null 2>&1 && bad "--completions rejects unknown" || ok "--completions rejects unknown" ( cd "$work" && "$mkproj" --lang=cpp eqform ) >/dev/null 2>&1 && [ -f "$work/eqform/main.cpp" ] && ok "--lang=cpp (=form)" || bad "--lang=cpp (=form)" echo echo "name validation (these must be rejected):" for badname in "bad name" "a/b" ".." "" 'x") evil('; do if ( cd "$work" && "$mkproj" "$badname" ) >/dev/null 2>&1 then bad "rejects [$badname]" else ok "rejects [$badname]" fi done echo echo "build matrix:" for bs in make cmake meson premake xmake; do tool=$bs [ "$bs" = premake ] && tool=premake5 if ! have "$tool"; then skip "$bs (no $tool)"; continue; fi if [ "$bs" = meson ] && ! have ninja; then skip "meson (no ninja)"; continue; fi check "$bs / c" "$bs" "t_${bs}_c" check "$bs / cpp" "$bs" "t_${bs}_cpp" -l cpp check "$bs / src" "$bs" "t_${bs}_src" --src src [ "$(uname)" = Darwin ] && check "$bs / objc" "$bs" "t_${bs}_objc" -l objc check_lib "$bs" "t_${bs}_lib" check_shared "$bs" "t${bs}sh" done if [ "$(uname)" = Darwin ] && have nasm; then # native nasm is x86-64; it links here but only runs under Rosetta, so # check the binary builds rather than requiring it to run. ( cd "$work" && "$mkproj" -l nasm t_nasm ) >/dev/null 2>&1 && ( cd "$work/t_nasm" && make ) >/dev/null 2>&1 && [ -x "$work/t_nasm/t_nasm" ] && ok "nasm / make (build)" || bad "nasm / make (build)" else skip "nasm (needs macOS + nasm)" fi if have swiftc; then ( cd "$work" && "$mkproj" -l swift t_swift ) >/dev/null 2>&1 && build_run make "$work/t_swift" t_swift | grep -q 'type shi' && ok "swift / make" || bad "swift / make" else skip "swift (no swiftc)" fi if have rustc; then ( cd "$work" && "$mkproj" -l crust t_crust ) >/dev/null 2>&1 && build_run make "$work/t_crust" t_crust | grep -q 'type shi' && ok "crust / make" || bad "crust / make" else skip "crust (no rustc)" fi # swift and crust are make-only, executables-only, no -std / clang-format ( cd "$work" && "$mkproj" -l swift -b cmake sc ) >/dev/null 2>&1 && bad "swift rejects cmake" || ok "swift rejects cmake" ( cd "$work" && "$mkproj" -l crust -k lib cl ) >/dev/null 2>&1 && bad "crust rejects lib" || ok "crust rejects lib" ( cd "$work" && "$mkproj" -l swift -k shared ssh ) >/dev/null 2>&1 && bad "swift rejects shared" || ok "swift rejects shared" ( cd "$work" && "$mkproj" --docker -k lib dl ) >/dev/null 2>&1 && bad "--docker rejects lib" || ok "--docker rejects lib" ( cd "$work" && "$mkproj" -l swift --style ss ) >/dev/null 2>&1 && bad "swift rejects --style" || ok "swift rejects --style" ( cd "$work" && "$mkproj" --bin . dotbin ) >/dev/null 2>&1 && bad "--bin . rejected" || ok "--bin . rejected" ( cd "$work" && "$mkproj" --bin .. dotdot ) >/dev/null 2>&1 && bad "--bin .. rejected" || ok "--bin .. rejected" ( cd "$work" && "$mkproj" --bin-int / slashint ) >/dev/null 2>&1 && bad "--bin-int / rejected" || ok "--bin-int / rejected" ( cd "$work" && "$mkproj" --single -b cmake scm ) >/dev/null 2>&1 && bad "--single rejects cmake" || ok "--single rejects cmake" ( cd "$work" && "$mkproj" --single -l swift sws ) >/dev/null 2>&1 && bad "--single rejects swift" || ok "--single rejects swift" ( cd "$work" && "$mkproj" --single -k lib slib ) >/dev/null 2>&1 && bad "--single rejects lib" || ok "--single rejects lib" echo echo "generated files:" ( cd "$work" && "$mkproj" --license mit lic_mit ) >/dev/null 2>&1 grep -q 'MIT License' "$work/lic_mit/LICENSE" 2>/dev/null && ok "--license mit" || bad "--license mit" ( cd "$work" && MKPROJ_BUILD=cmake "$mkproj" envdef ) >/dev/null 2>&1 [ -f "$work/envdef/CMakeLists.txt" ] && ok "MKPROJ_BUILD env default" || bad "MKPROJ_BUILD env default" ( cd "$work" && "$mkproj" --docker dok ) >/dev/null 2>&1 [ -f "$work/dok/Dockerfile" ] && [ -f "$work/dok/.dockerignore" ] && ok "--docker files" || bad "--docker files" ( cd "$work" && "$mkproj" --docker -l nasm dok_nasm ) >/dev/null 2>&1 [ -f "$work/dok_nasm/Dockerfile" ] && grep -q 'elf64' "$work/dok_nasm/Makefile" && ok "--docker nasm targets elf64" || bad "--docker nasm targets elf64" ( cd "$work" && "$mkproj" --docker -l objc dok_objc ) >/dev/null 2>&1 && bad "--docker rejects objc" || ok "--docker rejects objc" ( cd "$work" && "$mkproj" rootbin ) >/dev/null 2>&1 && ( cd "$work/rootbin" && make ) >/dev/null 2>&1 && [ -x "$work/rootbin/rootbin" ] && ok "binary at root (default)" || bad "binary at root (default)" ( cd "$work" && "$mkproj" --no-root norootbin ) >/dev/null 2>&1 && ( cd "$work/norootbin" && make ) >/dev/null 2>&1 && [ -x "$work/norootbin/bin/norootbin" ] && ok "--no-root binary in bin/" || bad "--no-root binary in bin/" ( cd "$work" && "$mkproj" --git gitted ) >/dev/null 2>&1 [ -d "$work/gitted/.git" ] && [ -f "$work/gitted/.gitignore" ] && ok "--git repo + .gitignore" || bad "--git repo + .gitignore" [ -f "$work/rootbin/.gitignore" ] && bad "no git/.gitignore by default" || ok "no git/.gitignore by default" ( cd "$work" && "$mkproj" --single singlep ) >/dev/null 2>&1 && ( cd "$work/singlep" && make ) >/dev/null 2>&1 && [ -x "$work/singlep/singlep" ] && ok "--single builds" || bad "--single builds" grep -q 'OBJ :=' "$work/singlep/Makefile" 2>/dev/null && bad "--single has no object dir" || ok "--single has no object dir" grep -q 'rm -f singlep' "$work/singlep/Makefile" 2>/dev/null && ok "--single clean is rm -f binary" || bad "--single clean is rm -f binary" ( cd "$work" && "$mkproj" --no-validation noval ) >/dev/null 2>&1 { ! grep -q 'check:' "$work/noval/Makefile" && ! grep -q 'fsanitize' "$work/noval/Makefile"; } 2>/dev/null && ok "--no-validation drops check + sanitizers" || bad "--no-validation drops check + sanitizers" ( cd "$work" && "$mkproj" --style styled ) >/dev/null 2>&1 [ -f "$work/styled/.clang-format" ] && [ -f "$work/styled/.editorconfig" ] && ok "--style files" || bad "--style files" if have clang-format; then ( cd "$work/styled" && clang-format --style=file --dry-run --Werror main.c ) >/dev/null 2>&1 && ok "--style stub conforms" || bad "--style stub conforms" fi ( cd "$work" && "$mkproj" --style -l nasm sn ) >/dev/null 2>&1 && bad "--style rejects nasm" || ok "--style rejects nasm" ( cd "$work" && "$mkproj" runt ) >/dev/null 2>&1 && ( cd "$work/runt" && make run ) 2>/dev/null | grep -q 'type shi' && ok "make run" || bad "make run" ( cd "$work" && "$mkproj" --ci cit ) >/dev/null 2>&1 [ -f "$work/cit/.github/workflows/ci.yml" ] && ok "--ci workflow" || bad "--ci workflow" grep -q 'ubuntu-latest' "$work/cit/.github/workflows/ci.yml" 2>/dev/null && ok "--ci c runs on ubuntu" || bad "--ci c runs on ubuntu" ( cd "$work" && "$mkproj" --ci -l swift cis ) >/dev/null 2>&1 grep -q 'macos-latest' "$work/cis/.github/workflows/ci.yml" 2>/dev/null && ok "--ci swift runs on macos" || bad "--ci swift runs on macos" ( cd "$work" && "$mkproj" --src source srcdir ) >/dev/null 2>&1 && [ -f "$work/srcdir/source/main.c" ] && ( cd "$work/srcdir" && make ) >/dev/null 2>&1 && [ -x "$work/srcdir/srcdir" ] && ok "--src custom source dir" || bad "--src custom source dir" mkdir -p "$work/inplace" ( cd "$work/inplace" && "$mkproj" . ) >/dev/null 2>&1 && [ -f "$work/inplace/main.c" ] && [ -f "$work/inplace/Makefile" ] && ( cd "$work/inplace" && make ) >/dev/null 2>&1 && [ -x "$work/inplace/inplace" ] && ok "mkproj . (in place)" || bad "mkproj . (in place)" ( cd "$work/inplace" && "$mkproj" . ) >/dev/null 2>&1 && bad "in place refuses to clobber" || ok "in place refuses to clobber" echo printf '%d passed, %d failed\n' "$pass" "$fail" [ "$fail" -eq 0 ]