summaryrefslogtreecommitdiffstats
path: root/ship/build
blob: 486f4bed2137a00d652da6126461daf03769c29f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#! /bin/sh
set -euf

## SYNOPSIS
# [debug=true] build compile SRCFILE DSTFILE
# [debug=true] build deps SRCFILE...
build() {
  case "$1" in
    compile) build_compile "$2" "$3";;
    deps) shift; build_deps "$@";;
    *) echo "build: $1: bad command" >&2; return 23;;
  esac
}

# usage: debug_script VARNAME [DESCRIPTION]
debug_script() {
  if test "${debug-false}" = true; then
    printf '====== %s%s\n%s\n' \
        "$1" \
        "${2+" ($2)"}" \
        "$(eval echo \"\$$1\" | nl -b a)" >&2
  fi
}

## build directives
build_info_directive='#@info'
build_include_directive='#@include \([0-9A-Za-z]\+\)'

input_parser="\
s:^ *\([0-9]\+\) "$build_info_directive"$:build_info \1:
s:^ *\([0-9]\+\) "$build_include_directive"$:build_include \1 \2:
t
s:^ *\([0-9]\+\) .*:echo \1p:"
debug_script input_parser

## usage: build_include LINENO LIBNAME
build_include() { cat<<EOF
$1a\\
# begin $2
$1r$(build_resolve $2)
$1a\\
# end $2
EOF
}

## usage: build_info LINENO
build_info() { cat<<EOF
$1a\\
# this file was generated by //ship/build\\
#   date: $(date -u --rfc-3339=s)\\
#   version: $(git rev-parse HEAD)
EOF
}

## usage: build_compile SRCFILE DSTFILE
build_compile() {
  srcfile="$(cat "$1")"
  debug_script srcfile 'SRCFILE'

  while needs_compilation "$srcfile"; do
    script="$(make_sedscript_maker_shellscript "$srcfile")"
    srcfile="$(echo "$srcfile" | sed -n "$script")"
    debug_script srcfile 'sed sedscript srcfile'
  done

  echo "$srcfile" > "$2"
  chmod +x "$2"
}

## usage: needs_compilation SHELLSCRIPT
# Returns true if SRCFILE contains compilation directives.
needs_compilation() {
  echo "$1" |
      grep -q "^\\($build_include_directive\\|$build_info_directive\\)$"
}

## usage: make_sedscript_maker_shellscript SRCFILE
# Print a shellscript that creates a sedscript that resolves all the build
# directives in SRCFILE.
make_sedscript_maker_shellscript() {
  sedscript_generator="$(echo "$1" | nl -b a -s ' ' | sed "$input_parser")"
  debug_script sedscript_generator 'sed input_parser srcfile'

  sedscript="$(eval "$sedscript_generator")"
  debug_script sedscript 'eval sedscript_generator'

  echo "$sedscript"
}

## usage: build_deps SRCFILE...
# Print all the dependencies of SRCFILE... (in alphabetic order) to stdout.
build_deps() {
  while test $# -gt 0; do
    deps="$(
      for f; do
        for d in $(sed -n 's:^'"$build_include_directive"'$:\1:p' "$f"); do
          build_resolve $d
        done
      done
    )"
    set -- $deps
    if test $# -gt 0; then
      echo "$deps"
    fi
  done | sort | uniq
}

## usage: build_resolve LIBNAME
build_resolve() {
  echo "$BUILD_PATH" | tr : \\n |
    xargs -I: printf '%s/%s\n' : "$1" |
    xargs -I: ls -d : 2>/dev/null |
    head -n 1 |
    grep . ||
  {
    echo "build resolve: $1: library not found" >&2
    return 23
  }
}

build "$@"