Zalo Inbox

Package dependencies và splitting trong Yocto


  • author-image

    devlinux

  • blog-tag yocto
  • blog-comment 0 Bình luận
  • blog-comment 121 Views
  • created-date 10 Feb, 2025
blog-image


Trong bài viết này, chúng ta sẽ cùng khám phá hai khái niệm quan trọng trong Yocto: Package Dependencies (phụ thuộc của gói) và Package Splitting (phân chia gói). Đây là những khái niệm cốt lõi giúp tối ưu hóa hiệu suất và tăng tính linh hoạt khi xây dựng hệ thống nhúng hoặc phân phối Linux.

Khi làm việc với Yocto, việc quản lý các gói phụ thuộc và phân chia gói đóng vai trò thiết yếu trong việc giảm dung lượng, cải thiện hiệu quả, và nâng cao khả năng bảo trì hệ thống.

1. Package dependencies

Package Dependencies là các yêu cầu cần thiết để một gói hoạt động bình thường. Nếu một gói cần thư viện hoặc các gói khác, Yocto sẽ đảm bảo các yêu cầu này được thiết lập trước (hoặc sau) khi build package. Trong Yocto, có hai loại phụ thuộc chính:

  • Build time dependencies: Các phụ thuộc cần thiết trong quá trình build.
  • Run time dependencies: Các phụ thuộc cần thiết khi package đang chạy.

1.1. Build time dependencies

Tại thời điểm build, một số thư viện hoặc package khác là cần thiết để đảm bảo quá trình build diễn ra thành công. Trong Yocto, biến DEPENDS được sử dụng để chỉ định các gói hoặc thư viện cần thiết cho quá trình này.

1.1.1. Ví dụ

Trong bài trước, chúng ta đã tạo một layer "helloworld" đơn giản trong Yocto. Hôm nay, chúng ta sẽ thêm một recipe mới có tên demo-dependencies vào layer này để làm ví dụ.

Thiết lập môi trường:

~/yocto/poky$ source oe-init-build-env

1.1.2. Tạo mã nguồn và file recipe

Tạo thư mục chứa recipe demo-dependencies trong meta-devlinux/recipes-apps, sau đó tạo thêm file mã nguồn và recipe theo cấu trúc như sau:

~/yocto/poky/meta-devlinux/recipes-apps$ mkdir demo-dependencies
~/yocto/poky/meta-devlinux/recipes-apps/demo-dependencies$ tree
.
├── files
│   ├── depend_buildtime.c
│   ├── depend_runtime.c
│   ├── package_split.c
│   └── Makefile
└── demo-dependencies.bb

Source code và makefile của demo như sau :

1.1.2.1. depend_buildtime.c

#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
    CURL *curl;
    CURLcode res;

    printf("Devlinux\n");
    curl = curl_easy_init();
    if (curl) {
        printf("Build dependency check: OK\n");
        curl_easy_cleanup(curl);
    }

    return 0;
}

1.1.2.2. depend_runtime.c

#include <stdio.h>

int main(void)
{
    printf("Devlinux\n");
    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;
}

1.1.2.3. package_split.c

#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
    printf("Devlinux: package splitting!\n");
    return 0;
}

1.1.2.4. Makefile.c

CC = gcc
CFLAGS = -Wall

all: depend_buildtime depend_runtime package_split

depend_buildtime: depend_buildtime.c
        $(CC) depend_buildtime.c $(CFLAGS) -lcurl -o depend_buildtime

depend_runtime: depend_runtime.c
        $(CC) depend_runtime.c $(CFLAGS) -o depend_runtime

package_split: package_split.c
        $(CC) package_split.c  $(CFLAGS) -o package_split

clean:
        rm -rf depend_buildtime package_split depend_runtime

1.1.2.5. demo-dependencies.bb

DESCRIPTION = "Demo program showcasing package dependencies and splitting"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

S = "${WORKDIR}"
SRC_URI = "file://depend_buildtime.c \
           file://depend_runtime.c \
           file://package_split.c \
           file://Makefile"

# DEPENDS = "curl"

EXTRA_OEMAKE = "CC='${CC}' CFLAGS='${CFLAGS} -Wl,--hash-style=gnu'"

do_compile() {
    oe_runmake
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 depend_buildtime ${D}${bindir}/depend_buildtime
    install -m 0755 depend_runtime ${D}${bindir}/depend_runtime
    install -m 0755 package_split ${D}${bindir}/package_split
}

1.1.3. Build recipe

Sau khi hoàn tất, chạy lệnh sau để build recipe:

~/yocto/poky$ bitbake demo-dependencies

Vì thiếu thư viện curl, bạn sẽ gặp lỗi:

| depend_buildtime.c:2:10: fatal error: curl/curl.h: No such file or directory
|     2 | #include <curl/curl.h>
|       |          ^~~~~~~~~~~~~

Khi build recipe chúng ta sẽ nhận được một thông báo lỗi không tìm thấy file curl.h. Bởi vì trong mã nguồn chúng ta có dùng thư viện đó, Makefile có chỉ định nhưng Yocto chưa thiết lập thư viện đó trước khi thực hiện task do_compile() nên dẫn đên lỗi. Để sửa lỗi này, uncomment dòng DEPENDS = "curl" trong file .bb rồi build lại.

Khi build thành công bạn sẽ nhận được thông điệp như sau:

NOTE: Tasks Summary: Attempted 3353 tasks of which 3328 didn't need to be rerun and all succeeded.

1.1.4. Thêm libcurl vào file local.conf

Mở file local.conf và thêm dòng sau:

IMAGE_INSTALL_append = " libcurl"

Dòng này sẽ đảm bảo rằng thư viện libcurl được tích hợp vào image của bạn.

1.1.5. Build Image, chạy ứng dụng

Sau khi cập nhật file cấu hình, bạn cần build lại image:

~/yocto/poky$ bitbake core-image-minimal

Sau khi quá trình xây dựng hoàn tất, bạn có thể bắt đầu chạy máy ảo QEMU.

~/yocto/poky$ runqemu qemux86-64 nographic

Dùng lệnh modify để đưa source code của recipe vào workspace:

~/yocto/poky/build$ devtool modify demo-dependencies

Thực hiện build bằng lệnh:

~/yocto/poky/build$ devtool build demo-dependencies

Từ máy local, sử dụng lệnh devtool deploy-target để triển khai recipe demo-dependencies lên QEMU. Giả sử địa chỉ IP của QEMU là 192.168.7.2, chạy lệnh sau:

~/yocto/poky/build$ devtool deploy-target demo-dependencies root@192.168.7.2

Chạy chương trình test:

root@qemux86-64:~# depend_buildtime
Devlinux
Build dependency check: OK
root@qemux86-64:~# depend_runtime
Devlinux
sh: bash: not found
root@qemux86-64:~# package_split
Devlinux: package splitting!

Kết quả:

  • depend_buildtime: Chạy thành công, không có lỗi.
  • depend_runtime: Chạy bị lỗi vì không tìm thấy bash.
  • package_split: Chạy thành công.

1.2. Run time dependencies

Run time dependencies là các gói, thư viện hoặc công cụ cần thiết để một chương trình có thể hoạt động khi đang chạy.

Trong Yocto, biến RDEPENDS_${PN} được sử dụng để khai báo các phụ thuộc này. Khi bạn khai báo RDEPENDS_${PN}, Yocto sẽ tự động thêm các gói được liệt kê vào hệ thống runtime (image) để đảm bảo chương trình hoạt động đúng.

1.2.1. Ví dụ

Lỗi xảy ra với ứng dụng depend_runtime ở trên là do runtime dependency (phụ thuộc lúc chạy) của nó không được khai báo trong file .bb. Cụ thể, chương trình này sử dụng lệnh bash thông qua hàm system() trong file mã nguồn depend_runtime.c, nhưng hệ thống không có gói bash được cài đặt, dẫn đến lỗi "sh: bash: not found".

Để khắc phục lỗi này, bạn cần khai báo phụ thuộc runtime trong file .bb bằng cách sử dụng biến RDEPENDS_${PN}.

RDEPENDS_${PN} = "bash"

Mở file local.conf và thêm dòng sau:

IMAGE_INSTALL_append = " bash"

Sau khi cập nhật file cấu hình, bạn cần build lại image:

~/yocto/poky$ bitbake core-image-minimal

Điều này sẽ yêu cầu Yocto thêm gói bash vào image khi build, đảm bảo rằng chương trình có thể chạy mà không gặp lỗi.

Thực hiện lại các bước build và test như trên, khi chạy ứng dụng depend_runtime ta sẽ thu được kết quả:

root@qemux86-64:~# depend_runtime
Devlinux
This is executed by bash at runtime!

2. Package splitting

Trong quá trình phát triển hệ thống nhúng với Yocto, đôi khi không cần thiết phải cài đặt toàn bộ nội dung của một package vào image. Điều này đặc biệt hữu ích trong việc tối ưu hóa dung lượng, giảm tải cho hệ thống, hoặc phân tách các phần của ứng dụng để dễ dàng quản lý, cập nhật. Package Splitting chính là kỹ thuật giúp chia một package gốc thành các package con, từ đó kiểm soát tốt hơn những gì sẽ được tích hợp vào hệ thống.

Yocto cung cấp các biến PACKAGES và FILES để hỗ trợ chia nhỏ package. Dưới đây là cách thực hiện thông qua ví dụ.

2.1. Cấu hình chia package trong file recipe

DESCRIPTION = "Demo program showcasing package dependencies and splitting"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

S = "${WORKDIR}"
SRC_URI = "file://depend_buildtime.c \
           file://depend_runtime.c \
           file://package_split.c \
           file://Makefile"

DEPENDS = "curl"
RDEPENDS_${PN} = "bash"
RDEPENDS_package-split = "bash"

EXTRA_OEMAKE = "CC='${CC}' CFLAGS='${CFLAGS} -Wl,--hash-style=gnu'"

# Khai báo các package con
PACKAGES =+ "demo-tools package-split"

# Gộp depend_buildtime và depend_runtime vào package demo-tools
FILES_demo-tools = "${bindir}/depend_buildtime \
                    ${bindir}/depend_runtime"

# Khai báo package riêng cho package_split
FILES_package-split = "${bindir}/package_split"

do_compile() {
    oe_runmake
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 depend_buildtime ${D}${bindir}/depend_buildtime
    install -m 0755 depend_runtime ${D}${bindir}/depend_runtime
    install -m 0755 package_split ${D}${bindir}/package_split
}

2.2 Giải thích thay đổi

Gộp các file vào cùng một package con:

  • Biến FILES_demo-tools được sử dụng để khai báo cả hai ứng dụng depend-buildtimedepend-runtime.
  • Điều này đảm bảo rằng khi package demo-tools được cài đặt, cả hai ứng dụng sẽ được tích hợp vào hệ thống.

Tách riêng file package-split:

  • Biến FILES_package-split vẫn giữ nguyên để chỉ định ứng dụng package-split thuộc package riêng biệt.

2.3. Cài đặt package demo-tools

Để chỉ cài đặt package demo-tools, bạn cần cập nhật file local.conf như sau:

IMAGE_INSTALL_append = " demo-tools"

Sau khi cập nhật cấu hình, chạy lệnh:

bitbake core-image-minimal

Sau khi image được build thành công, chạy máy ảo QEMU và kiểm tra:

runqemu qemux86-64 nographic

Chạy thử chương trình:

root@qemux86-64:~# depend_buildtime
Devlinux
Build dependency check: OK
root@qemux86-64:~# depend_runtime
Devlinux
This is executed by bash at runtime!
root@qemux86-64:~# package_split
-sh: package_split: command not found

3. Kết luận

Qua bài viết này, chúng ta đã tìm hiểu hai khái niệm quan trọng trong Yocto: Package Dependencies (phụ thuộc của gói) và Package Splitting (phân chia gói). Đây là những công cụ mạnh mẽ giúp tối ưu hóa hệ thống nhúng, tăng tính linh hoạt trong quản lý và giảm dung lượng của image.

  • Package Dependencies đảm bảo các gói hoặc thư viện cần thiết được tích hợp đầy đủ trong quá trình build và runtime. Việc sử dụng đúng các biến như DEPENDS và RDEPENDS giúp hệ thống hoạt động ổn định và tránh các lỗi không mong muốn.
  • Package Splitting cho phép chia nhỏ một package thành các phần riêng biệt, giúp kiểm soát tốt hơn những gì được cài đặt vào hệ thống. Điều này đặc biệt hữu ích khi cần tối ưu hóa dung lượng hoặc quản lý các phần của ứng dụng một cách độc lập.
author_photo
devlinux

0 Bình luận