1#!/usr/bin/env python3 2# 3# Build the required docker image to run package unit tests 4# 5# Script Variables: 6# DOCKER_IMG_NAME: <optional, the name of the docker image to generate> 7# default is openbmc/ubuntu-unit-test 8# DISTRO: <optional, the distro to build a docker image against> 9# default is ubuntu:eoan 10# BRANCH: <optional, branch to build from each of the openbmc/ 11# repositories> 12# default is master, which will be used if input branch not 13# provided or not found 14# UBUNTU_MIRROR: <optional, the URL of a mirror of Ubuntu to override the 15# default ones in /etc/apt/sources.list> 16# default is empty, and no mirror is used. 17# http_proxy The HTTP address of the proxy server to connect to. 18# Default: "", proxy is not setup if this is not set 19 20import os 21import sys 22from sh import docker, git, nproc, uname 23 24# Read a bunch of environment variables. 25docker_image_name = os.environ.get("DOCKER_IMAGE_NAME", "openbmc/ubuntu-unit-test") 26distro = os.environ.get("DISTRO", "ubuntu:focal") 27branch = os.environ.get("BRANCH", "master") 28ubuntu_mirror = os.environ.get("UBUNTU_MIRROR") 29http_proxy = os.environ.get("http_proxy") 30 31# Set up some common variables. 32proc_count = nproc().strip() 33username = os.environ.get("USER") 34homedir = os.environ.get("HOME") 35gid = os.getgid() 36uid = os.getuid() 37 38# Determine the architecture for Docker. 39arch = uname("-m").strip() 40if arch == "ppc64le": 41 docker_base = "ppc64le/" 42elif arch == "x86_64": 43 docker_base = "" 44else: 45 print(f"Unsupported system architecture({arch}) found for docker image") 46 sys.exit(1) 47 48# These packages we use 'HEAD' for. 49head_pkgs = [ 50 "openbmc/phosphor-objmgr", 51 "openbmc/sdbusplus", 52 "openbmc/sdeventplus", 53 "openbmc/stdplus", 54 "openbmc/gpioplus", 55 "openbmc/phosphor-logging", 56 "openbmc/phosphor-dbus-interfaces", 57 "open-power/pdbg", 58 "openbmc/pldm", 59] 60 61# Packages with fixed revisions. 62pkg_rev = { 63 "boost": "1.74.0", 64 "cereal": "v1.3.0", 65 "catch2": "v2.12.2", 66 "CLI11": "v1.9.0", 67 "fmt": "6.2.1", 68 # Snapshot from 2020-01-03 69 "function2": "3a0746bf5f601dfed05330aefcb6854354fce07d", 70 # Snapshot from 2020-02-13 71 "googletest": "23b2a3b1cf803999fb38175f6e9e038a4495c8a5", 72 # Release 2020-08-06 73 "json": "v3.9.1", 74 # Snapshot from 2019-05-24 75 "lcov": "75fbae1cfc5027f818a0bb865bf6f96fab3202da", 76 # dev-5.0 2019-05-03 77 "linux-headers": "8bf6567e77f7aa68975b7c9c6d044bba690bf327", 78 # Snapshot from 2019-09-03 79 "libvncserver": "1354f7f1bb6962dab209eddb9d6aac1f03408110", 80 "span-lite": "v0.7.0", 81 # version from meta-openembedded/meta-oe/recipes-support/libtinyxml2/libtinyxml2_5.0.1.bb 82 "tinyxml2": "37bc3aca429f0164adf68c23444540b4a24b5778", 83 # version from /meta-openembedded/meta-oe/recipes-devtools/boost-url/boost-url_git.bb 84 "boost-url": "a56ae0df6d3078319755fbaa67822b4fa7fd352b", 85 # version from meta-openembedded/meta-oe/recipes-devtools/valijson/valijson_git.bb 86 "valijson": "c2f22fddf599d04dc33fcd7ed257c698a05345d9", 87 # version from meta-openembedded/meta-oe/recipes-devtools/nlohmann-fifo/nlohmann-fifo_git.bb 88 "fifo_map": "0dfbf5dacbb15a32c43f912a7e66a54aae39d0f9", 89} 90 91# Look up the HEAD for 'head_pkgs' and insert them into 'pkg_rev'. 92pkg_lookups = {} 93for pkg in head_pkgs: 94 pkg_lookups[pkg] = git( 95 "ls-remote", "--heads", f"https://github.com/{pkg}", _bg=True 96 ) 97for pkg, result in pkg_lookups.items(): 98 for line in result.stdout.decode().split("\n"): 99 if f"refs/heads/{branch}" in line: 100 pkg_rev[pkg] = line.strip().split()[0] 101 elif "refs/heads/master" in line and p not in pkg_rev: 102 pkg_rev[pkg] = line.strip().split()[0] 103 104# Create the contents of the '/tmp/depcache'. 105# This needs to be sorted for consistency. 106depcache = "" 107for pkg in sorted(head_pkgs): 108 if pkg in pkg_rev: 109 depcache += "%s:%s," % (pkg, pkg_rev[pkg]) 110 111# Define common flags used for builds 112prefix = "/usr/local" 113configure_flags = " ".join( 114 [ 115 f"--prefix={prefix}", 116 ] 117) 118cmake_flags = " ".join( 119 [ 120 "-DCMAKE_BUILD_TYPE=RelWithDebInfo", 121 "-DBUILD_SHARED_LIBS=ON", 122 f"-DCMAKE_INSTALL_PREFIX:PATH={prefix}", 123 ] 124) 125meson_flags = " ".join( 126 [ 127 "--wrap-mode=nodownload", 128 f"-Dprefix={prefix}", 129 ] 130) 131 132 133def stagename(name): 134 if not name.startswith("openbmc/"): 135 name = "openbmc/" + name 136 return name.replace("/", "-") 137 138 139# Build the commands needed to compose our final image 140# We must sort the packages, otherwise we might produce an unstable 141# docker file and rebuild the image unnecessarily 142copy_cmds = "" 143for pkg in sorted(pkg_rev.keys()): 144 copy_cmds += f"COPY --from={stagename(pkg)} {prefix} {prefix}\n" 145 # Workaround for upstream docker bug and multiple COPY cmds 146 # https://github.com/moby/moby/issues/37965 147 copy_cmds += "RUN true\n" 148 149# Special flags if setting up a deb mirror. 150mirror = "" 151if "ubuntu" in distro and ubuntu_mirror: 152 mirror = f""" 153RUN echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME) main restricted universe multiverse" > /etc/apt/sources.list && \\ 154 echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME)-updates main restricted universe multiverse" >> /etc/apt/sources.list && \\ 155 echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME)-security main restricted universe multiverse" >> /etc/apt/sources.list && \\ 156 echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME)-proposed main restricted universe multiverse" >> /etc/apt/sources.list && \\ 157 echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME)-backports main restricted universe multiverse" >> /etc/apt/sources.list 158""" 159 160# Special flags for proxying. 161proxy_cmd = "" 162proxy_args = [] 163if http_proxy: 164 proxy_cmd = f""" 165RUN echo "[http]" >> {homedir}/.gitconfig && \ 166 echo "proxy = {http_proxy}" >> {homedir}/.gitconfig 167""" 168 proxy_args.extend( 169 [ 170 "--build-arg", 171 f"http_proxy={http_proxy}", 172 "--build-arg", 173 "https_proxy={https_proxy}", 174 ] 175 ) 176 177# Create docker image that can run package unit tests 178dockerfile = f""" 179FROM {docker_base}{distro} as openbmc-base 180 181{mirror} 182 183ENV DEBIAN_FRONTEND noninteractive 184 185ENV PYTHONPATH "/usr/local/lib/python3.8/site-packages/" 186 187# We need the keys to be imported for dbgsym repos 188# New releases have a package, older ones fall back to manual fetching 189# https://wiki.ubuntu.com/Debug%20Symbol%20Packages 190RUN apt-get update && ( apt-get install ubuntu-dbgsym-keyring || ( apt-get install -yy dirmngr && \ 191 apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F2EDC64DC5AEE1F6B9C621F0C8CAB6595FDFF622 ) ) 192 193# Parse the current repo list into a debug repo list 194RUN sed -n '/^deb /s,^deb [^ ]* ,deb http://ddebs.ubuntu.com ,p' /etc/apt/sources.list >/etc/apt/sources.list.d/debug.list 195 196# Remove non-existent debug repos 197RUN sed -i '/-\(backports\|security\) /d' /etc/apt/sources.list.d/debug.list 198 199RUN cat /etc/apt/sources.list.d/debug.list 200 201RUN apt-get update && apt-get dist-upgrade -yy && apt-get install -yy \ 202 gcc-10 \ 203 g++-10 \ 204 libc6-dbg \ 205 libc6-dev \ 206 libtool \ 207 bison \ 208 libdbus-1-dev \ 209 flex \ 210 cmake \ 211 python3 \ 212 python3-dev\ 213 python3-yaml \ 214 python3-mako \ 215 python3-pip \ 216 python3-setuptools \ 217 python3-git \ 218 python3-socks \ 219 pkg-config \ 220 autoconf \ 221 autoconf-archive \ 222 libsystemd-dev \ 223 systemd \ 224 libssl-dev \ 225 libevdev-dev \ 226 libevdev2-dbgsym \ 227 libjpeg-dev \ 228 libpng-dev \ 229 ninja-build \ 230 sudo \ 231 curl \ 232 git \ 233 dbus \ 234 iputils-ping \ 235 clang-10 \ 236 clang-format-10 \ 237 clang-tidy-10 \ 238 clang-tools-10 \ 239 shellcheck \ 240 npm \ 241 iproute2 \ 242 libnl-3-dev \ 243 libnl-genl-3-dev \ 244 libconfig++-dev \ 245 libsnmp-dev \ 246 valgrind \ 247 valgrind-dbg \ 248 libpam0g-dev \ 249 xxd \ 250 libi2c-dev \ 251 wget \ 252 libldap2-dev \ 253 libprotobuf-dev \ 254 libperlio-gzip-perl \ 255 libjson-perl \ 256 protobuf-compiler \ 257 libgpiod-dev \ 258 device-tree-compiler \ 259 cppcheck \ 260 libpciaccess-dev \ 261 libmimetic-dev \ 262 libxml2-utils \ 263 libxml-simple-perl 264 265RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 1000 \ 266 --slave /usr/bin/g++ g++ /usr/bin/g++-10 \ 267 --slave /usr/bin/gcov gcov /usr/bin/gcov-10 \ 268 --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-10 \ 269 --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-10 270 271 272RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-10 1000 \ 273 --slave /usr/bin/clang++ clang++ /usr/bin/clang++-10 \ 274 --slave /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-10 \ 275 --slave /usr/bin/clang-format clang-format /usr/bin/clang-format-10 \ 276 --slave /usr/bin/run-clang-tidy.py run-clang-tidy.py /usr/bin/run-clang-tidy-10.py 277 278RUN pip3 install inflection 279RUN pip3 install pycodestyle 280RUN pip3 install jsonschema 281RUN pip3 install meson==0.54.3 282RUN pip3 install protobuf 283 284FROM openbmc-base as openbmc-lcov 285RUN curl -L https://github.com/linux-test-project/lcov/archive/{pkg_rev['lcov']}.tar.gz | tar -xz && \ 286cd lcov-* && \ 287make -j{proc_count} && \ 288make install 289 290FROM openbmc-base as openbmc-function2 291RUN mkdir {prefix}/include/function2 && \ 292curl -L -o {prefix}/include/function2/function2.hpp https://raw.githubusercontent.com/Naios/function2/{pkg_rev['function2']}/include/function2/function2.hpp 293 294FROM openbmc-base as openbmc-googletest 295RUN curl -L https://github.com/google/googletest/archive/{pkg_rev['googletest']}.tar.gz | tar -xz && \ 296cd googletest-* && \ 297mkdir build && \ 298cd build && \ 299CXXFLAGS=-std=c++17 cmake {cmake_flags} -DTHREADS_PREFER_PTHREAD_FLAG=ON .. && \ 300make -j{proc_count} && \ 301make install 302 303FROM openbmc-base as openbmc-catch2 304RUN curl -L https://github.com/catchorg/Catch2/archive/{pkg_rev['catch2']}.tar.gz | tar -xz && \ 305cd Catch2-* && \ 306mkdir build && \ 307cd build && \ 308cmake {cmake_flags} -DBUILD_TESTING=OFF -DCATCH_INSTALL_DOCS=OFF .. && \ 309make -j{proc_count} && \ 310make install 311 312FROM openbmc-base as openbmc-cereal 313RUN curl -L https://github.com/USCiLab/cereal/archive/{pkg_rev['cereal']}.tar.gz | tar -xz && \ 314cp -a cereal-*/include/cereal/ {prefix}/include/ 315 316FROM openbmc-base as openbmc-CLI11 317RUN curl -L https://github.com/CLIUtils/CLI11/archive/{pkg_rev['CLI11']}.tar.gz | tar -xz && \ 318cd CLI11-* && \ 319mkdir build && \ 320cd build && \ 321cmake {cmake_flags} -DCLI11_BUILD_DOCS=OFF -DBUILD_TESTING=OFF -DCLI11_BUILD_EXAMPLES=OFF .. && \ 322make -j{proc_count} && \ 323make install 324 325FROM openbmc-base as openbmc-fmt 326RUN curl -L https://github.com/fmtlib/fmt/archive/{pkg_rev['fmt']}.tar.gz | tar -xz && \ 327cd fmt-* && \ 328mkdir build && \ 329cd build && \ 330cmake {cmake_flags} -DFMT_DOC=OFF -DFMT_TEST=OFF .. && \ 331make -j{proc_count} && \ 332make install 333 334FROM openbmc-base as openbmc-json 335RUN mkdir {prefix}/include/nlohmann/ && \ 336curl -L -o {prefix}/include/nlohmann/json.hpp https://github.com/nlohmann/json/releases/download/{pkg_rev['json']}/json.hpp && \ 337ln -s nlohmann/json.hpp {prefix}/include/json.hpp 338 339FROM openbmc-base as openbmc-fifo_map 340RUN curl -L https://github.com/nlohmann/fifo_map/archive/{pkg_rev['fifo_map']}.tar.gz | tar -xz && \ 341cd fifo_map-*/src && cp fifo_map.hpp {prefix}/include/ 342 343FROM openbmc-base as openbmc-span-lite 344RUN curl -L https://github.com/martinmoene/span-lite/archive/{pkg_rev['span-lite']}.tar.gz | tar -xz && \ 345cd span-lite-* && \ 346mkdir build && \ 347cd build && \ 348cmake {cmake_flags} -DSPAN_LITE_OPT_BUILD_TESTS=OFF .. && \ 349make -j{proc_count} && \ 350make install 351 352FROM openbmc-base as openbmc-linux-headers 353RUN curl -L https://github.com/openbmc/linux/archive/{pkg_rev['linux-headers']}.tar.gz | tar -xz && \ 354cd linux-* && \ 355make -j{proc_count} defconfig && \ 356make INSTALL_HDR_PATH=/usr/local headers_install 357 358FROM openbmc-base as openbmc-boost 359RUN curl -L https://dl.bintray.com/boostorg/release/{pkg_rev['boost']}/source/boost_$(echo "{pkg_rev['boost']}" | tr '.' '_').tar.bz2 | tar -xj && \ 360cd boost_*/ && \ 361./bootstrap.sh --prefix={prefix} --with-libraries=context,coroutine && \ 362./b2 && ./b2 install --prefix={prefix} 363 364FROM openbmc-base as openbmc-tinyxml2 365RUN curl -L https://github.com/leethomason/tinyxml2/archive/{pkg_rev['tinyxml2']}.tar.gz | tar -xz && \ 366cd tinyxml2-* && \ 367mkdir build && \ 368cd build && \ 369cmake {cmake_flags} .. && \ 370make -j{proc_count} && \ 371make install 372 373FROM openbmc-base as openbmc-boost-url 374RUN curl -L https://github.com/CPPAlliance/url/archive/{pkg_rev['boost-url']}.tar.gz | tar -xz && \ 375cd url-* && \ 376mkdir buildir && \ 377cd buildir && \ 378cmake {cmake_flags} -DBOOST_URL_STANDALONE=ON -DBOOST_URL_BUILD_TESTS=OFF -DBOOST_URL_BUILD_EXAMPLES=OFF .. && \ 379make -j{proc_count} && \ 380make install 381 382FROM openbmc-base as openbmc-valijson 383RUN curl -L https://github.com/tristanpenman/valijson/archive/{pkg_rev['valijson']}.tar.gz | tar -xz && \ 384cd valijson-* && \ 385mkdir build && \ 386cd build && \ 387cmake {cmake_flags} -DINSTALL_HEADERS=1 -DBUILD_TESTS=0 .. && \ 388make -j{proc_count} && \ 389make install 390 391FROM openbmc-base as openbmc-libvncserver 392RUN curl -L https://github.com/LibVNC/libvncserver/archive/{pkg_rev['libvncserver']}.tar.gz | tar -xz && \ 393cd libvncserver-* && \ 394mkdir build && \ 395cd build && \ 396cmake {cmake_flags} .. && \ 397make -j{proc_count} && \ 398make install 399 400FROM openbmc-base as openbmc-stdplus 401COPY --from=openbmc-fmt {prefix} {prefix} 402COPY --from=openbmc-span-lite {prefix} {prefix} 403RUN curl -L https://github.com/openbmc/stdplus/archive/{pkg_rev['openbmc/stdplus']}.tar.gz | tar -xz && \ 404cd stdplus-* && \ 405meson build {meson_flags} -Dtests=disabled -Dexamples=false && \ 406ninja -C build && \ 407ninja -C build install 408 409FROM openbmc-base as openbmc-sdbusplus 410RUN curl -L https://github.com/openbmc/sdbusplus/archive/{pkg_rev['openbmc/sdbusplus']}.tar.gz | tar -xz && \ 411cd sdbusplus-* && \ 412cd tools && ./setup.py install --root=/ --prefix={prefix} && \ 413cd .. && meson build {meson_flags} -Dtests=disabled -Dexamples=disabled && \ 414ninja -C build && \ 415ninja -C build install 416 417FROM openbmc-base as openbmc-sdeventplus 418COPY --from=openbmc-function2 {prefix} {prefix} 419COPY --from=openbmc-stdplus {prefix} {prefix} 420RUN curl -L https://github.com/openbmc/sdeventplus/archive/{pkg_rev['openbmc/sdeventplus']}.tar.gz | tar -xz && \ 421cd sdeventplus-* && \ 422meson build {meson_flags} -Dtests=disabled -Dexamples=false && \ 423ninja -C build && \ 424ninja -C build install 425 426FROM openbmc-base as openbmc-gpioplus 427COPY --from=openbmc-stdplus {prefix} {prefix} 428RUN curl -L https://github.com/openbmc/gpioplus/archive/{pkg_rev['openbmc/gpioplus']}.tar.gz | tar -xz && \ 429cd gpioplus-* && \ 430meson build {meson_flags} -Dtests=disabled -Dexamples=false && \ 431ninja -C build && \ 432ninja -C build install 433 434FROM openbmc-base as openbmc-phosphor-dbus-interfaces 435COPY --from=openbmc-sdbusplus {prefix} {prefix} 436RUN curl -L https://github.com/openbmc/phosphor-dbus-interfaces/archive/{pkg_rev['openbmc/phosphor-dbus-interfaces']}.tar.gz | tar -xz && \ 437cd phosphor-dbus-interfaces-* && \ 438meson build {meson_flags} -Ddata_org_open_power=true -Ddata_com_ibm=true && \ 439ninja -C build && \ 440ninja -C build install 441 442FROM openbmc-base as openbmc-phosphor-logging 443COPY --from=openbmc-cereal {prefix} {prefix} 444COPY --from=openbmc-sdbusplus {prefix} {prefix} 445COPY --from=openbmc-sdeventplus {prefix} {prefix} 446COPY --from=openbmc-phosphor-dbus-interfaces {prefix} {prefix} 447COPY --from=openbmc-fifo_map {prefix} {prefix} 448RUN curl -L https://github.com/openbmc/phosphor-logging/archive/{pkg_rev['openbmc/phosphor-logging']}.tar.gz | tar -xz && \ 449cd phosphor-logging-* && \ 450./bootstrap.sh && \ 451./configure {configure_flags} --enable-metadata-processing YAML_DIR={prefix}/share/phosphor-dbus-yaml/yaml && \ 452make -j{proc_count} && \ 453make install 454 455FROM openbmc-base as openbmc-phosphor-objmgr 456COPY --from=openbmc-boost {prefix} {prefix} 457COPY --from=openbmc-sdbusplus {prefix} {prefix} 458COPY --from=openbmc-tinyxml2 {prefix} {prefix} 459COPY --from=openbmc-phosphor-logging {prefix} {prefix} 460RUN curl -L https://github.com/openbmc/phosphor-objmgr/archive/{pkg_rev['openbmc/phosphor-objmgr']}.tar.gz | tar -xz && \ 461cd phosphor-objmgr-* && \ 462./bootstrap.sh && \ 463./configure {configure_flags} && \ 464make -j{proc_count} && \ 465make install 466 467FROM openbmc-base as openbmc-open-power-pdbg 468RUN curl -L https://github.com/open-power/pdbg/archive/{pkg_rev['open-power/pdbg']}.tar.gz | tar -xz && \ 469cd pdbg-* && \ 470./bootstrap.sh && \ 471./configure {configure_flags} && \ 472make -j{proc_count} && \ 473make install 474 475FROM openbmc-base as openbmc-pldm 476COPY --from=openbmc-sdbusplus {prefix} {prefix} 477COPY --from=openbmc-sdeventplus {prefix} {prefix} 478COPY --from=openbmc-boost {prefix} {prefix} 479COPY --from=openbmc-phosphor-dbus-interfaces {prefix} {prefix} 480COPY --from=openbmc-phosphor-logging {prefix} {prefix} 481COPY --from=openbmc-json {prefix} {prefix} 482COPY --from=openbmc-CLI11 {prefix} {prefix} 483RUN curl -L https://github.com/openbmc/pldm/archive/{pkg_rev['openbmc/pldm']}.tar.gz | tar -xz && \ 484cd pldm-* && \ 485meson build {meson_flags} -Dlibpldm-only=enabled -Doem-ibm=enabled -Dtests=disabled && \ 486ninja -C build && \ 487ninja -C build install 488 489# Build the final output image 490FROM openbmc-base 491{copy_cmds} 492 493# Some of our infrastructure still relies on the presence of this file 494# even though it is no longer needed to rebuild the docker environment 495# NOTE: The file is sorted to ensure the ordering is stable. 496RUN echo '{depcache}' > /tmp/depcache 497 498# Final configuration for the workspace 499RUN grep -q {gid} /etc/group || groupadd -g {gid} {username} 500RUN mkdir -p "{os.path.dirname(homedir)}" 501RUN grep -q {uid} /etc/passwd || useradd -d {homedir} -m -u {uid} -g {gid} {username} 502RUN sed -i '1iDefaults umask=000' /etc/sudoers 503RUN echo "{username} ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers 504 505{proxy_cmd} 506 507RUN /bin/bash 508""" 509 510# Do the docker build. 511for line in docker.build( 512 proxy_args, 513 "--network=host", 514 "-t", 515 docker_image_name, 516 "-", 517 _in=dockerfile, 518 _iter=True, 519): 520 print(line, end="", flush=True) 521