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.

Your Challenge: Create a file named exactly Dockerfile (no extension) in your project root. Translate the manual steps you just took into an automated recipe that meets the following strict specifications:

  1. Base Image: Must use ubuntu:22.04.
  2. Environment: Set DEBIAN_FRONTEND to noninteractive to prevent the build from hanging on timezone prompts.
  3. Architecture: Use a RUN command to add the arm64 architecture to dpkg.
  4. The Mega-Layer: Use a single RUN command to update apt-get, install all required tools (build-essential cmake pkg-config g++-aarch64-linux-gnu qemu-user qemu-user-static libsqlite3-dev:arm64), and immediately clean up the apt cache (rm -rf /var/lib/apt/lists/*) to keep the image size small. (Hint: Chain your commands with && and use \ for multi-line formatting).
  5. Workspace: Set the working directory inside the container to /app.
  6. PID 1: Set the default command (CMD) to launch /bin/bash so the container stays alive interactively.

Build and Test Your Image: Once you have written your Dockerfile, test your infrastructure code by building and running it. It should behave exactly like your manual container did:

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

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.