Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8cd1bf2 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge changes I1d7432b5,I0888a4e0,I24933033,Iff0a4f64 am: fb34ed57 am: 89b965d7 am: f1e50d06

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/1995133

Change-Id: I8532b96c4ef4162911363103975c58f592bbe02a
parents c62ceb2a f1e50d06
Loading
Loading
Loading
Loading

floss/OWNERS

0 → 100644
+1 −0
Original line number Diff line number Diff line
include platform/packages/modules/Bluetooth:/OWNERS_chromeos

floss/build/Dockerfile

0 → 100644
+76 −0
Original line number Diff line number Diff line
# Build environment for Floss
#
# This dockerfile generates the build environment required to build Floss, which
# is the Linux build for the Fluoride Bluetooth stack.

# Inherit from a recent Debian version. The slim version is a smaller variant
# meant for containers.
FROM debian:bookworm-slim

# First install all required apt packages.
RUN apt-get update && \
    apt-get install -y \
    bison \
    build-essential \
    clang \
    cmake \
    curl \
    debmake \
    flatbuffers-compiler \
    flex \
    g++-multilib \
    gcc-multilib \
    generate-ninja \
    gnupg \
    gperf \
    libabsl-dev \
    libc++abi-dev \
    libc++-dev \
    libdbus-1-dev \
    libdouble-conversion-dev \
    libevent-dev \
    libflatbuffers-dev \
    libflatbuffers1 \
    libgl1-mesa-dev \
    libglib2.0-dev \
    libgtest-dev \
    libgmock-dev \
    liblz4-tool \
    libncurses5 \
    libnss3-dev \
    libprotobuf-dev \
    libre2-9 \
    libre2-dev \
    libssl-dev \
    libtinyxml2-dev \
    libx11-dev \
    libxml2-utils \
    ninja-build \
    openssl \
    protobuf-compiler \
    python3 \
    unzip \
    x11proto-core-dev \
    xsltproc \
    zip \
    zlib1g-dev \
    ;

# Next install the Rust toolchain.
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y

# Add .cargo/bin to $PATH
ENV PATH="/root/.cargo/bin:${PATH}"

# Install cargo packages required on build image.
RUN cargo install cxxbridge-cmd --version 1.0.42

# Rename llvm packages. By default, they are named 11vm-ar-13, etc. which won't
# work properly with the build.
ADD llvm-rename.sh /tmp
RUN /tmp/llvm-rename.sh 13

# At this point, the base container is ready. Now we need to build and install
# both libchrome and modp-b64. If you ran this via `docker-build-image.py`, this
# will be done after the image is created and tagged. Otherwise, you need to
# manually mount the source and run the dpkg builders in `system/build/dpkg`.

floss/build/README.md

0 → 100644
+23 −0
Original line number Diff line number Diff line
# Docker build for Floss

This repo contains the Docker image build rule, used to generate the docker
image necessary to build Floss. If building a new docker image, run
`docker-build-image.py` with the tag `floss:latest`.

## Using the docker image to build

Once the Docker image is built (and assuming it's tagged as `floss:latest`), you
should use the `build-in-docker.py` script to build the current repo.

This script will use the local `floss:latest` (or pull it from the registry),
mount (or create) the `floss-out` volume to `/root/.floss` and the current
source to `/root/src` before running these commands in the container:

* `cd /root/src`
* `./build.py --run-bootstrap`
* `./build.py --libdir=/usr/lib/x86-64_linux_gnu/`

If you want to run the build more quickly (or pass other commands), run
`build-in-docker.py --only-start`. This will only start the container for you
(doing the correct mounts) and will print the commands it would have run via
docker exec normally.
+163 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3

import argparse
import os
import subprocess
import sys


class FlossDockerRunner:
    """Runs Floss build inside docker container."""

    # Commands to run for build
    BUILD_COMMANDS = [
        # First run bootstrap to get latest code + create symlinks
        ['/root/src/build.py', '--run-bootstrap'],

        # Clean up any previous artifacts inside the volume
        ['/root/src/build.py', '--target', 'clean'],

        # Run normal code builder
        ['/root/src/build.py', '--target', 'all'],

        # Run tests
        ['/root/src/build.py', '--target', 'test'],
    ]

    def __init__(self, workdir, rootdir, image_tag, volume_tag):
        """ Constructor.

        Args:
            workdir: Current working directory (should be the script path).
            rootdir: Root directory for Bluetooth.
            build_tag: Tag for docker image used for building.
        """
        self.workdir = workdir
        self.rootdir = rootdir
        self.image_tag = image_tag
        self.env = os.environ.copy()

        # Name of running container
        self.container_name = 'floss-docker-runner'

        # Name of volume where we'll send build output
        self.volume_name = volume_tag

    def run_command(self, target, args, cwd=None, env=None, ignore_rc=False):
        """ Run command and stream the output.
        """
        # Set some defaults
        if not cwd:
            cwd = self.workdir
        if not env:
            env = self.env

        rc = 0
        process = subprocess.Popen(args, cwd=cwd, env=env, stdout=subprocess.PIPE)
        while True:
            line = process.stdout.readline()
            print(line.decode('utf-8'), end="")
            if not line:
                rc = process.poll()
                if rc is not None:
                    break

                time.sleep(0.1)

        if rc != 0 and not ignore_rc:
            raise Exception("{} failed. Return code is {}".format(target, rc))

    def _create_volume_if_needed(self):
        # Check if the volume exists. Otherwise create it.
        try:
            subprocess.check_output(['docker', 'volume', 'inspect', self.volume_name])
        finally:
            self.run_command('docker volume create', ['docker', 'volume', 'create', self.volume_name])

    def start_container(self):
        """Starts the docker container with correct mounts."""
        # Stop any previously started container.
        self.stop_container(ignore_error=True)

        # Create volume and create mount string
        self._create_volume_if_needed()
        mount_output_volume = 'type=volume,src={},dst=/root/.floss'.format(self.volume_name)

        # Mount the source directory
        mount_src_dir = 'type=bind,src={},dst=/root/src'.format(self.rootdir)

        # Run the docker image. It will run `tail` indefinitely so the container
        # doesn't close and we can run `docker exec` on it.
        self.run_command('docker run', [
            'docker', 'run', '--name', self.container_name, '--mount', mount_output_volume, '--mount', mount_src_dir,
            '-d', self.image_tag, 'tail', '-f', '/dev/null'
        ])

    def stop_container(self, ignore_error=False):
        """Stops the docker container for build."""
        self.run_command('docker stop', ['docker', 'stop', '-t', '1', self.container_name], ignore_rc=ignore_error)
        self.run_command('docker rm', ['docker', 'rm', self.container_name], ignore_rc=ignore_error)

    def do_build(self):
        """Runs the basic build commands."""
        # Start container before building
        self.start_container()

        # Run all commands
        for i, cmd in enumerate(self.BUILD_COMMANDS):
            self.run_command('docker exec #{}'.format(i), ['docker', 'exec', '-it', self.container_name] + cmd)

        # Stop container before exiting
        self.stop_container()

    def print_do_build(self):
        """Prints the commands for building."""
        docker_exec = ['docker', 'exec', '-it', self.container_name]
        print('Normally, build would run the following commands: \n')
        for cmd in self.BUILD_COMMANDS:
            print(' '.join(docker_exec + cmd))

    def check_docker_runnable(self):
        try:
            subprocess.check_output(['docker', 'ps'], stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as err:
            if 'denied' in err.output.decode('utf-8'):
                print('Run script as sudo')
            else:
                print('Unexpected error: {}'.format(err.output.decode('utf-8')))

            return False

        # No exception means docker is ok
        return True


if __name__ == "__main__":
    parser = argparse.ArgumentParser('Builder Floss inside docker image.')
    parser.add_argument(
        '--only-start',
        action='store_true',
        default=False,
        help='Only start the container. Prints the commands it would have ran.')
    parser.add_argument('--only-stop', action='store_true', default=False, help='Only stop the container and exit.')
    parser.add_argument('--image-tag', default='floss:latest', help='Docker image to use to build.')
    parser.add_argument('--volume-tag', default='floss-out', help='Name of volume to use.')
    args = parser.parse_args()

    # cwd should be set to same directory as this script (that's where Dockerfile
    # is kept).
    workdir = os.path.dirname(os.path.abspath(sys.argv[0]))
    rootdir = os.path.abspath(os.path.join(workdir, '../..'))

    fdr = FlossDockerRunner(workdir, rootdir, args.image_tag, args.volume_tag)

    # Make sure docker is runnable before continuing
    if fdr.check_docker_runnable():
        # Handle some flags
        if args.only_start:
            fdr.start_container()
            fdr.print_do_build()
        elif args.only_stop:
            fdr.stop_container()
        else:
            fdr.do_build()
+147 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3

import argparse
import os
import sys
import subprocess

SRC_MOUNT = "/root/src"


class DockerImageBuilder:
    """Builds the docker image for Floss build environment."""

    def __init__(self, workdir, rootdir, tag):
        """ Constructor.

        Args:
            workdir: Working directory for this script. Dockerfile should exist here.
            rootdir: Root directory for Bluetooth.
            tag: Label in format |name:version|.
        """
        self.workdir = workdir
        self.rootdir = rootdir
        (self.name, self.version) = tag.split(':')
        self.build_tag = '{}:{}'.format(self.name, 'buildtemp')
        self.container_name = 'floss-buildtemp'
        self.final_tag = tag
        self.env = os.environ.copy()

        # Mark dpkg builders for docker
        self.env['LIBCHROME_DOCKER'] = '1'
        self.env['MODP_DOCKER'] = '1'

    def run_command(self, target, args, cwd=None, env=None, ignore_rc=False):
        """ Run command and stream the output.
        """
        # Set some defaults
        if not cwd:
            cwd = self.workdir
        if not env:
            env = self.env

        rc = 0
        process = subprocess.Popen(args, cwd=cwd, env=env, stdout=subprocess.PIPE)
        while True:
            line = process.stdout.readline()
            print(line.decode('utf-8'), end="")
            if not line:
                rc = process.poll()
                if rc is not None:
                    break

                time.sleep(0.1)

        if rc != 0 and not ignore_rc:
            raise Exception("{} failed. Return code is {}".format(target, rc))

    def _docker_build(self):
        self.run_command('docker build', ['docker', 'build', '-t', self.build_tag, '.'])

    def _build_dpkg_and_commit(self):
        # Try to remove any previous instance of the container that may be
        # running if this script didn't complete cleanly last time.
        self.run_command('docker stop', ['docker', 'stop', '-t', '1', self.container_name], ignore_rc=True)
        self.run_command('docker rm', ['docker', 'rm', self.container_name], ignore_rc=True)

        # Runs never terminating application on the newly built image in detached mode
        mount_str = 'type=bind,src={},dst={},readonly'.format(self.rootdir, SRC_MOUNT)
        self.run_command('docker run', [
            'docker', 'run', '--name', self.container_name, '--mount', mount_str, '-d', self.build_tag, 'tail', '-f',
            '/dev/null'
        ])

        commands = [
            # Create the output directories
            ['mkdir', '-p', '/tmp/libchrome', '/tmp/modpb64'],

            # Run the dpkg builder for modp_b64
            ['/root/src/system/build/dpkg/modp_b64/gen-src-pkg.sh', '/tmp/modpb64'],

            # Install modp_b64 since libchrome depends on it
            ['find', '/tmp/modpb64', '-name', 'modp*.deb', '-exec', 'dpkg', '-i', '{}', '+'],

            # Run the dpkg builder for libchrome
            ['/root/src/system/build/dpkg/libchrome/gen-src-pkg.sh', '/tmp/libchrome'],

            # Install libchrome.
            ['find', '/tmp/libchrome', '-name', 'libchrome_*.deb', '-exec', 'dpkg', '-i', '{}', '+'],

            # Delete intermediate files
            ['rm', '-rf', '/tmp/libchrome', '/tmp/modpb64'],
        ]

        # Run commands in container first to install everything.
        for i, cmd in enumerate(commands):
            self.run_command('docker exec #{}'.format(i), ['docker', 'exec', '-it', self.container_name] + cmd)

        # Commit changes into the final tag name
        self.run_command('docker commit', ['docker', 'commit', self.container_name, self.final_tag])

        # Stop running the container and remove it
        self.run_command('docker stop', ['docker', 'stop', '-t', '1', self.container_name])
        self.run_command('docker rm', ['docker', 'rm', self.container_name])

    def _check_docker_runnable(self):
        try:
            subprocess.check_output(['docker', 'ps'], stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as err:
            if 'denied' in err.output.decode('utf-8'):
                print('Run script as sudo')
            else:
                print('Unexpected error: {}'.format(err.output.decode('utf-8')))

            return False

        # No exception means docker is ok
        return True

    def build(self):
        if not self._check_docker_runnable():
            return

        # First build the docker image
        self._docker_build()

        # Then build libchrome and modp-b64 inside the docker image and install
        # them. Commit those changes to the final label.
        self._build_dpkg_and_commit()


def main():
    parser = argparse.ArgumentParser(description='Build docker image for Floss build environment.')
    parser.add_argument('--tag', required=True, help='Tag for docker image. i.e. floss:latest')
    args = parser.parse_args()

    # cwd should be set to same directory as this script (that's where Dockerfile
    # is kept).
    workdir = os.path.dirname(os.path.abspath(sys.argv[0]))
    rootdir = os.path.abspath(os.path.join(workdir, '../..'))

    # Build the docker image
    dib = DockerImageBuilder(workdir, rootdir, args.tag)
    dib.build()


if __name__ == '__main__':
    main()
Loading