Adding Applications and Third-Party Libraries

Overview

This document guides users to add custom applications and third-party libraries to the Yocto build system (using helloworld as an example). Through the steps in this document, you can:

  • Integrate custom code into Yocto projects
  • Generate IPK installation packages
  • Compile and use third-party library files
  • Integrate components into system images

Prerequisites

Environment Requirements

  • Necessary development toolkits installed
  • Yocto project code cloned
  • Familiar with basic C programming
  • Understand Makefile and BitBake basics

Project Directory Structure

Create a custom meta layer, recommended directory structure:

meta-custom/
├── conf/
│   └── layer.conf                    # Layer configuration file
├── recipes-apps/                      # Application recipe directory
│   └── helloworld/
│       ├── files/
│       │   ├── helloworld.c          # Application source code
│       │   ├── Makefile              # Build script
│       │   └── helloworld.service    # systemd service file
│       └── helloworld_1.0.bb         # BitBake recipe
└── recipes-libs/                      # Library recipe directory
    └── lib-helloworld/
        ├── files/
        │   ├── lib_helloworld.c      # Library source code
        │   ├── lib_helloworld.h      # Library header file
        │   └── Makefile              # Build script
        └── lib-helloworld_1.0.bb     # BitBake recipe

Adding Custom Applications

1. Create Layer Directory Structure

Create Directories

mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-apps/helloworld/files
mkdir -p ${YOCTO_DIR}/layers/meta-custom/conf

Configure Custom Layer

Add the following to the layers/meta-custom/conf/layer.conf file:

BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb"
LAYERDEPENDS_meta-custom = "core"
LAYERSERIES_COMPAT_meta-custom = "kirkstone"

Register Layer to Build System

Edit layers/meta-qcom-distro/conf/bblayers.conf, add custom layer path:

BBLAYERS = " \
    ${WORKSPACE}/layers/meta-custom \
"

Add to Image Recipe

Edit layers/meta-quectel/recipes-products/images/qcom-multimedia-image.bbappend, add:

# Add application
IMAGE_INSTALL:append = " helloworld"

# Add library files (if needed)
IMAGE_INSTALL:append = " \
    lib-helloworld \
    lib-helloworld-dev \
    lib-helloworld-staticdev \
"

2. Create Application Source Code

Write Main Program

Create helloworld.c in the meta-custom/recipes-apps/helloworld/files/ directory:

// helloworld.c
#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

Write Makefile

Create Makefile in the meta-custom/recipes-apps/helloworld/files/ directory:

# Use compilation flags provided by Yocto
all: helloworld

helloworld: helloworld.c
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<

clean:
    rm -f helloworld

.PHONY: all clean

Create Systemd Service File

Create helloworld.service in the meta-custom/recipes-apps/helloworld/files/ directory:

[Unit]
Description=Hello World Service

[Service]
Type=simple
ExecStart=/usr/bin/helloworld

[Install]
WantedBy=multi-user.target

3. Write BitBake Recipe

Create helloworld_1.0.bb in the meta-custom/recipes-apps/helloworld/ directory:

SUMMARY = "Hello World application"
DESCRIPTION = "A simple Hello World application."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

# Source code file list
SRC_URI = "file://helloworld.c \
           file://Makefile \
           file://helloworld.service"

# Set working directory
S = "${WORKDIR}"

# Inherit systemd class
inherit systemd

# Define systemd service file name
SYSTEMD_SERVICE:${PN} = "helloworld.service"

# Configuration phase (skip)
do_configure() {
    bbnote "skip do_configure"
}

# Compilation phase
do_compile() {
    # Clean first
    oe_runmake -f ${S}/Makefile clean
    # Execute compilation
    oe_runmake -f ${S}/Makefile
}

# Installation phase
do_install() {
    # Create executable installation directory
    install -d ${D}${bindir}
    # Install executable
    install -m 0755 ${S}/helloworld ${D}${bindir}

    # Create systemd service directory
    install -d ${D}${systemd_system_unitdir}
    # Install service file
    install -m 0644 ${S}/helloworld.service ${D}${systemd_system_unitdir}
}

4. Build and Install Application

Configure Build Environment

Enter the code working directory and execute:

source quectel_build/compile/build.sh

Method 1: Compile and Install IPK Package Separately

Step 1: Compile Application

bitbake helloworld

Step 2: Find Generated IPK Package

find ~/build-qcom-wayland/tmp-glibc/deploy -name "helloworld*.ipk"

Example output:

/home/quectel/build-qcom-wayland/tmp-glibc/deploy/ipk/armv8-2a/helloworld_1.0-r0_armv8-2a.ipk

Step 3: Push to Device

adb push helloworld_1.0-r0_armv8-2a.ipk /mnt

Step 4: Install IPK Package

Execute on the device:

# Mount filesystem as read-write mode
mount -o remount,rw /usr

# Install IPK package
opkg install /mnt/helloworld_1.0-r0_armv8-2a.ipk

Step 5: Verify Installation

/usr/bin/helloworld

Expected output:

Hello, world!

Method 2: Full System Image Build

Step 1: Configure Build Parameters

# Users can customize input parameters, select from prompted valid options
buildconfig QSM565DWF SG565DWFPARL1A01_BL01BP01K0M01_QDP_LP6.6.0XX.01.00X_V0X STD

Step 2: Compile Entire System

buildall

Step 3: Package Image

buildpackage

Step 4: Flash Image

The generated image is located at quectel_build/SG565DWFPARL1A01_BL01BP01K0M01_QDP_LP6.6.0XX.01.00X_V0X.

Refer to the following documents for flashing:

Step 5: Verify Installation

After flashing is complete, execute on the device:

/usr/bin/helloworld

Expected output:

Hello, world!

Adding Third-Party Libraries

1. Create Library Files

Create Directory

mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-libs/lib-helloworld/files

Create Library Header File

Create lib_helloworld.h in the meta-custom/recipes-libs/lib-helloworld/files/ directory:

// lib_helloworld.h
#ifndef LIB_HELLO_H
#define LIB_HELLO_H

void hello_print(const char* name);

#endif

Create Library Source Code

Create lib_helloworld.c in the meta-custom/recipes-libs/lib-helloworld/files/ directory:

// lib_helloworld.c
#include <stdio.h>
#include "lib_helloworld.h"

void hello_print(const char* name) {
    printf("[LIB] Hello, %s!\n", name);
}

Write Makefile

Create Makefile in the meta-custom/recipes-libs/lib-helloworld/files/ directory:

LIB_VERSION = 1.0

all:
    ${CC} ${CFLAGS} -fPIC -c lib_helloworld.c -o lib_helloworld.o
    ${CC} ${CFLAGS} -fPIC -shared lib_helloworld.o -o libhello.so.${LIB_VERSION} \
        -Wl,-soname,libhello.so.1 ${LDFLAGS}
    ar rcs libhello.a lib_helloworld.o
    ln -sf libhello.so.${LIB_VERSION} libhello.so
    ln -sf libhello.so.${LIB_VERSION} libhello.so.1

clean:
    rm -f libhello.so* lib_helloworld.o libhello.a

2. Write BitBake Recipe

Create lib-helloworld_1.0.bb in the meta-custom/recipes-libs/lib-helloworld/ directory:

SUMMARY = "Hello World Library"
SECTION = "libs"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://lib_helloworld.c \
           file://lib_helloworld.h \
           file://Makefile"

S = "${WORKDIR}"

EXTRA_OEMAKE = "'CC=${CC}' 'CFLAGS=${CFLAGS} -fPIC' 'LDFLAGS=${LDFLAGS}'"

do_install() {
    # Create target directories
    install -d ${D}${libdir}
    install -d ${D}${includedir}

    # Install shared library and symbolic links
    install -m 0755 ${S}/libhello.so.${PV} ${D}${libdir}/
    ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so
    ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so.1

    # Install static library
    install -m 0644 ${S}/libhello.a ${D}${libdir}/

    # Install header file
    install -m 0644 ${S}/lib_helloworld.h ${D}${includedir}/
}

# Package file allocation
FILES:${PN} = " \
    ${libdir}/libhello.so.1 \
    ${libdir}/libhello.so.${PV} \
"

FILES:${PN}-dev = " \
    ${libdir}/libhello.so \
    ${includedir}/lib_helloworld.h \
"

FILES:${PN}-staticdev = " \
    ${libdir}/libhello.a \
"

3. Build and Use Third-Party Libraries

Configure Build Environment

source quectel_build/compile/build.sh

Method 1: Compile and Install Separately

Step 1: Compile Library

bitbake lib-helloworld

Step 2: Find Generated Library Files

find ./* -name "libhello*"

Step 3: Push to Device

adb push libhello.so libhello.so.1 libhello.so.1.0 libhello.a /usr/lib

Step 4: Fix Permissions and Symbolic Links

Execute on the device:

# Set execute permissions
chmod 755 /usr/lib/libhello.so*
chmod 644 /usr/lib/libhello.a

# Rebuild symbolic links
cd /usr/lib
ln -sf libhello.so.1.0 libhello.so.1
ln -sf libhello.so.1 libhello.so

Step 5: Write Test Program

Create static library test program static_test_helloworld.c:

#include <stdio.h>

void hello_print(const char*);

int main() {
    printf("static lib test\n");
    hello_print("world");
    return 0;
}

Create dynamic library test program dynamic_test_helloworld.c:

#include <stdio.h>

void hello_print(const char*);

int main() {
    printf("dynamic lib test\n");
    hello_print("world");
    return 0;
}

Step 6: Compile and Run Test Program

Static library test:

gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello

Expected output:

static lib test
[LIB] Hello, world!

Dynamic library test:

gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello

Expected output:

dynamic lib test
[LIB] Hello, world!

Method 2: Full System Image Build

Compile and Flash

Refer to the steps in the "Method 2: Full System Image Build" section of the "Adding Custom Applications" chapter for compilation and flashing.

Verify Library Files

After flashing is complete, check library files on the device:

ls -l /usr/lib/libhello*

Expected output:

-rw-r--r--. 2 root root 5002 Jan  1  1970 /usr/lib/libhello.a
lrwxrwxrwx. 1 root root   15 Jul 25 06:26 /usr/lib/libhello.so -> libhello.so.1.0
lrwxrwxrwx. 1 root root   15 Jul 25 06:26 /usr/lib/libhello.so.1 -> libhello.so.1.0
-rwxr-xr-x. 2 root root 5968 Jan  1  1970 /usr/lib/libhello.so.1.0

Run Test Program

Static library test:

gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello

Expected output:

static lib test
[LIB] Hello, world!

Dynamic library test:

gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello

Expected output:

dynamic lib test
[LIB] Hello, world!

Notes

  1. Layer Priority: Ensure custom layer configuration is correct to avoid conflicts with other layers
  2. License Declaration: Must correctly set LICENSE and LIC_FILES_CHKSUM
  3. File Permissions: Pay attention to executable and library file permission settings
  4. Symbolic Links: Dynamic library symbolic links must be correctly set
  5. Dependencies: If the library depends on other packages, declare DEPENDS in the recipe
  6. Version Management: It is recommended to use version numbers to manage library files

Common Issues

Q1: BitBake Compilation Failed?

Check Items:

  • Is the recipe syntax correct
  • Are source file paths correct
  • Does the LICENSE file exist

Q2: IPK Installation Failed?

Solution:

  • Ensure filesystem is mounted as read-write mode
  • Check if disk space is sufficient
  • View opkg error logs

Q3: Dynamic Library Not Found?

Solution:

  • Check LD_LIBRARY_PATH environment variable
  • Confirm symbolic links are correct
  • Run ldconfig to update library cache

Summary

Through the guidance in this document, you can in the Yocto environment:

  • Create custom applications and libraries
  • Generate IPK installation packages for separate deployment
  • Integrate components into system images
  • Verify and test custom components

After mastering these skills, you can expand and customize system functionality according to actual needs.