1#!/usr/bin/env python3
2# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
3
4import os
5import re
6import sys
7
8try:
9    import DistUtilsExtra.auto
10except ImportError:
11    print('To build ulauncher you need "python3-distutils-extra"', file=sys.stderr)
12    sys.exit(1)
13
14assert DistUtilsExtra.auto.__version__ >= '2.18', \
15    'needs DistUtilsExtra.auto >= 2.18'
16
17
18def update_config(libdir, values={}):
19
20    filename = os.path.join(libdir, 'ulauncher/config.py')
21    oldvalues = {}
22    try:
23        fin = open(filename, 'r')
24        fout = open(filename + '.new', 'w')
25
26        for line in fin:
27            fields = line.split(' = ')  # Separate variable from value
28            if fields[0] in values:
29                oldvalues[fields[0]] = fields[1].strip()
30                line = "%s = %s\n" % (fields[0], values[fields[0]])
31            fout.write(line)
32
33        fout.flush()
34        fout.close()
35        fin.close()
36        os.rename(fout.name, fin.name)
37    except (OSError, IOError):
38        print("ERROR: Can't find %s" % filename)
39        sys.exit(1)
40
41    return oldvalues
42
43
44def move_desktop_file(root, target_data, prefix):
45    # The desktop file is rightly installed into install_data.  But it should
46    # always really be installed into prefix, because while we can install
47    # normal data files anywhere we want, the desktop file needs to exist in
48    # the main system to be found.  Only actually useful for /opt installs.
49
50    old_desktop_path = os.path.normpath(root + target_data + '/share/applications')
51    old_desktop_file = old_desktop_path + '/ulauncher.desktop'
52    desktop_path = os.path.normpath(root + prefix + '/share/applications')
53    desktop_file = desktop_path + '/ulauncher.desktop'
54
55    if not os.path.exists(old_desktop_file):
56        print("ERROR: Can't find", old_desktop_file)
57        sys.exit(1)
58    elif os.path.normpath(target_data) != os.path.normpath(prefix):
59        # This is an /opt install, so rename desktop file to use extras-
60        desktop_file = desktop_path + '/extras-ulauncher.desktop'
61        try:
62            os.makedirs(desktop_path)
63            os.rename(old_desktop_file, desktop_file)
64            os.rmdir(old_desktop_path)
65        except OSError as e:
66            print("ERROR: Can't rename", old_desktop_file, ":", e)
67            sys.exit(1)
68
69    return desktop_file
70
71
72def update_desktop_file(filename, target_pkgdata, target_scripts):
73    try:
74        with open(filename, "r") as fin:
75            src = fin.read()
76
77        dst = re.sub(
78            r"((Try)?Exec)=(.*?)(/[^ ]+/)?ulauncher(.*)",
79            r"\1=\3{}ulauncher\5".format(target_scripts),
80            src
81        )
82
83        with open(filename, "w") as fout:
84            fout.write(dst)
85    except (OSError, IOError):
86        print("ERROR: Can't find %s" % filename)
87        sys.exit(1)
88
89
90class InstallAndUpdateDataDirectory(DistUtilsExtra.auto.install_auto):
91    def run(self):
92        DistUtilsExtra.auto.install_auto.run(self)
93
94        # Root is undefined if not installing into an alternate root
95        root = self.root or "/"
96        target_data = '/' + os.path.relpath(self.install_data, root) + '/'
97        target_pkgdata = target_data + 'share/ulauncher/'
98        target_scripts = '/' + os.path.relpath(self.install_scripts,
99                                               root) + '/'
100
101        values = {'__ulauncher_data_directory__': "'%s'" % (target_pkgdata),
102                  '__version__': "'%s'" % self.distribution.get_version()}
103        update_config(self.install_lib, values)
104
105        desktop_file = move_desktop_file(root, target_data, self.prefix)
106        update_desktop_file(desktop_file, target_pkgdata, target_scripts)
107
108
109class DataFileList(list):
110
111    def append(self, item):
112        # don't add node_modules to data_files that DistUtilsExtra tries to
113        # add automatically
114        filename = item[1][0]
115        if 'node_modules' in filename \
116           or 'bower_components' in filename or '.tmp' in filename:
117            return
118        else:
119            return super().append(item)
120
121
122def exclude_files(patterns=[]):
123    """
124    Suppress completely useless warning about files DistUtilsExta.aut does
125    recognize because require developer to scroll past them to get to useful
126    output.
127
128    Example of the useless warnings:
129
130    WARNING: the following files are not recognized by DistUtilsExtra.auto:
131    Dockerfile.build
132    Dockerfile.build-arch
133    Dockerfile.build-rpm
134    PKGBUILD.template
135    scripts/aur-update.py
136    """
137
138    # it's maddening the DistUtilsExtra does not offer a way to exclude globs
139    # from it's scans and just using "print" to print the warning instead of
140    # using warning module which has a mechanism for suppressions
141    # it forces us to take the approach of monkeypatching their src_find
142    # function.
143    original_src_find = DistUtilsExtra.auto.src_find
144
145    def src_find_with_excludes(attrs):
146        src = original_src_find(attrs)
147
148        for pattern in patterns:
149            DistUtilsExtra.auto.src_markglob(src, pattern)
150
151        return src
152
153    DistUtilsExtra.auto.src_find = src_find_with_excludes
154
155    return original_src_find
156
157
158def main():
159
160    # exclude files/folder patterns from being considered by distutils-extra
161    # this returns the original DistUtilsExtra.auto.src_find function
162    # so we can patch bit back in later
163    original_find_src = exclude_files([
164        "*.sh",
165        "ul",
166        "Dockerfile.build*",
167        "PKGBUILD.template",
168        "scripts/*",
169        "docs/*",
170        "glade",
171        "test",
172        "ulauncher.desktop.dev",
173        "requirements.txt",
174        "conftest.py"
175    ])
176
177    DistUtilsExtra.auto.setup(
178        name='ulauncher',
179        version='5.14.2',
180        license='GPL-3',
181        author='Aleksandr Gornostal',
182        author_email='ulauncher.app@gmail.com',
183        description='Application launcher for Linux',
184        url='https://ulauncher.io',
185        data_files=DataFileList([
186            ('share/icons/hicolor/48x48/apps', [
187                'data/media/icons/hicolor/ulauncher.svg'
188            ]),
189            ('share/icons/hicolor/48x48/apps', [
190                'data/media/icons/hicolor/ulauncher-indicator.svg'
191            ]),
192            ('share/icons/hicolor/scalable/apps', [
193                'data/media/icons/hicolor/ulauncher.svg'
194            ]),
195            ('share/icons/hicolor/scalable/apps', [
196                'data/media/icons/hicolor/ulauncher-indicator.svg'
197            ]),
198            # for fedora + GNOME
199            ('share/icons/gnome/scalable/apps', [
200                'data/media/icons/hicolor/ulauncher.svg'
201            ]),
202            ('share/icons/gnome/scalable/apps', [
203                'data/media/icons/hicolor/ulauncher-indicator.svg'
204            ]),
205            # for ubuntu
206            ('share/icons/breeze/apps/48', [
207                'data/media/icons/ubuntu-mono-light/ulauncher-indicator.svg'
208            ]),
209            ('share/icons/ubuntu-mono-dark/scalable/apps', [
210                'data/media/icons/hicolor/ulauncher-indicator.svg'
211            ]),
212            ('share/icons/ubuntu-mono-light/scalable/apps', [
213                'data/media/icons/ubuntu-mono-light/ulauncher-indicator.svg'
214            ]),
215            ('share/icons/elementary/scalable/apps', [
216                'data/media/icons/elementary/ulauncher-indicator.svg'
217            ]),
218            ('share/applications', [
219                'build/share/applications/ulauncher.desktop'
220            ]),
221            ('lib/systemd/user', [
222                'ulauncher.service'
223            ])
224        ]),
225        cmdclass={'install': InstallAndUpdateDataDirectory}
226    )
227
228    # unpatch distutils-extra src_find
229    DistUtilsExtra.auto.src_find = original_find_src
230
231
232if __name__ == '__main__':
233    main()
234