#!/bin/bash # MKit - simple install helper # See LICENSE file for copyright and license details. mkit_import ini mkit_import facts mkit_import plugin mkit_import util build__file() { # # Process one skeleton $1 of type $3 (or guessed) to path $2 # local srcpath=$1 # skeleton path local dstpath=$2 # destination meaty animal path local ftype=$3 # file/builder type test -n "$dstpath" || dstpath=${srcpath%.skel} test -n "$ftype" || ftype=MKIT_COMMON debug_var srcpath dstpath ftype <"$srcpath" __build__file_type "$ftype" >"$dstpath" build__record "$dstpath" } __build__file_type() { # # Build a file of type $1; fom stdin to stdout # local ftype=$1 # file/builder type if test "$ftype" == MKIT_COMMON; then __build__macro_expand "build:macros" elif plugin__isvalid "$ftype"; then __build__macro_expand "$ftype:macros" else die "unknown file type: $ftype" fi } __build__macro_ls() { # # List known macros # find "${MacroDirs[@]}" -mindepth 1 -depth -printf '%P\n' \ | sed 's/[.]/:/g' } __build__macro_read() { # # Read macro $1 # local key=$1 find "${MacroDirs[@]}" -mindepth 1 -depth -name "$key" \ | head -1 \ | xargs -r cat } __build__macro_put() { # # Write stdin as macro $1 # local fqkey=$1 local file="$MKIT_LOCAL/data/${fqkey//:/.}" mkdir -p "${file%/*}" cat >"$file" } __build__macro_expand_line() { # # Expand macro from config in single line $1 # # If macro value has multiple lines, repeat original line with # different substitution. # # E.g. if macro value is "foo\nbar" and macro name is __FOO__, # line `see: "__FOO__"` will expand to two lines: `see: "foo"` # and `see: "bar"`. # local raw_line=$1 # line to process local macro_name # macro name local macro_value_line # line of macro value local expanded_line # expanded line expanded_line=$raw_line for macro_name in $(__build__macro_ls); do if ! test "${raw_line//$macro_name}" == "$raw_line"; then expanded_line=$( while IFS= read -r macro_value_line; do echo "${raw_line//"$macro_name"/"$macro_value_line"}" done <<<"$(__build__macro_read "$macro_name")" ) fi raw_line=$expanded_line done echo "$expanded_line" return 1 } __build__macro_expand() { # # Read stdin, expanding macros from extra sections $@ # local MacroDirs=() # directories to lookup macros in local mdir # every ^^ local line # each line on stdin local bltndata="$MKIT_LOCAL/data/MKIT_BUILTIN" local usrdata="$MKIT_LOCAL/data/macros" test -d "$bltndata" && MacroDirs+=("$bltndata") test -d "$usrdata" && MacroDirs+=("$usrdata") for extra in "$@"; do mdir="$MKIT_LOCAL/data/${extra//:/.}" test -d "$mdir" || continue MacroDirs+=("$mdir") done while IFS= read -r line; do __build__macro_expand_line "$line" done } build__cached() { # # Cached value $1 of function $1() # # In order to support git-less builds, some values might be cached # in $MKIT_LOCAL. This function gets file $1 from that cache (cache # hit) or re-creates it (cache miss), but prints its body in either # case. # # The command to re-create file is the same as the key (ie. no # arguments). # local name=$1 __local_get "$name" && debug "cache hit with: $name()" && return 0 debug "cache miss with: $name()" "$name" | __local_putb "$name" __local_get "$name" } __local_putb() { # # Make file $1 in $MKIT_LOCAL from stdin and mark as built # local fpath=$1 __local_put "$fpath" && build__record "$MKIT_LOCAL/$fpath" } __local_put() { # # Make file $1 in $MKIT_LOCAL from stdin # local fpath="$MKIT_LOCAL/$1" { mkdir -p "${fpath%/*}" && cat >"$fpath"; } \ || die "cannot write to local cache: $fpath" } __local_get() { # # Read file $1 in $MKIT_LOCAL # local fpath="$MKIT_LOCAL/$1" cat "$fpath" 2>/dev/null } build__fact() { # # Make fact file $1 in $MKIT_LOCAL from stdin; mark as built # local name=$1 __local_put "facts/$name" || die build__recordr "$MKIT_LOCAL/facts" } build__factr() { # # Read fact file $1 from $MKIT_LOCAL # local name=$1 cat "$MKIT_LOCAL/facts/$name" || die } build__record() { # # Record file $1 for deletion on `clean` # local file=$1 mkdir -p "$MKIT_LOCAL" echo "1:$file" >> "$MKIT_LOCAL/built.lst" } build__recordr() { # # Record dir $1 for recursive deletion on `clean` # local path=$1 mkdir -p "$MKIT_LOCAL" echo "r:$path" >> "$MKIT_LOCAL/built.lst" } _mkit_builddata() { # # Build config data # local macro local section test -d "$MKIT_LOCAL/data/build.macros" && { warn "mkit: using cached _mkit_builddata: $(date -Isec -r "$MKIT_LOCAL/data/build.macros")" \ "mkit: (hint: run 'make clean' to regenerate it)" return 0 } build__recordr "$MKIT_LOCAL/data" for macro in $(ini lskeys "build:macros"); do ini values "build:macros:$macro" | __build__macro_put "build:macros/$macro" done } _mkit_inidata() { # # Build INI data # test -d "$MKIT_LOCAL/data/ini" && { warn "mkit: using cached _mkit_inidata: $(date -Isec -r "$MKIT_LOCAL/data/ini")" \ "mkit: (hint: run 'make clean' to regenerate it)" return 0 } build__recordr "$MKIT_LOCAL/data" local sect local key ini lssect \ | while read -r sect; do mkdir -p "$MKIT_LOCAL/data/ini/$sect" ini lskeys "$sect" \ | while read -r key; do ini values "$sect:$key" >"$MKIT_LOCAL/data/ini/$sect/$key" done done } _mkit_metadata() { # # Build meta data # local plg_sections=() test -d "$MKIT_LOCAL/data/MKIT_BUILTIN" && { warn "mkit: using cached _mkit_metadata: $(date -Isec -r "$MKIT_LOCAL/data/MKIT_BUILTIN")" \ "mkit: (hint: run 'make clean' to regenerate it)" return 0 } build__recordr "$MKIT_LOCAL/data" echo "$MKIT_VERSION" | __build__macro_put MKIT_BUILTIN/__MKIT_MKIT_VERSION__ ini 1value project:name | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_NAME__ ini 1value project:codename | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_CODENAME__ ini 1value project:license | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_LICENSE__ ini 1value project:pkgname | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_PKGNAME__ ini 1value project:tagline | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_TAGLINE__ ini 1value project:maintainer | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_MAINTAINER__ ini 1value project:vcs_browser | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_VCS_BROWSER__ build__cached git_lasthash | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_GIT_LASTHASH__ build__cached git_lastsummary | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_GIT_LASTSUMMARY__ build__cached semver | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_VERSION__ util__loadarr plg_sections __plugin_macro_sections debug_var plg_sections for section in macros "${plg_sections[@]}"; do for macro in $(ini lskeys "$section"); do ini values "$section:$macro" | __build__macro_put "$section/$macro" done done } __subdirs() { # # List direct sub-directories of $1 # find "$1" -maxdepth 1 -mindepth 1 -printf '%P\n' -type d } __subfiles() { # # List direct sub-files of $1 # find "$1" -maxdepth 1 -mindepth 1 -printf '%P\n' -type f } __plugin_macro_sections() { # # List macro sections for plugins # plugin__ls \ | sed 's/$/:macros/' } _mkit_show_metadata() { # # Show sampler of macro values # __show_msection MKIT_BUILTIN __show_msection macros __plugin_macro_sections \ | while read -r section; do __show_msection "$section" done } _mkit_show_builddata() { # # Show sampler of config values # __show_msection build:macros } __show_msection() { # # Show macros of section $1 # local section=$1 local macro_name local first=true local label=$section local secdir="$MKIT_LOCAL/data/${section//:/.}" test -d "$secdir" || return 0 test "$section" == MKIT_BUILTIN && label="(builtin)" for macro_name in $(__subfiles "$secdir"); do $first && echo "$label:"; first=false echo " $macro_name => '$(<"$secdir/$macro_name")'" done } build() { # # Add meat to all skeletons # local srcpath # each source path find . -type f -name '*.skel' \ | while read -r srcpath; do build__file "$srcpath" done } clean() { # # Clean up tree after building # local path local line local depth test -f "$MKIT_LOCAL/built.lst" || return 0 { cat "$MKIT_LOCAL/built.lst" echo "1:$MKIT_LOCAL/built.lst" echo "1:$MKIT_LOCAL" } \ | grep -v -e '\.\.' -e ^/ -e '^~' \ | while IFS=: read -r depth path; do test -e "$path" || continue case $depth in 1) warn "removing: $path" test -d "$path" \ && rmdir -p --ignore-fail-on-non-empty "$path" test -f "$path" && rm "$path" ;; r) warn "removing recursively: $path" rm -r "$path" ;; *) warn "invalid built.lst format!" ;; esac done true } dist() { # # Create distributable tarball # #FIXME: lacking Makefile skills, we do this step twice for # rpmstuff, hence -f hack for gzip # local version # tarball version local git_lasthash # last git commit hash local dirname # directory and tarball name version=$(semver) dirname=$MKIT_PROJ_PKGNAME-$version git_lasthash=$(git_lasthash) debug_var version dirname git_lasthash mkdir -p "$dirname" ini values "dist:tarball" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname" mkdir -p "$dirname/.mkit" echo -n "$version" > "$dirname/.mkit/semver" echo -n "$git_lasthash" > "$dirname/.mkit/git_lasthash" cp -r "$MKIT_LOCAL/data" "$dirname/.mkit/" tar -cf "$dirname.tar" "$dirname" gzip -f "$dirname.tar" # see above FIXME build__record "$dirname.tar.gz" rm -rf "$dirname" }