1#! /bin/sh
2#
3#   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
4#   SPDX-License-Identifier: BSD-2-Clause
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9#
10#   1. Redistributions of source code must retain the above copyright
11#      notice, this list of conditions and the following disclaimer.
12#   2. Redistributions in binary form must reproduce the above copyright
13#      notice, this list of conditions and the following disclaimer in the
14#      documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26# POSSIBILITY OF SUCH DAMAGE.
27#
28### END LICENSES
29
30### USAGE
31#
32# Shell script to help build an AppImage for Calamares.
33#
34# Usage:
35#   AppImage.sh [-T|--tools-dir <dir>]
36#               [-C|--cmake-args <args>]
37#               [-c|--config-dir <dir>]
38#               [-s|--skip-build]
39#               [-p|--with-python]
40#
41# Multiple --cmake-args arguments will be collected together and passed to
42# CMake before building the application.
43#
44# Use --tools-dir to indicate where the linuxdeploy tools are located.
45#
46# Use --config to copy a config-directory (with settings.conf and others)
47# into the resulting image,
48#
49# Option --skip-build assumes that there is an already-built Calamares
50# available in the AppImage build directory; use this when you are, e.g.
51# re-packaging the image with different configuration. Option --with-python
52# adds the Conda Python packaging ecosystem to the AppImage, which will make
53# it **more** portable by disconnecting from the system Python libraries.
54#
55# The build process for AppImage proceeds in a directory build-AppImage
56# that is created in the current directory.
57#
58# The resulting AppImage has XDG_* enabled, and appends the in-image
59# directories to the current environment. You can set XDG_* in the
60# current environment to use other configurations and data, e.g. the
61# data in the current live environment. Or leave it unset, to try
62# Calamares with only the configuration contained in the AppImage.
63#
64### END USAGE
65
66TOOLS_DIR="."
67CMAKE_ARGS=""
68DO_REBUILD="true"
69DO_CONDA="false"
70CONFIG_DIR=""
71while test "$#" -gt 0
72do
73    case "x$1" in
74    x--help|x-h)
75        sed -e '1,/USAGE/d' -e '/END.USAGE/,$d' < "$0"
76        return 0
77        ;;
78    x--tools-dir|x-T)
79        TOOLS_DIR="$2"
80        shift
81        ;;
82    x--cmake-args|x-C)
83        CMAKE_ARGS="$CMAKE_ARGS $2"
84        shift
85        ;;
86    x--config-dir|x-c)
87        CONFIG_DIR="$2"
88        shift
89        ;;
90    x--skip-build|x-s)
91        DO_REBUILD="false"
92        ;;
93    x--with-python|x-p)
94        DO_CONDA="true"
95        ;;
96    *)
97        echo "! Unknown argument '$1'."
98        exit 1
99        ;;
100    esac
101    test "$#" -gt 0 || { echo "! Missing arguments."; exit 1; }
102    shift
103done
104
105### Check where we're running
106#
107BIN_DIR=$( cd $( dirname "$0" ) && pwd -P )
108test -d "$BIN_DIR" || { echo "! Could not find BIN_DIR"; exit 1; }
109test -f "$BIN_DIR/AppImage.sh" || { echo "! $BIN_DIR does not have AppImage.sh"; exit 1; }
110
111SRC_DIR=$( cd "$BIN_DIR/.." && pwd -P )
112test -d "$SRC_DIR" || { echo "! Could not find SRC_DIR"; exit 1; }
113test -d "$SRC_DIR/ci" || { echo "! $SRC_DIR isn't a top-level Calamares checkout"; exit 1; }
114test -f "$SRC_DIR/CMakeLists.txt" || { echo "! SRC_DIR is missing CMakeLists.txt"; exit 1; }
115
116### Check pre-requisites
117#
118BUILD_DIR=build-AppImage
119test -d "$BUILD_DIR" || mkdir -p "$BUILD_DIR"
120test -d "$BUILD_DIR" || { echo "! Could not create $BUILD_DIR"; exit 1; }
121
122TOOLS_LIST="linuxdeploy-x86_64.AppImage linuxdeploy-plugin-qt-x86_64.AppImage"
123$DO_CONDA && TOOLS_LIST="$TOOLS_LIST linuxdeploy-plugin-conda.sh"
124
125for tool in $TOOLS_LIST
126do
127    if test -x "$BUILD_DIR/$tool" ; then
128        # This tool is ok
129        :
130    else
131        if test -f "$TOOLS_DIR/$tool" ; then
132            cp "$TOOLS_DIR/$tool" "$BUILD_DIR/$tool" || exit 1
133        else
134            fetch=$( grep "^# URL .*$tool\$" "$0" | sed 's/# URL *//' )
135            curl -L -o "$BUILD_DIR/$tool" "$fetch"
136        fi
137        chmod +x "$BUILD_DIR/$tool"
138        test -x "$BUILD_DIR/$tool" || { echo "! Missing tool $tool in tools-dir $TOOLS_DIR"; exit 1; }
139    fi
140done
141
142if test -n "$CONFIG_DIR" ; then
143    test -f "$CONFIG_DIR/settings.conf" || { echo "! No settings.conf in $CONFIG_DIR"; exit 1; }
144fi
145
146### Clean up build-directory
147#
148rm -rf "$BUILD_DIR/AppDir"
149if $DO_REBUILD ; then
150    rm -rf "$BUILD_DIR/build"
151    mkdir "$BUILD_DIR/build" || { echo "! Could not create $BUILD_DIR/build for the cmake-build."; exit 1; }
152else
153    test -d "$BUILD_DIR/build" || { echo "! No build found in $BUILD_DIR, but --skip-build is given."; exit 1; }
154    test -x "$BUILD_DIR/build/calamares" || { echo "! No complete build found in $BUILD_DIR/build ."; exit 1; }
155fi
156mkdir "$BUILD_DIR/AppDir" || { echo "! Could not create $BUILD_DIR/AppDir for the AppImage install."; exit 1; }
157LOG_FILE="$BUILD_DIR/AppImage.log"
158rm -f "$LOG_FILE"
159{ echo "# Calamares build started" `date` ; echo "# .. build directory $BUILD_DIR"; echo "# .. log file $LOG_FILE"; } > "$LOG_FILE"
160cat "$LOG_FILE"
161
162### Python Support
163#
164#
165if $DO_CONDA ; then
166    export CONDA_CHANNELS="conda-forge;anaconda"
167    export CONDA_PACKAGES="gettext;py-boost"
168
169    (
170        cd "$BUILD_DIR" &&
171        ./linuxdeploy-x86_64.AppImage --appdir=AppDir/ --plugin=conda
172    )
173
174    . "$BUILD_DIR/AppDir/usr/conda/bin/activate"
175fi
176
177### Build Calamares
178#
179if $DO_REBUILD ; then
180    echo "# Running cmake ..."
181    (
182        cd "$BUILD_DIR/build" &&
183        cmake "$SRC_DIR" -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib $CMAKE_ARGS
184    ) >> "$LOG_FILE" 2>&1 || { tail -10 "$LOG_FILE" ; echo "! Could not run CMake"; exit 1; }
185    echo "# Running make ..."
186    (
187        cd "$BUILD_DIR/build" &&
188        make -j4
189    ) >> "$LOG_FILE" 2>&1 || { tail -10 "$LOG_FILE" ; echo "! Could not run make"; exit 1; }
190fi
191echo "# Running make install ..."
192(
193    cd "$BUILD_DIR/build" &&
194    make install DESTDIR=../AppDir
195) >> "$LOG_FILE" 2>&1 || { tail -10 "$LOG_FILE" ; echo "! Could not run make install"; exit 1; }
196
197### Modify installation
198#
199IMAGE_DIR="$BUILD_DIR/AppDir"
200
201# Munge the desktop file to not use absolute paths or pkexec
202sed -i \
203    -e 's+^Exec=.*+Exec=calamares+' \
204    -e 's+^Name=.*+Name=Calamares+' \
205    "$IMAGE_DIR"/usr/share/applications/calamares.desktop
206
207# Replace the executable with a shell-proxy
208test -x "$IMAGE_DIR/usr/bin/calamares" || { echo "! Does not seem to have installed calamares"; exit 1; }
209mv "$IMAGE_DIR/usr/bin/calamares" "$IMAGE_DIR/usr/bin/calamares.bin"
210cat > "$IMAGE_DIR/usr/bin/calamares" <<"EOF"
211#! /bin/sh
212#
213# Calamares proxy-script. Runs Calamares with XDG support enabled,
214# and in-image XDG dirs set up so that compiled-in configuration can be used.
215test -n "${XDG_DATA_DIRS}" && XDG_DATA_DIRS="${XDG_DATA_DIRS}:"
216test -n "${XDG_CONFIG_DIRS}" $$ XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS}:"
217export XDG_DATA_DIRS="${XDG_DATA_DIRS}${APPDIR}/usr/share/"
218export XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS}${APPDIR}/etc/:${APPDIR}/usr/share/"
219export PYTHONPATH="${APPDIR}/usr/lib:"
220cd "$APPDIR"
221exec "$APPDIR"/usr/bin/calamares.bin -X "$@"
222EOF
223chmod 755 "$IMAGE_DIR/usr/bin/calamares"
224test -x "$IMAGE_DIR/usr/bin/calamares" || { echo "! Does not seem to have proxy for calamares"; exit 1; }
225
226### Install additional files
227#
228PLUGIN_DIR=$( qmake  -query QT_INSTALL_PLUGINS )
229for plugin in \
230    libpmsfdiskbackendplugin.so \
231    libpmdummybackendplugin.so \
232    libpmlibpartedbackendplugin.so
233do
234    # Warning, but not fatal: generally you only have two out of three available
235    # depending on the KPMCore version.
236    cp "$PLUGIN_DIR/$plugin" "$IMAGE_DIR/usr/lib" 2> /dev/null || { echo "! Could not copy KPMCore plugin $plugin"; }
237done
238
239# Install configuration files
240ETC_DIR="$IMAGE_DIR"/etc/calamares
241mkdir -p "$ETC_DIR"
242test -d "$ETC_DIR" || { echo "! Could not create /etc/calamares in image."; exit 1; }
243
244if test -z "$CONFIG_DIR" ; then
245    echo "# Using basic settings.conf"
246    cp "$SRC_DIR/settings.conf" "$ETC_DIR"
247else
248    test -f "$CONFIG_DIR/settings.conf" || { echo "! No settings.conf in $CONFIG_DIR"; exit 1; }
249    mkdir -p "$ETC_DIR/modules"
250    cp "$CONFIG_DIR/settings.conf" "$ETC_DIR"
251    test -d "$CONFIG_DIR/modules" && cp -r "$CONFIG_DIR/modules" "$ETC_DIR"
252    test -d "$CONFIG_DIR/branding" && cp -r "$CONFIG_DIR/branding" "$IMAGE_DIR/usr/share/calamares"
253fi
254
255### Build the AppImage
256#
257#
258echo "# Building AppImage"
259(
260    export QT_SELECT=qt5  # Otherwise might pick Qt4 in image
261    export LD_LIBRARY_PATH=AppDir/usr/lib  # RPATH isn't set in the executable
262    cd "$BUILD_DIR" &&
263    ./linuxdeploy-x86_64.AppImage --appdir=AppDir/ --plugin=qt --output=appimage
264) >> "$LOG_FILE" 2>&1 || { tail -10 "$LOG_FILE" ; echo "! Could not create image"; exit 1; }
265
266echo "# Created in $BUILD_DIR/Calamares-x86_64.AppImage"
267echo "# .. log file at $LOG_FILE"
268
269exit 0
270### Database for installation
271#
272# URL https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
273# URL https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
274# URL https://raw.githubusercontent.com/TheAssassin/linuxdeploy-plugin-conda/master/linuxdeploy-plugin-conda.sh
275