Adding Custom Applications and Libraries in the Yocto Environment
Overview
This document guides users in adding custom applications and library files (using "helloworld" as an example) to the Yocto build system. Through the following steps, you can integrate your code into the Yocto project and generate installation packages and libraries for use.
Table of Contents
Environment Setup
Installation and Setup
- Install necessary supporting software packages.
- Clone the code.
For specific environment preparation, refer to: Development Environment Setup
Creating Project Directories
- Set up directory structures for better management and development.
meta-custom/
├── conf/
│ └── layer.conf
├── recipes-apps/
│ └── helloworld/
│ ├── files/
│ │ ├── helloworld.c
│ │ ├── Makefile
│ │ └── helloworld.service
│ └── helloworld_1.0.bb
└── recipes-libs/
└── lib-helloworld/
├── files/
│ ├── lib_helloworld.c
│ ├── lib_helloworld.h
│ └── Makefile
└── lib-helloworld_1.0.bb
Adding a Custom Application
Creating Application Files
Creating Layer Directories
mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-apps/helloworld/files
mkdir -p ${YOCTO_DIR}/layers/meta-custom/conf
Configuring Layer
- Edit the
layers/meta-custom/conf/layer.conf
file:
BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb"
LAYERDEPENDS_meta-custom = "core"
LAYERSERIES_COMPAT_meta-custom = "kirkstone"
- Add the following content to
layers/meta-qcom-distro/conf/bblayers.conf
:
BBLAYERS = "
${WORKSPACE}/layers/meta-custom \
"
- Additionally, add the following content to
layers/meta-quectel/recipes-products/images/qcom-multimedia-image.bbappend
:
IMAGE_INSTALL:append = "helloworld"
IMAGE_INSTALL:append = " \
lib-helloworld \
lib-helloworld-dev \
lib-helloworld-staticdev \
"
Creating Application Source Code
- Create
helloworld.c
in themeta-custom/recipes-apps/helloworld/files
directory:
// helloworld.c
#include <stdio.h>
int main() {
printf("Hello, world!\n");
return 0;
}
Writing the Makefile
- Create a
Makefile
in themeta-custom/recipes-apps/helloworld/files
directory:
# Using compilation flags provided by Yocto
all: helloworld
helloworld: helloworld.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
clean:
rm -f helloworld
.PHONY: all clean
Creating the Systemd Service File
- Create
helloworld.service
in themeta-custom/recipes-apps/helloworld/files
directory:
[Unit]
Description=Hello World Service
[Service]
Type=simple
ExecStart=/usr/bin/helloworld
[Install]
WantedBy=multi-user.target
Writing the BitBake Recipe
- Create
helloworld_1.0.bb
in themeta-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"
# List of source code files (including helloworld.c, Makefile, and systemd service file)
SRC_URI = "file://helloworld.c \
file://Makefile \
file://helloworld.service"
# Set the working directory (${WORKDIR} is the BitBake build directory)
S = "${WORKDIR}"
# Inherit the systemd class (add support for systemd services)
inherit systemd
# Define the systemd service file name (${PN} automatically changes to the package name)
SYSTEMD_SERVICE:${PN} = "helloworld.service"
# Configuration stage (skip configuration here)
do_configure() {
bbnote skip do_configure # Log skipping the configuration stage
}
# Compilation stage
do_compile() {
# First execute make clean to clean up
oe_runmake -f ${S}/Makefile clean
# Then execute make to compile
oe_runmake -f ${S}/Makefile
}
# Installation stage
do_install() {
# Create installation directories (${D} is the target root directory, ${bindir} is typically /usr/bin)
install -d ${D}${bindir}
# Install the helloworld executable to /usr/bin with permission 755
install -m 0755 ${S}/helloworld ${D}${bindir}
# Create systemd service directories (${systemd_system_unitdir} is typically /lib/systemd/system)
install -d ${D}${systemd_system_unitdir}
# Install the helloworld.service service file with permission 644
install -m 0644 ${S}/helloworld.service ${D}${systemd_system_unitdir}
}
Building and Running the Application
Setting Up the Build Environment
- Enter the build environment code directory and configure the build environment:
source quectel_build/compile/build.sh
- Users can choose to install applications based on their needs, using single compilation or rootfs compilation.
Installing the Application (Single Compilation, IPK Installation)
- Execute the following command in the
meta-custom/recipes-apps/
directory:
bitbake helloworld
- After building, the generated
.ipk
file is located at:
find ~/build-qcom-wayland/tmp-glibc/deploy -name "helloworld*.ipk"
/home/quectel/build-qcom-wayland/tmp-glibc/deploy/ipk/armv8-2a/helloworld_1.0-r0_armv8-2a.ipk
- Push the generated
.ipk
file to the device:
adb push .\helloworld_1.0-r0_armv8-2a.ipk /mnt
- Install the
.ipk
file:
# Remount the filesystem in read-write mode before installation
mount -o remount,rw /usr
root@qcm6490-idp:/mnt# opkg install helloworld_1.0-r0_armv8-2a.ipk
- Verify the installation:
root@qcm6490-idp:/mnt# /usr/bin/helloworld
Hello, world!
Installing the Application (Rootfs Compilation)
- Execute the following commands in the
meta-custom/recipes-apps/
directory:
# Customize by inputting "ProjectName", "ProjectRev", "CustName" as needed. Select valid fields from the prompts "Valid Projects" and "Valid CUST_NAME".
buildconfig QSM565DWF SG565DWFPARL1A01_BL01BP01K0M01_QDP_LP6.6.0XX.01.00X_V0X STD
# Execute buildall after the above command completes
buildall
# Execute buildpackage after the above command completes
buildpackage
The generated image is included in the path
quectel_build/SG565DWFPARL1A01_BL01BP01K0M01_QDP_LP6.6.0XX.01.00X_V0X
. Users can refer to the links Building PI-SG565D Single-board Computer Image with Yocto and QDL Introduction and Image Burning for burning.Verify the installation:
root@qcm6490-idp:~# /usr/bin/helloworld
Hello, world!
Adding a Third-party Library
Creating Library Files
reating Layer Directories
mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-libs/lib-helloworld/files
Creating Library Source Code
- Create
lib_helloworld.c
andlib_helloworld.h
in themeta-custom/recipes-libs/lib-helloworld/files/
directory:
// lib_helloworld.c
#include <stdio.h>
void hello_print(const char* name) {
printf("[LIB] Hello, %s!\n", name);
}
// lib_helloworld.h
#ifndef LIB_HELLO_H
#define LIB_HELLO_H
void hello_print(const char*);
#endif
Writing the Makefile
- Create a
Makefile
in themeta-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*
rm -f lib_helloworld.o
rm -f libhello.a
Writing the BitBake Recipe
- Create
lib-helloworld_1.0.bb
in themeta-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://Makefile \
file://lib_helloworld.h"
S = "${WORKDIR}"
EXTRA_OEMAKE = "'CC=${CC}' 'CFLAGS=${CFLAGS} -fPIC' 'LDFLAGS=${LDFLAGS}'"
do_install() {
# 1. Create target directories
install -d ${D}${libdir}
# 2. Install shared library and create 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
# 3. Install static library
install -m 0644 ${S}/libhello.a ${D}${libdir}/
}
# Package file distribution
FILES:${PN} = " \
${libdir}/libhello.so.1 \
${libdir}/libhello.so.${PV} \
"
FILES:${PN}-dev = " \
${libdir}/libhello.so \
"
FILES:${PN}-staticdev = " \
${libdir}/libhello.a \
"
Building and Using the Library
Setting Up the Build Environment
- Enter the build environment code directory and configure the build environment:
source quectel_build/compile/build.sh
Using the Third-party Library (Single Compilation with BitBake)
- Build using bitbake:
bitbake lib-helloworld
- Search for the generated dynamic library files at the root directory:
find ./* -name "libhello*"
- Push the library files to the device:
adb push libhello.so libhello.so.1 libhello.so.1.0 libhello.a /usr/lib
- Fix execution permissions and recreate symbolic links:
chmod +x /usr/lib/libhello.so /usr/lib/libhello.so.1 /usr/lib/libhello.so.1.0 /usr/lib/libhello.a
ln -sf libhello.so.1.0 libhello.so.1
ln -sf libhello.so.1 libhello.so
- Write a 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;
}
// dynamic_test_helloworld.c
#include <stdio.h>
void hello_print(const char*);
int main() {
printf("dynamic lib test\n");
hello_print("world");
return 0;
}
- Compile and run the test program:
# Static library test
gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello
# Output:
static lib test
[LIB] Hello, world!
# Dynamic library test
gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello
# Output:
dynamic lib test
[LIB] Hello, world!
Using the Third-party Library (Rootfs Compilation)
The compilation and burning process can be referenced from the method described above (Installing Applications with Rootfs Compilation).
Verify the usage (requires writing test programs as referenced above Using the Third-party Library (Single Compilation with BitBake)):
/mnt# ls -l /usr/lib/libhello*
-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
# Static library test
gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello
# Output:
static lib test
[LIB] Hello, world!
# Dynamic library test
gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello
# Output:
dynamic lib test
[LIB] Hello, world!
Conclusion
Through the guidance provided in this document, users can successfully add custom applications and libraries in the Yocto environment, achieving personalized functional integration.