Yocto · Bài 5/9
Bài 5: Package dependencies và splitting trong Yocto
2 lượt đọc5 phút đọc
Khi xây dựng hệ thống nhúng, hai việc rất hay bị bỏ qua là quản lý phần phụ thuộc giữa các gói và chia nhỏ gói. Làm đúng, image của bạn đủ thứ cần chạy mà không thừa thứ nào. Bài này giải thích hai khái niệm đó bằng một ví dụ nhỏ trên nhánh scarthgap.
1.Phụ thuộc giữa các gói (package dependencies)
Một gói muốn chạy được thường cần gói khác: thư viện, công cụ, chương trình đi kèm. Yocto chia sự phụ thuộc thành hai loại:
- Phụ thuộc lúc build (build time): cần có khi biên dịch gói, khai báo bằng biến
DEPENDS. - Phụ thuộc lúc chạy (run time): cần có trên thiết bị khi gói đang chạy, khai báo bằng biến
RDEPENDS.
Ví dụ chuẩn bị
Trong layer meta-helloworld, tạo recipe hello-pack-depend-split với 3 chương trình dùng thư viện libcurl:
meta-helloworld/recipes-example/hello-pack-depend-split/
├── files
│ ├── hello_depen.c
│ ├── hello_pack_split.c
│ ├── hello_run_time_depen.c
│ └── Makefile
└── hello-pack-depend-split.bbHai chương trình đầu có cùng nội dung, chỉ khác tên. Mã mẫu của hello_depen.c:
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl = curl_easy_init();
if (curl) {
printf("Init curl OK\n");
printf("In file %s\n", __FILE__);
curl_easy_cleanup(curl);
}
return 0;
}Chương trình thứ ba, hello_run_time_depen.c, giống như trên nhưng có thêm đoạn gọi bash lúc chạy:
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl = curl_easy_init();
if (curl) {
printf("Init curl OK\n");
printf("In file %s\n", __FILE__);
curl_easy_cleanup(curl);
}
int status = system("bash -c 'echo This is executed by bash at runtime!'");
if (status == -1) {
perror("Failed to run bash command");
return 1;
}
return 0;
}Makefile:
CC = gcc
CFLAGS = -Wall
all: hello_depen hello_pack_split hello_run_time_depen
hello_depen: hello_depen.c
$(CC) hello_depen.c $(CFLAGS) -lcurl -o hello_depen
hello_pack_split: hello_pack_split.c
$(CC) hello_pack_split.c $(CFLAGS) -lcurl -o hello_pack_split
hello_run_time_depen: hello_run_time_depen.c
$(CC) hello_run_time_depen.c $(CFLAGS) -lcurl -o hello_run_time_depen
clean:
rm -f hello_depen hello_pack_split hello_run_time_depenRecipe lúc đầu, chưa khai báo phụ thuộc:
DESCRIPTION = "Simple program to simulate package dependencies and splitting"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
S = "${WORKDIR}"
SRC_URI = file://hello_depen.c \
file://hello_pack_split.c \
file://hello_run_time_depen.c \
file://Makefile
EXTRA_OEMAKE = "CC='${CC}' CFLAGS='${CFLAGS}'"
do_compile() {
oe_runmake
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello_depen ${D}${bindir}/hello_depen
install -m 0755 hello_pack_split ${D}${bindir}/hello_pack_split
install -m 0755 hello_run_time_depen ${D}${bindir}/hello_run_time_depen
}1.1. Phụ thuộc lúc build: DEPENDS
Thử build:
bitbake hello-pack-depend-splitBạn sẽ gặp lỗi:
fatal error: curl/curl.h: No such file or directoryLý do: mã dùng libcurl, nhưng Yocto chưa biết cần chuẩn bị thư viện đó trước bước do_compile. Cách sửa là khai báo trong recipe:
DEPENDS = "curl"Build lại sẽ qua. Khi có DEPENDS, BitBake build curl trước và đặt tệp header, thư viện của nó vào thư mục sysroot dành cho gói của bạn.
Bạn không cần khai báo thêm phụ thuộc lúc chạy cho libcurl. Yocto tự phát hiện những thư viện dùng chung mà chương trình liên kết và thêm vào phụ thuộc lúc chạy của gói.
1.2. Phụ thuộc lúc chạy: RDEPENDS
Cài image và chạy chương trình thứ ba trong QEMU:
root@qemux86-64:~# /usr/bin/hello_run_time_depen
Init curl OK
In file hello_run_time_depen.c
sh: bash: not foundThông báo bash: not found cho thấy chương trình gọi bash nhưng image không có bash. Yocto không thể tự đoán lệnh nào được gọi qua system(), nên bạn phải khai báo bằng tay:
RDEPENDS:${PN} = "bash"Từ bản Honister, cú pháp là RDEPENDS:${PN} (dấu hai chấm). Cú pháp cũ RDEPENDS_${PN} không còn đúng trên scarthgap. Build lại image, kết quả:
root@qemux86-64:~# /usr/bin/hello_run_time_depen
Init curl OK
In file hello_run_time_depen.c
This is executed by bash at runtime!Nhớ rằng DEPENDS là chuyện của máy build, còn RDEPENDS là chuyện của thiết bị. Nhầm hai biến này là lỗi rất phổ biến.
2.Chia nhỏ gói (package splitting)
Ở ví dụ trên, cả ba chương trình đều được cài vào image. Trong thực tế đôi khi bạn chỉ cần một phần. Chia gói cho phép tách một recipe thành nhiều gói con để cài chọn lọc. Lợi ích: image nhỏ hơn, dễ cập nhật từng phần, dễ quản lý.

Dùng hai biến:
PACKAGES: danh sách các gói mà recipe tạo ra.FILES:<ten-goi>: các tệp thuộc về gói đó.
Recipe sau khi chia gói (đặt tên gói bằng dấu gạch ngang, vì tên gói trong Yocto không nên chứa dấu gạch dưới):
DESCRIPTION = "Simple program to simulate package dependencies and splitting"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
S = "${WORKDIR}"
SRC_URI = file://hello_depen.c \
file://hello_pack_split.c \
file://hello_run_time_depen.c \
file://Makefile
DEPENDS = "curl"
EXTRA_OEMAKE = "CC='${CC}' CFLAGS='${CFLAGS}'"
PACKAGES =+ "hello-depen hello-pack-split hello-run-time"
FILES:hello-depen = "${bindir}/hello_depen"
FILES:hello-pack-split = "${bindir}/hello_pack_split"
FILES:hello-run-time = "${bindir}/hello_run_time_depen"
RDEPENDS:hello-run-time = "bash"
do_compile() {
oe_runmake
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello_depen ${D}${bindir}/hello_depen
install -m 0755 hello_pack_split ${D}${bindir}/hello_pack_split
install -m 0755 hello_run_time_depen ${D}${bindir}/hello_run_time_depen
}Có hai điểm đáng chú ý:
- Toán tử
=+đặt các gói mới lên đầu danh sáchPACKAGES, trước gói chính${PN}. Nhờ vậy các tệp được gán cho gói con trước, không bị gói chính "nuốt" hết. RDEPENDSgiờ gắn riêng cho góihello-run-time, vì chỉ gói này cầnbash.
Cài chọn lọc vào image
Trong local.conf, thay vì cài cả recipe, chỉ cài gói con mong muốn:
IMAGE_INSTALL:append = " hello-run-time"Sau khi build image và chạy QEMU:
root@qemux86-64:~# ls -l /usr/bin/hello*
-rwxr-xr-x 1 root root 14456 /usr/bin/hello_run_time_depen
root@qemux86-64:~# /usr/bin/hello_run_time_depen
Init curl OK
In file hello_run_time_depen.c
This is executed by bash at runtime!Trên thiết bị chỉ còn đúng chương trình bạn chọn, hai chương trình còn lại không được cài.
3.Kết luận
DEPENDSkhai báo thứ cần cho lúc build,RDEPENDSkhai báo thứ cần cho lúc chạy.PACKAGESvàFILESgiúp chia một recipe thành nhiều gói con để cài chọn lọc.- Với bản scarthgap, nhớ dùng cú pháp mới có dấu hai chấm:
RDEPENDS:,FILES:,:append.
Bài tiếp theo sẽ nói về cách tạo SDK, bộ công cụ giúp bạn biên dịch chéo ứng dụng ngay trên máy host.
