~fboucault/qtcreator-plugin-ubuntu/newarch_arm64

« back to all changes in this revision

Viewing changes to share/qtcreator/ubuntu/scripts/usdk-target-build

  • Committer: Benjamin Zeller
  • Date: 2016-06-08 15:09:39 UTC
  • mfrom: (443.2.36 ubuntu)
  • Revision ID: benjamin.zeller@canonical.com-20160608150939-v4ffv2a9xy5lcr5s
LXD rewrite for building and running apps in LXD containers

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python3
 
2
# Copyright (C) 2016 Canonical Ltd.
 
3
# Authors: Colin Watson <cjwatson@ubuntu.com>,
 
4
#          Brian Murray <brian@ubuntu.com>
 
5
#          Michael Vogt <mvo@ubuntu.com>
 
6
#          Benjamin Zeller <benjamin.zeller@canonical.com>
 
7
#          Zoltán Balogh <zoltan.balogh@canonical.com>
 
8
#
 
9
# This program is free software: you can redistribute it and/or modify
 
10
# it under the terms of the GNU General Public License as published by
 
11
# the Free Software Foundation; version 3 of the License.
 
12
#
 
13
# This program is distributed in the hope that it will be useful,
 
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
# GNU General Public License for more details.
 
17
#
 
18
# You should have received a copy of the GNU General Public License
 
19
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
 
 
21
from __future__ import print_function
 
22
 
 
23
try:
 
24
    from urllib.error import URLError
 
25
    from urllib.request import urlopen
 
26
except ImportError:
 
27
    from urllib2 import URLError, urlopen
 
28
import os
 
29
import pwd
 
30
import re
 
31
import shutil
 
32
import stat
 
33
import subprocess
 
34
import sys
 
35
import argparse
 
36
import time
 
37
import hashlib
 
38
from textwrap import dedent
 
39
from xml.etree import ElementTree
 
40
import json
 
41
import os.path
 
42
from time import gmtime, strftime
 
43
 
 
44
framework_base = {
 
45
    # 15.04
 
46
    "ubuntu-sdk-15.04-html": "ubuntu-sdk-15.04",
 
47
    "ubuntu-sdk-15.04-papi": "ubuntu-sdk-15.04",
 
48
    "ubuntu-sdk-15.04-qml": "ubuntu-sdk-15.04",
 
49
    # 15.10
 
50
    "ubuntu-sdk-15.10-html-dev1": "ubuntu-sdk-15.10-dev1",
 
51
    "ubuntu-sdk-15.10-papi-dev1": "ubuntu-sdk-15.10-dev1",
 
52
    "ubuntu-sdk-15.10-qml-dev1": "ubuntu-sdk-15.10-dev1",
 
53
    }
 
54
 
 
55
 
 
56
framework_series = {
 
57
    "ubuntu-sdk-15.04": "vivid",
 
58
    "ubuntu-sdk-15.10": "wily",
 
59
    "ubuntu-sdk-16.04": "xenial",
 
60
    "ubuntu-sdk-16.10": "yakketi",
 
61
    }
 
62
 
 
63
extra_packages = {
 
64
    "ubuntu-sdk-15.04": [
 
65
        "ubuntu-sdk-libs:{TARGET}",
 
66
        "ubuntu-sdk-libs-dev:{TARGET}",
 
67
        "ubuntu-sdk-libs-tools",
 
68
        "oxideqt-codecs-extra",
 
69
        "ubuntu-html5-container:{TARGET}",
 
70
        "qt5-doc",
 
71
        "language-pack-en",
 
72
        ],
 
73
    "ubuntu-sdk-15.10-dev1": [
 
74
        "ubuntu-sdk-libs:{TARGET}",
 
75
        "ubuntu-sdk-libs-dev:{TARGET}",
 
76
        "ubuntu-sdk-libs-tools",
 
77
        "oxideqt-codecs-extra",
 
78
        "qt5-doc",
 
79
        "language-pack-en",
 
80
        ],
 
81
    "ubuntu-sdk-16.04": [
 
82
        ],
 
83
    "ubuntu-sdk-16.10": [
 
84
        ]
 
85
    }
 
86
 
 
87
# all runtime only packages, they are only installed on NON cross chroots
 
88
runtime_packages = [
 
89
    "unity-scope-tool",
 
90
    "ssh",
 
91
    "gdbserver"
 
92
]
 
93
 
 
94
metadata_template = """\
 
95
architecture: {ARCH}
 
96
creation_date: {CREATETIME}
 
97
properties:
 
98
  architecture: {ARCH}
 
99
  description: {FULLNAME}
 
100
  name: {FULLNAME}
 
101
  os: ubuntu
 
102
  release: {SERIES}
 
103
  variant: default
 
104
templates:
 
105
  "/etc/hostname":
 
106
    template: hostname.tpl
 
107
    when:
 
108
    - create
 
109
  "/etc/hosts":
 
110
    template: hosts.tpl
 
111
    when:
 
112
    - create
 
113
"""
 
114
 
 
115
network_settings = """\
 
116
# This file describes the network interfaces available on your system
 
117
# and how to activate them. For more information, see interfaces(5).
 
118
 
 
119
# The loopback network interface
 
120
auto lo
 
121
iface lo inet loopback
 
122
 
 
123
auto eth0
 
124
iface eth0 inet dhcp
 
125
"""
 
126
 
 
127
primary_arches = ["amd64", "i386"]
 
128
lxd_arch = {
 
129
    "i386": "i686",
 
130
    "amd64": "x86_64",
 
131
    "armhf": "armv7l",
 
132
    "arm64": "aarch64",
 
133
    "powerpc": "ppc",
 
134
    "ppc64el": "ppc64le"
 
135
}
 
136
 
 
137
# list of target architecures supported by host architectures
 
138
compatible_arches = {
 
139
    "i386": ["i386", "armhf"],
 
140
    "amd64": ["i386", "amd64", "armhf"],
 
141
    "armhf": ["armhf"],
 
142
}
 
143
 
 
144
non_meta_re = re.compile(r'^[a-zA-Z0-9+,./:=@_-]+$')
 
145
GEOIP_SERVER = "http://geoip.ubuntu.com/lookup"
 
146
overlay_ppa = "ci-train-ppa-service/stable-phone-overlay"
 
147
rootfs = "rootfs"
 
148
qemu_arm_static = "/usr/bin/qemu-arm-static"
 
149
 
 
150
template_directory = "templates"
 
151
hostname_tpl_filename = "hostname.tpl"
 
152
hostname_tpl = "{{ container.name }}"
 
153
hosts_tpl_filename = "hosts.tpl"
 
154
hosts_tpl = "127.0.0.1   localhost\
 
155
             \n127.0.1.1   {{ container.name }}\
 
156
             \n# The following lines are desirable for IPv6 capable hosts\
 
157
             \n::1     ip6-localhost ip6-loopback\
 
158
             \nfe00::0 ip6-localnet\
 
159
             \nff00::0 ip6-mcastprefix\
 
160
             \nff02::1 ip6-allnodes\
 
161
             \nff02::2 ip6-allrouters"
 
162
upstart_override_tpl_filename = "upstart-override.tpl"
 
163
upstart_override_tpl = "manual"
 
164
 
 
165
 
 
166
class bcolors:
 
167
    HEADER = '\033[95m'
 
168
    OKBLUE = '\033[94m'
 
169
    OKGREEN = '\033[92m'
 
170
    WARNING = '\033[93m'
 
171
    FAIL = '\033[91m'
 
172
    ENDC = '\033[0m'
 
173
 
 
174
    def disable(self):
 
175
        self.HEADER = ''
 
176
        self.OKBLUE = ''
 
177
        self.OKGREEN = ''
 
178
        self.WARNING = ''
 
179
        self.FAIL = ''
 
180
        self.ENDC = ''
 
181
 
 
182
 
 
183
def make_md5_sha256(file_name, combined_hash_sha256):
 
184
    hash_md5 = hashlib.md5()
 
185
    hash_sha256 = hashlib.sha256()
 
186
    with open(file_name, "rb") as f:
 
187
        for chunk in iter(lambda: f.read(65536), b""):
 
188
            hash_sha256.update(chunk)
 
189
            hash_md5.update(chunk)
 
190
            combined_hash_sha256.update(chunk)
 
191
    return (hash_md5.hexdigest(),
 
192
            hash_sha256.hexdigest(),
 
193
            combined_hash_sha256.hexdigest())
 
194
 
 
195
 
 
196
def add_image_to_download(framework, base_arch, target_arch, image_type):
 
197
    match = re.search('ubuntu-sdk-(.*)', framework)
 
198
    if match:
 
199
        ubuntu_version = match.group(1)
 
200
    series = framework_series[framework]
 
201
    root_image = "%s-%s-%s-root.tar.xz" % (framework, base_arch, target_arch)
 
202
    lxd_image = "%s-%s-%s-lxd.tar.xz" % (framework, base_arch, target_arch)
 
203
    if not os.path.isfile(root_image):
 
204
        print("The %s root file does not exist" % root_image)
 
205
        return
 
206
    if not os.path.isfile(lxd_image):
 
207
        print("The %s lxd file does not exist" % lxd_image)
 
208
        return
 
209
    str_time = strftime("%a, %d %b %Y %H:%M:%S %z", gmtime())
 
210
    short_date = strftime("%Y%m%d", gmtime())
 
211
    combined_hash_sha256 = hashlib.sha256()
 
212
    lxd_md5,\
 
213
        lxd_sha256,\
 
214
        combined_sha256 = make_md5_sha256(lxd_image, combined_hash_sha256)
 
215
    root_md5,\
 
216
        root_sha256,\
 
217
        combined_sha256 = make_md5_sha256(root_image, combined_hash_sha256)
 
218
    download_json = "com.ubuntu.sdkimage:released:download.json"
 
219
    index_json = "index.json"
 
220
    license = "http://www.canonical.com/intellectual-property-policy"
 
221
    image_category = "com.ubuntu.sdk-image:released:download"
 
222
    download_json_path = "streams/v1/%s" % download_json
 
223
    download_template = '{"content_id" : \
 
224
                          "com.ubuntu.sdk-image:released:download",\
 
225
                          "datatype" : "image-downloads",\
 
226
                          "format" : "products:1.0",\
 
227
                          "license" : "%s",\
 
228
                          "products" : "",\
 
229
                          "updated" : "%s"}' % (license, str_time)
 
230
    index_template = '{"index": {\
 
231
                           "com.ubuntu.sdk-image:released:download": {\
 
232
                               "datatype": "image-downloads",\
 
233
                               "path": "%s",\
 
234
                               "updated": "%s",\
 
235
                               "products": [],\
 
236
                               "format": "products:1.0"\
 
237
                              }\
 
238
                          },\
 
239
                      "updated": "%s",\
 
240
                      "format": "index:1.0"\
 
241
                      }' % (download_json_path, str_time, str_time)
 
242
    if os.path.isfile(download_json):
 
243
        with open(download_json) as download_json_file:
 
244
            download_data = json.load(download_json_file)
 
245
    else:
 
246
        download_data = json.loads(download_template)
 
247
    if os.path.isfile(index_json):
 
248
        with open(index_json) as index_json_file:
 
249
            index_data = json.load(index_json_file)
 
250
    else:
 
251
        index_data = json.loads(index_template)
 
252
    products = download_data["products"]
 
253
    pruduct_id = "com.ubuntu.sdkimage:builder:%s:%s:%s"\
 
254
                 % (base_arch, ubuntu_version, target_arch)
 
255
    if pruduct_id not in index_data["index"][image_category]["products"]:
 
256
        index_data["index"][image_category]["products"].append(pruduct_id)
 
257
    lxd_file = '{"size": %s,\
 
258
                "ftype": "lxd.tar.xz",\
 
259
                "path": "releases/%s/%s",\
 
260
                "combined_sha256": "%s",\
 
261
                "sha256": "%s",\
 
262
                "md5": "%s"\
 
263
                }'\
 
264
                % (os.path.getsize(lxd_image),
 
265
                   series,
 
266
                   lxd_image,
 
267
                   combined_sha256,
 
268
                   lxd_sha256,
 
269
                   lxd_md5)
 
270
    root_file = '{"size": %s,\
 
271
                 "ftype": "root.tar.xz",\
 
272
                 "path": "releases/%s/%s",\
 
273
                 "combined_sha256": "%s",\
 
274
                 "sha256": "%s",\
 
275
                 "md5": "%s"\
 
276
                 }' % (os.path.getsize(root_image),
 
277
                       series,
 
278
                       root_image,
 
279
                       combined_sha256,
 
280
                       root_sha256,
 
281
                       root_md5)
 
282
    items = '{"lxd.tar.xz": %s,\
 
283
              "root.tar.xz": %s\
 
284
             }' % (lxd_file, root_file)
 
285
    date_section = '{"items" : %s,\
 
286
                    "pubname": "ubuntu-%s-%s-overlay-%s-builder-%s",\
 
287
                    "label": "sdk-%s"\
 
288
                    }' % (items,
 
289
                          series,
 
290
                          ubuntu_version,
 
291
                          target_arch,
 
292
                          short_date,
 
293
                          target_arch)
 
294
    versions = '{"%s": %s\
 
295
                }' % (short_date, date_section)
 
296
    product_object = '{"versions": %s,\
 
297
                       "arch": "%s",\
 
298
                       "supported": true,\
 
299
                       "release_title": "%s",\
 
300
                       "release_codename": "%s",\
 
301
                       "version": "%s",\
 
302
                       "release": "%s",\
 
303
                       "aliases": \
 
304
                       "%s-%s-%s-%s,ubuntu-framework-%s-%s-%s-%s",\
 
305
                       "os": "ubuntu",\
 
306
                       "support_eol": "2019-04-17"\
 
307
                      }' % (versions,
 
308
                            lxd_arch[base_arch],
 
309
                            series,
 
310
                            series.capitalize(),
 
311
                            ubuntu_version,
 
312
                            ubuntu_version,
 
313
                            framework,
 
314
                            base_arch,
 
315
                            target_arch,
 
316
                            image_type,
 
317
                            ubuntu_version,
 
318
                            base_arch,
 
319
                            target_arch,
 
320
                            image_type)
 
321
    products_string = '{"%s": %s}' % (pruduct_id, product_object)
 
322
    new_product = json.loads(products_string)
 
323
    merged_products = dict(products, **new_product)
 
324
    download_data["products"] = merged_products
 
325
    download_data["updated"] = str_time
 
326
    with open(download_json, 'w') as outfile:
 
327
        json.dump(download_data, outfile, indent=4)
 
328
    index_data["updated"] = str_time
 
329
    index_data["index"][image_category]["updated"] = str_time
 
330
    with open(index_json, 'w') as outfile:
 
331
        json.dump(index_data, outfile, indent=4)
 
332
 
 
333
 
 
334
def get_geoip_country_code_prefix():
 
335
    click_no_local_mirror = os.environ.get('CLICK_NO_LOCAL_MIRROR', 'auto')
 
336
    if click_no_local_mirror == '1':
 
337
        return ""
 
338
    try:
 
339
        with urlopen(GEOIP_SERVER) as f:
 
340
            xml_data = f.read()
 
341
        et = ElementTree.fromstring(xml_data)
 
342
        cc = et.find("CountryCode")
 
343
        if not cc:
 
344
            return ""
 
345
        return cc.text.lower() + "."
 
346
    except (ElementTree.ParseError, URLError):
 
347
        pass
 
348
    return ""
 
349
 
 
350
 
 
351
def generate_sources(series, native_arch, target_arch,
 
352
                     archive_mirror, ports_mirror, components):
 
353
    """Generate a list of strings for apts sources.list.
 
354
    Arguments:
 
355
    series -- the distro series (e.g. vivid)
 
356
    native_arch -- the native architecture (e.g. amd64)
 
357
    target_arch -- the target architecture (e.g. armhf)
 
358
    archive_mirror -- main mirror, e.g. http://archive.ubuntu.com/ubuntu
 
359
    ports_mirror -- ports mirror, e.g. http://ports.ubuntu.com/ubuntu-ports
 
360
    components -- the components as string, e.g. "main restricted universe"
 
361
    """
 
362
    pockets = ['%s' % series]
 
363
    for pocket in ['updates', 'security']:
 
364
        pockets.append('%s-%s' % (series, pocket))
 
365
    sources = []
 
366
    # write binary lines
 
367
    arches = [target_arch]
 
368
    if native_arch != target_arch:
 
369
        arches.append(native_arch)
 
370
    for arch in arches:
 
371
        if arch not in primary_arches:
 
372
            mirror = ports_mirror
 
373
        else:
 
374
            mirror = archive_mirror
 
375
        for pocket in pockets:
 
376
            sources.append("deb [arch=%s] %s %s %s" %
 
377
                           (arch, mirror, pocket, components))
 
378
    # write source lines
 
379
    for pocket in pockets:
 
380
        sources.append("deb-src %s %s %s" %
 
381
                       (archive_mirror, pocket, components))
 
382
    return sources
 
383
 
 
384
 
 
385
def shell_escape(command):
 
386
    escaped = []
 
387
    for arg in command:
 
388
        if non_meta_re.match(arg):
 
389
            escaped.append(arg)
 
390
        else:
 
391
            escaped.append("'%s'" % arg.replace("'", "'\\''"))
 
392
    return " ".join(escaped)
 
393
 
 
394
 
 
395
def strip_dev_series_from_framework(framework):
 
396
    """Remove trailing -dev[0-9]+ from a framework name"""
 
397
    return re.sub(r'^(.*)-dev[0-9]+$', r'\1', framework)
 
398
 
 
399
 
 
400
class ClickChroot:
 
401
 
 
402
    DAEMON_POLICY = dedent("""\
 
403
    #!/bin/sh
 
404
    while true; do
 
405
        case "$1" in
 
406
          -*) shift ;;
 
407
          makedev) exit 0;;
 
408
          x11-common) exit 0;;
 
409
          *) exit 101;;
 
410
        esac
 
411
    done
 
412
    """)
 
413
 
 
414
    def __init__(self,
 
415
                 system_arch,
 
416
                 target_arch,
 
417
                 framework,
 
418
                 image_type,
 
419
                 series=None,
 
420
                 chroots_dir=None):
 
421
        self.target_arch = target_arch
 
422
        self.framework = strip_dev_series_from_framework(framework)
 
423
        self.image_type = image_type
 
424
        if series is None:
 
425
            series = framework_series[self.framework_base]
 
426
        self.series = series
 
427
        self.native_arch = self._get_native_arch(system_arch, self.target_arch)
 
428
        if chroots_dir is None:
 
429
            chroots_dir = os.getcwd()
 
430
        self.chroots_dir = chroots_dir
 
431
        if "SUDO_USER" in os.environ:
 
432
            self.user = os.environ["SUDO_USER"]
 
433
        elif "PKEXEC_UID" in os.environ:
 
434
            self.user = pwd.getpwuid(int(os.environ["PKEXEC_UID"])).pw_name
 
435
        else:
 
436
            self.user = pwd.getpwuid(os.getuid()).pw_name
 
437
        self.dpkg_architecture = self._dpkg_architecture()
 
438
 
 
439
    def _get_native_arch(self, system_arch, target_arch):
 
440
        """Determine the proper native architecture for a chroot.
 
441
 
 
442
        Some combinations of system and target architecture do not require
 
443
        cross-building, so in these cases we just create a chroot suitable
 
444
        for native building.
 
445
        """
 
446
        if (system_arch, target_arch) in (
 
447
                ("amd64", "i386"),
 
448
                # This will only work if the system is running a 64-bit
 
449
                # kernel; but there's no alternative since no i386-to-amd64
 
450
                # cross-compiler is available in the Ubuntu archive.
 
451
                ("i386", "amd64"),
 
452
                ):
 
453
            return target_arch
 
454
        else:
 
455
            return system_arch
 
456
 
 
457
    def _dpkg_architecture(self):
 
458
        dpkg_architecture = {}
 
459
        command = ["dpkg-architecture", "-a%s" % self.target_arch]
 
460
        env = dict(os.environ)
 
461
        env["CC"] = "true"
 
462
        # Force dpkg-architecture to recalculate everything rather than
 
463
        # picking up values from the environment, which will be present when
 
464
        # running the test suite under dpkg-buildpackage.
 
465
        for key in list(env):
 
466
            if key.startswith("DEB_BUILD_") or key.startswith("DEB_HOST_"):
 
467
                del env[key]
 
468
        lines = subprocess.check_output(
 
469
            command, env=env, universal_newlines=True).splitlines()
 
470
        for line in lines:
 
471
            try:
 
472
                key, value = line.split("=", 1)
 
473
            except ValueError:
 
474
                continue
 
475
            dpkg_architecture[key] = value
 
476
        if self.native_arch == self.target_arch:
 
477
            # We may have overridden the native architecture (see
 
478
            # _get_native_arch above), so we need to force DEB_BUILD_* to
 
479
            # match.
 
480
            for key in list(dpkg_architecture):
 
481
                if key.startswith("DEB_HOST_"):
 
482
                    new_key = "DEB_BUILD_" + key[len("DEB_HOST_"):]
 
483
                    dpkg_architecture[new_key] = dpkg_architecture[key]
 
484
        return dpkg_architecture
 
485
 
 
486
    def _generate_daemon_policy(self, mount):
 
487
        daemon_policy = "%s/usr/sbin/policy-rc.d" % mount
 
488
        with open(daemon_policy, "w") as policy:
 
489
            policy.write(self.DAEMON_POLICY)
 
490
        return daemon_policy
 
491
 
 
492
    def _generate_apt_proxy_file(self, mount, proxy):
 
493
        apt_conf_d = os.path.join(mount, "etc", "apt", "apt.conf.d")
 
494
        if not os.path.exists(apt_conf_d):
 
495
            os.makedirs(apt_conf_d)
 
496
        apt_conf_f = os.path.join(apt_conf_d, "99-click-chroot-proxy")
 
497
        if proxy:
 
498
            with open(apt_conf_f, "w") as f:
 
499
                f.write(dedent("""\
 
500
                // proxy settings copied by click chroot
 
501
                Acquire {
 
502
                    HTTP {
 
503
                        Proxy "%s";
 
504
                    };
 
505
                };
 
506
                """) % proxy)
 
507
        return apt_conf_f
 
508
 
 
509
    def _generate_finish_script(self, mount, build_pkgs):
 
510
        finish_script = "%s/finish.sh" % mount
 
511
        with open(finish_script, 'w') as finish:
 
512
            finish.write(dedent("""\
 
513
                #!/bin/bash
 
514
                set -e
 
515
                # Configure target arch
 
516
                dpkg --add-architecture {target_arch}
 
517
                # Reload package lists
 
518
                apt-get update || true
 
519
                # Pull down signature requirements
 
520
                apt-get -y --force-yes install gnupg ubuntu-keyring
 
521
            """).format(target_arch=self.target_arch))
 
522
            if self.series == "vivid":
 
523
                finish.write(dedent("""\
 
524
                    apt-get -y --force-yes install software-properties-common
 
525
                    add-apt-repository -y ppa:{ppa}
 
526
                    echo "Package: *"  \
 
527
                        > /etc/apt/preferences.d/stable-phone-overlay.pref
 
528
                    echo \
 
529
                        "Pin: release o=LP-PPA-{pin_ppa}" \
 
530
                        >> /etc/apt/preferences.d/stable-phone-overlay.pref
 
531
                    echo "Pin-Priority: 1001" \
 
532
                        >> /etc/apt/preferences.d/stable-phone-overlay.pref
 
533
                """).format(ppa=overlay_ppa,
 
534
                            pin_ppa=re.sub('/', '-', overlay_ppa)))
 
535
            finish.write(dedent("""\
 
536
                # Reload package lists
 
537
                apt-get update || true
 
538
                # Disable debconf questions
 
539
                # so that automated builds won't prompt
 
540
                echo set debconf/frontend Noninteractive | debconf-communicate
 
541
                echo set debconf/priority critical | debconf-communicate
 
542
                apt-get -y --force-yes dist-upgrade
 
543
                # Install basic build tool set to match buildd
 
544
                apt-get -y --force-yes install {build_pkgs}
 
545
                # Make sure network is initialized
 
546
                systemctl enable systemd-networkd.service
 
547
                # Make sure dpkg variables are correct
 
548
                for i in $(dpkg-architecture -a {target_arch} 2>/dev/null); \
 
549
                    do echo "export $i" >> /etc/profile.d/clickvars.sh ; \
 
550
                done
 
551
                # Clean up
 
552
                rm /finish.sh
 
553
                apt-get clean
 
554
                [[ -e /sbin/initctl_tmp ]] && \
 
555
                mv /sbin/initctl_tmp /sbin/initctl
 
556
                rm /usr/sbin/policy-rc.d
 
557
            """).format(build_pkgs=' '.join(build_pkgs),
 
558
                        target_arch=self.target_arch))
 
559
        return finish_script
 
560
 
 
561
    def _debootstrap(self, components, mount, archive_mirror, ports_mirror):
 
562
        if self.native_arch in primary_arches:
 
563
            mirror = archive_mirror
 
564
            subprocess.check_call([
 
565
                "debootstrap",
 
566
                "--arch", self.native_arch,
 
567
                "--variant=buildd",
 
568
                "--components=%s" % ','.join(components),
 
569
                self.series,
 
570
                mount,
 
571
                mirror
 
572
                ])
 
573
        else:
 
574
            mirror = ports_mirror
 
575
            subprocess.check_call([
 
576
                "debootstrap",
 
577
                "--arch", self.native_arch,
 
578
                "--foreign",
 
579
                "--variant=buildd",
 
580
                "--components=%s" % ','.join(components),
 
581
                self.series,
 
582
                mount,
 
583
                mirror
 
584
                ])
 
585
 
 
586
    @property
 
587
    def framework_base(self):
 
588
        if self.framework in framework_base:
 
589
            return framework_base[self.framework]
 
590
        else:
 
591
            return self.framework
 
592
 
 
593
    @property
 
594
    def full_name(self):
 
595
        return "%s-%s-%s" % (self.framework_base,
 
596
                             self.native_arch,
 
597
                             self.target_arch)
 
598
 
 
599
    def _make_executable(self, path):
 
600
        mode = stat.S_IMODE(os.stat(path).st_mode)
 
601
        os.chmod(path, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
 
602
 
 
603
    def _make_cross_package(self, prefix):
 
604
        if self.native_arch == self.target_arch:
 
605
            return prefix
 
606
        else:
 
607
            target_tuple = self.dpkg_architecture["DEB_HOST_GNU_TYPE"]
 
608
            return "%s-%s" % (prefix, target_tuple)
 
609
 
 
610
    def _is_crossbuild(self):
 
611
        return (self.native_arch != self.target_arch)
 
612
 
 
613
    def create(self):
 
614
        components = ["main", "restricted", "universe", "multiverse"]
 
615
        mount = "%s/%s" % (os.getcwd(), rootfs)
 
616
        proxy = None
 
617
        if not proxy and "http_proxy" in os.environ:
 
618
            proxy = os.environ["http_proxy"]
 
619
        if not proxy:
 
620
            proxy = subprocess.check_output(
 
621
                'unset x; eval "$(apt-config shell x Acquire::HTTP::Proxy)"; \
 
622
                 echo "$x"',
 
623
                shell=True, universal_newlines=True).strip()
 
624
        build_pkgs = [
 
625
            # sort alphabetically
 
626
            "apt-utils",
 
627
            "build-essential",
 
628
            "cmake",
 
629
            "dpkg-cross",
 
630
            "fakeroot",
 
631
            "libc-dev:%s" % self.target_arch,
 
632
            # build pkg names dynamically
 
633
            self._make_cross_package("g++"),
 
634
            self._make_cross_package("pkg-config"),
 
635
        ]
 
636
        for package in extra_packages.get(self.framework_base, []):
 
637
            package = package.format(TARGET=self.target_arch)
 
638
            build_pkgs.append(package)
 
639
        if not self._is_crossbuild():
 
640
            for package in runtime_packages:
 
641
                package = package.format(TARGET=self.target_arch)
 
642
                build_pkgs.append(package)
 
643
        if not os.path.exists(mount):
 
644
            os.makedirs(mount)
 
645
        else:
 
646
            print("The " +
 
647
                  bcolors.FAIL +
 
648
                  "%s" % mount +
 
649
                  bcolors.ENDC +
 
650
                  " directory already exists")
 
651
            sys.exit(1)
 
652
        country_code = get_geoip_country_code_prefix()
 
653
        archive_mirror = "http://%sarchive.ubuntu.com/ubuntu" % country_code
 
654
        ports_mirror = "http://%sports.ubuntu.com/ubuntu-ports" % country_code
 
655
        # this doesn't work because we are running this under sudo
 
656
        if 'DEBOOTSTRAP_MIRROR' in os.environ:
 
657
            archive_mirror = os.environ['DEBOOTSTRAP_MIRROR']
 
658
        self._debootstrap(components, mount, archive_mirror, ports_mirror)
 
659
        # in case of creating foreign arch image we need to add the emulator
 
660
        if self.native_arch not in primary_arches:
 
661
            if os.path.isfile(qemu_arm_static):
 
662
                shutil.copy2(qemu_arm_static, "%s/usr/bin/" % mount)
 
663
            else:
 
664
                print(bcolors.FAIL +
 
665
                      "The %s is not present, please install\
 
666
                      qemu-user-static package"
 
667
                      % qemu_arm_static +
 
668
                      bcolors.ENDC)
 
669
                sys.exit(1)
 
670
            ret_code = subprocess.call(["/usr/sbin/chroot",
 
671
                                        mount,
 
672
                                        "/debootstrap/debootstrap",
 
673
                                        "--second-stage"])
 
674
            if ret_code != 0:
 
675
                print(bcolors.FAIL +
 
676
                      "Second stage of the debootstrapping failed." +
 
677
                      bcolors.ENDC)
 
678
                sys.exit(1)
 
679
        sources = generate_sources(self.series, self.native_arch,
 
680
                                   self.target_arch,
 
681
                                   archive_mirror, ports_mirror,
 
682
                                   ' '.join(components))
 
683
        with open("%s/etc/apt/sources.list" % mount, "w") as sources_list:
 
684
            for line in sources:
 
685
                print(line, file=sources_list)
 
686
        daemon_policy = self._generate_daemon_policy(mount)
 
687
        self._make_executable(daemon_policy)
 
688
        initctl = "%s/sbin/initctl" % mount
 
689
        if os.path.exists(initctl):
 
690
            shutil.copyfile(initctl, initctl + "_tmp")
 
691
            os.remove(initctl)
 
692
            os.symlink("%s/bin/true" % mount, initctl)
 
693
        self._generate_apt_proxy_file(mount, proxy)
 
694
        finish_script = self._generate_finish_script(mount, build_pkgs)
 
695
        self._make_executable(finish_script)
 
696
        ret_code = subprocess.call(["/usr/sbin/chroot", mount, "/finish.sh"])
 
697
        if ret_code != 0:
 
698
            print(bcolors.FAIL +
 
699
                  "Debootstrapping the chroot failed." +
 
700
                  bcolors.ENDC)
 
701
            sys.exit(1)
 
702
        metadata = metadata_template.format(
 
703
            ARCH=lxd_arch[self.native_arch],
 
704
            CREATETIME=int(time.time()),
 
705
            FULLNAME=self.full_name,
 
706
            SERIES=self.series
 
707
        )
 
708
        with open("metadata.yaml", "w") as metadata_file:
 
709
                print(metadata, file=metadata_file)
 
710
        # make sure /tmp is not cleaned when the chroot boots
 
711
        with open("%s/etc/tmpfiles.d/tmp.conf" % mount,
 
712
                  "w") as notmpclean_file:
 
713
            print("d /tmp 1777 root root -", file=notmpclean_file)
 
714
        # boot up network
 
715
        with open("%s/etc/network/interfaces" % mount, "w") as network_file:
 
716
            print(network_settings, file=network_file)
 
717
        # setup sshd for running and debugging
 
718
        if not self._is_crossbuild():
 
719
            with open("%s/etc/ssh/sshd_config" % mount, "a") as sshd_conf_file:
 
720
                print("\nAuthorizedKeysFile /etc/ssh/authorized_keys.d/%u",
 
721
                      file=sshd_conf_file)
 
722
        if not os.path.exists(template_directory):
 
723
            os.makedirs(template_directory)
 
724
        print("%s/%s" % (template_directory, hostname_tpl_filename))
 
725
        with open("%s/%s" % (template_directory,
 
726
                  hostname_tpl_filename),
 
727
                  "w") as hostname_tpl_file:
 
728
                print(hostname_tpl, file=hostname_tpl_file)
 
729
        with open("%s/%s" % (template_directory,
 
730
                  hosts_tpl_filename),
 
731
                  "w") as hosts_tpl_file:
 
732
                print(hosts_tpl, file=hosts_tpl_file)
 
733
 
 
734
        with open("%s/%s" % (template_directory,
 
735
                  upstart_override_tpl_filename),
 
736
                  "w") as upstart_override_tpl_file:
 
737
                print(upstart_override_tpl, file=upstart_override_tpl_file)
 
738
        print("Packaging the LXD image.....")
 
739
        rootfs_parts = os.listdir(rootfs)
 
740
        ret = subprocess.call(["tar",
 
741
                               "-C",
 
742
                               "%s" % rootfs,
 
743
                               "-ScpJf",
 
744
                               "%s-root.tar.xz"
 
745
                               % self.full_name] + rootfs_parts)
 
746
        ret = subprocess.call(["tar",
 
747
                               "-ScpJf",
 
748
                               "%s-lxd.tar.xz" % self.full_name,
 
749
                               "metadata.yaml",
 
750
                               "templates"])
 
751
        print("Clean up the build artifacts.")
 
752
        shutil.rmtree(rootfs)
 
753
        shutil.rmtree("templates")
 
754
        os.remove("metadata.yaml")
 
755
        print("Create the download.json")
 
756
        add_image_to_download(self.framework,
 
757
                              self.native_arch,
 
758
                              self.target_arch,
 
759
                              self.image_type)
 
760
        sys.exit(ret)
 
761
 
 
762
 
 
763
def check_valid_arch(value):
 
764
    valid_arch_set = ["i386", "amd64", "armhf"]
 
765
    if value not in valid_arch_set:
 
766
        raise argparse.ArgumentTypeError("%s is not a valid arch type" % value)
 
767
    return value
 
768
 
 
769
 
 
770
def check_valid_framework(value):
 
771
    if value not in framework_series:
 
772
        raise argparse.ArgumentTypeError("%s is not a valid arch type" % value)
 
773
    return value
 
774
 
 
775
 
 
776
def check_valid_type(value):
 
777
    ota_re = '^ota\d+$|^dev$'
 
778
    if not re.search(ota_re, value):
 
779
        raise argparse.ArgumentTypeError("%s is not a valid image type"
 
780
                                         % value)
 
781
    return value
 
782
 
 
783
 
 
784
def main():
 
785
    if os.geteuid() != 0:
 
786
        exit("You need to have" +
 
787
             bcolors.FAIL +
 
788
             " root privileges" +
 
789
             bcolors.ENDC +
 
790
             " to run this tool.\nPlease try again, using 'sudo'")
 
791
 
 
792
    parser = argparse.ArgumentParser(description="Ubuntu SDK target builder")
 
793
    parser.add_argument('-a',
 
794
                        '--target_architecture',
 
795
                        action="store",
 
796
                        default="armhf",
 
797
                        type=check_valid_arch)
 
798
    parser.add_argument('-b',
 
799
                        '--base_architecture',
 
800
                        action="store",
 
801
                        default="amd64",
 
802
                        type=check_valid_arch)
 
803
    parser.add_argument('-f',
 
804
                        '--framework',
 
805
                        action='store',
 
806
                        default="ubuntu-sdk-15.04",
 
807
                        type=check_valid_framework)
 
808
    parser.add_argument('-t',
 
809
                        '--image_type',
 
810
                        action='store',
 
811
                        default="dev",
 
812
                        type=check_valid_type)
 
813
    options = parser.parse_args()
 
814
    if os.isatty(sys.stdout.fileno()):
 
815
        print("Building " +
 
816
              bcolors.OKGREEN +
 
817
              options.image_type +
 
818
              bcolors.ENDC +
 
819
              " type image for " +
 
820
              bcolors.OKGREEN +
 
821
              options.target_architecture +
 
822
              bcolors.ENDC +
 
823
              " on " +
 
824
              bcolors.OKGREEN +
 
825
              options.base_architecture + bcolors.ENDC +
 
826
              " base arch with " + bcolors.OKGREEN +
 
827
              options.framework + bcolors.ENDC +
 
828
              " framework")
 
829
    else:
 
830
        bcolors.disable()
 
831
    click = ClickChroot(options.base_architecture,
 
832
                        options.target_architecture,
 
833
                        options.framework,
 
834
                        options.image_type,
 
835
                        None,
 
836
                        os.getcwd())
 
837
    sys.exit(click.create())
 
838
 
 
839
main()