123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. __build1() {
  7. #
  8. # Process one skeleton $1 of type $3 (or guessed) to path $2
  9. #
  10. local srcpath=$1 # skeleton path
  11. local dstpath=$2 # destination meaty animal path
  12. local ftype=$3 # file/builder type
  13. test -n "$dstpath" || dstpath=${srcpath%.skel}
  14. test -n "$ftype" || ftype=$(__guess_ftype "$dstpath")
  15. debug_var srcpath dstpath ftype
  16. <"$srcpath" __build1_ftype "$ftype" >"$dstpath"
  17. __rec_built "$dstpath"
  18. }
  19. __build1_ftype() {
  20. #
  21. # Build a file of type $1; fom stdin to stdout
  22. #
  23. local ftype=$1 # file/builder type
  24. case $ftype in
  25. MKIT_COMMON) __expand_macros "macros" ;;
  26. rpmstuff) __expand_macros "macros" "rpmstuff:macros" ;;
  27. debstuff) __expand_macros "macros" "debstuff:macros" ;;
  28. *) die "unknown file type: $ftype" ;;
  29. esac
  30. }
  31. __expand_line() {
  32. #
  33. # Expand macro from $MacroMap in single line $1
  34. #
  35. # If macro value has multiple lines, repeat original line with
  36. # different substitution.
  37. #
  38. # E.g. if macro value is "foo\nbar" and macro name is __FOO__,
  39. # line `see: "__FOO__"` will expand to two lines: `see: "foo"`
  40. # and `see: "bar"`.
  41. #
  42. local line=$1 # line to process
  43. local mname # macro name
  44. local mvline # line of macro value
  45. local xline # expanded line
  46. xline=$line
  47. for mname in "${!MacroMap[@]}"; do
  48. if ! test "${line//$mname}" == "$line"; then
  49. xline=$(
  50. while IFS= read -r mvline; do
  51. echo "${line//$mname/$mvline}"
  52. done <<<"${MacroMap[$mname]}"
  53. )
  54. fi
  55. line=$xline
  56. done
  57. echo "$xline"
  58. return 1
  59. }
  60. __expand_macros() {
  61. #
  62. # Read stdin, expanding macros from sections $@
  63. #
  64. local section # each section to expand macros from
  65. local line # each line on stdin
  66. local mname # each macro name
  67. local -A MacroMap # macro value map
  68. MacroMap[__MKIT_MKIT_VERSION__]=$MKIT_VERSION
  69. MacroMap[__MKIT_PROJ_NAME__]=$(ini 1value project:name)
  70. MacroMap[__MKIT_PROJ_CODENAME__]=$(ini 1value project:codename)
  71. MacroMap[__MKIT_PROJ_LICENSE__]=$(ini 1value project:license)
  72. MacroMap[__MKIT_PROJ_PKGNAME__]=$(ini 1value project:pkgname)
  73. MacroMap[__MKIT_PROJ_TAGLINE__]=$(ini 1value project:tagline)
  74. MacroMap[__MKIT_PROJ_MAINTAINER__]=$(ini 1value project:maintainer)
  75. MacroMap[__MKIT_PROJ_VCS_BROWSER__]=$(ini 1value project:vcs_browser)
  76. MacroMap[__MKIT_PROJ_GIT_LASTHASH__]=$(__cached git_lasthash)
  77. MacroMap[__MKIT_PROJ_GIT_LASTSUMMARY__]=$(__cached git_lastsummary)
  78. MacroMap[__MKIT_PROJ_VERSION__]=$(__cached semver)
  79. for section in "$@"; do
  80. for mname in $(ini lskeys "$section"); do
  81. MacroMap[$mname]=$(ini values "$section:$mname")
  82. done
  83. done
  84. debug_var MacroMap
  85. while IFS= read -r line; do
  86. __expand_line "$line"
  87. done
  88. }
  89. __guess_ftype() {
  90. #
  91. # Guess file type from destination path $1
  92. #
  93. local dstpath=$1 # destination path
  94. case $dstpath in
  95. *) echo MKIT_COMMON ;;
  96. esac
  97. }
  98. __qfs() {
  99. #
  100. # Quote for our sed scipt's RHS
  101. #
  102. sed '
  103. s:\\:\\\\:g
  104. s:|:\\|:g
  105. '
  106. }
  107. __cached() {
  108. #
  109. # Cached value $1 of function $1()
  110. #
  111. # In order to support git-less builds, some values might be cached
  112. # in $MKIT_LOCAL. This function gets file $1 from that cache (cache
  113. # hit) or re-creates it (cache miss), but prints its body in either
  114. # case.
  115. #
  116. # The command to re-create file is the same as the key (ie. no
  117. # arguments).
  118. #
  119. local name=$1
  120. __local_get "$name" && return 0
  121. "$name" | __local_putb "$name"
  122. __local_get "$name"
  123. }
  124. __local_putb() {
  125. #
  126. # Make file $1 in $MKIT_LOCAL from stdin and mark as built
  127. #
  128. local fpath=$1
  129. __local_put "$fpath" && __rec_built "$MKIT_LOCAL/$fpath"
  130. }
  131. __local_put() {
  132. #
  133. # Make file $1 in $MKIT_LOCAL from stdin
  134. #
  135. local fpath="$MKIT_LOCAL/$1"
  136. { mkdir -p "${fpath%/*}" && cat >"$fpath"; } \
  137. || die "cannot write to local cache: $fpath"
  138. }
  139. __local_get() {
  140. #
  141. # Read file $1 in $MKIT_LOCAL
  142. #
  143. local fpath="$MKIT_LOCAL/$1"
  144. cat "$fpath" 2>/dev/null
  145. }
  146. __rec_built() {
  147. #
  148. # Record file $1 for deletion on `clean`
  149. #
  150. local file=$1
  151. mkdir -p "$MKIT_LOCAL"
  152. echo "1:$file" >> "$MKIT_LOCAL/built.lst"
  153. }
  154. _mkit_data() {
  155. #
  156. # Build sampler showing all macro values
  157. #
  158. local macro
  159. local section
  160. local sections
  161. sections=()
  162. ini lskeys macros | grep -q . && sections=(macros)
  163. sections+=( $(ini lssect | grep ':macros$') )
  164. {
  165. echo "(builtin):"
  166. echo " x_MKIT_MKIT_VERSION__ => '__MKIT_MKIT_VERSION__'"
  167. echo " x_MKIT_PROJ_NAME__ => '__MKIT_PROJ_NAME__'"
  168. echo " x_MKIT_PROJ_CODENAME__ => '__MKIT_PROJ_CODENAME__'"
  169. echo " x_MKIT_PROJ_LICENSE__ => '__MKIT_PROJ_LICENSE__'"
  170. echo " x_MKIT_PROJ_PKGNAME__ => '__MKIT_PROJ_PKGNAME__'"
  171. echo " x_MKIT_PROJ_TAGLINE__ => '__MKIT_PROJ_TAGLINE__'"
  172. echo " x_MKIT_PROJ_MAINTAINER__ => '__MKIT_PROJ_MAINTAINER__'"
  173. echo " x_MKIT_PROJ_VCS_BROWSER__ => '__MKIT_PROJ_VCS_BROWSER__'"
  174. echo " x_MKIT_PROJ_GIT_LASTHASH__ => '__MKIT_PROJ_GIT_LASTHASH__'"
  175. echo " x_MKIT_PROJ_GIT_LASTSUMMARY__ => '__MKIT_PROJ_GIT_LASTSUMMARY__'"
  176. echo " x_MKIT_PROJ_VERSION__ => '__MKIT_PROJ_VERSION__'"
  177. for section in "${sections[@]}"; do
  178. echo "$section:"
  179. for macro in $(ini lskeys "$section"); do
  180. echo " x${macro:1} => '$macro'"
  181. done
  182. done
  183. } \
  184. | __expand_macros "MKIT_BUILTIN" "${sections[@]}" \
  185. | sed '/^ x/ s|x|_|'
  186. }
  187. build() {
  188. #
  189. # Add meat to all skeletons
  190. #
  191. local srcpath # each source path
  192. find . -type f -name '*.skel' \
  193. | while read -r srcpath; do
  194. __build1 "$srcpath"
  195. done
  196. }
  197. clean() {
  198. #
  199. # Clean up tree after building
  200. #
  201. local path
  202. local line
  203. local depth
  204. test -f "$MKIT_LOCAL/built.lst" || return 0
  205. {
  206. cat "$MKIT_LOCAL/built.lst"
  207. echo "1:$MKIT_LOCAL/built.lst"
  208. echo "1:$MKIT_LOCAL"
  209. } \
  210. | grep -v -e '\.\.' -e ^/ -e '^~' \
  211. | while IFS=: read -r depth path; do
  212. test -e "$path" || continue
  213. case $depth in
  214. 1) warn "removing: $path"
  215. test -d "$path" \
  216. && rmdir -p --ignore-fail-on-non-empty "$path"
  217. test -f "$path" && rm "$path"
  218. ;;
  219. r) warn "removing recursively: $path"
  220. rm -r "$path"
  221. ;;
  222. *) warn "invalid built.lst format!"
  223. ;;
  224. esac
  225. done
  226. true
  227. }
  228. debstuff() {
  229. #
  230. # Build Debian stuff (eamed tarball, debian dir)
  231. #
  232. local version # package version
  233. local debian_skel # 'debian' folder skeleton
  234. local dfsrc # each source file from ^^
  235. local dftgt # each built packaging file
  236. version=$(__cached semver)
  237. # tarball - we should already have by means of 'dist'
  238. #
  239. mv "${MKIT_PROJ_PKGNAME}-$version.tar.gz" \
  240. "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz" \
  241. || die "could not rename tarball"
  242. __rec_built "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz"
  243. # read content of each mandatory file from debian_skel
  244. #
  245. debian_skel=$(ini 1value dist:debstuff)
  246. test -n "$debian_skel" || die "dist:debstuff not specified"
  247. test -d "$debian_skel" || die "debian directory template found: $debian_skel"
  248. mkdir -p debian/source
  249. find "$debian_skel" -type f \
  250. | while read -r dfsrc; do
  251. dftgt="debian/${dfsrc#$debian_skel}"
  252. mkdir -p "$(dirname "$dftgt")"
  253. __build1 "$dfsrc" "$dftgt" debstuff
  254. done
  255. __rec_built debian
  256. }
  257. dist() {
  258. #
  259. # Create distributable tarball
  260. #
  261. #FIXME: lacking Makefile skills, we do this step twice for
  262. # rpmstuff, hence -f hack for gzip
  263. #
  264. local version # tarball version
  265. local git_lasthash # last git commit hash
  266. local dirname # directory and tarball name
  267. version=$(semver)
  268. dirname=$MKIT_PROJ_PKGNAME-$version
  269. git_lasthash=$(git_lasthash)
  270. mkdir -p "$dirname"
  271. ini values "dist:tarball" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname"
  272. mkdir -p "$dirname/.mkit"
  273. echo -n "$version" > "$dirname/.mkit/semver"
  274. echo -n "$git_lasthash" > "$dirname/.mkit/git_lasthash"
  275. tar -cf "$dirname.tar" "$dirname"
  276. gzip -f "$dirname.tar" # see above FIXME
  277. __rec_built "$dirname.tar.gz"
  278. rm -rf "$dirname"
  279. }
  280. rpmstuff() {
  281. #
  282. # Build specfile
  283. #
  284. local specname=$MKIT_PROJ_PKGNAME.spec # .spec filename
  285. local specsrc # source of specfile
  286. specsrc="$(ini 1value "dist:rpmstuff")"
  287. test -n "$specsrc" || die "dist:rpmstuff not specified"
  288. test -f "$specsrc" || die "specfile template not found: $specsrc"
  289. __build1 "$specsrc" "$specname" rpmstuff
  290. }