build-unit-test-docker: convert to Python

Do a near identical conversion from bash to Python for the script which
builds the unit-test docker containers.  I plan to do additional
enhancements to build the sub-containers in parallel, so I needed to
first do this conversion to Python in order to make that implementation
sane.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I6df6f294b41f3a01f95fbe23a6dcd1ea21a315a9
diff --git a/build-unit-test-docker.sh b/build-unit-test-docker.sh
deleted file mode 100755
index 20ad2da..0000000
--- a/build-unit-test-docker.sh
+++ /dev/null
@@ -1,534 +0,0 @@
-#!/usr/bin/env bash
-#
-# Build the required docker image to run package unit tests
-#
-# Script Variables:
-#   DOCKER_IMG_NAME:  <optional, the name of the docker image to generate>
-#                     default is openbmc/ubuntu-unit-test
-#   DISTRO:           <optional, the distro to build a docker image against>
-#                     default is ubuntu:eoan
-#   BRANCH:           <optional, branch to build from each of the openbmc/
-#                     repositories>
-#                     default is master, which will be used if input branch not
-#                     provided or not found
-#   UBUNTU_MIRROR:    <optional, the URL of a mirror of Ubuntu to override the
-#                     default ones in /etc/apt/sources.list>
-#                     default is empty, and no mirror is used.
-#   http_proxy        The HTTP address of the proxy server to connect to.
-#                     Default: "", proxy is not setup if this is not set
-
-set -xeuo pipefail
-
-DOCKER_IMG_NAME=${DOCKER_IMG_NAME:-"openbmc/ubuntu-unit-test"}
-DISTRO=${DISTRO:-"ubuntu:focal"}
-BRANCH=${BRANCH:-"master"}
-UBUNTU_MIRROR=${UBUNTU_MIRROR:-""}
-http_proxy=${http_proxy:-}
-
-# Determine the architecture
-ARCH=$(uname -m)
-case ${ARCH} in
-    "ppc64le")
-        DOCKER_BASE="ppc64le/"
-        ;;
-    "x86_64")
-        DOCKER_BASE=""
-        ;;
-    *)
-        echo "Unsupported system architecture(${ARCH}) found for docker image"
-        exit 1
-esac
-
-# Setup temporary files
-DEPCACHE_FILE=""
-cleanup() {
-  local status="$?"
-  if [[ -n "$DEPCACHE_FILE" ]]; then
-    rm -f "$DEPCACHE_FILE"
-  fi
-  trap - EXIT ERR
-  exit "$status"
-}
-trap cleanup EXIT ERR INT TERM QUIT
-DEPCACHE_FILE="$(mktemp)"
-
-HEAD_PKGS=(
-  openbmc/phosphor-objmgr
-  openbmc/sdbusplus
-  openbmc/sdeventplus
-  openbmc/stdplus
-  openbmc/gpioplus
-  openbmc/phosphor-logging
-  openbmc/phosphor-dbus-interfaces
-  open-power/pdbg
-  openbmc/pldm
-)
-
-# Generate a list of depcache entries
-# We want to do this in parallel since the package list is growing
-# and the network lookup is low overhead but decently high latency.
-# This doesn't worry about producing a stable DEPCACHE_FILE, that is
-# done by readers who need a stable ordering.
-generate_depcache_entry() {
-  local package="$1"
-
-  local tip
-  # Need to continue if branch not found, hence || true at end
-  tip=$(git ls-remote --heads "https://github.com/${package}" |
-        grep "refs/heads/$BRANCH" | awk '{ print $1 }' || true)
-
-  # If specific branch is not found then try master
-  if [[ -z "$tip" ]]; then
-    tip=$(git ls-remote --heads "https://github.com/${package}" |
-         grep "refs/heads/master" | awk '{ print $1 }')
-  fi
-
-  # Lock the file to avoid interlaced writes
-  exec 3>> "$DEPCACHE_FILE"
-  flock 3
-  echo "$package:$tip" >&3
-  exec 3>&-
-}
-for package in "${HEAD_PKGS[@]}"; do
-  generate_depcache_entry "$package" &
-done
-wait
-
-# A list of package versions we are building
-# Start off by listing the stating versions of third-party sources
-declare -A PKG_REV=(
-  [boost]=1.75.0
-  [cereal]=v1.3.0
-  [catch2]=v2.12.2
-  [CLI11]=v1.9.0
-  [fmt]=6.2.1
-  # Snapshot from 2020-01-03
-  [function2]=3a0746bf5f601dfed05330aefcb6854354fce07d
-  # Snapshot from 2020-02-13
-  [googletest]=23b2a3b1cf803999fb38175f6e9e038a4495c8a5
-  # Release 2020-08-06
-  [json]=v3.9.1
-  # Snapshot from 2019-05-24
-  [lcov]=75fbae1cfc5027f818a0bb865bf6f96fab3202da
-  # dev-5.0 2019-05-03
-  [linux-headers]=8bf6567e77f7aa68975b7c9c6d044bba690bf327
-  # Snapshot from 2019-09-03
-  [libvncserver]=1354f7f1bb6962dab209eddb9d6aac1f03408110
-  [span-lite]=v0.7.0
-  # version from meta-openembedded/meta-oe/recipes-support/libtinyxml2/libtinyxml2_5.0.1.bb
-  [tinyxml2]=37bc3aca429f0164adf68c23444540b4a24b5778
-  # version from /meta-openembedded/meta-oe/recipes-devtools/boost-url/boost-url_git.bb
-  [boost-url]=a56ae0df6d3078319755fbaa67822b4fa7fd352b
-  # version from meta-openembedded/meta-oe/recipes-devtools/valijson/valijson_git.bb
-  [valijson]=c2f22fddf599d04dc33fcd7ed257c698a05345d9
-  # version from meta-openembedded/meta-oe/recipes-devtools/nlohmann-fifo/nlohmann-fifo_git.bb
-  [fifo_map]=0dfbf5dacbb15a32c43f912a7e66a54aae39d0f9
-)
-
-# Turn the depcache into a dictionary so we can reference the HEAD of each repo
-while IFS= read -r line; do
-  # shellcheck disable=SC2207 # Expecting to word-split tr results.
-  linearr=($(echo "$line" | tr ':' ' '))
-  PKG_REV["${linearr[0]}"]="${linearr[1]}"
-done < "$DEPCACHE_FILE"
-
-# Define common flags used for builds
-PREFIX="/usr/local"
-CONFIGURE_FLAGS=(
-  "--prefix=${PREFIX}"
-)
-CMAKE_FLAGS=(
-  "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
-  "-DBUILD_SHARED_LIBS=ON"
-  "-DCMAKE_INSTALL_PREFIX:PATH=${PREFIX}"
-)
-MESON_FLAGS=(
-  "--wrap-mode=nodownload"
-  "-Dprefix=${PREFIX}"
-)
-
-stagename()
-{
-  local cooked="$1"
-
-  if ! echo "$cooked" | grep -q '/'
-  then
-    cooked=openbmc-"$cooked"
-  fi
-  echo "$cooked" | tr '/' '-'
-}
-
-# Build the commands needed to compose our final image
-COPY_CMDS=""
-# We must sort the packages, otherwise we might produce an unstable
-# docker file and rebuild the image unnecessarily
-for pkg in $(echo "${!PKG_REV[@]}" | tr ' ' '\n' | LC_COLLATE=C sort -s); do
-  COPY_CMDS+="COPY --from=$(stagename "${pkg}") ${PREFIX} ${PREFIX}"$'\n'
-  # Workaround for upstream docker bug and multiple COPY cmds
-  # https://github.com/moby/moby/issues/37965
-  COPY_CMDS+="RUN true"$'\n'
-done
-
-################################# docker img # #################################
-# Create docker image that can run package unit tests
-if [[ "${DISTRO}" == "ubuntu"* ]]; then
-
-MIRROR=""
-if [[ -n "${UBUNTU_MIRROR}" ]]; then
-    MIRROR="RUN echo \"deb ${UBUNTU_MIRROR} \$(. /etc/os-release && echo \$VERSION_CODENAME) main restricted universe multiverse\" > /etc/apt/sources.list && \
-        echo \"deb ${UBUNTU_MIRROR} \$(. /etc/os-release && echo \$VERSION_CODENAME)-updates main restricted universe multiverse\" >> /etc/apt/sources.list && \
-        echo \"deb ${UBUNTU_MIRROR} \$(. /etc/os-release && echo \$VERSION_CODENAME)-security main restricted universe multiverse\" >> /etc/apt/sources.list && \
-        echo \"deb ${UBUNTU_MIRROR} \$(. /etc/os-release && echo \$VERSION_CODENAME)-proposed main restricted universe multiverse\" >> /etc/apt/sources.list && \
-        echo \"deb ${UBUNTU_MIRROR} \$(. /etc/os-release && echo \$VERSION_CODENAME)-backports main restricted universe multiverse\" >> /etc/apt/sources.list"
-
-fi
-
-PROXY_CMD=""
-if [[ -n "${http_proxy}" ]]; then
-  PROXY_CMD="RUN echo \"[http]\" > \$HOME/.gitconfig && \
-      echo \"proxy = ${http_proxy}\" >> \$HOME/.gitconfig"
-fi
-
-Dockerfile=$(cat << EOF
-FROM ${DOCKER_BASE}${DISTRO} as openbmc-base
-
-${MIRROR}
-
-ENV DEBIAN_FRONTEND noninteractive
-
-ENV PYTHONPATH "/usr/local/lib/python3.8/site-packages/"
-
-# We need the keys to be imported for dbgsym repos
-# New releases have a package, older ones fall back to manual fetching
-# https://wiki.ubuntu.com/Debug%20Symbol%20Packages
-RUN apt-get update && ( apt-get install ubuntu-dbgsym-keyring || ( apt-get install -yy dirmngr && \
-    apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F2EDC64DC5AEE1F6B9C621F0C8CAB6595FDFF622 ) )
-
-# Parse the current repo list into a debug repo list
-RUN sed -n '/^deb /s,^deb [^ ]* ,deb http://ddebs.ubuntu.com ,p' /etc/apt/sources.list >/etc/apt/sources.list.d/debug.list
-
-# Remove non-existent debug repos
-RUN sed -i '/-\(backports\|security\) /d' /etc/apt/sources.list.d/debug.list
-
-RUN cat /etc/apt/sources.list.d/debug.list
-
-RUN apt-get update && apt-get dist-upgrade -yy && apt-get install -yy \
-    gcc-10 \
-    g++-10 \
-    libc6-dbg \
-    libc6-dev \
-    libtool \
-    bison \
-    libdbus-1-dev \
-    flex \
-    cmake \
-    python3 \
-    python3-dev\
-    python3-yaml \
-    python3-mako \
-    python3-pip \
-    python3-setuptools \
-    python3-git \
-    python3-socks \
-    pkg-config \
-    autoconf \
-    autoconf-archive \
-    libsystemd-dev \
-    systemd \
-    libssl-dev \
-    libevdev-dev \
-    libevdev2-dbgsym \
-    libjpeg-dev \
-    libpng-dev \
-    ninja-build \
-    sudo \
-    curl \
-    git \
-    dbus \
-    iputils-ping \
-    clang-10 \
-    clang-format-10 \
-    clang-tidy-10 \
-    clang-tools-10 \
-    shellcheck \
-    npm \
-    iproute2 \
-    libnl-3-dev \
-    libnl-genl-3-dev \
-    libconfig++-dev \
-    libsnmp-dev \
-    valgrind \
-    valgrind-dbg \
-    libpam0g-dev \
-    xxd \
-    libi2c-dev \
-    wget \
-    libldap2-dev \
-    libprotobuf-dev \
-    libperlio-gzip-perl \
-    libjson-perl \
-    protobuf-compiler \
-    libgpiod-dev \
-    device-tree-compiler \
-    cppcheck \
-    libpciaccess-dev \
-    libmimetic-dev \
-    libxml2-utils \
-    libxml-simple-perl
-
-RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 1000 \
-  --slave /usr/bin/g++ g++ /usr/bin/g++-10 \
-  --slave /usr/bin/gcov gcov /usr/bin/gcov-10 \
-  --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-10 \
-  --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-10
-
-
-RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-10 1000 \
-  --slave /usr/bin/clang++ clang++ /usr/bin/clang++-10 \
-  --slave /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-10 \
-  --slave /usr/bin/clang-format clang-format /usr/bin/clang-format-10 \
-  --slave /usr/bin/run-clang-tidy.py run-clang-tidy.py /usr/bin/run-clang-tidy-10.py
-
-RUN pip3 install inflection
-RUN pip3 install pycodestyle
-RUN pip3 install jsonschema
-RUN pip3 install meson==0.54.3
-RUN pip3 install protobuf
-
-FROM openbmc-base as openbmc-lcov
-RUN curl -L https://github.com/linux-test-project/lcov/archive/${PKG_REV['lcov']}.tar.gz | tar -xz && \
-cd lcov-* && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-function2
-RUN mkdir ${PREFIX}/include/function2 && \
-curl -L -o ${PREFIX}/include/function2/function2.hpp https://raw.githubusercontent.com/Naios/function2/${PKG_REV['function2']}/include/function2/function2.hpp
-
-FROM openbmc-base as openbmc-googletest
-RUN curl -L https://github.com/google/googletest/archive/${PKG_REV['googletest']}.tar.gz | tar -xz && \
-cd googletest-* && \
-mkdir build && \
-cd build && \
-CXXFLAGS=-std=c++17 cmake ${CMAKE_FLAGS[@]} -DTHREADS_PREFER_PTHREAD_FLAG=ON .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-catch2
-RUN curl -L https://github.com/catchorg/Catch2/archive/${PKG_REV['catch2']}.tar.gz | tar -xz && \
-cd Catch2-* && \
-mkdir build && \
-cd build && \
-cmake ${CMAKE_FLAGS[@]} -DBUILD_TESTING=OFF -DCATCH_INSTALL_DOCS=OFF .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-cereal
-RUN curl -L https://github.com/USCiLab/cereal/archive/${PKG_REV['cereal']}.tar.gz | tar -xz && \
-cp -a cereal-*/include/cereal/ ${PREFIX}/include/
-
-FROM openbmc-base as openbmc-CLI11
-RUN curl -L https://github.com/CLIUtils/CLI11/archive/${PKG_REV['CLI11']}.tar.gz | tar -xz && \
-cd CLI11-* && \
-mkdir build && \
-cd build && \
-cmake ${CMAKE_FLAGS[@]} -DCLI11_BUILD_DOCS=OFF -DBUILD_TESTING=OFF -DCLI11_BUILD_EXAMPLES=OFF .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-fmt
-RUN curl -L https://github.com/fmtlib/fmt/archive/${PKG_REV['fmt']}.tar.gz | tar -xz && \
-cd fmt-* && \
-mkdir build && \
-cd build && \
-cmake ${CMAKE_FLAGS[@]} -DFMT_DOC=OFF -DFMT_TEST=OFF .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-json
-RUN mkdir ${PREFIX}/include/nlohmann/ && \
-curl -L -o ${PREFIX}/include/nlohmann/json.hpp https://github.com/nlohmann/json/releases/download/${PKG_REV['json']}/json.hpp && \
-ln -s nlohmann/json.hpp ${PREFIX}/include/json.hpp
-
-FROM openbmc-base as openbmc-fifo_map
-RUN curl -L https://github.com/nlohmann/fifo_map/archive/${PKG_REV['fifo_map']}.tar.gz | tar -xz && \
-cd fifo_map-*/src && cp fifo_map.hpp ${PREFIX}/include/
-
-FROM openbmc-base as openbmc-span-lite
-RUN curl -L https://github.com/martinmoene/span-lite/archive/${PKG_REV['span-lite']}.tar.gz | tar -xz && \
-cd span-lite-* && \
-mkdir build && \
-cd build && \
-cmake ${CMAKE_FLAGS[@]} -DSPAN_LITE_OPT_BUILD_TESTS=OFF .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-linux-headers
-RUN curl -L https://github.com/openbmc/linux/archive/${PKG_REV['linux-headers']}.tar.gz | tar -xz && \
-cd linux-* && \
-make -j$(nproc) defconfig && \
-make INSTALL_HDR_PATH=/usr/local headers_install
-
-FROM openbmc-base as openbmc-boost
-RUN curl -L https://dl.bintray.com/boostorg/release/${PKG_REV['boost']}/source/boost_$(echo "${PKG_REV['boost']}" | tr '.' '_').tar.bz2 | tar -xj && \
-cd boost_*/ && \
-./bootstrap.sh --prefix=${PREFIX} --with-libraries=context,coroutine && \
-./b2 && ./b2 install --prefix=${PREFIX}
-
-FROM openbmc-base as openbmc-tinyxml2
-RUN curl -L https://github.com/leethomason/tinyxml2/archive/${PKG_REV['tinyxml2']}.tar.gz | tar -xz && \
-cd tinyxml2-* && \
-mkdir build && \
-cd build && \
-cmake ${CMAKE_FLAGS[@]} .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-boost-url
-RUN curl -L https://github.com/CPPAlliance/url/archive/${PKG_REV['boost-url']}.tar.gz | tar -xz && \
-cd url-* && \
-mkdir buildir && \
-cd buildir && \
-cmake ${CMAKE_FLAGS[@]} -DBOOST_URL_STANDALONE=ON -DBOOST_URL_BUILD_TESTS=OFF -DBOOST_URL_BUILD_EXAMPLES=OFF .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-valijson
-RUN curl -L https://github.com/tristanpenman/valijson/archive/${PKG_REV['valijson']}.tar.gz | tar -xz && \
-cd valijson-* && \
-mkdir build && \
-cd build && \
-cmake ${CMAKE_FLAGS[@]} -DINSTALL_HEADERS=1 -DBUILD_TESTS=0 .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-libvncserver
-RUN curl -L https://github.com/LibVNC/libvncserver/archive/${PKG_REV['libvncserver']}.tar.gz | tar -xz && \
-cd libvncserver-* && \
-mkdir build && \
-cd build && \
-cmake ${CMAKE_FLAGS[@]} .. && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-stdplus
-COPY --from=openbmc-fmt ${PREFIX} ${PREFIX}
-COPY --from=openbmc-span-lite ${PREFIX} ${PREFIX}
-RUN curl -L https://github.com/openbmc/stdplus/archive/${PKG_REV['openbmc/stdplus']}.tar.gz | tar -xz && \
-cd stdplus-* && \
-meson build ${MESON_FLAGS[@]} -Dtests=disabled -Dexamples=false && \
-ninja -C build && \
-ninja -C build install
-
-FROM openbmc-base as openbmc-sdbusplus
-RUN curl -L https://github.com/openbmc/sdbusplus/archive/${PKG_REV['openbmc/sdbusplus']}.tar.gz | tar -xz && \
-cd sdbusplus-* && \
-cd tools && ./setup.py install --root=/ --prefix=${PREFIX} && \
-cd .. && meson build ${MESON_FLAGS[@]} -Dtests=disabled -Dexamples=disabled && \
-ninja -C build && \
-ninja -C build install
-
-FROM openbmc-base as openbmc-sdeventplus
-COPY --from=openbmc-function2 ${PREFIX} ${PREFIX}
-COPY --from=openbmc-stdplus ${PREFIX} ${PREFIX}
-RUN curl -L https://github.com/openbmc/sdeventplus/archive/${PKG_REV['openbmc/sdeventplus']}.tar.gz | tar -xz && \
-cd sdeventplus-* && \
-meson build ${MESON_FLAGS[@]} -Dtests=disabled -Dexamples=false && \
-ninja -C build && \
-ninja -C build install
-
-FROM openbmc-base as openbmc-gpioplus
-COPY --from=openbmc-stdplus ${PREFIX} ${PREFIX}
-RUN curl -L https://github.com/openbmc/gpioplus/archive/${PKG_REV['openbmc/gpioplus']}.tar.gz | tar -xz && \
-cd gpioplus-* && \
-meson build ${MESON_FLAGS[@]} -Dtests=disabled -Dexamples=false && \
-ninja -C build && \
-ninja -C build install
-
-FROM openbmc-base as openbmc-phosphor-dbus-interfaces
-COPY --from=openbmc-sdbusplus ${PREFIX} ${PREFIX}
-RUN curl -L https://github.com/openbmc/phosphor-dbus-interfaces/archive/${PKG_REV['openbmc/phosphor-dbus-interfaces']}.tar.gz | tar -xz && \
-cd phosphor-dbus-interfaces-* && \
-meson build ${MESON_FLAGS[@]} -Ddata_org_open_power=true -Ddata_com_ibm=true && \
-ninja -C build && \
-ninja -C build install
-
-FROM openbmc-base as openbmc-phosphor-logging
-COPY --from=openbmc-cereal ${PREFIX} ${PREFIX}
-COPY --from=openbmc-sdbusplus ${PREFIX} ${PREFIX}
-COPY --from=openbmc-sdeventplus ${PREFIX} ${PREFIX}
-COPY --from=openbmc-phosphor-dbus-interfaces ${PREFIX} ${PREFIX}
-COPY --from=openbmc-fifo_map ${PREFIX} ${PREFIX}
-RUN curl -L https://github.com/openbmc/phosphor-logging/archive/${PKG_REV['openbmc/phosphor-logging']}.tar.gz | tar -xz && \
-cd phosphor-logging-* && \
-./bootstrap.sh && \
-./configure ${CONFIGURE_FLAGS[@]} --enable-metadata-processing YAML_DIR=${PREFIX}/share/phosphor-dbus-yaml/yaml && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-phosphor-objmgr
-COPY --from=openbmc-boost ${PREFIX} ${PREFIX}
-COPY --from=openbmc-sdbusplus ${PREFIX} ${PREFIX}
-COPY --from=openbmc-tinyxml2 ${PREFIX} ${PREFIX}
-COPY --from=openbmc-phosphor-logging ${PREFIX} ${PREFIX}
-RUN curl -L https://github.com/openbmc/phosphor-objmgr/archive/${PKG_REV['openbmc/phosphor-objmgr']}.tar.gz | tar -xz && \
-cd phosphor-objmgr-* && \
-./bootstrap.sh && \
-./configure ${CONFIGURE_FLAGS[@]} && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as open-power-pdbg
-RUN curl -L https://github.com/open-power/pdbg/archive/${PKG_REV['open-power/pdbg']}.tar.gz | tar -xz && \
-cd pdbg-* && \
-./bootstrap.sh && \
-./configure ${CONFIGURE_FLAGS[@]} && \
-make -j$(nproc) && \
-make install
-
-FROM openbmc-base as openbmc-pldm
-COPY --from=openbmc-sdbusplus ${PREFIX} ${PREFIX}
-COPY --from=openbmc-sdeventplus ${PREFIX} ${PREFIX}
-COPY --from=openbmc-boost ${PREFIX} ${PREFIX}
-COPY --from=openbmc-phosphor-dbus-interfaces ${PREFIX} ${PREFIX}
-COPY --from=openbmc-phosphor-logging ${PREFIX} ${PREFIX}
-COPY --from=openbmc-json ${PREFIX} ${PREFIX}
-COPY --from=openbmc-CLI11 ${PREFIX} ${PREFIX}
-RUN curl -L https://github.com/openbmc/pldm/archive/${PKG_REV['openbmc/pldm']}.tar.gz | tar -xz && \
-cd pldm-* && \
-meson build ${MESON_FLAGS[@]} -Dlibpldm-only=enabled -Doem-ibm=enabled -Dtests=disabled && \
-ninja -C build && \
-ninja -C build install
-
-# Build the final output image
-FROM openbmc-base
-${COPY_CMDS}
-
-# Some of our infrastructure still relies on the presence of this file
-# even though it is no longer needed to rebuild the docker environment
-# NOTE: The file is sorted to ensure the ordering is stable.
-RUN echo '$(LC_COLLATE=C sort -s "$DEPCACHE_FILE" | tr '\n' ',')' > /tmp/depcache
-
-# Final configuration for the workspace
-RUN grep -q ${GROUPS[0]} /etc/group || groupadd -g ${GROUPS[0]} ${USER}
-RUN mkdir -p "$(dirname "${HOME}")"
-RUN grep -q ${UID} /etc/passwd || useradd -d ${HOME} -m -u ${UID} -g ${GROUPS[0]} ${USER}
-RUN sed -i '1iDefaults umask=000' /etc/sudoers
-RUN echo "${USER} ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers
-
-${PROXY_CMD}
-
-RUN /bin/bash
-EOF
-)
-fi
-################################# docker img # #################################
-
-proxy_args=""
-if [[ -n "${http_proxy}" ]]; then
-  proxy_args="--build-arg http_proxy=${http_proxy} --build-arg https_proxy=${http_proxy}"
-fi
-
-# Build above image
-# shellcheck disable=SC2086 # proxy_args requires word splitting.
-docker build ${proxy_args} --network=host -t "${DOCKER_IMG_NAME}" - <<< "${Dockerfile}"
diff --git a/jenkins/run-build-script-ci b/jenkins/run-build-script-ci
index 2c60164..3181b80 100755
--- a/jenkins/run-build-script-ci
+++ b/jenkins/run-build-script-ci
@@ -9,8 +9,8 @@
 
 export LANG=en_US.UTF8
 
-cd ${WORKSPACE}/openbmc-build-scripts
+cd "${WORKSPACE}"/openbmc-build-scripts
 
 # Now run some of our most used CI scripts
-./build-unit-test-docker.sh
+./scripts/build-unit-test-docker
 ./scripts/build-qemu-robot-docker.sh
diff --git a/run-unit-test-docker.sh b/run-unit-test-docker.sh
index 40e1a51..696b6b8 100755
--- a/run-unit-test-docker.sh
+++ b/run-unit-test-docker.sh
@@ -85,12 +85,12 @@
 
 # Configure docker build
 cd "${WORKSPACE}"/${OBMC_BUILD_SCRIPTS}
-echo "Building docker image with build-unit-test-docker.sh"
+echo "Building docker image with build-unit-test-docker"
 # Export input env variables
 export DOCKER_IMG_NAME
 export DISTRO
 export BRANCH
-./build-unit-test-docker.sh
+./scripts/build-unit-test-docker
 
 # Allow the user to pass options through to unit-test.py:
 #   EXTRA_UNIT_TEST_ARGS="-r 100" ...
diff --git a/scripts/build-unit-test-docker b/scripts/build-unit-test-docker
new file mode 100755
index 0000000..94baa67
--- /dev/null
+++ b/scripts/build-unit-test-docker
@@ -0,0 +1,520 @@
+#!/usr/bin/env python3
+#
+# Build the required docker image to run package unit tests
+#
+# Script Variables:
+#   DOCKER_IMG_NAME:  <optional, the name of the docker image to generate>
+#                     default is openbmc/ubuntu-unit-test
+#   DISTRO:           <optional, the distro to build a docker image against>
+#                     default is ubuntu:eoan
+#   BRANCH:           <optional, branch to build from each of the openbmc/
+#                     repositories>
+#                     default is master, which will be used if input branch not
+#                     provided or not found
+#   UBUNTU_MIRROR:    <optional, the URL of a mirror of Ubuntu to override the
+#                     default ones in /etc/apt/sources.list>
+#                     default is empty, and no mirror is used.
+#   http_proxy        The HTTP address of the proxy server to connect to.
+#                     Default: "", proxy is not setup if this is not set
+
+import os
+import sys
+from sh import docker, git, nproc, uname
+
+# Read a bunch of environment variables.
+docker_image_name = os.environ.get("DOCKER_IMAGE_NAME", "openbmc/ubuntu-unit-test")
+distro = os.environ.get("DISTRO", "ubuntu:focal")
+branch = os.environ.get("BRANCH", "master")
+ubuntu_mirror = os.environ.get("UBUNTU_MIRROR")
+http_proxy = os.environ.get("http_proxy")
+
+# Set up some common variables.
+proc_count = nproc().strip()
+username = os.environ.get("USER")
+homedir = os.environ.get("HOME")
+gid = os.getgid()
+uid = os.getuid()
+
+# Determine the architecture for Docker.
+arch = uname("-m").strip()
+if arch == "ppc64le":
+    docker_base = "ppc64le/"
+elif arch == "x86_64":
+    docker_base = ""
+else:
+    print(f"Unsupported system architecture({arch}) found for docker image")
+    sys.exit(1)
+
+# These packages we use 'HEAD' for.
+head_pkgs = [
+    "openbmc/phosphor-objmgr",
+    "openbmc/sdbusplus",
+    "openbmc/sdeventplus",
+    "openbmc/stdplus",
+    "openbmc/gpioplus",
+    "openbmc/phosphor-logging",
+    "openbmc/phosphor-dbus-interfaces",
+    "open-power/pdbg",
+    "openbmc/pldm",
+]
+
+# Packages with fixed revisions.
+pkg_rev = {
+    "boost": "1.74.0",
+    "cereal": "v1.3.0",
+    "catch2": "v2.12.2",
+    "CLI11": "v1.9.0",
+    "fmt": "6.2.1",
+    # Snapshot from 2020-01-03
+    "function2": "3a0746bf5f601dfed05330aefcb6854354fce07d",
+    # Snapshot from 2020-02-13
+    "googletest": "23b2a3b1cf803999fb38175f6e9e038a4495c8a5",
+    # Release 2020-08-06
+    "json": "v3.9.1",
+    # Snapshot from 2019-05-24
+    "lcov": "75fbae1cfc5027f818a0bb865bf6f96fab3202da",
+    # dev-5.0 2019-05-03
+    "linux-headers": "8bf6567e77f7aa68975b7c9c6d044bba690bf327",
+    # Snapshot from 2019-09-03
+    "libvncserver": "1354f7f1bb6962dab209eddb9d6aac1f03408110",
+    "span-lite": "v0.7.0",
+    # version from meta-openembedded/meta-oe/recipes-support/libtinyxml2/libtinyxml2_5.0.1.bb
+    "tinyxml2": "37bc3aca429f0164adf68c23444540b4a24b5778",
+    # version from /meta-openembedded/meta-oe/recipes-devtools/boost-url/boost-url_git.bb
+    "boost-url": "a56ae0df6d3078319755fbaa67822b4fa7fd352b",
+    # version from meta-openembedded/meta-oe/recipes-devtools/valijson/valijson_git.bb
+    "valijson": "c2f22fddf599d04dc33fcd7ed257c698a05345d9",
+    # version from meta-openembedded/meta-oe/recipes-devtools/nlohmann-fifo/nlohmann-fifo_git.bb
+    "fifo_map": "0dfbf5dacbb15a32c43f912a7e66a54aae39d0f9",
+}
+
+# Look up the HEAD for 'head_pkgs' and insert them into 'pkg_rev'.
+pkg_lookups = {}
+for pkg in head_pkgs:
+    pkg_lookups[pkg] = git(
+        "ls-remote", "--heads", f"https://github.com/{pkg}", _bg=True
+    )
+for pkg, result in pkg_lookups.items():
+    for line in result.stdout.decode().split("\n"):
+        if f"refs/heads/{branch}" in line:
+            pkg_rev[pkg] = line.strip().split()[0]
+        elif "refs/heads/master" in line and p not in pkg_rev:
+            pkg_rev[pkg] = line.strip().split()[0]
+
+# Create the contents of the '/tmp/depcache'.
+# This needs to be sorted for consistency.
+depcache = ""
+for pkg in sorted(head_pkgs):
+    if pkg in pkg_rev:
+        depcache += "%s:%s," % (pkg, pkg_rev[pkg])
+
+# Define common flags used for builds
+prefix = "/usr/local"
+configure_flags = " ".join(
+    [
+        f"--prefix={prefix}",
+    ]
+)
+cmake_flags = " ".join(
+    [
+        "-DCMAKE_BUILD_TYPE=RelWithDebInfo",
+        "-DBUILD_SHARED_LIBS=ON",
+        f"-DCMAKE_INSTALL_PREFIX:PATH={prefix}",
+    ]
+)
+meson_flags = " ".join(
+    [
+        "--wrap-mode=nodownload",
+        f"-Dprefix={prefix}",
+    ]
+)
+
+
+def stagename(name):
+    if not name.startswith("openbmc/"):
+        name = "openbmc/" + name
+    return name.replace("/", "-")
+
+
+# Build the commands needed to compose our final image
+# We must sort the packages, otherwise we might produce an unstable
+# docker file and rebuild the image unnecessarily
+copy_cmds = ""
+for pkg in sorted(pkg_rev.keys()):
+    copy_cmds += f"COPY --from={stagename(pkg)} {prefix} {prefix}\n"
+    # Workaround for upstream docker bug and multiple COPY cmds
+    # https://github.com/moby/moby/issues/37965
+    copy_cmds += "RUN true\n"
+
+# Special flags if setting up a deb mirror.
+mirror = ""
+if "ubuntu" in distro and ubuntu_mirror:
+    mirror = f"""
+RUN echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME) main restricted universe multiverse" > /etc/apt/sources.list && \\
+    echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME)-updates main restricted universe multiverse" >> /etc/apt/sources.list && \\
+    echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME)-security main restricted universe multiverse" >> /etc/apt/sources.list && \\
+    echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME)-proposed main restricted universe multiverse" >> /etc/apt/sources.list && \\
+    echo "deb {ubuntu_mirror} $(. /etc/os-release && echo $VERSION_CODENAME)-backports main restricted universe multiverse" >> /etc/apt/sources.list
+"""
+
+# Special flags for proxying.
+proxy_cmd = ""
+proxy_args = []
+if http_proxy:
+    proxy_cmd = f"""
+RUN echo "[http]" >> {homedir}/.gitconfig && \
+    echo "proxy = {http_proxy}" >> {homedir}/.gitconfig
+"""
+    proxy_args.extend(
+        [
+            "--build-arg",
+            f"http_proxy={http_proxy}",
+            "--build-arg",
+            "https_proxy={https_proxy}",
+        ]
+    )
+
+# Create docker image that can run package unit tests
+dockerfile = f"""
+FROM {docker_base}{distro} as openbmc-base
+
+{mirror}
+
+ENV DEBIAN_FRONTEND noninteractive
+
+ENV PYTHONPATH "/usr/local/lib/python3.8/site-packages/"
+
+# We need the keys to be imported for dbgsym repos
+# New releases have a package, older ones fall back to manual fetching
+# https://wiki.ubuntu.com/Debug%20Symbol%20Packages
+RUN apt-get update && ( apt-get install ubuntu-dbgsym-keyring || ( apt-get install -yy dirmngr && \
+    apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F2EDC64DC5AEE1F6B9C621F0C8CAB6595FDFF622 ) )
+
+# Parse the current repo list into a debug repo list
+RUN sed -n '/^deb /s,^deb [^ ]* ,deb http://ddebs.ubuntu.com ,p' /etc/apt/sources.list >/etc/apt/sources.list.d/debug.list
+
+# Remove non-existent debug repos
+RUN sed -i '/-\(backports\|security\) /d' /etc/apt/sources.list.d/debug.list
+
+RUN cat /etc/apt/sources.list.d/debug.list
+
+RUN apt-get update && apt-get dist-upgrade -yy && apt-get install -yy \
+    gcc-10 \
+    g++-10 \
+    libc6-dbg \
+    libc6-dev \
+    libtool \
+    bison \
+    libdbus-1-dev \
+    flex \
+    cmake \
+    python3 \
+    python3-dev\
+    python3-yaml \
+    python3-mako \
+    python3-pip \
+    python3-setuptools \
+    python3-git \
+    python3-socks \
+    pkg-config \
+    autoconf \
+    autoconf-archive \
+    libsystemd-dev \
+    systemd \
+    libssl-dev \
+    libevdev-dev \
+    libevdev2-dbgsym \
+    libjpeg-dev \
+    libpng-dev \
+    ninja-build \
+    sudo \
+    curl \
+    git \
+    dbus \
+    iputils-ping \
+    clang-10 \
+    clang-format-10 \
+    clang-tidy-10 \
+    clang-tools-10 \
+    shellcheck \
+    npm \
+    iproute2 \
+    libnl-3-dev \
+    libnl-genl-3-dev \
+    libconfig++-dev \
+    libsnmp-dev \
+    valgrind \
+    valgrind-dbg \
+    libpam0g-dev \
+    xxd \
+    libi2c-dev \
+    wget \
+    libldap2-dev \
+    libprotobuf-dev \
+    libperlio-gzip-perl \
+    libjson-perl \
+    protobuf-compiler \
+    libgpiod-dev \
+    device-tree-compiler \
+    cppcheck \
+    libpciaccess-dev \
+    libmimetic-dev \
+    libxml2-utils \
+    libxml-simple-perl
+
+RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 1000 \
+  --slave /usr/bin/g++ g++ /usr/bin/g++-10 \
+  --slave /usr/bin/gcov gcov /usr/bin/gcov-10 \
+  --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-10 \
+  --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-10
+
+
+RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-10 1000 \
+  --slave /usr/bin/clang++ clang++ /usr/bin/clang++-10 \
+  --slave /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-10 \
+  --slave /usr/bin/clang-format clang-format /usr/bin/clang-format-10 \
+  --slave /usr/bin/run-clang-tidy.py run-clang-tidy.py /usr/bin/run-clang-tidy-10.py
+
+RUN pip3 install inflection
+RUN pip3 install pycodestyle
+RUN pip3 install jsonschema
+RUN pip3 install meson==0.54.3
+RUN pip3 install protobuf
+
+FROM openbmc-base as openbmc-lcov
+RUN curl -L https://github.com/linux-test-project/lcov/archive/{pkg_rev['lcov']}.tar.gz | tar -xz && \
+cd lcov-* && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-function2
+RUN mkdir {prefix}/include/function2 && \
+curl -L -o {prefix}/include/function2/function2.hpp https://raw.githubusercontent.com/Naios/function2/{pkg_rev['function2']}/include/function2/function2.hpp
+
+FROM openbmc-base as openbmc-googletest
+RUN curl -L https://github.com/google/googletest/archive/{pkg_rev['googletest']}.tar.gz | tar -xz && \
+cd googletest-* && \
+mkdir build && \
+cd build && \
+CXXFLAGS=-std=c++17 cmake {cmake_flags} -DTHREADS_PREFER_PTHREAD_FLAG=ON .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-catch2
+RUN curl -L https://github.com/catchorg/Catch2/archive/{pkg_rev['catch2']}.tar.gz | tar -xz && \
+cd Catch2-* && \
+mkdir build && \
+cd build && \
+cmake {cmake_flags} -DBUILD_TESTING=OFF -DCATCH_INSTALL_DOCS=OFF .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-cereal
+RUN curl -L https://github.com/USCiLab/cereal/archive/{pkg_rev['cereal']}.tar.gz | tar -xz && \
+cp -a cereal-*/include/cereal/ {prefix}/include/
+
+FROM openbmc-base as openbmc-CLI11
+RUN curl -L https://github.com/CLIUtils/CLI11/archive/{pkg_rev['CLI11']}.tar.gz | tar -xz && \
+cd CLI11-* && \
+mkdir build && \
+cd build && \
+cmake {cmake_flags} -DCLI11_BUILD_DOCS=OFF -DBUILD_TESTING=OFF -DCLI11_BUILD_EXAMPLES=OFF .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-fmt
+RUN curl -L https://github.com/fmtlib/fmt/archive/{pkg_rev['fmt']}.tar.gz | tar -xz && \
+cd fmt-* && \
+mkdir build && \
+cd build && \
+cmake {cmake_flags} -DFMT_DOC=OFF -DFMT_TEST=OFF .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-json
+RUN mkdir {prefix}/include/nlohmann/ && \
+curl -L -o {prefix}/include/nlohmann/json.hpp https://github.com/nlohmann/json/releases/download/{pkg_rev['json']}/json.hpp && \
+ln -s nlohmann/json.hpp {prefix}/include/json.hpp
+
+FROM openbmc-base as openbmc-fifo_map
+RUN curl -L https://github.com/nlohmann/fifo_map/archive/{pkg_rev['fifo_map']}.tar.gz | tar -xz && \
+cd fifo_map-*/src && cp fifo_map.hpp {prefix}/include/
+
+FROM openbmc-base as openbmc-span-lite
+RUN curl -L https://github.com/martinmoene/span-lite/archive/{pkg_rev['span-lite']}.tar.gz | tar -xz && \
+cd span-lite-* && \
+mkdir build && \
+cd build && \
+cmake {cmake_flags} -DSPAN_LITE_OPT_BUILD_TESTS=OFF .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-linux-headers
+RUN curl -L https://github.com/openbmc/linux/archive/{pkg_rev['linux-headers']}.tar.gz | tar -xz && \
+cd linux-* && \
+make -j{proc_count} defconfig && \
+make INSTALL_HDR_PATH=/usr/local headers_install
+
+FROM openbmc-base as openbmc-boost
+RUN curl -L https://dl.bintray.com/boostorg/release/{pkg_rev['boost']}/source/boost_$(echo "{pkg_rev['boost']}" | tr '.' '_').tar.bz2 | tar -xj && \
+cd boost_*/ && \
+./bootstrap.sh --prefix={prefix} --with-libraries=context,coroutine && \
+./b2 && ./b2 install --prefix={prefix}
+
+FROM openbmc-base as openbmc-tinyxml2
+RUN curl -L https://github.com/leethomason/tinyxml2/archive/{pkg_rev['tinyxml2']}.tar.gz | tar -xz && \
+cd tinyxml2-* && \
+mkdir build && \
+cd build && \
+cmake {cmake_flags} .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-boost-url
+RUN curl -L https://github.com/CPPAlliance/url/archive/{pkg_rev['boost-url']}.tar.gz | tar -xz && \
+cd url-* && \
+mkdir buildir && \
+cd buildir && \
+cmake {cmake_flags} -DBOOST_URL_STANDALONE=ON -DBOOST_URL_BUILD_TESTS=OFF -DBOOST_URL_BUILD_EXAMPLES=OFF .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-valijson
+RUN curl -L https://github.com/tristanpenman/valijson/archive/{pkg_rev['valijson']}.tar.gz | tar -xz && \
+cd valijson-* && \
+mkdir build && \
+cd build && \
+cmake {cmake_flags} -DINSTALL_HEADERS=1 -DBUILD_TESTS=0 .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-libvncserver
+RUN curl -L https://github.com/LibVNC/libvncserver/archive/{pkg_rev['libvncserver']}.tar.gz | tar -xz && \
+cd libvncserver-* && \
+mkdir build && \
+cd build && \
+cmake {cmake_flags} .. && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-stdplus
+COPY --from=openbmc-fmt {prefix} {prefix}
+COPY --from=openbmc-span-lite {prefix} {prefix}
+RUN curl -L https://github.com/openbmc/stdplus/archive/{pkg_rev['openbmc/stdplus']}.tar.gz | tar -xz && \
+cd stdplus-* && \
+meson build {meson_flags} -Dtests=disabled -Dexamples=false && \
+ninja -C build && \
+ninja -C build install
+
+FROM openbmc-base as openbmc-sdbusplus
+RUN curl -L https://github.com/openbmc/sdbusplus/archive/{pkg_rev['openbmc/sdbusplus']}.tar.gz | tar -xz && \
+cd sdbusplus-* && \
+cd tools && ./setup.py install --root=/ --prefix={prefix} && \
+cd .. && meson build {meson_flags} -Dtests=disabled -Dexamples=disabled && \
+ninja -C build && \
+ninja -C build install
+
+FROM openbmc-base as openbmc-sdeventplus
+COPY --from=openbmc-function2 {prefix} {prefix}
+COPY --from=openbmc-stdplus {prefix} {prefix}
+RUN curl -L https://github.com/openbmc/sdeventplus/archive/{pkg_rev['openbmc/sdeventplus']}.tar.gz | tar -xz && \
+cd sdeventplus-* && \
+meson build {meson_flags} -Dtests=disabled -Dexamples=false && \
+ninja -C build && \
+ninja -C build install
+
+FROM openbmc-base as openbmc-gpioplus
+COPY --from=openbmc-stdplus {prefix} {prefix}
+RUN curl -L https://github.com/openbmc/gpioplus/archive/{pkg_rev['openbmc/gpioplus']}.tar.gz | tar -xz && \
+cd gpioplus-* && \
+meson build {meson_flags} -Dtests=disabled -Dexamples=false && \
+ninja -C build && \
+ninja -C build install
+
+FROM openbmc-base as openbmc-phosphor-dbus-interfaces
+COPY --from=openbmc-sdbusplus {prefix} {prefix}
+RUN curl -L https://github.com/openbmc/phosphor-dbus-interfaces/archive/{pkg_rev['openbmc/phosphor-dbus-interfaces']}.tar.gz | tar -xz && \
+cd phosphor-dbus-interfaces-* && \
+meson build {meson_flags} -Ddata_org_open_power=true -Ddata_com_ibm=true && \
+ninja -C build && \
+ninja -C build install
+
+FROM openbmc-base as openbmc-phosphor-logging
+COPY --from=openbmc-cereal {prefix} {prefix}
+COPY --from=openbmc-sdbusplus {prefix} {prefix}
+COPY --from=openbmc-sdeventplus {prefix} {prefix}
+COPY --from=openbmc-phosphor-dbus-interfaces {prefix} {prefix}
+COPY --from=openbmc-fifo_map {prefix} {prefix}
+RUN curl -L https://github.com/openbmc/phosphor-logging/archive/{pkg_rev['openbmc/phosphor-logging']}.tar.gz | tar -xz && \
+cd phosphor-logging-* && \
+./bootstrap.sh && \
+./configure {configure_flags} --enable-metadata-processing YAML_DIR={prefix}/share/phosphor-dbus-yaml/yaml && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-phosphor-objmgr
+COPY --from=openbmc-boost {prefix} {prefix}
+COPY --from=openbmc-sdbusplus {prefix} {prefix}
+COPY --from=openbmc-tinyxml2 {prefix} {prefix}
+COPY --from=openbmc-phosphor-logging {prefix} {prefix}
+RUN curl -L https://github.com/openbmc/phosphor-objmgr/archive/{pkg_rev['openbmc/phosphor-objmgr']}.tar.gz | tar -xz && \
+cd phosphor-objmgr-* && \
+./bootstrap.sh && \
+./configure {configure_flags} && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-open-power-pdbg
+RUN curl -L https://github.com/open-power/pdbg/archive/{pkg_rev['open-power/pdbg']}.tar.gz | tar -xz && \
+cd pdbg-* && \
+./bootstrap.sh && \
+./configure {configure_flags} && \
+make -j{proc_count} && \
+make install
+
+FROM openbmc-base as openbmc-pldm
+COPY --from=openbmc-sdbusplus {prefix} {prefix}
+COPY --from=openbmc-sdeventplus {prefix} {prefix}
+COPY --from=openbmc-boost {prefix} {prefix}
+COPY --from=openbmc-phosphor-dbus-interfaces {prefix} {prefix}
+COPY --from=openbmc-phosphor-logging {prefix} {prefix}
+COPY --from=openbmc-json {prefix} {prefix}
+COPY --from=openbmc-CLI11 {prefix} {prefix}
+RUN curl -L https://github.com/openbmc/pldm/archive/{pkg_rev['openbmc/pldm']}.tar.gz | tar -xz && \
+cd pldm-* && \
+meson build {meson_flags} -Dlibpldm-only=enabled -Doem-ibm=enabled -Dtests=disabled && \
+ninja -C build && \
+ninja -C build install
+
+# Build the final output image
+FROM openbmc-base
+{copy_cmds}
+
+# Some of our infrastructure still relies on the presence of this file
+# even though it is no longer needed to rebuild the docker environment
+# NOTE: The file is sorted to ensure the ordering is stable.
+RUN echo '{depcache}' > /tmp/depcache
+
+# Final configuration for the workspace
+RUN grep -q {gid} /etc/group || groupadd -g {gid} {username}
+RUN mkdir -p "{os.path.dirname(homedir)}"
+RUN grep -q {uid} /etc/passwd || useradd -d {homedir} -m -u {uid} -g {gid} {username}
+RUN sed -i '1iDefaults umask=000' /etc/sudoers
+RUN echo "{username} ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers
+
+{proxy_cmd}
+
+RUN /bin/bash
+"""
+
+# Do the docker build.
+for line in docker.build(
+    proxy_args,
+    "--network=host",
+    "-t",
+    docker_image_name,
+    "-",
+    _in=dockerfile,
+    _iter=True,
+):
+    print(line, end="", flush=True)