1#!/usr/bin/env bash
2# MSYS2 build and test script for MyPaint.
3# All rights waived: https://creativecommons.org/publicdomain/zero/1.0/
4#
5#: Usage:
6#:   $ msys2_build.sh [OPTIONS]
7#:
8#: OPTIONS:
9#:   installdeps  Build+install dependencies.
10#:   build        Build MyPaint itself from this source tree.
11#:   clean        Clean the build tree.
12#:   tests        Runs tests on the built source.
13#:   doctest      Check to make sure all python docs work.
14#:   bundle       Creates installer bundles in ./out/bundles
15#:
16#:  This script is designed to be called by AppVeyor or Tea-CI. However
17#:  it's clean enough to run from an interactive shell. It expects to be
18#:  called with MSYSTEM="MINGW{64,32}", i.e. from an MSYS2 "native" shell.
19#:
20#:  Build artifacts are written to ./out/pkgs and ./out/bundles by default.
21
22set -e
23
24# ANSI control codes
25RED='\033[0;31m'
26GREEN='\033[0;32m'
27CYAN='\033[0;36m'
28NC='\033[0m' # No Color
29
30# Script name and location.
31SCRIPT=`basename "$0"`
32SCRIPTDIR=`dirname "$0"`
33cd "$SCRIPTDIR/.."
34
35# Main repository location, as an absolute path.
36TOPDIR=`pwd`
37cd "$TOPDIR"
38
39# Ensure we're being run from one of MSYS2's "native shells".
40case "$MSYSTEM" in
41    "MINGW64")
42        PKG_PREFIX="mingw-w64-x86_64"
43        MINGW_INSTALLS="mingw64"
44        BUNDLE_ARCH="w64"
45        ;;
46    "MINGW32")
47        PKG_PREFIX="mingw-w64-i686"
48        MINGW_INSTALLS="mingw32"
49        BUNDLE_ARCH="w32"
50        ;;
51    *)
52        echo >&2 "$SCRIPT must only be called from a MINGW64/32 login shell."
53        exit 1
54        ;;
55esac
56export MINGW_INSTALLS
57
58# This script pulls down and maintains a clone of the pkgbuild tree for
59# MSYS2's MINGW32 and MINGW64 software.
60SRC_ROOT="${SRC_ROOT:-/tmp/src}"
61SRC_PROJECT="mingw"
62SRC_DIR="${SRC_ROOT}/${SRC_PROJECT}"
63SRC_CLONEURI="https://github.com/Alexpux/MINGW-packages.git"
64
65# Output location for build artifacts.
66OUTPUT_ROOT="${OUTPUT_ROOT:-$TOPDIR/out}"
67
68install_dependencies() {
69
70    loginfo "Removing potential package conflicts..."
71    pacman --remove --noconfirm ${PKG_PREFIX}-mypaint || true
72    pacman --remove --noconfirm ${PKG_PREFIX}-mypaint || true
73    pacman --remove --noconfirm ${PKG_PREFIX}-libmypaint || true
74    pacman --remove --noconfirm ${PKG_PREFIX}-mypaint-brushes2 || true
75
76    loginfo "Installing pre-built dependencies for MyPaint"
77    pacman -S --noconfirm --needed --noprogressbar \
78        ${PKG_PREFIX}-toolchain \
79        ${PKG_PREFIX}-pkg-config \
80        ${PKG_PREFIX}-glib2 \
81        ${PKG_PREFIX}-gtk3 \
82        ${PKG_PREFIX}-json-c \
83        ${PKG_PREFIX}-lcms2 \
84        ${PKG_PREFIX}-python3-cairo \
85        ${PKG_PREFIX}-pygobject-devel \
86        ${PKG_PREFIX}-python3-gobject \
87        ${PKG_PREFIX}-python3-numpy \
88        ${PKG_PREFIX}-hicolor-icon-theme \
89        ${PKG_PREFIX}-librsvg \
90        ${PKG_PREFIX}-gobject-introspection \
91        ${PKG_PREFIX}-python3-nose \
92        ${PKG_PREFIX}-python3-setuptools \
93        ${PKG_PREFIX}-swig \
94        ${PKG_PREFIX}-gsettings-desktop-schemas \
95        base-devel \
96        git
97
98    loginfo "Installing pre-built dependencies for Styrene + its install"
99    pacman -S --noconfirm --needed --noprogressbar \
100        ${PKG_PREFIX}-nsis \
101        ${PKG_PREFIX}-gcc \
102        ${PKG_PREFIX}-binutils \
103        ${PKG_PREFIX}-python3 \
104        ${PKG_PREFIX}-python3-pip \
105        zip \
106        p7zip
107
108    logok "Dependencies installed."
109
110    gdk-pixbuf-query-loaders --update-cache
111    logok "Pixbuf query loaders cache updated"
112}
113
114
115loginfo() {
116    echo -ne "${CYAN}"
117    echo -n "$@"
118    echo -e "${NC}"
119}
120
121
122logok() {
123    echo -ne "${GREEN}"
124    echo -n "$@"
125    echo -e "${NC}"
126}
127
128
129logerr() {
130    echo -ne "${RED}ERROR: "
131    echo -n "$@"
132    echo -e "${NC}"
133}
134
135
136check_output_dir() {
137    type="$1"
138    if test -d "$OUTPUT_ROOT/$type"; then
139        return
140    fi
141    mkdir -vp "$OUTPUT_ROOT/$type"
142}
143
144
145update_mingw_src() {
146    # Initialize or update the managed MINGW-packages sources dir.
147    if test -d "$SRC_DIR"; then
148        loginfo "Updating $SRC_DIR..."
149        pushd "$SRC_DIR"
150        git pull
151    else
152        loginfo "Creating $SRC_ROOT"
153        mkdir -vp "$SRC_ROOT"
154        pushd "$SRC_ROOT"
155        loginfo "Shallow-cloning $SRC_CLONEURI into $SRC_DIR..."
156        git clone --depth 1 "$SRC_CLONEURI" "$SRC_PROJECT"
157    fi
158    popd
159    logok "Updated $SRC_DIR"
160}
161
162
163seed_mingw_src_mypaint_repo() {
164    # Seed the MyPaint source repository that makepkg-mingw wants
165    # from this one if it doesn't yet exist.
166    # The mypaint repo is quite big, so let's save some bandwidth!
167    repo="$SRC_DIR/mingw-w64-mypaint/mypaint"
168    test -d "$TOPDIR/.git" || return
169    test -d "$repo" && return
170    loginfo "Seeding $repo..."
171    git clone --local --no-hardlinks --bare "$TOPDIR" "$repo"
172    pushd "$repo"
173    git remote remove origin
174    git remote add origin https://github.com/mypaint/mypaint.git
175    git fetch --tags origin
176    popd
177    logok "Seeded $repo"
178}
179
180
181build_pkg() {
182    # Build and optionally install a .pkg.tar.zst from the
183    # managed tree of PKGBUILDs.
184    #
185    # Usage: build_pkg PKGNAMESTEM {true|false}
186
187    if ! test -d "$SRC_DIR"; then
188        logerr "Managed src dir $SRC_DIR does not exist (update_mingw_src 1st)"
189        exit 2
190    fi
191
192    pkgstem="$1"
193    install="$2"
194    src="${SRC_DIR}/mingw-w64-$pkgstem"
195    pushd "$src"
196    rm -vf *.pkg.tar.zst
197
198    # This only builds for the arch in MINGW_INSTALLS, i.e. the current
199    # value of MSYSTEM.
200    loginfo "Building in $src for $MINGW_INSTALLS ..."
201    MSYSTEM=MSYS2 bash --login -c 'cd "$1" && makepkg-mingw -f' - "$src"
202    logok "Build finished."
203
204    if $install; then
205        loginfo "Installing built packages..."
206        pacman -U --noconfirm *.pkg.tar.zst
207        logok "Install finished."
208    fi
209    popd
210
211    loginfo "Capturing build artifacts..."
212    check_output_dir "pkgs"
213    mv -v "$src"/*.pkg.tar.zst "$OUTPUT_ROOT/pkgs"
214    logok "Packages moved."
215}
216
217
218bundle_mypaint() {
219    # Convert local and repository *.pkg.tar.zst into nice bundles
220    # for users to install.
221    # Needs the libmypaint and mypaint .pkg.tar.zst artifacts.
222    styrene_path=`which styrene||true`
223    if [ "x$styrene_path" = "x" ]; then
224        mkdir -vp "$SRC_ROOT"
225        pushd "$SRC_ROOT"
226        if [ -d styrene ]; then
227            loginfo "Updating managed Styrene source"
228            pushd styrene
229            git pull
230        else
231            loginfo "Cloning managed Styrene source"
232            git clone https://github.com/mypaint/mypaint-styrene.git styrene
233            pushd styrene
234        fi
235        loginfo "Installing styrene with pip3..."
236        pip3 install .
237        loginfo "Installed styrene."
238        popd
239        popd
240    fi
241
242    check_output_dir "bundles"
243    loginfo "Creating installer bundles..."
244
245    tmpdir="/tmp/styrene.$$"
246    mkdir -p "$tmpdir"
247    styrene --colour=yes \
248            --no-zip \
249            --7z \
250            --pkg-dir="$OUTPUT_ROOT/pkgs" \
251            --output-dir="$tmpdir" \
252            "$TOPDIR/windows/styrene/mypaint.cfg"
253
254    output_version=$(echo $BUNDLE_ARCH-$APPVEYOR_BUILD_VERSION | sed -e 's/[^a-zA-Z0-9._-]/-/g')
255
256    mv -v "$tmpdir"/*-standalone.7z \
257        "$OUTPUT_ROOT/bundles/mypaint-$output_version-standalone.7z"
258    mv -v "$tmpdir"/*-installer.exe  \
259        "$OUTPUT_ROOT/bundles/mypaint-$output_version-installer.exe"
260
261    ls -l "$OUTPUT_ROOT/bundles"/*.*
262
263    rm -fr "$tmpdir"
264
265    logok "Bundle creation finished."
266}
267
268# Test Build, Clean, and Install tools to make sure all of setup.py is
269# working as intended.
270
271build_for_testing() {
272    loginfo "Building MyPaint from source"
273    python3 setup.py build
274    logok "Build finished."
275}
276
277clean_local_repo() {
278    loginfo "Cleaning local build"
279    python3 setup.py clean --all
280    rm -vf lib/*_wrap.c*
281    logok "Clean finished."
282}
283
284install_test(){
285    # TODO: Look into this to find out why it is failing.
286    loginfo "Testing setup.py managed installation commands"
287    python3 setup.py managed_install
288    python3 setup.py managed_uninstall
289    logok "Install-test finished finished."
290}
291
292# Can't test everything from TeaCI due to wine crashing.
293# However, it's always appropriate to run the doctests.
294# With Appveyor, the tests scripts should run just fine.
295
296run_doctest() {
297    loginfo "Running unit tests."
298    python3 setup.py nosetests --tests lib
299    logok "Unit tests done."
300}
301
302run_tests() {
303    loginfo "Runnning startup/shutdown test"
304    MYPAINT_DEBUG=1 python setup.py demo --args='--run-and-quit'
305    loginfo "Running conformance tests."
306    python3 setup.py test
307    logok "Tests done."
308}
309
310
311# Command line processing
312
313case "$1" in
314    installdeps)
315        install_dependencies
316        update_mingw_src
317        src="${SRC_DIR}/mingw-w64-libmypaint"
318        cp ./windows/PKGBUILD-libmypaint $src/PKGBUILD
319        build_pkg "libmypaint" true
320        src="${SRC_DIR}/mingw-w64-mypaint-brushes2"
321        cp ./windows/PKGBUILD-mypaint-brushes2 $src/PKGBUILD
322        build_pkg "mypaint-brushes2" true
323        ;;
324    build)
325        build_for_testing
326        ;;
327    clean)
328        clean_local_repo
329        ;;
330    tests)
331        run_tests
332        # install_test
333        ;;
334    doctest)
335        run_doctest
336        ;;
337    bundle)
338        update_mingw_src
339        seed_mingw_src_mypaint_repo
340        src="${SRC_DIR}/mingw-w64-mypaint"
341        cp ./windows/PKGBUILD-mypaint $src/PKGBUILD
342        build_pkg "mypaint" false
343        bundle_mypaint
344        ;;
345    *)
346        grep '^#:' $0 | cut -d ':' -f 2-50
347        exit 2
348        ;;
349esac
350