summaryrefslogtreecommitdiff
path: root/cerbero/packages/linux_bundle.py
blob: 7b6c38d9f67a1b1359ec7d6132befbc631aa4449 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# cerbero - a multi-platform build system for Open Source software
# Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.


import os
import shutil

from cerbero.commands import gensdkshell
from cerbero.errors import FatalError
from cerbero.utils import _, N_, shell
from cerbero.utils import messages as m
from cerbero.packages import PackagerBase


LAUNCH_BUNDLE_COMMAND = """# Try to discover plugins only once
LOCKFILE="/tmp/%(appname)s-bundle.lock"
if [ -e $LOCKFILE ]
then
    PID=$(cat $LOCKFILE)
    if kill -0 $PID 2> /dev/null
    then
        echo "$? $PID pitivi already running -> exiting"
        exit 1
    else
        echo "Stalled process $PID allow restarting"
    fi
fi
echo $$ > $LOCKFILE

PLUGINS_SYMLINK=${HOME}/.cache/gstreamer-1.0/%(appname)s-gstplugins
rm ${PLUGINS_SYMLINK} > /dev/null 2>&1
ln -s ${APPDIR}/lib/gstreamer-1.0/ ${PLUGINS_SYMLINK}
if [ $? -ne 0 ]; then
    export GST_PLUGIN_PATH=${APPDIR}/lib/gstreamer-1.0/
else
    export GST_PLUGIN_PATH=${PLUGINS_SYMLINK}
fi

trap 'ECODE=$?;
      echo "[statsgen] Removing lock. Exit: ${ETXT[ECODE]}($ECODE)" >&2
      rm "${PLUGINS_SYMLINK}" "${LOCKFILE}"' 0

which gdk-pixbuf-query-loaders > /dev/null 2>&1
if [ $? -eq 0 ]; then
    export GDK_PIXBUF_MODULE_FILE=/tmp/$(basename $APPDIR).gdk-pixbuf-loader.cache
    gdk-pixbuf-query-loaders $APPDIR/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.so > $GDK_PIXBUF_MODULE_FILE 2>&1
fi

if [[ -e /etc/fonts/fonts.conf && -z "$FONTCONFIG_FILE" ]]
then
    export FONTCONFIG_FILE=/etc/fonts/fonts.conf
fi

if test -z ${APP_IMAGE_TEST}; then
    # Invoke the app with the arguments passed
    cd ${APPDIR}
    ${APPDIR}/%(executable_path)s $*
else
    if [ $SHELL = "/bin/zsh" ]; then
        export ZDOTDIR=$(mktemp -d)
        mkdir -p $ZDOTDIR
        cp ~/.zshrc $ZDOTDIR
        echo "autoload -Uz bashcompinit; bashcompinit" >> $ZDOTDIR/.zshrc
        echo "PROMPT=[%(appname)s]\ \$PROMPT" >> $ZDOTDIR/.zshrc
        zsh
    elif [ $SHELL = "/bin/bash" ]; then
        RCFILE=$(mktemp -d)/.bashrc
        cp ~/.bashrc $RCFILE
        echo "" >> $RCFILE
        echo "export PS1=[%(appname)s]\ \$PS1" >> $RCFILE
        /bin/bash --rcfile $RCFILE
    else
        CERBERO_ENV="[%(appname)s]" $SHELL
    fi
fi

# Cleaning up the link to gstplugins
rm ${PLUGINS_SYMLINK}
"""

class LinuxBundler(PackagerBase):
    doc = N_('Bundle after building packages')
    name = 'bundle'

    def __init__(self, config, package, store):
        PackagerBase.__init__(self, config, package, store)
        self.bundle_name = "%s-%s-%s" % (self.package.name, self.package.version, self.config.arch)
        if not hasattr(self.package, "desktop_file"):
            raise FatalError("Can not create a linux bundle if the package does "
                             "not have a 'desktop_file' property set")

    def pack(self, output_dir, devel=True, force=False, keep_temp=False):
        self.tmp_install_dir = os.path.join(output_dir, "bundle_root")
        self._force = force
        if self._force:
            try:
                shutil.rmtree(self.tmp_install_dir)
            except OSError:
                pass

        self.desktop_file = os.path.join(self.tmp_install_dir, self.package.desktop_file)
        self.output_dir = output_dir
        self.devel = devel
        self.keep_temp = keep_temp

        self.bundle()

        return []

    def _copy_installdir(self):
        '''
        Copy all the files that are going to be packaged to the bundle's
        temporary directory
        '''
        os.makedirs(self.tmp_install_dir)
        for f in set(self.package.files_list()):
            in_path = os.path.join(self.config.prefix, f)
            if not os.path.exists(in_path):
                m.warning("File %s is missing and won't be added to the "
                          "package" % in_path)
                continue
            out_path = os.path.join(self.tmp_install_dir, f)
            odir = os.path.split(out_path)[0]
            if not os.path.exists(odir):
                os.makedirs(odir)
            shutil.copy(in_path, out_path)

    def _make_paths_relative(self):
        sofiles = shell.find_files('*.so', self.tmp_install_dir)
        for sof in sofiles:
            try:
                shell.call("chrpath -d %s" % sof, self.tmp_install_dir,
                           fail=False)
            except FatalError:
                m.warning("Could not 'chrpath' %s" % sof)

        shell.call("ln -s . usr", self.tmp_install_dir, fail=False)

        for icondir in os.listdir(os.path.join(self.tmp_install_dir, "share/icons/")):
            if os.path.exists(os.path.join(icondir, "index.theme")):
                shell.call("gtk-update-icon-cache %s" % icondir, fail=False)

        shell.call("update-mime-database %s" % os.path.join(self.tmp_install_dir, "share", "mime"), fail=False)
        shell.call("glib-compile-schemas %s/share/glib-2.0/schemas" % self.tmp_install_dir)

        # Use system wide applications in case the bundle needs to open apps not included in
        # the bundle (to show the documentation most probably)
        shell.call("rm -rf %s" % os.path.join(self.tmp_install_dir, "share", "applications"), fail=False)
        shell.call("ln -s %s %s" % (os.path.join("/usr", "share", "applications"),
                                    os.path.join(self.tmp_install_dir, "share", "applications")),
                   fail=False)

    def _install_bundle_specific_files(self):
        # Installing desktop file and runner script
        shell.call("cp %s %s" % (self.desktop_file, self.tmp_install_dir), fail=False)
        # Base environment variables
        env = {}
        env['GSETTINGS_SCHEMA_DIR'] = '${APPDIR}/share/glib-2.0/schemas/:${GSETTINGS_SCHEMA_DIR}'
        env['GST_REGISTRY'] = '${HOME}/.cache/gstreamer-1.0/%s-bundle-registry' % self.package.name
        env['GST_REGISTRY_1_0'] = '${HOME}/.cache/gstreamer-1.0/%s-bundle-registry' % self.package.name
        if hasattr(self.package, "default_gtk_theme"):
            env['GTK_THEME'] = self.package.default_gtk_theme

        launch_command = LAUNCH_BUNDLE_COMMAND % ({"prefix": self.tmp_install_dir,
                                                   "executable_path": self.package.commands[0][1],
                                                   "appname": self.package.name})

        shellvarsgen = gensdkshell.GenSdkShell()

        shellvarsgen.runargs(self.config, "AppRun", self.tmp_install_dir,
                             "${APPDIR}", "${APPDIR}/lib",
                             self.config.py_prefix,
                             cmd=launch_command,
                             env=env,
                             prefix_env_name="APPDIR")

    def _generate_bundle(self):
        opath = os.path.join(self.output_dir, self.bundle_name)
        if self._force:
            try:
                os.remove(opath)
            except OSError:
                pass
        elif os.path.exists(opath):
            raise OSError("Bundle output path %s exists", opath)

        shell.call("AppImageAssistant %s %s" % (self.tmp_install_dir, self.bundle_name),
                   self.output_dir)

    def _clean_tmps(self):
        shell.call("rm -rf %s" % self.tmp_install_dir)

    def _generate_md5sum(self):
        md5name = "%s.md5sum" % self.bundle_name
        shell.call("md5sum %s > %s" % (self.bundle_name, md5name),
                   self.output_dir)
        m.action(_("Bundle avalaible in: %s") % os.path.join(self.output_dir, self.bundle_name))

    def bundle(self):
        # If not devel wanted, we make a clean bundle with only
        # file needed to execute
        steps = [
            ("prepare-install-dir",
             [(_("Copy install path"), self._copy_installdir, True),
              (_("Installing bundle files"), self._install_bundle_specific_files, True),
              (_("Make all paths relatives"), self._make_paths_relative, True),
              ]),
            ("generate-tarball",
             [(_("Running AppImageAssistant"), self._generate_bundle, True),
              (_("Generating md5"), self._generate_md5sum, True)]),
            ("clean-install-dir",
             [(_("Clean tmp dirs"), self._clean_tmps, not self.keep_temp)])]

        for step in steps:
            shell.set_logfile_output("%s/%s-bundle-%s.log" % (self.config.logs, self.package.name, step[0]))
            for substep in step[1]:
                m.build_step('1', '1', self.package.name + " linux bundle", substep[0])
                if substep[2] is True:
                    substep[1]()
                else:
                    m.action(_("Step not wanted"))
            shell.close_logfile_output()