initial commit
This commit is contained in:
commit
656501f916
6 changed files with 187 additions and 0 deletions
79
README.md
Normal file
79
README.md
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# shellbuild
|
||||||
|
|
||||||
|
shellbuild is an experimental build system meant to be tiny and easy to use. It has only a couple of functions and it's written in shell.
|
||||||
|
|
||||||
|
# using
|
||||||
|
|
||||||
|
Information here is subject to be changed. Please make sure to report inconsistencies.
|
||||||
|
|
||||||
|
## create a project
|
||||||
|
|
||||||
|
creating a project goes as follows:
|
||||||
|
- create a new folder
|
||||||
|
- optionally set up Git
|
||||||
|
- if you decided to set up git, make sure to add `.sbcache` to the .gitignore (along with object files and other artifacts of course)
|
||||||
|
- create a file called `sbrc` and mark it as executable
|
||||||
|
|
||||||
|
## configuration
|
||||||
|
|
||||||
|
**tip**: an example config file is available in the `example` subdirectory of this repository.
|
||||||
|
|
||||||
|
## sbrc structure
|
||||||
|
|
||||||
|
A `sbrc` is based on functions that can be called by itself or the user. These functions are always prefixed with an `_`.
|
||||||
|
|
||||||
|
The sbrc below would print `Hello!` when ran with `sb hello`.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
_hello() {
|
||||||
|
echo "Hello!"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When you run shellbuild, it simply loads the sbrc (and runs the function specified by the user.)
|
||||||
|
|
||||||
|
If no argument is passed to sb, it will try to run `__default` (yes, with **two** underscores):
|
||||||
|
```shell
|
||||||
|
__default() {
|
||||||
|
echo "hello"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Running the example above with just `sb` would print `hello`.
|
||||||
|
|
||||||
|
## built-in functions
|
||||||
|
|
||||||
|
sb comes with a couple of built-in functions. Some of them are simply aliases to common commands to make your code more readable:
|
||||||
|
- `match_files` runs `find` with the pattern provided as the first argument
|
||||||
|
- `match_files_recurse` is like `match_files` but with no defined recursion limit
|
||||||
|
- `ext` finds all files with the specified extension
|
||||||
|
- `ext_all` finds all files with the specified extension but with no defined recursion limit.
|
||||||
|
|
||||||
|
## the `depends` built-in function
|
||||||
|
|
||||||
|
This function is so interesting that it deserves its own section. Every argument is either a file, or a function (prefixed by an underscore).
|
||||||
|
|
||||||
|
If any one of the files in the arguments has changed since the last time the program ran, it will echo `modified="FILES_CHANGED"`, where `FILES_CHANGED` is a space-separated list of all the files that changed since the last time the program ran.
|
||||||
|
|
||||||
|
If none of the files were modified, it will echo `return 3`.
|
||||||
|
|
||||||
|
You may notice that the output of this function looks like code. That's because it is! You're meant to eval its output, something like this: `eval $(depends mycfile.c _my_function)`.
|
||||||
|
|
||||||
|
This lets us make functions like this:
|
||||||
|
```shell
|
||||||
|
__default() {
|
||||||
|
eval $(depends hello.c)
|
||||||
|
echo "you will only see this message if hello.c was modified since the last time this program ran! cool, innit?"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## how the `depends` built-in function works
|
||||||
|
|
||||||
|
You don't really need to know this if you're making a simple program, but it can absolutely come in handy once you start working on more advanced sbrc's.
|
||||||
|
|
||||||
|
When ran, it will iterate through every argument passed to it and, if it comes across a file, it will create a corresponding file in the `.sbcache` directory. This file contains the last modified date of the source file. If the cache file exists, the time it was modified is comapred against the one in the cache. If they're different, it means it was changed.
|
||||||
|
|
||||||
|
If it comes across a function, it will call it. If that function's return status code is `3`, it is deemed as unchanged.
|
||||||
|
|
||||||
|
## notice
|
||||||
|
|
||||||
|
This project is just an experiment - I don't even know if it would work in a real-world project (I am yet to test that) or if it has some fundamental flaw.
|
7
example/hello.c
Normal file
7
example/hello.c
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "message.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("%s\n\n", generate_hello_message());
|
||||||
|
return 0;
|
||||||
|
}
|
3
example/message.c
Normal file
3
example/message.c
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const char* generate_hello_message() {
|
||||||
|
return "Hello, world!!!!!!!";
|
||||||
|
}
|
6
example/message.h
Normal file
6
example/message.h
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef _MESSAGE_H
|
||||||
|
#define _MESSAGE_H
|
||||||
|
|
||||||
|
const char* generate_hello_message();
|
||||||
|
|
||||||
|
#endif
|
19
example/sbrc
Normal file
19
example/sbrc
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
CC="gcc"
|
||||||
|
CFLAGS=""
|
||||||
|
|
||||||
|
_message() {
|
||||||
|
eval $(depends message.c message.h)
|
||||||
|
$CC ${CFLAGS} -c message.c
|
||||||
|
}
|
||||||
|
|
||||||
|
_hello() {
|
||||||
|
eval $(depends hello.c _message)
|
||||||
|
$CC ${CFLAGS} -c hello.c
|
||||||
|
}
|
||||||
|
|
||||||
|
__default() {
|
||||||
|
eval $(depends _hello)
|
||||||
|
$CC hello.o message.o ${CFLAGS} -o main
|
||||||
|
}
|
73
sb
Executable file
73
sb
Executable file
|
@ -0,0 +1,73 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "$0: error: $1"
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
sbcache="$PWD/.sbcache"
|
||||||
|
[ ! -d $sbcache ] && mkdir "$sbcache"
|
||||||
|
|
||||||
|
# [utilities]
|
||||||
|
match_files() {
|
||||||
|
find . -maxdepth 1 -type f -name "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
match_files_recurse() {
|
||||||
|
find . -type f -name "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
ext() {
|
||||||
|
match_files "*.$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
ext_all() {
|
||||||
|
match_files_recurse "*.$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
depends() {
|
||||||
|
modified=""
|
||||||
|
continue_anyway="false"
|
||||||
|
for i in "$@"; do
|
||||||
|
# if any of the files are new or modifed, we will let the caller continue execution
|
||||||
|
case "$i" in
|
||||||
|
_*)
|
||||||
|
"$i" >/dev/null
|
||||||
|
if [ $? -ne 3 ]; then
|
||||||
|
continue_anyway="true"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
cachepath="$sbcache/.$(basename $i).sbmod"
|
||||||
|
if [ ! -f "$cachepath" ] || [ $(cat "$cachepath") != $(stat -c '%Y' "$i") ]; then
|
||||||
|
echo $(stat -c '%Y' "$i") > "$cachepath"
|
||||||
|
modified="${modified} ${i}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$modified" = "" ] && [ "$continue_anyway" == "false" ]; then
|
||||||
|
echo "return 3"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo 'modified="$modified"'
|
||||||
|
}
|
||||||
|
|
||||||
|
# load the file, making its functions and variables available here and vice-versa
|
||||||
|
sbfile="$PWD/sbrc"
|
||||||
|
[ ! -f "$sbfile" ] && die "no such sbrc in current directory"
|
||||||
|
. "$sbfile"
|
||||||
|
|
||||||
|
# run functions from the file as requested by the user
|
||||||
|
case "$1" in
|
||||||
|
"")
|
||||||
|
__default
|
||||||
|
if [ $? -eq 3 ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
"_$1" || die "failed to run script $1"
|
||||||
|
;;
|
||||||
|
esac
|
Reference in a new issue