build.sh 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. #!/bin/bash
  2. . "$MKIT_DIR/include/ini.sh" || die "cannot import ini.sh"
  3. build() {
  4. #
  5. # Add meat to all skeletons
  6. #
  7. local srcpath
  8. find src -type f -name '*.skel' \
  9. | while read srcpath;
  10. do
  11. build1 "$srcpath"
  12. done
  13. }
  14. build1() {
  15. #
  16. # Process one skeleton
  17. #
  18. local srcpath="$1"
  19. local dstpath="$2"
  20. local ftype="$3"
  21. test -n "$dstpath" || dstpath=${srcpath%.skel}
  22. test -n "$ftype" || ftype=$(guess_ftype "$dstpath")
  23. debug_var srcpath dstpath ftype
  24. <"$srcpath" build1_ftype "$ftype" >"$dstpath"
  25. mkdir -p "$MKIT_LOCAL"
  26. echo "$dstpath" >> "$MKIT_LOCAL/built.lst"
  27. }
  28. guess_ftype() {
  29. #
  30. # Guess file type from destination path $1
  31. #
  32. local dstpath="$1"
  33. case $dstpath in
  34. *.md) echo markdown ;;
  35. *) echo MKIT_COMMON ;;
  36. esac
  37. }
  38. build1_ftype() {
  39. #
  40. # Build a file of type $1
  41. #
  42. local ftype="$1"
  43. case $ftype in
  44. MKIT_COMMON) expand_variables "vars" ;;
  45. markdown) expand_includes | expand_variables "vars" ;;
  46. rpmstuff) expand_variables "vars" "rpmstuff:vars" ;;
  47. *) die "unknown file type: $ftype" ;;
  48. esac
  49. }
  50. build_manpages() {
  51. local manfile mdfile
  52. if command -v ronn >/dev/null;
  53. then
  54. ini lskeys "files:man" \
  55. | while read manfile;
  56. do
  57. mdfile="$manfile.md"
  58. ronn -r "$mdfile"
  59. mkdir -p "$MKIT_LOCAL"
  60. echo "$manfile" >> "$MKIT_LOCAL/built.lst"
  61. done
  62. else
  63. echo "ronn is not installed"
  64. return 1
  65. fi
  66. }
  67. clean() {
  68. #
  69. # Clean up tree after building
  70. #
  71. test -f "$MKIT_LOCAL/built.lst" && {
  72. <"$MKIT_LOCAL/built.lst" grep -v -e '\.\.' -e ^/ \
  73. | xargs -r rm -rf
  74. rm -f "$MKIT_LOCAL/built.lst"
  75. rmdir --ignore-fail-on-non-empty "$MKIT_LOCAL"
  76. }
  77. true
  78. }
  79. dist() {
  80. #
  81. # Create distributable tarball
  82. #
  83. #FIXME: lacking Makefile skills, we do this step twice fot
  84. # rpmstuff, hence -f hack for gzip
  85. #
  86. local version=$(get_version)
  87. local dirname=$MKIT_PROJ_PKGNAME-$version
  88. mkdir -p "$dirname"
  89. ini values "lists:dist" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname"
  90. update_version "$version" "$dirname/mkit.ini"
  91. tar -cf "$dirname.tar" "$dirname"
  92. gzip -f "$dirname.tar" # see above FIXME
  93. mkdir -p "$MKIT_LOCAL"
  94. echo "$dirname.tar.gz" >> "$MKIT_LOCAL/built.lst"
  95. rm -rf "$dirname"
  96. }
  97. debstuff() {
  98. #
  99. # Build Debian stuff (eamed tarball, debian dir)
  100. #
  101. local version="$(get_version)"
  102. # tarball - we should already have by means of 'dist'
  103. #
  104. mv "${MKIT_PROJ_PKGNAME}-$version.tar.gz" \
  105. "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz" \
  106. || die "could not rename tarball"
  107. echo "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz" >> "$MKIT_LOCAL/built.lst"
  108. # read content of each mandatory file from debian_skel
  109. #
  110. local debian_skel=$(ini 1value debstuff:debian_skel)
  111. test -n "$debian_skel" || die "debstuff:debian_skel not specified"
  112. test -d "$debian_skel" || die "debian directory template found: $debian_skel"
  113. mkdir -p debian/source
  114. local dfsrc dftgt
  115. find "$debian_skel" -type f \
  116. | while read dfsrc;
  117. do
  118. dftgt="debian/${dfsrc#$debian_skel}"
  119. mkdir -p "$(dirname "$dftgt")"
  120. build1 "$dfsrc" "$dftgt"
  121. done
  122. echo debian >> "$MKIT_LOCAL/built.lst"
  123. }
  124. rpmstuff() {
  125. #
  126. # Build specfile
  127. #
  128. local specname="$MKIT_PROJ_PKGNAME.spec"
  129. local specsrc="$(ini 1value "rpmstuff:spec_skel")"
  130. test -n "$specsrc" || die "rpmstuff:spec_skel not specified"
  131. test -f "$specsrc" || die "specfile template not found: $specsrc"
  132. build1 "$specsrc" "$specname"
  133. }
  134. expand_includes() {
  135. #
  136. # Expand include directives
  137. #
  138. # Expand e.g. `<!-- include4: foo.sh -->` to include code of foo.sh
  139. #
  140. perl -we '
  141. use strict;
  142. my $text;
  143. while (<>) {
  144. chomp;
  145. if (m/<!-- include4: (\S+) -->/) {
  146. open my $fh, $1 or warn "cannot find: $1";
  147. my $text = do { local($/); <$fh> };
  148. close $fh;
  149. $text =~ s/^(.)/ $1/gm;
  150. chomp $text;
  151. print "$text\n";
  152. } else {
  153. print "$_\n";
  154. }
  155. }
  156. '
  157. }
  158. expand_variables() {
  159. #
  160. # Expand variables from sections $@
  161. #
  162. local script=$(mktemp --tmpdir mkit-tmp.XXXXXXXXXX)
  163. local section varname varvalue
  164. {
  165. for section in "$@";
  166. do
  167. debug_var section
  168. ini lskeys "$section" \
  169. | while read varname;
  170. do
  171. varvalue="$(ini 1value "$section:$varname" | sed -e 's/\$/\\$/' )"
  172. echo "s|$varname|$varvalue|;"
  173. debug_var varname varvalue
  174. done
  175. done
  176. echo "s|__MKIT_PROJ_CODENAME__|$(ini 1value project:codename)|;"
  177. echo "s|__MKIT_PROJ_PKGNAME__|$(ini 1value project:pkgname)|;"
  178. echo "s|__MKIT_PROJ_TAGLINE__|$(ini 1value project:tagline)|;"
  179. echo "s|__MKIT_PROJ_VERSION__|$(get_version)|;"
  180. echo "s|__MKIT_SELF_VERSION__|$MKIT_VERSION|;"
  181. } >> "$script"
  182. perl -wp "$script" || die "expand_variables failed"
  183. rm "$script"
  184. }
  185. get_version() {
  186. #
  187. # Build semver version string with build metadata
  188. #
  189. # Build version string from available info using following
  190. # logic:
  191. #
  192. # 1. use project.version (from mkit.ini)
  193. # 2. if we are in git, override the version with last tag
  194. # 3. if set, add project:prerl (from mkit.ini) as pre-release ID
  195. # (afer dash)
  196. # 4. if we are at a later commit than the last tag, add branch
  197. # name and commit sha1 to build metadata (after plus sign)
  198. # 5. if the tree is "dirty", i.e. has uncommited changes,
  199. # add "dirty" to build metadata
  200. #
  201. # The version is compatible with SemVer 2.0.0.
  202. #
  203. # Examples:
  204. #
  205. # myprog v1.0.7 # all clear
  206. # myprog v1.0.7-alpha # mkit.ini: project:prerl="alpha"
  207. # myprog v1.0.7-alpha+g1aef811.master # ^^ + some commits after
  208. # myprog v1.0.7-alpha+gf14fc4f.api2 # ^^ + on a feature branch
  209. # myprog v1.0.7-alpha+gf14fc4f.api2.dirty # ^^ + tree edited
  210. # myprog v1.0.7-alpha+dirty # tag OK but tree edited
  211. # myprog v1.0.7+dirty # ^^ but no pre-release id
  212. #
  213. # Note that versions with "dirty" should be perceived as kind of
  214. # dangerous outside developer's own machine. Versions with sha1 are
  215. # safer but must not be released.
  216. #
  217. # I have considered decorating the git commit refs to make them
  218. # sort of sortable (e.g. "r1.g1aef811"), but on second thought,
  219. # I don't think it's good idea to give *any* semantics to meta-data
  220. # at all. First, there is no rule that r1<r2<r3; a commit can be
  221. # removing what other just added and one change can be split to
  222. # multiple commits. Also, the whole thing breaks anyway once you
  223. # rebase your branch (no, it's not a sin). The sole purpose of
  224. # meta-data is to *identify* the code, and provide safe path back
  225. # to tree; commit refs are already perfect for that.
  226. #
  227. # FIXME: Using project:prerl for release IDs may not be compatible with
  228. # release strategy implemented in release.sh
  229. #
  230. local version=$(ini 1value project:version)
  231. local prerl=$(ini 1value project:prerl)
  232. grep ":" <<<"$prerl" && warn "colon in project:prerl may corrupt version data: $prerl"
  233. if git rev-parse HEAD >&/dev/null;
  234. then # we are in git repo... so we can get smart
  235. local latest_tag=$(
  236. git log --format="%D" \
  237. | sed 's/,/\n/g' \
  238. | sed 's/^[[:blank:]]*//; ' \
  239. | grep -E '^tag: v[[:digit:]]+\.' \
  240. | cut -d' ' -f2 \
  241. | head -1
  242. )
  243. if ! git describe --tags --exact-match HEAD >&/dev/null;
  244. then # we are at a later commit than the last tag
  245. local sha=g$(git log -1 --pretty=format:%h HEAD)
  246. local curbranch=$(git rev-parse --abbrev-ref HEAD)
  247. local commit="$curbranch.$sha"
  248. fi
  249. if test "$(git diff --shortstat 2>/dev/null)" != "";
  250. then # the tree is "dirty", i.e. has been edited
  251. local dirty=dirty
  252. fi
  253. test -n "$latest_tag" && version=${latest_tag:1}
  254. local suffix=""
  255. case "$commit:$dirty" in
  256. :) suffix="" ;;
  257. :dirty) suffix="+$dirty" ;;
  258. *:) suffix="+$commit" ;;
  259. *:dirty) suffix="+$commit.$dirty" ;;
  260. esac
  261. test -b "$prerl" && suffix="-$prerl$suffix"
  262. version="$version$suffix"
  263. fi
  264. echo "$version"
  265. }