SDPT Lab 5

De la WikiLabs
Jump to navigationJump to search

Week 5 Lab Activity: Containerization & Cross-Compilation

Objective

This week, we eliminate the "It works on my machine" problem. You will learn how to build isolated Linux environments, cross-compile your OvenController C++ code for an ARM architecture, and solve the complex problem of linking third-party static and dynamic libraries compiled for foreign architectures.

We will accomplish this in three phases:

  1. Preparing CMake: Updating the build system to link an external database library.
  2. The Manual Container (The "Old Way"): Building the cross-compilation environment by hand to understand the underlying mechanics.
  3. Infrastructure as Code (The "Modern Way"): Automating the entire environment with a Dockerfile.

Phase 1: Preparing CMake for Foreign Libraries

Imagine our OvenController needs to log telemetry data locally using an SQLite database. To do this, we need to link the sqlite3 library.

Open your CMakeLists.txt from Week 4 and add the standard linking commands. (Note: CMake is smart enough to look for the correct architecture version of this library based on the compiler we provide later).

# Add this near the bottom of your CMakeLists.txt
find_package(PkgConfig REQUIRED)
pkg_check_modules(SQLITE3 REQUIRED sqlite3)

# Add the includes and link the library to your executable
target_include_directories(unit_tests PRIVATE ${SQLITE3_INCLUDE_DIRS})
target_link_libraries(unit_tests PRIVATE ${SQLITE3_LIBRARIES})

Phase 2: The Manual Container (The "Old Way")

Before we automate, let's understand what Docker is actually doing under the hood.

1. Start a raw, interactive Ubuntu container: Open your terminal and run a fresh Ubuntu 22.04 image, mounting your current code directory into /app:

docker run -it --name manual-build-env -v ${PWD}:/app ubuntu:22.04 /bin/bash

Notice your terminal prompt has changed. You are now the root user inside an isolated Linux environment.

2. The "Multiarch" Superpower: If we just run apt-get install libsqlite3-dev, Ubuntu will download the x86_64 version. If we try to cross-compile our ARM code against it, the linker will violently crash. We must explicitly tell Ubuntu's package manager to accept packages for a foreign architecture (arm64).

dpkg --add-architecture arm64
apt-get update

3. Install the Toolchain and Foreign Libraries: Now we install our cross-compiler, QEMU emulator, and the specifically compiled ARM version of our library (:arm64).

apt-get install -y build-essential cmake pkg-config g++-aarch64-linux-gnu qemu-user qemu-user-static
apt-get install -y libsqlite3-dev:arm64

4. Cross-Compile and Emulate: Navigate to your mounted code and build the project for ARM. We use a command-line flag to tell CMake to use the ARM compiler instead of native GCC.

cd /app
mkdir build_arm && cd build_arm
cmake -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ ..
make

If you try to run ./unit_tests natively, Linux will throw an Exec format error. Use QEMU to emulate the execution of your ARM binary. Because we linked a dynamic library, we must tell QEMU where to find the ARM shared objects (-L flag):

qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./unit_tests

Verify that your Google Tests pass on the emulated architecture! Type exit to leave and stop the container.


Phase 3: Infrastructure as Code (The "Modern Way")

Manually typing apt-get commands, enabling architectures, and committing containers is tedious and error-prone. Modern DevOps engineers automate this using a Dockerfile.

1. Write the Dockerfile: Create a file named exactly Dockerfile (no extension) in your project root. Translate the manual steps you just took into an automated recipe.

# 1. Base Image
FROM ubuntu:22.04

# 2. Prevent interactive timezone prompts
ENV DEBIAN_FRONTEND=noninteractive

# 3. Enable ARM64 Architecture for foreign libraries
RUN dpkg --add-architecture arm64

# 4. Install toolchain & ARM libraries in ONE layer to save space
RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    pkg-config \
    g++-aarch64-linux-gnu \
    qemu-user \
    qemu-user-static \
    libsqlite3-dev:arm64 \
    && rm -rf /var/lib/apt/lists/*

# 5. Set Workspace
WORKDIR /app

# 6. Keep the container alive interactively
CMD ["/bin/bash"]

2. Build the Automated Image: Ask Docker Engine to read your recipe and build the image from scratch.

docker build -t automated-arm-env:latest .

3. Run your Final Environment:

docker run -it --rm -v ${PWD}:/app automated-arm-env:latest

You now have a perfectly reproducible, portable embedded toolchain that natively resolves third-party ARM dependencies.


Assignment Submission

Upload ONLY your Dockerfile to the Moodle VPL assignment. The automated grading server will run static analysis on your infrastructure-as-code syntax to verify your architecture setup, toolchain requirements, and layer optimization.