vix/vix
2025-06-18 18:33:47 +04:00

418 lines
7.9 KiB
Bash
Executable file

#!/bin/sh
# set global variables
PROGNAME=${0##*/}
VERSION="0.1.4"
COLOUR_SET_R="\033[0;31m"
COLOUR_SET_G="\033[0;32m"
COLOUR_SET_B="\033[0;34m"
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 <<EOT
Usage: vix ...
help
version
new PATH [--module MODULE] [--app APP]
run
build
test
clean
release [--tarball]
EOT
exit 0
}
help_version(){
printf "%s: v%s\n" "${PROGNAME}" "${VERSION}"
}
help_tbd(){
printf "This feature is not implemented yet...\n"
}
perror(){
printf "${COLOUR_SET_R}[-] ${COLOUR_END} %s\n" "${@}" >&2
exit 1
}
pinfo(){
printf "${COLOUR_SET_B}[+] ${COLOUR_END} %s\n" "${@}" >&2
}
paction(){
printf "${COLOUR_SET_G}[*] ${COLOUR_END} %s\n" "${@}" >&2
}
confirm_create_path(){
perror "The directory \"${PATH_ARG}\" already exists."
}
check_path(){
stat "$PATH_ARG" >/dev/null 2>&1 && confirm_create_path
}
create_path(){
paction "Creating ${PATHBASE}"
mkdir -p "${PATH_ARG}"
}
create_readme(){
paction "Creating ${PATHBASE}/README.md"
cat <<EOF > "${PATH_ARG}"/README.md
# ${APP}
**TODO: Add description**
## Building
\`\`\`
vix build
\`\`\`
## Testing
\`\`\`
vix test
\`\`\`
EOF
}
create_gitattributes(){
paction "Creating ${PATHBASE}/.gitattributes"
cat <<EOF > "${PATH_ARG}"/.gitattributes
# Set the language to Oberon
*.Mod linguist-language=Oberon
*.mod linguist-language=Oberon
EOF
}
create_gitignore(){
paction "Creating ${PATHBASE}/.gitignore"
cat <<EOF > "${PATH_ARG}"/.gitignore
build
release
EOF
}
create_vipakfile(){
paction "Creating ${PATHBASE}/vipakfile"
cat <<EOF > "${PATH_ARG}"/vipakfile
NAME = ${APP}
VERSION = 0.1.0
AUTHOR =
LICENSE =
DEPS =
RUN = ./${MODULE}Main
MAIN = %projdir%/src/${MODULE}Main.Mod
BUILD = %projdir%/src/${MODULE}.Mod
TEST_RUN = ./${MODULE}Test
TEST_MAIN = %projdir%/test/${MODULE}Test.Mod
TEST =
EOF
}
create_src(){
paction "Creating ${PATHBASE}/src"
mkdir -p "${PATH_ARG}/src"
}
create_src_prog(){
paction "Creating ${PATHBASE}/src/${MODULE}.Mod"
cat <<EOF > "${PATH_ARG}"/src/${MODULE}.Mod
MODULE ${MODULE};
IMPORT Out;
PROCEDURE Run*(): INTEGER;
BEGIN
RETURN 42
END Run;
END ${MODULE}.
EOF
}
create_src_progmain(){
paction "Creating ${PATHBASE}/src/${MODULE}Main.Mod"
cat <<EOF > "${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"
}
create_test_progtest(){
paction "Creating ${PATHBASE}/test/${MODULE}Test.Mod"
cat <<EOF > "${PATH_ARG}"/test/${MODULE}Test.Mod
MODULE ${MODULE}Test;
IMPORT ${MODULE}, Out;
BEGIN
IF ${MODULE}.Run() = 42 THEN
Out.String("All works!");
Out.Ln;
ELSE
Out.String("Test Failed...");
Out.Ln;
HALT(1)
END;
END ${MODULE}Test.
EOF
}
success_msg(){
pinfo 'All good!'
cat <<EOF
Your Oberon project was created successfully.
You can use "vix" to build it, test it, and more:
cd ${PATHBASE}
vix build && vix test
vix run
Run "vix help" for more commands.
EOF
}
new_proj() {
shift
if [ $# -lt 1 ]; then
perror "missing PATH argument for '${PROGNAME} new'"
fi
PATH_ARG="$1"
PATHBASE=$(basename "${PATH_ARG}")
shift
MODULE=""
APP=""
# Parse optional flags
while [ $# -gt 0 ]; do
case "$1" in
--module)
shift
MODULE="$1"
;;
--app)
shift
APP="$1"
;;
*)
perror "unknown option '${1}'"
;;
esac
shift
done
# --module is required if PATH is '.'
if [ -z "$MODULE" ]; then
if [ "$PATH_ARG" = "." ]; then
perror "--module is required when PATH is '.'"
fi
# Derive PascalCase module name from path
MODULE=$(printf "%s" "$PATHBASE" | awk -F'[_-]' '{
for (i = 1; i <= NF; i++)
{
$i = toupper(substr($i,1,1)) substr($i,2)
}
print $0
}' OFS='')
fi
# Validate MODULE as Oberon identifier
if ! printf "%s" "$MODULE" | grep -Eq '^[A-Za-z][A-Za-z0-9]*$'; then
perror "Invalid module name: '${MODULE}'" \
"Must match: letter {letter | digit}" \
"(no underscores, dashes, or leading digits)"
fi
# Default APP to basename of path
if [ -z "$APP" ]; then
APP="${PATHBASE}"
fi
pinfo "PATH : $PATH_ARG"
pinfo "MODULE : $MODULE"
pinfo "APP : $APP"
check_path
create_path
create_readme
create_gitattributes
create_gitignore
create_vipakfile
create_src
create_src_prog
create_src_progmain
create_test
create_test_progtest
success_msg
}
_process_mods() {
key=$1; mode=$2
[ -f vipakfile ] || perror "vipakfile not found"
# grab & massage the RHS
line=$(
grep -E "^${key}[[:space:]]*=" vipakfile \
| cut -d= -f2- \
| sed -e 's/^[[:space:]]*//' \
-e "s|%projdir%|${PROJECT_ROOT}|g"
)
paction "Processing ${key} (${mode}) in '${VIX_TARGET_DIR}/'"
mkdir -p "${VIX_TARGET_DIR}"
cd "${VIX_TARGET_DIR}" || perror "cd to ${VIX_TARGET_DIR} failed"
oldIFS=$IFS; IFS=';'
for entry in $line; do
# trim
entry=$(printf "%s" "$entry" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
[ -z "$entry" ] && continue
paction "voc ${mode} ${entry}"
voc "${mode}" "${entry}" 2>/dev/null \
|| perror "${key} step failed: ${entry}"
done
IFS=$oldIFS
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:]]*//' \
-e "s|%projdir%|${PROJECT_ROOT}|g"
)
[ -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() {
_process_mods BUILD -s
_process_mods MAIN -m
pinfo "Build complete"
}
run_proj() {
_run_cmds RUN
}
test_proj() {
_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
case "$1" in
help) help_usage ;;
version) help_version ;;
new) new_proj "$@" ;;
build) build_proj ;;
test) test_proj ;;
run) run_proj ;;
release) release_proj ;;
clean) clean_proj ;;
*) help_usage ;;
esac
}
vix_main "$@"