SDPT Lab 5
- 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).*
```cmake
- 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`: ```bash 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`). ```bash 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`). ```bash 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. ```bash 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): ```bash 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.
```dockerfile
- 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. ```bash docker build -t automated-arm-env:latest . ```
- 3. Run your Final Environment:**
```bash 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.