imapfilter convenience wrapper

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. #!/bin/bash
  2. # MKit - simple install helper
  3. # See LICENSE file for copyright and license details.
  4. mkit_import ini
  5. mkit_import facts
  6. mkit_import plugin
  7. mkit_import util
  8. build__file() {
  9. #
  10. # Process one skeleton $1 of type $3 (or guessed) to path $2
  11. #
  12. local srcpath=$1 # skeleton path
  13. local dstpath=$2 # destination meaty animal path
  14. local ftype=$3 # file/builder type
  15. test -n "$dstpath" || dstpath=${srcpath%.skel}
  16. test -n "$ftype" || ftype=MKIT_COMMON
  17. debug_var srcpath dstpath ftype
  18. <"$srcpath" __build__file_type "$ftype" >"$dstpath"
  19. build__record "$dstpath"
  20. }
  21. __build__file_type() {
  22. #
  23. # Build a file of type $1; fom stdin to stdout
  24. #
  25. local ftype=$1 # file/builder type
  26. if test "$ftype" == MKIT_COMMON; then
  27. __build__macro_expand "build:macros"
  28. elif plugin__isvalid "$ftype"; then
  29. __build__macro_expand "$ftype:macros"
  30. else
  31. die "unknown file type: $ftype"
  32. fi
  33. }
  34. __build__macro_ls() {
  35. #
  36. # List known macros
  37. #
  38. find "${MacroDirs[@]}" -mindepth 1 -depth -printf '%P\n' \
  39. | sed 's/[.]/:/g'
  40. }
  41. __build__macro_read() {
  42. #
  43. # Read macro $1
  44. #
  45. local key=$1
  46. find "${MacroDirs[@]}" -mindepth 1 -depth -name "$key" \
  47. | head -1 \
  48. | xargs -r cat
  49. }
  50. __build__macro_put() {
  51. #
  52. # Write stdin as macro $1
  53. #
  54. local fqkey=$1
  55. local file="$MKIT_LOCAL/data/${fqkey//:/.}"
  56. mkdir -p "${file%/*}"
  57. cat >"$file"
  58. }
  59. __build__macro_expand_line() {
  60. #
  61. # Expand macro from config in single line $1
  62. #
  63. # If macro value has multiple lines, repeat original line with
  64. # different substitution.
  65. #
  66. # E.g. if macro value is "foo\nbar" and macro name is __FOO__,
  67. # line `see: "__FOO__"` will expand to two lines: `see: "foo"`
  68. # and `see: "bar"`.
  69. #
  70. local raw_line=$1 # line to process
  71. local macro_name # macro name
  72. local macro_value_line # line of macro value
  73. local expanded_line # expanded line
  74. expanded_line=$raw_line
  75. for macro_name in $(__build__macro_ls); do
  76. if ! test "${raw_line//$macro_name}" == "$raw_line"; then
  77. expanded_line=$(
  78. while IFS= read -r macro_value_line; do
  79. echo "${raw_line//"$macro_name"/"$macro_value_line"}"
  80. done <<<"$(__build__macro_read "$macro_name")"
  81. )
  82. fi
  83. raw_line=$expanded_line
  84. done
  85. echo "$expanded_line"
  86. return 1
  87. }
  88. __build__macro_expand() {
  89. #
  90. # Read stdin, expanding macros from extra sections $@
  91. #
  92. local MacroDirs=() # directories to lookup macros in
  93. local mdir # every ^^
  94. local line # each line on stdin
  95. local bltndata="$MKIT_LOCAL/data/MKIT_BUILTIN"
  96. local usrdata="$MKIT_LOCAL/data/macros"
  97. test -d "$bltndata" && MacroDirs+=("$bltndata")
  98. test -d "$usrdata" && MacroDirs+=("$usrdata")
  99. for extra in "$@"; do
  100. mdir="$MKIT_LOCAL/data/${extra//:/.}"
  101. test -d "$mdir" || continue
  102. MacroDirs+=("$mdir")
  103. done
  104. while IFS= read -r line; do
  105. __build__macro_expand_line "$line"
  106. done
  107. }
  108. build__cached() {
  109. #
  110. # Cached value $1 of function $1()
  111. #
  112. # In order to support git-less builds, some values might be cached
  113. # in $MKIT_LOCAL. This function gets file $1 from that cache (cache
  114. # hit) or re-creates it (cache miss), but prints its body in either
  115. # case.
  116. #
  117. # The command to re-create file is the same as the key (ie. no
  118. # arguments).
  119. #
  120. local name=$1
  121. __local_get "$name" && debug "cache hit with: $name()" && return 0
  122. debug "cache miss with: $name()"
  123. "$name" | __local_putb "$name"
  124. __local_get "$name"
  125. }
  126. __local_putb() {
  127. #
  128. # Make file $1 in $MKIT_LOCAL from stdin and mark as built
  129. #
  130. local fpath=$1
  131. __local_put "$fpath" && build__record "$MKIT_LOCAL/$fpath"
  132. }
  133. __local_put() {
  134. #
  135. # Make file $1 in $MKIT_LOCAL from stdin
  136. #
  137. local fpath="$MKIT_LOCAL/$1"
  138. { mkdir -p "${fpath%/*}" && cat >"$fpath"; } \
  139. || die "cannot write to local cache: $fpath"
  140. }
  141. __local_get() {
  142. #
  143. # Read file $1 in $MKIT_LOCAL
  144. #
  145. local fpath="$MKIT_LOCAL/$1"
  146. cat "$fpath" 2>/dev/null
  147. }
  148. build__fact() {
  149. #
  150. # Make fact file $1 in $MKIT_LOCAL from stdin; mark as built
  151. #
  152. local name=$1
  153. __local_put "facts/$name" || die
  154. build__recordr "$MKIT_LOCAL/facts"
  155. }
  156. build__factr() {
  157. #
  158. # Read fact file $1 from $MKIT_LOCAL
  159. #
  160. local name=$1
  161. cat "$MKIT_LOCAL/facts/$name" || die
  162. }
  163. build__record() {
  164. #
  165. # Record file $1 for deletion on `clean`
  166. #
  167. local file=$1
  168. mkdir -p "$MKIT_LOCAL"
  169. echo "1:$file" >> "$MKIT_LOCAL/built.lst"
  170. }
  171. build__recordr() {
  172. #
  173. # Record dir $1 for recursive deletion on `clean`
  174. #
  175. local path=$1
  176. mkdir -p "$MKIT_LOCAL"
  177. echo "r:$path" >> "$MKIT_LOCAL/built.lst"
  178. }
  179. _mkit_builddata() {
  180. #
  181. # Build config data
  182. #
  183. local macro
  184. local section
  185. test -d "$MKIT_LOCAL/data/build.macros" && {
  186. warn "mkit: using cached _mkit_builddata: $(date -Isec -r "$MKIT_LOCAL/data/build.macros")" \
  187. "mkit: (hint: run 'make clean' to regenerate it)"
  188. return 0
  189. }
  190. build__recordr "$MKIT_LOCAL/data"
  191. for macro in $(ini lskeys "build:macros"); do
  192. ini values "build:macros:$macro" | __build__macro_put "build:macros/$macro"
  193. done
  194. }
  195. _mkit_inidata() {
  196. #
  197. # Build INI data
  198. #
  199. test -d "$MKIT_LOCAL/data/ini" && {
  200. warn "mkit: using cached _mkit_inidata: $(date -Isec -r "$MKIT_LOCAL/data/ini")" \
  201. "mkit: (hint: run 'make clean' to regenerate it)"
  202. return 0
  203. }
  204. build__recordr "$MKIT_LOCAL/data"
  205. local sect
  206. local key
  207. ini lssect \
  208. | while read -r sect; do
  209. mkdir -p "$MKIT_LOCAL/data/ini/$sect"
  210. ini lskeys "$sect" \
  211. | while read -r key; do
  212. ini values "$sect:$key" >"$MKIT_LOCAL/data/ini/$sect/$key"
  213. done
  214. done
  215. }
  216. _mkit_metadata() {
  217. #
  218. # Build meta data
  219. #
  220. local plg_sections=()
  221. test -d "$MKIT_LOCAL/data/MKIT_BUILTIN" && {
  222. warn "mkit: using cached _mkit_metadata: $(date -Isec -r "$MKIT_LOCAL/data/MKIT_BUILTIN")" \
  223. "mkit: (hint: run 'make clean' to regenerate it)"
  224. return 0
  225. }
  226. build__recordr "$MKIT_LOCAL/data"
  227. echo "$MKIT_VERSION" | __build__macro_put MKIT_BUILTIN/__MKIT_MKIT_VERSION__
  228. ini 1value project:name | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_NAME__
  229. ini 1value project:codename | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_CODENAME__
  230. ini 1value project:license | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_LICENSE__
  231. ini 1value project:pkgname | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_PKGNAME__
  232. ini 1value project:tagline | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_TAGLINE__
  233. ini 1value project:maintainer | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_MAINTAINER__
  234. ini 1value project:vcs_browser | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_VCS_BROWSER__
  235. build__cached git_lasthash | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_GIT_LASTHASH__
  236. build__cached git_lastsummary | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_GIT_LASTSUMMARY__
  237. build__cached semver | __build__macro_put MKIT_BUILTIN/__MKIT_PROJ_VERSION__
  238. util__loadarr plg_sections __plugin_macro_sections
  239. debug_var plg_sections
  240. for section in macros "${plg_sections[@]}"; do
  241. for macro in $(ini lskeys "$section"); do
  242. ini values "$section:$macro" | __build__macro_put "$section/$macro"
  243. done
  244. done
  245. }
  246. __subdirs() {
  247. #
  248. # List direct sub-directories of $1
  249. #
  250. find "$1" -maxdepth 1 -mindepth 1 -printf '%P\n' -type d
  251. }
  252. __subfiles() {
  253. #
  254. # List direct sub-files of $1
  255. #
  256. find "$1" -maxdepth 1 -mindepth 1 -printf '%P\n' -type f
  257. }
  258. __plugin_macro_sections() {
  259. #
  260. # List macro sections for plugins
  261. #
  262. plugin__ls \
  263. | sed 's/$/:macros/'
  264. }
  265. _mkit_show_metadata() {
  266. #
  267. # Show sampler of macro values
  268. #
  269. __show_msection MKIT_BUILTIN
  270. __show_msection macros
  271. __plugin_macro_sections \
  272. | while read -r section; do
  273. __show_msection "$section"
  274. done
  275. }
  276. _mkit_show_builddata() {
  277. #
  278. # Show sampler of config values
  279. #
  280. __show_msection build:macros
  281. }
  282. __show_msection() {
  283. #
  284. # Show macros of section $1
  285. #
  286. local section=$1
  287. local macro_name
  288. local first=true
  289. local label=$section
  290. local secdir="$MKIT_LOCAL/data/${section//:/.}"
  291. test -d "$secdir" || return 0
  292. test "$section" == MKIT_BUILTIN && label="(builtin)"
  293. for macro_name in $(__subfiles "$secdir"); do
  294. $first && echo "$label:"; first=false
  295. echo " $macro_name => '$(<"$secdir/$macro_name")'"
  296. done
  297. }
  298. build() {
  299. #
  300. # Add meat to all skeletons
  301. #
  302. local srcpath # each source path
  303. find . -type f -name '*.skel' \
  304. | while read -r srcpath; do
  305. build__file "$srcpath"
  306. done
  307. }
  308. clean() {
  309. #
  310. # Clean up tree after building
  311. #
  312. local path
  313. local line
  314. local depth
  315. test -f "$MKIT_LOCAL/built.lst" || return 0
  316. {
  317. cat "$MKIT_LOCAL/built.lst"
  318. echo "1:$MKIT_LOCAL/built.lst"
  319. echo "1:$MKIT_LOCAL"
  320. } \
  321. | grep -v -e '\.\.' -e ^/ -e '^~' \
  322. | while IFS=: read -r depth path; do
  323. test -e "$path" || continue
  324. case $depth in
  325. 1) warn "removing: $path"
  326. test -d "$path" \
  327. && rmdir -p --ignore-fail-on-non-empty "$path"
  328. test -f "$path" && rm "$path"
  329. ;;
  330. r) warn "removing recursively: $path"
  331. rm -r "$path"
  332. ;;
  333. *) warn "invalid built.lst format!"
  334. ;;
  335. esac
  336. done
  337. true
  338. }
  339. dist() {
  340. #
  341. # Create distributable tarball
  342. #
  343. #FIXME: lacking Makefile skills, we do this step twice for
  344. # rpmstuff, hence -f hack for gzip
  345. #
  346. local version # tarball version
  347. local git_lasthash # last git commit hash
  348. local dirname # directory and tarball name
  349. version=$(semver)
  350. dirname=$MKIT_PROJ_PKGNAME-$version
  351. git_lasthash=$(git_lasthash)
  352. debug_var version dirname git_lasthash
  353. mkdir -p "$dirname"
  354. ini values "dist:tarball" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname"
  355. mkdir -p "$dirname/.mkit"
  356. echo -n "$version" > "$dirname/.mkit/semver"
  357. echo -n "$git_lasthash" > "$dirname/.mkit/git_lasthash"
  358. cp -r "$MKIT_LOCAL/data" "$dirname/.mkit/"
  359. tar -cf "$dirname.tar" "$dirname"
  360. gzip -f "$dirname.tar" # see above FIXME
  361. build__record "$dirname.tar.gz"
  362. rm -rf "$dirname"
  363. }