xref: /openbmc/openbmc/poky/bitbake/lib/toaster/tests/builds/buildtest.py (revision 169d7bccc02da43f8574d030502cfcf9308f505f)
196ff1984SBrad Bishop#! /usr/bin/env python3
2eb8dc403SDave Cobbley#
3eb8dc403SDave Cobbley# BitBake Toaster Implementation
4eb8dc403SDave Cobbley#
5eb8dc403SDave Cobbley# Copyright (C) 2016 Intel Corporation
6eb8dc403SDave Cobbley#
7c342db35SBrad Bishop# SPDX-License-Identifier: GPL-2.0-only
8eb8dc403SDave Cobbley#
9eb8dc403SDave Cobbley
10eb8dc403SDave Cobbleyimport os
11eb8dc403SDave Cobbleyimport sys
12eb8dc403SDave Cobbleyimport time
13eb8dc403SDave Cobbleyimport unittest
14eb8dc403SDave Cobbley
15eb8dc403SDave Cobbleyfrom orm.models import Project, Release, ProjectTarget, Build, ProjectVariable
16eb8dc403SDave Cobbleyfrom bldcontrol.models import BuildEnvironment
17eb8dc403SDave Cobbley
18eb8dc403SDave Cobbleyfrom bldcontrol.management.commands.runbuilds import Command\
19eb8dc403SDave Cobbley    as RunBuildsCommand
20eb8dc403SDave Cobbley
21eb8dc403SDave Cobbleyfrom django.core.management import call_command
22eb8dc403SDave Cobbley
23eb8dc403SDave Cobbleyimport subprocess
24eb8dc403SDave Cobbleyimport logging
25eb8dc403SDave Cobbley
26eb8dc403SDave Cobbleylogger = logging.getLogger("toaster")
27eb8dc403SDave Cobbley
28eb8dc403SDave Cobbley# We use unittest.TestCase instead of django.test.TestCase because we don't
29eb8dc403SDave Cobbley# want to wrap everything in a database transaction as an external process
30eb8dc403SDave Cobbley# (bitbake needs access to the database)
31eb8dc403SDave Cobbley
32eb8dc403SDave Cobbleydef load_build_environment():
33eb8dc403SDave Cobbley    call_command('loaddata', 'settings.xml', app_label="orm")
34eb8dc403SDave Cobbley    call_command('loaddata', 'poky.xml', app_label="orm")
35eb8dc403SDave Cobbley
36eb8dc403SDave Cobbley    current_builddir = os.environ.get("BUILDDIR")
37eb8dc403SDave Cobbley    if current_builddir:
38eb8dc403SDave Cobbley        BuildTest.BUILDDIR = current_builddir
39eb8dc403SDave Cobbley    else:
40eb8dc403SDave Cobbley        # Setup a builddir based on default layout
41eb8dc403SDave Cobbley        # bitbake inside openebedded-core
42eb8dc403SDave Cobbley        oe_init_build_env_path = os.path.join(
43eb8dc403SDave Cobbley            os.path.dirname(os.path.abspath(__file__)),
44eb8dc403SDave Cobbley            os.pardir,
45eb8dc403SDave Cobbley            os.pardir,
46eb8dc403SDave Cobbley            os.pardir,
47eb8dc403SDave Cobbley            os.pardir,
48eb8dc403SDave Cobbley            os.pardir,
49eb8dc403SDave Cobbley            'oe-init-build-env'
50eb8dc403SDave Cobbley        )
51eb8dc403SDave Cobbley        if not os.path.exists(oe_init_build_env_path):
52eb8dc403SDave Cobbley            raise Exception("We had no BUILDDIR set and couldn't "
53eb8dc403SDave Cobbley                            "find oe-init-build-env to set this up "
54eb8dc403SDave Cobbley                            "ourselves please run oe-init-build-env "
55eb8dc403SDave Cobbley                            "before running these tests")
56eb8dc403SDave Cobbley
57eb8dc403SDave Cobbley        oe_init_build_env_path = os.path.realpath(oe_init_build_env_path)
58eb8dc403SDave Cobbley        cmd = "bash -c 'source oe-init-build-env %s'" % BuildTest.BUILDDIR
59eb8dc403SDave Cobbley        p = subprocess.Popen(
60eb8dc403SDave Cobbley            cmd,
61eb8dc403SDave Cobbley            cwd=os.path.dirname(oe_init_build_env_path),
62eb8dc403SDave Cobbley            shell=True,
63eb8dc403SDave Cobbley            stdout=subprocess.PIPE,
64eb8dc403SDave Cobbley            stderr=subprocess.PIPE)
65eb8dc403SDave Cobbley
66eb8dc403SDave Cobbley        output, err = p.communicate()
67eb8dc403SDave Cobbley        p.wait()
68eb8dc403SDave Cobbley
69eb8dc403SDave Cobbley        logger.info("oe-init-build-env %s %s" % (output, err))
70eb8dc403SDave Cobbley
71eb8dc403SDave Cobbley        os.environ['BUILDDIR'] = BuildTest.BUILDDIR
72eb8dc403SDave Cobbley
73eb8dc403SDave Cobbley    # Setup the path to bitbake we know where to find this
74eb8dc403SDave Cobbley    bitbake_path = os.path.join(
75eb8dc403SDave Cobbley        os.path.dirname(os.path.abspath(__file__)),
76eb8dc403SDave Cobbley        os.pardir,
77eb8dc403SDave Cobbley        os.pardir,
78eb8dc403SDave Cobbley        os.pardir,
79eb8dc403SDave Cobbley        os.pardir,
80eb8dc403SDave Cobbley        'bin',
81eb8dc403SDave Cobbley        'bitbake')
82eb8dc403SDave Cobbley    if not os.path.exists(bitbake_path):
83eb8dc403SDave Cobbley        raise Exception("Could not find bitbake at the expected path %s"
84eb8dc403SDave Cobbley                        % bitbake_path)
85eb8dc403SDave Cobbley
86eb8dc403SDave Cobbley    os.environ['BBBASEDIR'] = bitbake_path
87eb8dc403SDave Cobbley
88eb8dc403SDave Cobbleyclass BuildTest(unittest.TestCase):
89eb8dc403SDave Cobbley
90eb8dc403SDave Cobbley    PROJECT_NAME = "Testbuild"
91*169d7bccSPatrick Williams    BUILDDIR = os.environ.get("BUILDDIR")
92eb8dc403SDave Cobbley
93eb8dc403SDave Cobbley    def build(self, target):
94eb8dc403SDave Cobbley        # So that the buildinfo helper uses the test database'
95eb8dc403SDave Cobbley        self.assertEqual(
96eb8dc403SDave Cobbley            os.environ.get('DJANGO_SETTINGS_MODULE', ''),
97eb8dc403SDave Cobbley            'toastermain.settings_test',
98eb8dc403SDave Cobbley            "Please initialise django with the tests settings:  "
99eb8dc403SDave Cobbley            "DJANGO_SETTINGS_MODULE='toastermain.settings_test'")
100eb8dc403SDave Cobbley
101eb8dc403SDave Cobbley        built = self.target_already_built(target)
102eb8dc403SDave Cobbley        if built:
103eb8dc403SDave Cobbley            return built
104eb8dc403SDave Cobbley
105eb8dc403SDave Cobbley        load_build_environment()
106eb8dc403SDave Cobbley
107eb8dc403SDave Cobbley        BuildEnvironment.objects.get_or_create(
108eb8dc403SDave Cobbley            betype=BuildEnvironment.TYPE_LOCAL,
109eb8dc403SDave Cobbley            sourcedir=BuildTest.BUILDDIR,
110eb8dc403SDave Cobbley            builddir=BuildTest.BUILDDIR
111eb8dc403SDave Cobbley        )
112eb8dc403SDave Cobbley
113eb8dc403SDave Cobbley        release = Release.objects.get(name='local')
114eb8dc403SDave Cobbley
115eb8dc403SDave Cobbley        # Create a project for this build to run in
116eb8dc403SDave Cobbley        project = Project.objects.create_project(name=BuildTest.PROJECT_NAME,
117eb8dc403SDave Cobbley                                                 release=release)
118eb8dc403SDave Cobbley
119*169d7bccSPatrick Williams        passthrough_variable_names = ["SSTATE_DIR", "DL_DIR", "SSTATE_MIRRORS", "BB_HASHSERVE", "BB_HASHSERVE_UPSTREAM"]
120ac13d5f3SPatrick Williams        for variable_name in passthrough_variable_names:
121ac13d5f3SPatrick Williams            current_variable = os.environ.get(variable_name)
122ac13d5f3SPatrick Williams            if current_variable:
123ac13d5f3SPatrick Williams                ProjectVariable.objects.get_or_create(
124ac13d5f3SPatrick Williams                    name=variable_name,
125ac13d5f3SPatrick Williams                    value=current_variable,
126ac13d5f3SPatrick Williams                    project=project)
127ac13d5f3SPatrick Williams
128eb8dc403SDave Cobbley        if os.environ.get("TOASTER_TEST_USE_SSTATE_MIRROR"):
129eb8dc403SDave Cobbley            ProjectVariable.objects.get_or_create(
130eb8dc403SDave Cobbley                name="SSTATE_MIRRORS",
131*169d7bccSPatrick Williams                value="file://.* http://cdn.jsdelivr.net/yocto/sstate/all/PATH;downloadfilename=PATH",
132eb8dc403SDave Cobbley                project=project)
133eb8dc403SDave Cobbley
134eb8dc403SDave Cobbley        ProjectTarget.objects.create(project=project,
135eb8dc403SDave Cobbley                                     target=target,
136eb8dc403SDave Cobbley                                     task="")
137eb8dc403SDave Cobbley        build_request = project.schedule_build()
138eb8dc403SDave Cobbley
139eb8dc403SDave Cobbley        # run runbuilds command to dispatch the build
140eb8dc403SDave Cobbley        # e.g. manage.py runubilds
141eb8dc403SDave Cobbley        RunBuildsCommand().runbuild()
142eb8dc403SDave Cobbley
143eb8dc403SDave Cobbley        build_pk = build_request.build.pk
144eb8dc403SDave Cobbley        while Build.objects.get(pk=build_pk).outcome == Build.IN_PROGRESS:
145eb8dc403SDave Cobbley            sys.stdout.write("\rBuilding %s %d%%" %
146eb8dc403SDave Cobbley                             (target,
147eb8dc403SDave Cobbley                              build_request.build.completeper()))
148eb8dc403SDave Cobbley            sys.stdout.flush()
149eb8dc403SDave Cobbley            time.sleep(1)
150eb8dc403SDave Cobbley
151eb8dc403SDave Cobbley        self.assertEqual(Build.objects.get(pk=build_pk).outcome,
152eb8dc403SDave Cobbley                         Build.SUCCEEDED,
153eb8dc403SDave Cobbley                         "Build did not SUCCEEDED")
154eb8dc403SDave Cobbley
155eb8dc403SDave Cobbley        logger.info("\nBuild finished %s" % build_request.build.outcome)
156eb8dc403SDave Cobbley        return build_request.build
157eb8dc403SDave Cobbley
158eb8dc403SDave Cobbley    def target_already_built(self, target):
159eb8dc403SDave Cobbley        """ If the target is already built no need to build it again"""
160eb8dc403SDave Cobbley        for build in Build.objects.filter(
161eb8dc403SDave Cobbley                project__name=BuildTest.PROJECT_NAME):
162eb8dc403SDave Cobbley            targets = build.target_set.values_list('target', flat=True)
163eb8dc403SDave Cobbley            if target in targets:
164eb8dc403SDave Cobbley                return build
165eb8dc403SDave Cobbley
166eb8dc403SDave Cobbley        return None
167