#!/bin/bashset -eu vecho() { ${vecho-:} "$@"; }executable_p() { command -v "$1" >/dev/null 2>&1; }dgst='cksum "$@" | while read d _; do printf $d; done'! executable_p b3sum || dgst='b3sum --no-names "$@"'executable_p realpath || realpath() (cd "$1" &>/dev/null; pwd -P)eval "digest() { $dgst; } 2>/dev/null"die() { echo "$@" >&2; exit 111; }trap 'test $? -eq 0 || echo "build failed"' EXIT darwin=falseopenbsd=falsewsid="wsi/x11"clip="LC_CTYPE=UTF-8 xclip -i"paste="LC_CTYPE=UTF-8 xclip -o"uopen="echo 'Open "%s"' >&2"print="echo 'Print "%s"' >&2"case "$(uname)" in Darwin) darwin=true wsid="wsi/cocoa" clip="LC_CTYPE=UTF-8 pbcopy" paste="LC_CTYPE=UTF-8 pbaste" uopen='open "%s"' mjobs=$(getconf _NPROCESSORS_ONLN || echo 1) ;; Linux) mjobs=$(getconf _NPROCESSORS_ONLN || echo 1) ;; OpenBSD) openbsd=true mjobs=$(getconf NPROCESSORS_ONLN || echo 1) export CXX=c++ ;; *) die $(uname) is not supported;;esac test -n "${1-}" || die "usage: $0 build-directory" outd=$1srcd=$(dirname $0)mudir=$outd/mupdfmuinc="-I $mudir/include -I $mudir/thirdparty/freetype/include" test -d $mudir || die muPDF wasn\'t found in $outd/, consult $srcd/BUILDING mkdir -p $outd/{$wsid,lablGL} mbt=${mbt:-release}test -n "${gmk:-}" && gmk=false || gmk=true mulibs="$mudir/build/$mbt/libmupdf.a $mudir/build/$mbt/libmupdf-third.a"! $openbsd && gmake="make" || gmake="gmake"make="$gmake -C "$mudir" build=$mbt -j $mjobs libs"$make -q -s || $make oincs() { local b=$1 incs case "${2#$outd/}" in $wsid/wsi.cm[oi]|confstruct.cmo|help.cmo) incs="-I $b -I $b/$wsid";; glutils.cmo) incs="-I $b -I $b/lablGL";; uiutils.cmo|main.cmo) incs="-I $b -I $b/$wsid -I $b/lablGL";; ffi.cmo|help.cmi|parser.cmo) incs="-I $b";; config.cmo) incs="-I $b -I $b/$wsid" test "$b" = $outd || incs="$incs -I $outd" ;; lablGL/*) incs="-I $b/lablGL";; main.cmo|keys.cmo|utils.cmo|utf8syms.cmo) incs="-I $b";; config.cmi) incs="-I $outd -I $b -I $b/$wsid";; uiutils.cmi|ffi.cmi) incs="-I $b";; glutils.cmi) incs="-I $b/lablGL";; main.cmi|keys.cmi|utils.cmi|utf8syms.cmi|parser.cmi) ;; *) die "ocaml include paths for '$2' aren't set";; esac test -z "${incs-}" || echo $incs} oflags() { case "${1#$outd/}" in lablGL/*) f="-g";; utf8syms.cmo|confstruct.cmo|config.cmo|ffi.cmo|wsi/cocoa/wsi.cmo) f="-g -strict-sequence -strict-formats -alert @all-missing-mli";; *) f="-g -strict-sequence -strict-formats -alert @all -warn-error @A";; esac echo $(oincs $outd $1) $f} cflags() { case "${1#$outd/}" in version.o) f=-DLLPP_VERSION=$ver;; lablGL/*.o) f="-g -Wno-pointer-sign -Werror -O2";; link.o) f="-g -std=c11 $muinc -Wall -Werror -Wextra -pedantic " test "${mbt-}" = "debug" || f+="-O2 " $darwin && f+="-DMACOS -D_GNU_SOURCE -DGL_H='<OpenGL/gl.h>'" \ || f+="-D_POSIX_C_SOURCE -DGL_H='<GL/gl.h>'" f+=" -DTEXT_TYPE=GL_TEXTURE_RECTANGLE_ARB" #f+=" -DLLPARANOIDP" #f+=" -DTEXT_TYPE=GL_TEXTURE_2D" ;; *) f="-g -O2 -Wall -Werror";; esac ! $darwin || f+=" -DGL_SILENCE_DEPRECATION" ! $openbsd || f+=" -I/usr/X11R6/include" echo $f} mflags() { echo "-I $(ocamlc -where) -g -Wall -Werror -O2 -DGL_SILENCE_DEPRECATION"} overs=$(ocamlc -vnum 2>/dev/null) || overs=""if test "$overs" != "4.14.0~rc1"; then url=https://caml.inria.fr/pub/distrib/ocaml-4.14/ocaml-4.14.0~rc1.tar.xz txz=$outd/$(basename $url) test -r $txz || { if executable_p wget; then dl() { wget "$1" -O "$2"; } elif executable_p curl; then dl() { curl -L "$1" -o "$2"; } else die "no program to fetch remote urls found" fi dl $url $txz } && vecho "fresh $txz" absprefix=$(realpath $outd) export PATH=$absprefix/bin:$PATH ocamlc=$absprefix/bin/ocamlc test -x $ocamlc || ( # This will needlessly re{configure,make} ocaml since "past" # of configure/make is hard to ascertain. "Better safe than # sorry" approach is taken here. The check will work for a # single ocaml url/version, but _will_ redo _everything_ # otherwise (even if fully built artifacts are available) xzcat $txz | tar xf - -C $outd bn=$(basename $url) cd $outd/${bn%.tar.xz} ./configure --disable-ocamldoc --disable-ocamltest \ --enable-debugger=no --prefix=$absprefix $gmake -j $mjobs world $gmake install ) && vecho "fresh ocamlc" overs=$(ocamlc -vnum 2>/dev/null)fi while read k v; do case "$k" in "bytecomp_c_compiler:") ccomp=${CAML_CC-$v};; "word_size:") ! test "$darwin$v" = "true32" || die "need 64bit ocaml";; esacdone < <(ocamlc -config) read cvers < <($ccomp --version) seen=ord=$gmk || :>$outd/Makefilebocaml1() { local n=$1 s=$2 o=$3 deps= cmd d cmd="ocamlc -depend -bytecode -one-line $(oincs $srcd $o) $s" test -f "$o.depl" || { read _ _ depl < <(eval $cmd) || die "$cmd failed" for d in $depl; do if test "$d" = "$outd/confstruct.cmo"; then d=confstruct.cmo; else d=${d#$srcd/}; fi deps+="$d\n" done printf "$deps" >$o.depl deps= } && vecho "fresh $o.depl" # this saves time but is overly optimistic as interface (dis) # appearance will result in an invalid (stale) .depl (cache). not # using a cache is correct but slow(er (much)) way to handle this. while read d; do bocaml $d $((n+1)) deps+=" $outd/$d" done <$o.depl cmd="ocamlc $(oflags $o) -c -o $o $s" test -f "$o" || { printf "%*.s%s\n" $n '' "${o#$outd/}" eval "$cmd" || die "$cmd failed" } && vecho "fresh $o" $gmk || printf "$o: $deps\n\t%s\n" "$cmd" >>$outd/Makefile seen+=$o ord+=" $o"} cycle=bocaml() { [[ ! $seen =~ $1 ]] || return 0 local s o=$1 n=$2 cycle1=$cycle case $o in confstruct.cmo) s=$outd/confstruct.ml;; *.cmo) s=$srcd/${o%.cmo}.ml;; *.cmi) s=$srcd/${o%.cmi}.mli;; esac o=$outd/$o [[ "$cycle" =~ "$o" ]] && die cycle $o || cycle=$cycle$o bocaml1 $n $s $o cycle=$cycle1} baux() { local o=$1 cmd=$2 read 2>/dev/null _ d <$o.dep || d= test -f "$o" || { echo "${o#$outd/}" eval "$cmd" || die "$cmd failed" read _ d <$o.dep } && vecho "fresh $o" $gmk || printf "$o: $d\n\t$cmd\n" >>$outd/Makefile} bocamlc() { local o=$outd/$1 s=$srcd/${1%.o}.c cc=${CAML_CC:+-cc "'$CAML_CC'" } baux $o "ocamlc $cc-ccopt \"$(cflags $o) -MMD -MF $o.dep -MT_\" -o $o -c $s"} bobjc() { local o=$outd/$1 baux $o "$mcomp $(mflags $o) -MD -MF $o.dep -MT_ -c -o $o $srcd/${1%.o}.m"} ver=$(cd $srcd && git describe --tags --dirty) || ver="'built on $(date)'" gen=$srcd/genconfstruct.shout=$outd/confstruct.mlcmd="(export print paste clip uopen; . $gen >$out)"test -f "$out" || { echo "generating $out" eval "$cmd" || die $gen failed} && vecho "fresh $out" shift 1for target; do case "$target" in doc) md=$outd/doc mkdir -p $md for m in llpp llppac; do src=$srcd/adoc/$m.adoc o=$md/$m.1 conf=$srcd/man/asciidoc.conf cmd="a2x -f manpage -D $md $src" test -f "$o" || { echo "${o#$outd/}" eval "$cmd" || die "$cmd failed" } && vecho "fresh $o" done; exit;; *) die "no such target - '$target'";; esacdone flatten() { local o [[ ! "$seen" =~ "$1" ]] || return 0 bocaml $1 0 for o in $ord; do local wooutd=${o#$outd/} case $o in *.cmi) flatten ${wooutd%.cmi}.cmo;; *.cmo) flatten $wooutd;; esac done}flatten main.cmo modules=collectmodules() { # it might appear that following can be done inside bocaml* but # alas due to the early cmi->cmo descent this ought to be done # here (at least the solution inside bocaml* eludes me) local dep cmo this=$1 while read dep; do case $dep in *.cmi) cmo=${dep%.cmi}.cmo test $cmo = $this || collectmodules $cmo ;; *.cmo) collectmodules $dep cmo=$dep ;; esac [[ $modules =~ $cmo ]] || modules+=" $outd/$cmo" done <$outd/$1.depl}collectmodules main.cmo cobjs=for m in link cutils version; do bocamlc $m.o cobjs+=" $outd/$m.o"donefor m in ml_gl ml_glarray ml_raw; do bocamlc lablGL/$m.o cobjs+=" $outd/lablGL/$m.o"done libs="str.cma unix.cma"clibs="-L$mudir/build/$mbt -lmupdf -lmupdf-third -lpthread"if $darwin; then mcomp=$ccomp clibs+=" -framework Cocoa -framework OpenGL" cobjs+=" $outd/wsi/cocoa/cocoa.o" bobjc wsi/cocoa/cocoa.oelse ! $openbsd || clibs+=" -L/usr/X11R6/lib" clibs+=" -lGL -lX11" cobjs+=" $outd/wsi/x11/keysym2ucs.o $outd/wsi/x11/xlib.o" bocamlc wsi/x11/keysym2ucs.o bocamlc wsi/x11/xlib.ofi cmd="ocamlc -custom $libs -o $outd/llpp $cobjs $modules -cclib \"$clibs\""test -x "$outd/llpp" || { echo linking $outd/llpp eval "$cmd" || die "$cmd failed"} && vecho "fresh llpp"$gmk || printf "$outd/llpp: $cobjs $modules $mulibs\n\t$cmd\n" >>$outd/Makefile if $darwin; then out="$outd/llpp.app/Contents/Info.plist" test -f $out || { d=$(dirname $out) mkdir -p "$d" echo "generating $out" (. $srcd/wsi/cocoa/genplist.sh) >"$out" } && vecho "fresh plist" out=$outd/llpp.app/Contents/MacOS/llpp test -f $out || { echo "bundling $out" mkdir -p "$(dirname $out)" cp $outd/llpp $out } && vecho "fresh bundle"fi
#!/bin/bash
set -eu
vecho() { ${vecho-:} "$@"; }
executable_p() { command -v "$1" >/dev/null 2>&1; }
dgst='cksum "$@" | while read d _; do printf $d; done'
! executable_p b3sum || dgst='b3sum --no-names "$@"'
executable_p realpath || realpath() (cd "$1" &>/dev/null; pwd -P)
eval "digest() { $dgst; } 2>/dev/null"
die() { echo "$@" >&2; exit 111; }
trap 'test $? -eq 0 || echo "build failed"' EXIT
darwin=false
openbsd=false
wsid="wsi/x11"
clip="LC_CTYPE=UTF-8 xclip -i"
paste="LC_CTYPE=UTF-8 xclip -o"
uopen="echo 'Open "%s"' >&2"
print="echo 'Print "%s"' >&2"
case "$(uname)" in
Darwin)
darwin=true
wsid="wsi/cocoa"
clip="LC_CTYPE=UTF-8 pbcopy"
paste="LC_CTYPE=UTF-8 pbaste"
uopen='open "%s"'
mjobs=$(getconf _NPROCESSORS_ONLN || echo 1)
;;
Linux)
mjobs=$(getconf _NPROCESSORS_ONLN || echo 1)
;;
OpenBSD)
openbsd=true
mjobs=$(getconf NPROCESSORS_ONLN || echo 1)
export CXX=c++
;;
*) die $(uname) is not supported;;
esac
test -n "${1-}" || die "usage: $0 build-directory"
outd=$1
srcd=$(dirname $0)
mudir=$outd/mupdf
muinc="-I $mudir/include -I $mudir/thirdparty/freetype/include"
test -d $mudir || die muPDF wasn\'t found in $outd/, consult $srcd/BUILDING
mkdir -p $outd/{$wsid,lablGL}
mbt=${mbt:-release}
test -n "${gmk:-}" && gmk=false || gmk=true
mulibs="$mudir/build/$mbt/libmupdf.a $mudir/build/$mbt/libmupdf-third.a"
! $openbsd && gmake="make" || gmake="gmake"
make="$gmake -C "$mudir" build=$mbt -j $mjobs libs"
$make -q -s || $make
oincs() {
local b=$1 incs
case "${2#$outd/}" in
$wsid/wsi.cm[oi]|confstruct.cmo|help.cmo) incs="-I $b -I $b/$wsid";;
glutils.cmo) incs="-I $b -I $b/lablGL";;
uiutils.cmo|main.cmo) incs="-I $b -I $b/$wsid -I $b/lablGL";;
ffi.cmo|help.cmi|parser.cmo) incs="-I $b";;
config.cmo)
incs="-I $b -I $b/$wsid"
test "$b" = $outd || incs="$incs -I $outd"
;;
lablGL/*) incs="-I $b/lablGL";;
main.cmo|keys.cmo|utils.cmo|utf8syms.cmo) incs="-I $b";;
config.cmi) incs="-I $outd -I $b -I $b/$wsid";;
uiutils.cmi|ffi.cmi) incs="-I $b";;
glutils.cmi) incs="-I $b/lablGL";;
main.cmi|keys.cmi|utils.cmi|utf8syms.cmi|parser.cmi) ;;
*) die "ocaml include paths for '$2' aren't set";;
esac
test -z "${incs-}" || echo $incs
}
oflags() {
case "${1#$outd/}" in
lablGL/*) f="-g";;
utf8syms.cmo|confstruct.cmo|config.cmo|ffi.cmo|wsi/cocoa/wsi.cmo)
f="-g -strict-sequence -strict-formats -alert @all-missing-mli";;
*) f="-g -strict-sequence -strict-formats -alert @all -warn-error @A";;
esac
echo $(oincs $outd $1) $f
}
cflags() {
case "${1#$outd/}" in
version.o) f=-DLLPP_VERSION=$ver;;
lablGL/*.o) f="-g -Wno-pointer-sign -Werror -O2";;
link.o)
f="-g -std=c11 $muinc -Wall -Werror -Wextra -pedantic "
test "${mbt-}" = "debug" || f+="-O2 "
$darwin && f+="-DMACOS -D_GNU_SOURCE -DGL_H='<OpenGL/gl.h>'" \
|| f+="-D_POSIX_C_SOURCE -DGL_H='<GL/gl.h>'"
f+=" -DTEXT_TYPE=GL_TEXTURE_RECTANGLE_ARB"
#f+=" -DLLPARANOIDP"
#f+=" -DTEXT_TYPE=GL_TEXTURE_2D"
;;
*) f="-g -O2 -Wall -Werror";;
esac
! $darwin || f+=" -DGL_SILENCE_DEPRECATION"
! $openbsd || f+=" -I/usr/X11R6/include"
echo $f
}
mflags() {
echo "-I $(ocamlc -where) -g -Wall -Werror -O2 -DGL_SILENCE_DEPRECATION"
}
overs=$(ocamlc -vnum 2>/dev/null) || overs=""
if test "$overs" != "4.14.0~rc1"; then
url=https://caml.inria.fr/pub/distrib/ocaml-4.14/ocaml-4.14.0~rc1.tar.xz
txz=$outd/$(basename $url)
test -r $txz || {
if executable_p wget; then dl() { wget "$1" -O "$2"; }
elif executable_p curl; then dl() { curl -L "$1" -o "$2"; }
else die "no program to fetch remote urls found"
fi
dl $url $txz
} && vecho "fresh $txz"
absprefix=$(realpath $outd)
export PATH=$absprefix/bin:$PATH
ocamlc=$absprefix/bin/ocamlc
test -x $ocamlc || (
# This will needlessly re{configure,make} ocaml since "past"
# of configure/make is hard to ascertain. "Better safe than
# sorry" approach is taken here. The check will work for a
# single ocaml url/version, but _will_ redo _everything_
# otherwise (even if fully built artifacts are available)
xzcat $txz | tar xf - -C $outd
bn=$(basename $url)
cd $outd/${bn%.tar.xz}
./configure --disable-ocamldoc --disable-ocamltest \
--enable-debugger=no --prefix=$absprefix
$gmake -j $mjobs world
$gmake install
) && vecho "fresh ocamlc"
overs=$(ocamlc -vnum 2>/dev/null)
fi
while read k v; do
case "$k" in
"bytecomp_c_compiler:") ccomp=${CAML_CC-$v};;
"word_size:") ! test "$darwin$v" = "true32" || die "need 64bit ocaml";;
esac
done < <(ocamlc -config)
read cvers < <($ccomp --version)
seen=
ord=
$gmk || :>$outd/Makefile
bocaml1() {
local n=$1 s=$2 o=$3 deps= cmd d
cmd="ocamlc -depend -bytecode -one-line $(oincs $srcd $o) $s"
test -f "$o.depl" || {
read _ _ depl < <(eval $cmd) || die "$cmd failed"
for d in $depl; do
if test "$d" = "$outd/confstruct.cmo";
then d=confstruct.cmo; else d=${d#$srcd/}; fi
deps+="$d\n"
done
printf "$deps" >$o.depl
deps=
} && vecho "fresh $o.depl"
# this saves time but is overly optimistic as interface (dis)
# appearance will result in an invalid (stale) .depl (cache). not
# using a cache is correct but slow(er (much)) way to handle this.
while read d; do
bocaml $d $((n+1))
deps+=" $outd/$d"
done <$o.depl
cmd="ocamlc $(oflags $o) -c -o $o $s"
test -f "$o" || {
printf "%*.s%s\n" $n '' "${o#$outd/}"
eval "$cmd" || die "$cmd failed"
} && vecho "fresh $o"
$gmk || printf "$o: $deps\n\t%s\n" "$cmd" >>$outd/Makefile
seen+=$o
ord+=" $o"
}
cycle=
bocaml() {
[[ ! $seen =~ $1 ]] || return 0
local s o=$1 n=$2 cycle1=$cycle
case $o in
confstruct.cmo) s=$outd/confstruct.ml;;
*.cmo) s=$srcd/${o%.cmo}.ml;;
*.cmi) s=$srcd/${o%.cmi}.mli;;
esac
o=$outd/$o
[[ "$cycle" =~ "$o" ]] && die cycle $o || cycle=$cycle$o
bocaml1 $n $s $o
cycle=$cycle1
}
baux() {
local o=$1 cmd=$2
read 2>/dev/null _ d <$o.dep || d=
test -f "$o" || {
echo "${o#$outd/}"
eval "$cmd" || die "$cmd failed"
read _ d <$o.dep
} && vecho "fresh $o"
$gmk || printf "$o: $d\n\t$cmd\n" >>$outd/Makefile
}
bocamlc() {
local o=$outd/$1 s=$srcd/${1%.o}.c cc=${CAML_CC:+-cc "'$CAML_CC'" }
baux $o "ocamlc $cc-ccopt \"$(cflags $o) -MMD -MF $o.dep -MT_\" -o $o -c $s"
}
bobjc() {
local o=$outd/$1
baux $o "$mcomp $(mflags $o) -MD -MF $o.dep -MT_ -c -o $o $srcd/${1%.o}.m"
}
ver=$(cd $srcd && git describe --tags --dirty) || ver="'built on $(date)'"
gen=$srcd/genconfstruct.sh
out=$outd/confstruct.ml
cmd="(export print paste clip uopen; . $gen >$out)"
test -f "$out" || {
echo "generating $out"
eval "$cmd" || die $gen failed
} && vecho "fresh $out"
shift 1
for target; do
case "$target" in
doc)
md=$outd/doc
mkdir -p $md
for m in llpp llppac; do
src=$srcd/adoc/$m.adoc
o=$md/$m.1
conf=$srcd/man/asciidoc.conf
cmd="a2x -f manpage -D $md $src"
test -f "$o" || {
echo "${o#$outd/}"
eval "$cmd" || die "$cmd failed"
} && vecho "fresh $o"
done;
exit;;
*) die "no such target - '$target'";;
esac
done
flatten() {
local o
[[ ! "$seen" =~ "$1" ]] || return 0
bocaml $1 0
for o in $ord; do
local wooutd=${o#$outd/}
case $o in
*.cmi) flatten ${wooutd%.cmi}.cmo;;
*.cmo) flatten $wooutd;;
esac
done
}
flatten main.cmo
modules=
collectmodules() {
# it might appear that following can be done inside bocaml* but
# alas due to the early cmi->cmo descent this ought to be done
# here (at least the solution inside bocaml* eludes me)
local dep cmo this=$1
while read dep; do
case $dep in
*.cmi)
cmo=${dep%.cmi}.cmo
test $cmo = $this || collectmodules $cmo
;;
*.cmo)
collectmodules $dep
cmo=$dep
;;
esac
[[ $modules =~ $cmo ]] || modules+=" $outd/$cmo"
done <$outd/$1.depl
}
collectmodules main.cmo
cobjs=
for m in link cutils version; do
bocamlc $m.o
cobjs+=" $outd/$m.o"
done
for m in ml_gl ml_glarray ml_raw; do
bocamlc lablGL/$m.o
cobjs+=" $outd/lablGL/$m.o"
done
libs="str.cma unix.cma"
clibs="-L$mudir/build/$mbt -lmupdf -lmupdf-third -lpthread"
if $darwin; then
mcomp=$ccomp
clibs+=" -framework Cocoa -framework OpenGL"
cobjs+=" $outd/wsi/cocoa/cocoa.o"
bobjc wsi/cocoa/cocoa.o
else
! $openbsd || clibs+=" -L/usr/X11R6/lib"
clibs+=" -lGL -lX11"
cobjs+=" $outd/wsi/x11/keysym2ucs.o $outd/wsi/x11/xlib.o"
bocamlc wsi/x11/keysym2ucs.o
bocamlc wsi/x11/xlib.o
fi
cmd="ocamlc -custom $libs -o $outd/llpp $cobjs $modules -cclib \"$clibs\""
test -x "$outd/llpp" || {
echo linking $outd/llpp
eval "$cmd" || die "$cmd failed"
} && vecho "fresh llpp"
$gmk || printf "$outd/llpp: $cobjs $modules $mulibs\n\t$cmd\n" >>$outd/Makefile
if $darwin; then
out="$outd/llpp.app/Contents/Info.plist"
test -f $out || {
d=$(dirname $out)
mkdir -p "$d"
echo "generating $out"
(. $srcd/wsi/cocoa/genplist.sh) >"$out"
} && vecho "fresh plist"
out=$outd/llpp.app/Contents/MacOS/llpp
test -f $out || {
echo "bundling $out"
mkdir -p "$(dirname $out)"
cp $outd/llpp $out
} && vecho "fresh bundle"
fi