OpenWrt: Add Static Library =========================== This document describes how to create a custom static library (``.a``) in the OpenWrt build system and how to build a separate application that links against that library using the OpenWrt cross-compilation toolchain. In this section, you are going to learn .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to create and package a static library in OpenWrt? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to export static library headers and archives to both staging_dir and rootfs? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to build a separate application that links against the static library using the OpenWrt toolchain? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow How to install and run the resulting application inside an OpenWrt VM? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Topics in this section, * :ref:`Step 1: Static Library Directory Layout ` * :ref:`Step 1.1: Technical Notes ` * :ref:`Step 2: Static Library Package Makefile ` * :ref:`Step 2.1: Technical Notes ` * :ref:`Step 3: Build the Static Library ` * :ref:`Step 3.1: Technical Notes ` * :ref:`Step 4: Test Application Directory Layout ` * :ref:`Step 4.1: Technical Notes ` * :ref:`Step 5: Test Application Makefile ` * :ref:`Step 5.1: Technical Notes ` * :ref:`Step 6: Build the Test Application ` * :ref:`Step 6.1: Technical Notes ` * :ref:`Step 7: Install Packages on OpenWrt VM ` * :ref:`Step 7.1: Technical Notes ` * :ref:`Step 8: Run the Test Application ` .. _staticlib_step1: .. _staticlib_step1_1: .. tab-set:: .. tab-item:: Step 1: Static Library Directory Layout OpenWrt packages must follow OpenWrt’s standard build workflow. A static library must: - build with OpenWrt’s cross-toolchain - export both header file and ``.a`` archive into the staging directory - install artifacts into the root filesystem package output Example layout: :: openwrt/ └── package/ └── custom-staticlib/ ├── Makefile └── src/ ├── custommath.c └── custommath.h **custommath.c** .. code-block:: c int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } **custommath.h** .. code-block:: c int add(int a, int b); int sub(int a, int b); .. tab-item:: Step 1.1: Technical Notes - ``custommath.c`` provides implementation logic compiled into an object file. - ``custommath.h`` exposes the interface for downstream users. - Files must be copied to ``$(PKG_BUILD_DIR)`` to follow OpenWrt’s clean build model. - Building in ``$(PKG_BUILD_DIR)`` avoids modifying the original ``src/`` tree. .. _staticlib_step2: .. _staticlib_step2_1: .. tab-set:: .. tab-item:: Step 2: Static Library Package Makefile ``package/custom-staticlib/Makefile`` .. code-block:: make include $(TOPDIR)/rules.mk PKG_NAME:=custom-staticlib PKG_RELEASE:=1 include $(INCLUDE_DIR)/package.mk PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) define Package/custom-staticlib SECTION:=libs CATEGORY:=Libraries TITLE:=Custom sample static library endef define Package/custom-staticlib/description A simple custom static library exporting add/sub functions. endef define Build/Prepare mkdir -p $(PKG_BUILD_DIR) $(CP) ./src/* $(PKG_BUILD_DIR)/ endef define Build/Compile $(TARGET_CC) $(TARGET_CFLAGS) -c \ $(PKG_BUILD_DIR)/custommath.c \ -o $(PKG_BUILD_DIR)/custommath.o $(AR) rcs $(PKG_BUILD_DIR)/libcustommath.a \ $(PKG_BUILD_DIR)/custommath.o endef define Package/custom-staticlib/install $(INSTALL_DIR) $(1)/usr/lib $(INSTALL_DATA) $(PKG_BUILD_DIR)/libcustommath.a $(1)/usr/lib/ $(INSTALL_DIR) $(1)/usr/include/custommath $(INSTALL_DATA) $(PKG_BUILD_DIR)/custommath.h $(1)/usr/include/custommath/ endef define Build/InstallDev $(INSTALL_DIR) $(1)/usr/lib $(INSTALL_DATA) $(PKG_BUILD_DIR)/libcustommath.a $(1)/usr/lib/ $(INSTALL_DIR) $(1)/usr/include/custommath $(INSTALL_DATA) $(PKG_BUILD_DIR)/custommath.h $(1)/usr/include/custommath/ endef $(eval $(call BuildPackage,custom-staticlib)) .. tab-item:: Step 2.1: Technical Notes **define Package/custom-staticlib** - Defines metadata for OpenWrt’s package manager and ``menuconfig``. - Affects how the library appears in configuration menus, not how it builds. **define Package/custom-staticlib/description** - Provides help text in ``menuconfig``. - Useful for documenting library purpose and behavior. **define Build/Prepare** - Creates the private build directory used for all compilation. - Copies source code into the build directory. - This isolation is critical for reproducible builds. **define Build/Compile** - ``$(TARGET_CC)`` ensures cross-compilation for the target CPU. - Produces ``custommath.o``. - ``$(AR) rcs`` archives the object file into the static library ``libcustommath.a``. **define Package/custom-staticlib/install** - Installs deliverables into the final root filesystem: - ``/usr/lib/libcustommath.a`` - ``/usr/include/custommath/custommath.h`` - These files appear on the target OpenWrt device after installation. **define Build/InstallDev** - Installs files into the staging directory, used **only at build time** by other packages: - ``staging_dir/.../usr/include/custommath/custommath.h`` - ``staging_dir/.../usr/lib/libcustommath.a`` - This enables dependent packages to compile successfully. .. _staticlib_step3: .. _staticlib_step3_1: .. tab-set:: .. tab-item:: Step 3: Build the Static Library Run: .. code-block:: bash make package/custom-staticlib/{clean,compile} V=sc The resulting package appears in: :: bin/packages/x86_64/base/custom-staticlib-1.apk .. tab-item:: Step 3.1: Technical Notes - ``clean`` deletes the build directory, forcing a full rebuild. - ``compile`` automatically triggers: - **Prepare** → copy sources - **Compile** → cross-compile and archive - **InstallDev** → populate ``staging_dir`` for downstream builds - **Install** → populate the target root filesystem layout - The produced ``.apk`` includes only what ``Package/custom-staticlib/install`` defines. - The staging_dir version of this library is not in the ``.apk`` but is essential for the next step (compiling the test application). .. _staticlib_step4: .. _staticlib_step4_1: .. tab-set:: .. tab-item:: Step 4: Test Application Directory Layout This package demonstrates linking against the previously built custom static library. It verifies: - header export - library export - cross-linking - correct execution Layout: :: package/ └── custom-staticlib-test/ ├── Makefile └── src/ └── test.c ``test.c`` .. code-block:: c #include "custommath.h" #include int main() { int result = add(3, 5); printf("Result from static library: %d\n", result); return 0; } .. tab-item:: Step 4.1: Technical Notes - Including ``custommath.h`` confirms header export to ``staging_dir``. - Final binary will statically pull ``add`` (and possibly ``sub``) from ``libcustommath.a``. - No shared libraries are needed at runtime; all code is embedded. .. _staticlib_step5: .. _staticlib_step5_1: .. tab-set:: .. tab-item:: Step 5: Test Application Makefile ``package/custom-staticlib-test/Makefile`` .. code-block:: make include $(TOPDIR)/rules.mk PKG_NAME:=custom-staticlib-test PKG_RELEASE:=1 include $(INCLUDE_DIR)/package.mk PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) define Package/custom-staticlib-test SECTION:=utils CATEGORY:=Utilities TITLE:=Test app using custom static library DEPENDS:=+custom-staticlib endef define Package/custom-staticlib-test/description A simple test program that links against custom-staticlib. endef define Build/Prepare mkdir -p $(PKG_BUILD_DIR) $(CP) ./src $(PKG_BUILD_DIR)/ endef define Build/Compile $(TARGET_CC) \ $(TARGET_CFLAGS) \ -I$(STAGING_DIR)/usr/include \ -I$(STAGING_DIR)/usr/include/custommath \ -L$(STAGING_DIR)/usr/lib \ -o $(PKG_BUILD_DIR)/customtest \ $(PKG_BUILD_DIR)/src/test.c \ -lcustommath endef define Package/custom-staticlib-test/install $(INSTALL_DIR) $(1)/usr/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/customtest $(1)/usr/bin/ endef $(eval $(call BuildPackage,custom-staticlib-test)) .. tab-item:: Step 5.1: Technical Notes - ``DEPENDS:=+custom-staticlib`` ensures the library builds first and is available in staging_dir. - ``-I$(STAGING_DIR)/usr/include`` and ``-I$(STAGING_DIR)/usr/include/custommath`` ensure headers are found. - ``-L$(STAGING_DIR)/usr/lib`` and ``-lcustommath`` resolve the static library at link time. - The resulting binary ``customtest`` is installed into ``/usr/bin`` on the target rootfs. .. _staticlib_step6: .. _staticlib_step6_1: .. tab-set:: .. tab-item:: Step 6: Build the Test Application Run: .. code-block:: bash make package/custom-staticlib-test/{clean,compile} V=sc Result: :: bin/packages/x86_64/base/custom-staticlib-test-1.apk .. tab-item:: Step 6.1: Technical Notes During this build: 1. **Header Resolution** ``$(STAGING_DIR)/usr/include/custommath/custommath.h`` provides the function declarations. The compiler checks API correctness. 2. **Library Resolution** ``$(STAGING_DIR)/usr/lib/libcustommath.a`` is passed via ``-lcustommath``. The linker extracts only needed object code. 3. **Static Linking** The resulting binary contains all required library code internally; no dynamic libraries are needed at runtime. 4. **RootFS Packaging** The APK contains only: - ``/usr/bin/customtest`` The static library is **not** included in the test APK. .. _staticlib_step7: .. _staticlib_step7_1: .. tab-set:: .. tab-item:: Step 7: Install Packages on OpenWrt VM Copy the packages into OpenWrt: .. code-block:: bash scp -P bin/packages/x86_64/base/custom-staticlib*.apk \ root@127.0.0.1:/tmp/ scp -P bin/packages/x86_64/base/custom-staticlib-test*.apk \ root@127.0.0.1:/tmp/ Install them: .. code-block:: bash apk add --allow-untrusted /tmp/custom-staticlib*.apk apk add --allow-untrusted /tmp/custom-staticlib-test*.apk .. tab-item:: Step 7.1: Technical Notes - Installing the static library first ensures headers and archive are present on the target if needed for local builds. - The test application installs into ``/usr/bin/customtest``. - ``--allow-untrusted`` bypasses signature checks and is expected for developer-built packages. .. _staticlib_step8: .. tab-set:: .. tab-item:: Step 8: Run the Test Application Execute: :: root@OpenWrt:~# customtest Result from static library: 8 This proves: - the static library compiled correctly - staging_dir export worked - the test package successfully linked against the library - the application executed correctly on OpenWrt