summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.shmk4
-rwxr-xr-xshmk.shmk2
-rw-r--r--src/util/shmk.README121
-rwxr-xr-xsrc/util/shmk.sh64
4 files changed, 174 insertions, 17 deletions
diff --git a/build.shmk b/build.shmk
index 0a74b6c..55ba601 100755
--- a/build.shmk
+++ b/build.shmk
@@ -1,4 +1,6 @@
-#!/usr/bin/env shmk
+#!./shmk
+#
+# shmk build file for the xiutils project
CC=gcc
diff --git a/shmk.shmk b/shmk.shmk
index ac1a1d1..4bcce48 100755
--- a/shmk.shmk
+++ b/shmk.shmk
@@ -1,4 +1,6 @@
#!/usr/bin/env shmk
+#
+# shmk file to build shmk itself
LIBS="
src/lib/colors.sh
diff --git a/src/util/shmk.README b/src/util/shmk.README
new file mode 100644
index 0000000..a359fbc
--- /dev/null
+++ b/src/util/shmk.README
@@ -0,0 +1,121 @@
+# shmk
+
+shmk is a simple utility to build complicated shell projects
+
+## Usage
+
+### single file
+
+shmk can be invoked on a single sh file to "build" it:
+
+```sh
+$ shmk myprog.sh file_output.sh
+```
+
+### in a wider project
+
+shmk can also serve as a "make" tool, building libraries and executables, running tests (checks) and installing your project to a path.
+
+see [shmk project](#shmk project) for more info
+
+## Directives
+
+Shmk will take each file and parse it before making an output. Most of the file will remain unchanged, however some directives will be parsed:
+
+
+### `#include file`
+
+Will include a file 'in place'. Will not include a file more than once within the output
+
+The first file with the given name will be used. The search path by default follows this order:
+
+```
+./
+$DIST/
+/usr/lib/
+/usr/local/lib/
+/usr/share/shmk/
+```
+
+Using the `-I` option will prepend a file to this list. If no `.sh` extension is given, both the `filenanme` and the `filename.sh` will be checked
+
+# shmk project
+
+A shmk project is typically built using a `build.shmk` file
+
+An example build shmk file could like this (chek this project's shmk file for more examples):
+
+```sh
+#!/usr/bin/env shmk
+
+LIBS="
+ src/lib/mylibrary.sh
+ src/lib/second_library.sh
+"
+
+PROGS="
+ src/myutil.sh
+"
+
+CHECKS="
+ tests/test_myutil.sh
+"
+```
+
+By making this file executable, shmk will be invoked via its shebang and it will parse this file as a project fille.
+
+The difference between a lib and a prog (library and program) is functionally only in where the files get installed. Programs should be intsalled to a `bin/` directory and are inteded to be run directly. Libraries, even though they are functionally shell programs, are intended to be included by other shell programs and provide variables and functions that are to be used.
+
+## stages
+
+When shmk builds a project, it is able to execute the following steps: (by default in the given order)
+
+- **clean**
+ - cleans the project files
+ - removes outputted files, executables etc
+- **build**
+ - builds all libraries and programs using shmk
+- **check**
+ - run all the available tests
+- **install**
+ - install binaries and libraries
+ - by default will install progs to `/usr/local/bin/` and libraries to `/usr/local/lib/`
+ - `/usr/local/` can be changed using the env var `$PREFIX`
+ - instead of installing to system, package maintainers may use `$DESTDIR` to choose where to install shmk
+- **uninstall**
+ - this stage is not run by default
+ - opposite of install, will follow the same install path and remove any libraries or programs it would have installed
+
+## custom build stages
+
+If desired, a custom build stage can be created:
+
+```sh
+
+prog_mycprog () {
+ gcc -o ${DIST}/myprog src/myprog.c
+}
+
+```
+
+Any function prefixed with `prog_` or `lib_` will be treated as an extra program or library to be built, and said function will be run after handling all of the files mentioned in `LIBS` and `PROGS` variables. This will not override anything from these lists.
+These
+
+As per the example, this can be useful for incorporating non-shmk build stages. As shmk is interpreted like a normal shell file, the syntax should be the same as a shell allowing for extensible configuration of build stages.
+
+The prefix `check_` can also be used for custom check stages. These will not override the exsiting checks
+
+### Checks and tests
+
+A test in shmk is called a `check` (since test is already a shell command). The testing framework is rather rudimentary, and currenty can only be used with custom `check_` prefixed fucntions in the shmk build. Alternatively binaries from the `$CHECKS` variable will also be run. These are not to be confused with other build stages where the programs are never executed themselves: checks are run as-is.
+
+Checks are run consecutively until either all checks reutrn 0 (success) or a single check returns 1.
+
+This feature is intended to be used with the `shtests` tool.
+
+
+## Syntax and formatting
+
+shmk is intended to be as portable as possible, following exclusively POSIX shell syntax. This means that unlike in bash or csh, there is no formal structure for lists, instead whitespace separated strings are used. New lines or spaces both work, however for the sake of simplicity, newlines are prefered. The "standard" shmk syntax for a list is as follows
+
+`
diff --git a/src/util/shmk.sh b/src/util/shmk.sh
index 75e1445..b4c040d 100755
--- a/src/util/shmk.sh
+++ b/src/util/shmk.sh
@@ -13,7 +13,11 @@ ${BLUE}Available Options:
EOF
}
-find_file () {
+# find a file in the search tree
+#
+# _find_file filename
+#
+_find_file () {
for p in $search_path; do
for f in $p/$1.sh $p/$1; do
$verbose \
@@ -28,20 +32,31 @@ find_file () {
return 1
}
-parse_lines () {
+# parse the lines of a file
+#
+# _parse_lines < file
+#
+_parse_lines () {
+
+ # keep a running log of which files we've already included
included=""
- while IFS= read -r line; do
- case "$line" in
+
+ # go through the lines
+ while IFS= read -r line; do
+ case "$line" in
"#include "*)
- file="$(find_file ${line#\#include})"
- case "$included" in
- *"$file"*)
- ;;
+ # find the file
+ file="$(_find_file ${line#\#include})"
+
+ # if we have already included this file, do not parse it
+ case "$included" in
+ *"$file"*) ;;
*)
- cat $file | parse_lines
+ cat $file | _parse_lines
included="$included $file"
esac
;;
+
"#>"*)
eval ${line#'#>'}
;;
@@ -56,6 +71,11 @@ parse_lines () {
done
}
+# 'build' a shell file too an output
+# goes through the file without running it and interprets all shmk commands
+#
+# build_shmk file [output]
+#
build_shmk () {
[ -f "$1" ] || return 1
[ ! -z "$2" ] && output="$2"
@@ -72,11 +92,13 @@ build_shmk () {
[ ! -d "${output%/*}" ] && mkdir -p "${output%/*}"
cat $1 \
- | parse_lines > ${output} &&
+ | _parse_lines > ${output} &&
[ ! -z ${entry_function} ] && echo "$entry_function" >> ${output}
chmod +x ${output}
}
+# take a .shmk file and build it
+#
interpret_shmk () {
. $1
local cmdlist=""
@@ -173,14 +195,23 @@ interpret_shmk () {
}
search_path=".
-/usr/lib
-/usr/local/lib
-/usr/share/shmk
+$DIST/
+/usr/lib/
+/usr/local/lib/
+/usr/share/shmk/
"
verbose=false
clean=false
+# test shmk was invoked on a file if so put the filename at the end because it will break getopts
+# this is a sketchy workaround because the shebang will always put the
+# filename of the project file as the first arg, so this way we can
+# happily introduce more arguments
+[ -f "$1" ] && {
+ set -- $* $1
+}
+
while getopts ":e:I:o:chv" opt; do
case "${opt}" in
e)
@@ -193,8 +224,8 @@ while getopts ":e:I:o:chv" opt; do
clean=true
;;
I)
- search_path="$search_path
- $OPTARG"
+ search_path="$OPTARG
+ $search_path"
;;
v)
verbose=true
@@ -212,7 +243,9 @@ shift $((OPTIND-1))
}
echo "$0 $*"
+
[ -f "$1" ] && shebang="$(head -1 $1)"
+
case "$shebang" in
"#!"*"shmk"*)
interpret_shmk $@
@@ -222,4 +255,3 @@ case "$shebang" in
;;
esac
-