diff --git a/README.md b/README.md new file mode 100644 index 0000000..d6624cd --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# vix + +Vix is a simple, POSIX-shell-based project and build manager for [Vishap Oberon](https://vishap.oberon.am) projects. +It provides commands to create a new project skeleton, compile library modules, link and run your program, run tests, and produce release binaries. + +## vix in action + +[![asciicast](https://asciinema.org/a/723640.svg)](https://asciinema.org/a/723640) + +## Installation + +1. clone this repository +2. `sudo cp vix /usr/local/bin/vix` + +## Usage + +``` +Usage: vix ... + help + version + new PATH [--module MODULE] [--app APP] + run + build + test + release +``` + +``` +vix new hello_world +``` + +## Directory Layout + +``` +hello_world/ + src/ + HelloWorld.Mod ← library API + HelloWorldMain.Mod ← "main" program entry + test/ + HelloWorldTest.Mod ← test suite + vipakfile ← project manifest + README.md + .gitignore + .gitattributes +``` + +## vipakfile format + +``` +NAME = hello_world +VERSION = 0.1.0 + +AUTHOR = +LICENSE = + +DEPS = + +RUN = ./HelloWorldMain +MAIN = %projdir%/src/HelloWorldMain.Mod +BUILD = %projdir%/src/HelloWorld.Mod + +TEST_RUN = ./HelloWorldTest +TEST_MAIN = %projdir%/test/HelloWorldTest.Mod +TEST = +``` + +- `%projdir%` is replaced at runtime with the project root directory. +- Semicolons (`;`) separate multiple entries, preserving order. + +## TODO + +- Integrate `vix` with `vipak` for dependency management +- Incremental builds +- Release tarballs, Dockerfiles and Jailerfiles +- Rewrite vix in Oberon diff --git a/vix b/vix index 9e125de..6080f3c 100755 --- a/vix +++ b/vix @@ -3,18 +3,19 @@ # set global variables PROGNAME=${0##*/} -VERSION="0.1.0" -PROGPATH=$(readlink -f "$0") +VERSION="0.1.1" COLOUR_SET_R="\033[0;31m" COLOUR_SET_G="\033[0;32m" -COLOUR_SET_Y="\033[0;33m" COLOUR_SET_B="\033[0;34m" -COLOUR_SET_M="\033[0;35m" -COLOUR_SET_C="\033[0;36m" -COLOUR_SET_W="\033[0;37m" COLOUR_END="\033[0m" +PROJECT_ROOT=$(pwd) +host_os=$(uname -s) +host_arch=$(uname -m) + +: "${VIX_TARGET_DIR:=build/${host_os}-${host_arch}}" + help_usage(){ cat <&2 + printf "${COLOUR_SET_G}[*] ${COLOUR_END} %s\n" "${@}" >&2 } confirm_create_path(){ - printf "The directory \"${PATH_ARG}\" already exists." - printf " Are you sure you want to continue?" - read -p " [yN] " yesno - echo "${yesno}" - exit 0 + perror "The directory \"${PATH_ARG}\" already exists." } check_path(){ - #paction "check_path" stat "$PATH_ARG" >/dev/null 2>&1 && confirm_create_path } @@ -72,7 +69,7 @@ create_path(){ create_readme(){ paction "Creating ${PATHBASE}/README.md" - cat < ${PATH_ARG}/README.md + cat < "${PATH_ARG}"/README.md # ${APP} **TODO: Add description** @@ -94,7 +91,7 @@ EOF create_gitattributes(){ paction "Creating ${PATHBASE}/.gitattributes" - cat < ${PATH_ARG}/.gitattributes + cat < "${PATH_ARG}"/.gitattributes # Set the language to Oberon *.Mod linguist-language=Oberon *.mod linguist-language=Oberon @@ -103,7 +100,7 @@ EOF create_gitignore(){ paction "Creating ${PATHBASE}/.gitignore" - cat < ${PATH_ARG}/.gitignore + cat < "${PATH_ARG}"/.gitignore build release EOF @@ -111,18 +108,22 @@ EOF create_vipakfile(){ paction "Creating ${PATHBASE}/vipakfile" - cat < ${PATH_ARG}/vipakfile -NAME = ${APP} -VERSION = 0.1.0 + cat < "${PATH_ARG}"/vipakfile +NAME = ${APP} +VERSION = 0.1.0 -AUTHOR = -LICENSE = +AUTHOR = +LICENSE = -DEPS = +DEPS = -BUILD = voc %projdir%/src/${MODULE}.Mod -s +RUN = ./${MODULE}Main +MAIN = %projdir%/src/${MODULE}Main.Mod +BUILD = %projdir%/src/${MODULE}.Mod -TEST = voc %projdir%/test/${MODULE}Test.Mod -m ; ./${MODULE}Test +TEST_RUN = ./${MODULE}Test +TEST_MAIN = %projdir%/test/${MODULE}Test.Mod +TEST = EOF } @@ -133,22 +134,34 @@ create_src(){ create_src_prog(){ paction "Creating ${PATHBASE}/src/${MODULE}.Mod" - cat < ${PATH_ARG}/src/${MODULE}.Mod + cat < "${PATH_ARG}"/src/${MODULE}.Mod MODULE ${MODULE}; IMPORT Out; -PROCEDURE Hello*(s: ARRAY OF CHAR); +PROCEDURE Run*(): INTEGER; BEGIN - Out.String("Hello "); - Out.String(s); - Out.Ln; -END Hello; + RETURN 42 +END Run; END ${MODULE}. EOF } +create_src_progmain(){ + paction "Creating ${PATHBASE}/src/${MODULE}Main.Mod" + cat < "${PATH_ARG}"/src/${MODULE}Main.Mod +MODULE ${MODULE}Main; + + IMPORT ${MODULE}, Out; + +BEGIN + Out.Int(${MODULE}.Run(), 0); Out.Ln; +END ${MODULE}Main. +EOF +} + + create_test(){ paction "Creating ${PATHBASE}/test" mkdir -p "${PATH_ARG}/test" @@ -156,31 +169,48 @@ create_test(){ create_test_progtest(){ paction "Creating ${PATHBASE}/test/${MODULE}Test.Mod" - cat < ${PATH_ARG}/test/${MODULE}Test.Mod + cat < "${PATH_ARG}"/test/${MODULE}Test.Mod MODULE ${MODULE}Test; - IMPORT ${MODULE}; + IMPORT ${MODULE}, Out; BEGIN - ${MODULE}.Hello("world") + IF ${MODULE}.Run() = 42 THEN + Out.String("All works!") + ELSE + Out.String("Test Failed...") + END; + Out.Ln; END ${MODULE}Test. EOF } success_msg(){ pinfo 'All good!' + + cat </dev/null \ + || perror "${key} step failed: ${entry}" done IFS=$oldIFS - - pinfo "${key} complete" + cd - } +_run_cmds() { + key=$1 + [ -f vipakfile ] || perror "vipakfile not found" + + run_line=$( + grep -E "^${key}[[:space:]]*=" vipakfile \ + | cut -d= -f2- \ + | sed -e 's/^[[:space:]]*//' + ) + [ -n "$run_line" ] || perror "${key} not set in vipakfile" + + paction "Running ${key} in '${VIX_TARGET_DIR}/'" + cd "${VIX_TARGET_DIR}" || perror "cd to ${VIX_TARGET_DIR} failed" + + oldIFS=$IFS; IFS=';' + for cmd in $run_line; do + cmd=$(printf "%s" "$cmd" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + [ -z "$cmd" ] && continue + + paction "Executing: ${cmd}" + eval "${cmd}" \ + || perror "execution failed: ${cmd}" + done + IFS=$oldIFS + cd - +} + +# now the four subcommands: build_proj() { - _run_steps BUILD build + _process_mods BUILD -s + _process_mods MAIN -m + pinfo "Build complete" +} + +run_proj() { + _run_cmds RUN } test_proj() { - _run_steps TEST build + _process_mods TEST -s + _process_mods TEST_MAIN -m + _run_cmds TEST_RUN + pinfo "All tests passed" } +release_proj() { + [ -f vipakfile ] || perror "vipakfile not found in current directory" + _process_mods BUILD -s + _process_mods MAIN -M + + release_subdir="${VIX_TARGET_DIR}/release" + paction "Creating release directory '${release_subdir}'" + mkdir -p "${release_subdir}" + + # 4) copy each MAIN binary into release/ + main_line=$( + grep -E '^MAIN[[:space:]]*=' vipakfile \ + | cut -d= -f2- \ + | sed -e 's/^[[:space:]]*//' \ + -e "s|%projdir%|${PROJECT_ROOT}|g" + ) + + oldIFS=$IFS; IFS=';' + for entry in $main_line; do + entry=$(printf "%s" "$entry" \ + | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + [ -z "$entry" ] && continue + + bin=$(basename "$entry" .Mod) + paction "Copying '${bin}' → '${release_subdir}/'" + cp "${VIX_TARGET_DIR}/${bin}" "${release_subdir}/" \ + || perror "failed to install release binary: ${bin}" + done + IFS=$oldIFS + + pinfo "Release ready in '${release_subdir}'" +} + +clean_proj() { + paction "Removing build artifacts in '${VIX_TARGET_DIR}'" + rm -rf "${VIX_TARGET_DIR}" \ + || perror "failed to remove '${VIX_TARGET_DIR}'" + pinfo "Clean complete" +} + + vix_main(){ [ $# -eq 0 ] && help_usage @@ -302,6 +405,9 @@ vix_main(){ new) new_proj "$@" ;; build) build_proj ;; test) test_proj ;; + run) run_proj ;; + release) release_proj ;; + clean) clean_proj ;; *) help_usage ;; esac }