1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import, print_function
6
7import os
8import sys
9import tempfile
10import subprocess
11import glob
12
13from mozboot.base import BaseBootstrapper
14from mozboot.linux_common import StyloInstall
15
16
17class ArchlinuxBootstrapper(StyloInstall, BaseBootstrapper):
18    '''Archlinux experimental bootstrapper.'''
19
20    SYSTEM_PACKAGES = [
21        'autoconf2.13',
22        'base-devel',
23        'ccache',
24        'mercurial',
25        'nodejs',
26        'npm',
27        'python2',
28        'python2-setuptools',
29        'unzip',
30        'zip',
31    ]
32
33    BROWSER_PACKAGES = [
34        'alsa-lib',
35        'dbus-glib',
36        'desktop-file-utils',
37        'gconf',
38        'gtk2',
39        'gtk3',
40        'hicolor-icon-theme',
41        'hunspell',
42        'icu',
43        'libevent',
44        'libvpx',
45        'libxt',
46        'mime-types',
47        'mozilla-common',
48        'nss',
49        'sqlite',
50        'startup-notification',
51        'diffutils',
52        'gst-plugins-base-libs',
53        'imake',
54        'inetutils',
55        'libpulse',
56        'xorg-server-xvfb',
57        'yasm',
58        'gst-libav',
59        'gst-plugins-good',
60        'networkmanager',
61    ]
62
63    BROWSER_AUR_PACKAGES = [
64        'https://aur.archlinux.org/cgit/aur.git/snapshot/uuid.tar.gz',
65    ]
66
67    MOBILE_ANDROID_COMMON_PACKAGES = [
68        # It would be nice to handle alternative JDKs.  See
69        # https://wiki.archlinux.org/index.php/Java.
70        'jdk8-openjdk',
71        # For downloading the Android SDK and NDK.
72        'wget',
73        # See comment about 32 bit binaries and multilib below.
74        'multilib/lib32-libstdc++5',
75        'multilib/lib32-ncurses',
76        'multilib/lib32-readline',
77        'multilib/lib32-zlib',
78    ]
79
80    def __init__(self, version, dist_id, **kwargs):
81        print('Using an experimental bootstrapper for Archlinux.')
82        BaseBootstrapper.__init__(self, **kwargs)
83
84    def install_system_packages(self):
85        self.pacman_install(*self.SYSTEM_PACKAGES)
86
87    def install_browser_packages(self):
88        self.ensure_browser_packages()
89
90    def install_browser_artifact_mode_packages(self):
91        self.ensure_browser_packages(artifact_mode=True)
92
93    def install_mobile_android_packages(self):
94        self.ensure_mobile_android_packages()
95
96    def install_mobile_android_artifact_mode_packages(self):
97        self.ensure_mobile_android_packages(artifact_mode=True)
98
99    def ensure_browser_packages(self, artifact_mode=False):
100        # TODO: Figure out what not to install for artifact mode
101        self.aur_install(*self.BROWSER_AUR_PACKAGES)
102        self.pacman_install(*self.BROWSER_PACKAGES)
103
104    def ensure_mobile_android_packages(self, artifact_mode=False):
105        # Multi-part process:
106        # 1. System packages.
107        # 2. Android SDK. Android NDK only if we are not in artifact mode. Android packages.
108
109        # 1. This is hard to believe, but the Android SDK binaries are 32-bit
110        # and that conflicts with 64-bit Arch installations out of the box.  The
111        # solution is to add the multilibs repository; unfortunately, this
112        # requires manual intervention.
113        try:
114            self.pacman_install(*self.MOBILE_ANDROID_COMMON_PACKAGES)
115        except Exception as e:
116            print('Failed to install all packages.  The Android developer '
117                  'toolchain requires 32 bit binaries be enabled (see '
118                  'https://wiki.archlinux.org/index.php/Android).  You may need to '
119                  'manually enable the multilib repository following the instructions '
120                  'at https://wiki.archlinux.org/index.php/Multilib.')
121            raise e
122
123        # 2. Android pieces.
124        from mozboot import android
125        android.ensure_android('linux', artifact_mode=artifact_mode,
126                               no_interactive=self.no_interactive)
127
128    def suggest_mobile_android_mozconfig(self, artifact_mode=False):
129        from mozboot import android
130        android.suggest_mozconfig('linux', artifact_mode=artifact_mode)
131
132    def suggest_mobile_android_artifact_mode_mozconfig(self):
133        self.suggest_mobile_android_mozconfig(artifact_mode=True)
134
135    def _update_package_manager(self):
136        self.pacman_update
137
138    def upgrade_mercurial(self, current):
139        self.pacman_install('mercurial')
140
141    def upgrade_python(self, current):
142        self.pacman_install('python2')
143
144    def pacman_install(self, *packages):
145        command = ['pacman', '-S', '--needed']
146        if self.no_interactive:
147            command.append('--noconfirm')
148
149        command.extend(packages)
150
151        self.run_as_root(command)
152
153    def pacman_update(self):
154        command = ['pacman', '-S', '--refresh']
155
156        self.run_as_root(command)
157
158    def run(self, command, env=None):
159        subprocess.check_call(command, stdin=sys.stdin, env=env)
160
161    def download(self, uri):
162        command = ['curl', '-L', '-O', uri]
163        self.run(command)
164
165    def unpack(self, path, name, ext):
166        if ext == 'gz':
167            compression = '-z'
168        elif ext == 'bz':
169            compression == '-j'
170        elif exit == 'xz':
171            compression == 'x'
172
173        name = os.path.join(path, name) + '.tar.' + ext
174        command = ['tar', '-x', compression, '-f', name, '-C', path]
175        self.run(command)
176
177    def makepkg(self, name):
178        command = ['makepkg', '-s']
179        makepkg_env = os.environ.copy()
180        makepkg_env['PKGEXT'] = '.pkg.tar.xz'
181        self.run(command, env=makepkg_env)
182        pack = glob.glob(name + '*.pkg.tar.xz')[0]
183        command = ['pacman', '-U']
184        if self.no_interactive:
185            command.append('--noconfirm')
186        command.append(pack)
187        self.run_as_root(command)
188
189    def aur_install(self, *packages):
190        path = tempfile.mkdtemp()
191        if not self.no_interactive:
192            print('WARNING! This script requires to install packages from the AUR '
193                  'This is potentially unsecure so I recommend that you carefully '
194                  'read each package description and check the sources.'
195                  'These packages will be built in ' + path + '.')
196            choice = raw_input('Do you want to continue? (yes/no) [no]')
197            if choice != 'yes':
198                sys.exit(1)
199
200        base_dir = os.getcwd()
201        os.chdir(path)
202        for package in packages:
203            name, _, ext = package.split('/')[-1].split('.')
204            directory = os.path.join(path, name)
205            self.download(package)
206            self.unpack(path, name, ext)
207            os.chdir(directory)
208            self.makepkg(name)
209
210        os.chdir(base_dir)
211