建立編譯環境 (SDK for .ipk 封裝)

建立一個可以編譯 .ipk 封包的環境

在上一篇的編譯環境中,我們建立了一個 cross-compiler,可以把原生的 c code 編譯成執行檔,並在 OpenWRT 上執行。但是,這樣的環境終究不是一個標準的開發環境,無法產生可以透過 OPKG 管理的 .ipk 套件。於是,在這一篇文章中,就介紹如何建立 SDK 環境,以及產生 .ipk 的安裝套件。

建立 OpenWRT SDK

上一篇建立 cross-compile 環境時,說明 SDK 選項不要選擇。若是勾選該選項,則會產生一個開發環境的壓縮檔,以上一篇建立的環境 (18.06版本) 為例,會出現在 openwrt 目錄下:

bin/targets/ar71xx/generic/openwrt-sdk-ar71xx-generic_gcc-7.4.0_musl.Linux-x86_64.tar.xz

將此檔案移出並解壓縮,我們就可以得到一個完整的 SDK 開發環境,其指令如下:

cp bin/targets/ar71xx/generic/openwrt-sdk-ar71xx-generic_gcc-7.4.0_musl.Linux-x86_64.tar.xz ~/sdk
cd ~/sdk/
tar xvf openwrt-sdk-ar71xx-generic_gcc-7.4.0_musl.Linux-x86_64.tar.xz
mv openwrt-sdk-ar71xx-generic_gcc-7.4.0_musl.Linux-x86_64 openwrt

此時,我們建立一個開發環境,目錄為: ~/sdk/openwrt ,接著,我們轉寫一隻簡單的 helloworld.c,放在 ~/sdk/openwrt/package/hello/src 之下。

#include <stdio.h>
int main()
{
    printf("This is my hello word!\n");
    return 0;
}

在進行套件封裝之前,我們先在~/sdk/openwrt/package/hello/src寫一個 Makefile 來測試 helloworld.c 的執行

helloworld : helloworld.o
        $(CC) $(LDFLAGS) helloworld.o -o helloworld

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

clean :
        rm *.o helloworld

這裡要注意,在 Makefile 中,指令的開始要以 [tab] 開始。因此,直接複製上述文字,會執行失敗。必須手動把指令前的空白改成 [tab], 執行 make才會成功。

執行結果如下:

$ make
cc  -c helloworld.c
cc  helloworld.o -o helloworld
$ ./helloworld
This is my hello word!

編譯產生 .ipk 套件

接著,我們嘗試產生一個用以產生 .ipk 套件的 Makefile,此檔案格式和 OpenWRT 系統相關,我們直接參考 OpenWRT 上的範例,並修改一下 helloworld.c 檔案所在的位置。此 Makefile 放置於 ~/sdk/openwrt/package/hello

# 注意: 每行開始空格較長的為[tab]
include $(TOPDIR)/rules.mk

# Name, version and release number
# The name and version of your package are used to define the variable to point to the build directory of your package: $(PKG_BUILD_DIR)
PKG_NAME:=helloworld
PKG_VERSION:=1.0
PKG_RELEASE:=1

# Source settings (i.e. where to find the source codes)
# This is a custom variable, used below
SOURCE_DIR:=/home/ofwrt/sdk/openwrt/package/hello/src
# 寫入 helloworld.c 所在的資料夾

include $(INCLUDE_DIR)/package.mk

# Package definition; instructs on how and where our package will appear in the overall configuration menu ('make menuconfig')
define Package/helloworld
  SECTION:=examples
  CATEGORY:=Examples
  TITLE:=Hello, World!
endef
# 定義套件的屬性, 等一下 make menuconfig 時會用到

# Package description; a more verbose description on what our package does
define Package/helloworld/description
  A simple "Hello, world!" -application.
endef

# Package preparation instructions; create the build directory and copy the source code.
# The last command is necessary to ensure our preparation instructions remain compatible with the patching system.
define Build/Prepare
        mkdir -p $(PKG_BUILD_DIR)
        cp $(SOURCE_DIR)/* $(PKG_BUILD_DIR)
        $(Build/Patch)
endef

# Package build instructions; invoke the target-specific compiler to first compile the source file, and then to link the file into the final executable
define Build/Compile
        $(TARGET_CC) $(TARGET_CFLAGS) -o $(PKG_BUILD_DIR)/helloworld.o -c $(PKG_BUILD_DIR)/helloworld.c
        $(TARGET_CC) $(TARGET_LDFLAGS) -o $(PKG_BUILD_DIR)/$1 $(PKG_BUILD_DIR)/helloworld.o
endef
# 編譯後的檔案名稱, 可以隨著不同專案改變

# Package install instructions; create a directory inside the package to hold our executable, and then copy the executable we built previously into the folder
define Package/helloworld/install
        $(INSTALL_DIR) $(1)/usr/bin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/helloworld $(1)/usr/bin
endef
# 設定透過 opkg 要安裝的位置

# This command is always the last, it uses the definitions and variables we give above in order to get the job done
$(eval $(call BuildPackage,helloworld))

Makefile 的格式似乎會隨 OpenWRT 版本而變? 在網路上找了一些其他人的樣板,都無法順利完成,最後找到官方的樣板才解決此問題。如果用其他版本的 OpenWRT 開發可能要注意一下。

此時,整體資料結構為:

package
|-- hello
|   |-- Makefile (正在編輯的檔案, 用以 make .ipk 檔案)
|   `-- src
|       |-- helloworld.c
|       `-- Makefile (用以單機測試的檔案)
`-- Makefile (OpenWRT SDK )

接著,透過make menuconfig來選擇要編譯的目標:

完成後,儲存退出,接著執行: make package/hello/compile V=99 ,以下為執行結果:

$ make package/hello/compile V=99
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'r8169-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'e100-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'bnx2-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'ar3k-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'mwifiex-sdio-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'kmod-phy-bcm-ns-usb2', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'edgeport-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'kmod-phy-bcm-ns-usb3', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'amdgpu-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'radeon-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'prism54-firmware', which does not exist
WARNING: Makefile 'package/linux/Makefile' has a dependency on 'rtl8192su-firmware', which does not exist
tmp/.config-package.in:36:warning: ignoring type redefinition of 'PACKAGE_libc' from 'boolean' to 'tristate'
tmp/.config-package.in:64:warning: ignoring type redefinition of 'PACKAGE_libgcc' from 'boolean' to 'tristate'
tmp/.config-package.in:149:warning: ignoring type redefinition of 'PACKAGE_libpthread' from 'boolean' to 'tristate'
#
# configuration written to .config
#
make[1]: Entering directory '/home/ofwrt/sdk/openwrt'
make[2]: Entering directory '/home/ofwrt/sdk/openwrt/package/toolchain'
echo "libc" >> /home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/pkginfo/toolchain.default.install
echo "libgcc" >> /home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/pkginfo/toolchain.default.install
echo "libpthread" >> /home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/pkginfo/toolchain.default.install
touch -r /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/toolchain/.built /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/toolchain/.autoremove 2>/dev/null >/dev/null
find /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/toolchain -mindepth 1 -maxdepth 1 -not '(' -type f -and -name '.*' -and -size 0 ')' -and -not -name '.pkgdir' | xargs -r rm -rf
make[2]: Leaving directory '/home/ofwrt/sdk/openwrt/package/toolchain'
time: package/toolchain/compile#2.17#2.28#8.78
make[2]: Entering directory '/home/ofwrt/sdk/openwrt/package/hello'
touch /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.prepared_e3da17400d4db55e49688913f00d5372_18f1e190c5d53547fed41a3eaa76e9e9_check
mkdir -p /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0
cp /home/ofwrt/sdk/openwrt/package/hello/src/* /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0
touch /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.prepared_e3da17400d4db55e49688913f00d5372_18f1e190c5d53547fed41a3eaa76e9e9
rm -f /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.configured_*
rm -f /home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/stamp/.hello_installed
(cd /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/./; if [ -x ./configure ]; then find /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ -name config.guess | xargs -r chmod u+w; find /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ -name config.guess | xargs -r -n1 cp --remove-destination /home/ofwrt/sdk/openwrt/scripts/config.guess; find /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ -name config.sub | xargs -r chmod u+w; find /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ -name config.sub | xargs -r -n1 cp --remove-destination /home/ofwrt/sdk/openwrt/scripts/config.sub; AR="mips-openwrt-linux-musl-gcc-ar" AS="mips-openwrt-linux-musl-gcc -c -Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -iremap/home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0:helloworld-1.0 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro" LD=mips-openwrt-linux-musl-ld NM="mips-openwrt-linux-musl-gcc-nm" CC="mips-openwrt-linux-musl-gcc" GCC="mips-openwrt-linux-musl-gcc" CXX="mips-openwrt-linux-musl-g++" RANLIB="mips-openwrt-linux-musl-gcc-ranlib" STRIP=mips-openwrt-linux-musl-strip OBJCOPY=mips-openwrt-linux-musl-objcopy OBJDUMP=mips-openwrt-linux-musl-objdump SIZE=mips-openwrt-linux-musl-size CFLAGS="-Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -mips16 -minterlink-mips16 -iremap/home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0:helloworld-1.0 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro " CXXFLAGS="-Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -mips16 -minterlink-mips16 -iremap/home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0:helloworld-1.0 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro " CPPFLAGS="-I/home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/usr/include -I/home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/include -I/home/ofwrt/sdk/openwrt/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/usr/include -I/home/ofwrt/sdk/openwrt/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/include/fortify -I/home/ofwrt/sdk/openwrt/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/include " LDFLAGS="-L/home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/usr/lib -L/home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/lib -L/home/ofwrt/sdk/openwrt/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/usr/lib -L/home/ofwrt/sdk/openwrt/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/lib -znow -zrelro "   ./configure --target=mips-openwrt-linux --host=mips-openwrt-linux --build=x86_64-pc-linux-gnu --program-prefix="" --program-suffix="" --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/lib --sysconfdir=/etc --datadir=/usr/share --localstatedir=/var --mandir=/usr/man --infodir=/usr/info --disable-nls  ; fi; )
touch /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.configured_68b329da9893e34099c7d8ad5cb9c940
rm -f /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.built
touch /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.built_check
mips-openwrt-linux-musl-gcc -Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -mips16 -minterlink-mips16 -iremap/home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0:helloworld-1.0 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro -o /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/helloworld.o -c /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/helloworld.c
mips-openwrt-linux-musl-gcc -L/home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/usr/lib -L/home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/lib -L/home/ofwrt/sdk/openwrt/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/usr/lib -L/home/ofwrt/sdk/openwrt/staging_dir/toolchain-mips_24kc_gcc-7.4.0_musl/lib -znow -zrelro -o /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/helloworld /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/helloworld.o
touch /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.built
rm -rf /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.pkgdir/helloworld.installed /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.pkgdir/helloworld
mkdir -p /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.pkgdir/helloworld
install -d -m0755 /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.pkgdir/helloworld/usr/bin
install -m0755 /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/helloworld /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.pkgdir/helloworld/usr/bin
touch /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.pkgdir/helloworld.installed
mkdir -p /home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/root-ar71xx/stamp
SHELL= flock /home/ofwrt/sdk/openwrt/tmp/.root-copy.flock -c 'cp -fpR /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.pkgdir/helloworld/. /home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/root-ar71xx/'
touch /home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/root-ar71xx/stamp/.helloworld_installed
removed '/home/ofwrt/sdk/openwrt/bin/packages/mips_24kc/base/helloworld_1.0-1_mips_24kc.ipk'
mkdir -p /home/ofwrt/sdk/openwrt/bin/targets/ar71xx/generic/packages /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld/CONTROL /home/ofwrt/sdk/openwrt/staging_dir/target-mips_24kc_musl/pkginfo
install -d -m0755 /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld/usr/bin
install -m0755 /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/helloworld /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld/usr/bin
find /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| xargs -r rm -rf
export CROSS="mips-openwrt-linux-musl-"  NO_RENAME=1 ; NM="mips-openwrt-linux-musl-nm" STRIP="/home/ofwrt/sdk/openwrt/staging_dir/host/bin/sstrip" STRIP_KMOD="/home/ofwrt/sdk/openwrt/scripts/strip-kmod.sh" PATCHELF="/home/ofwrt/sdk/openwrt/staging_dir/host/bin/patchelf" /home/ofwrt/sdk/openwrt/scripts/rstrip.sh /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld
rstrip.sh: /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld/usr/bin/helloworld: executable
(cd /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld/CONTROL; ( echo "$CONTROL"; printf "Description: "; echo "$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; ) > control; chmod 644 control; ( echo "#!/bin/sh"; echo "[ \"\${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; echo "[ -x "\${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; echo ". \${IPKG_INSTROOT}/lib/functions.sh"; echo "default_postinst \$0 \$@"; ) > postinst; ( echo "#!/bin/sh"; echo "[ -x "\${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; echo ". \${IPKG_INSTROOT}/lib/functions.sh"; echo "default_prerm \$0 \$@"; ) > prerm; chmod 0755 postinst prerm;  )
install -d -m0755 /home/ofwrt/sdk/openwrt/bin/packages/mips_24kc/base
/home/ofwrt/sdk/openwrt/scripts/ipkg-build -c -o 0 -g 0 /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld /home/ofwrt/sdk/openwrt/bin/packages/mips_24kc/base
Packaged contents of /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/ipkg-mips_24kc/helloworld into /home/ofwrt/sdk/openwrt/bin/packages/mips_24kc/base/helloworld_1.0-1_mips_24kc.ipk
touch -r /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.built /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0/.autoremove 2>/dev/null >/dev/null
find /home/ofwrt/sdk/openwrt/build_dir/target-mips_24kc_musl/helloworld-1.0 -mindepth 1 -maxdepth 1 -not '(' -type f -and -name '.*' -and -size 0 ')' -and -not -name '.pkgdir' | xargs -r rm -rf
make[2]: Leaving directory '/home/ofwrt/sdk/openwrt/package/hello'
time: package/hello/compile#2.05#1.96#7.92
make[1]: Leaving directory '/home/ofwrt/sdk/openwrt'

編譯完成後的檔案在/sdk/openwrt/bin/packages/mips_24kc/base目錄下,我們可以看到以下的 .ipk 檔案:

ofwrt@ofwrt:~/sdk/openwrt/bin/packages/mips_24kc/base$ ls
helloworld_1.0-1_mips_24kc.ipk

在 OpenWRT 上安裝 .ipk 套件

透過 OPKG 指令,我們可以在 OpenWRT 上安裝並執行所開發的 helloworld 套件,指令如下:

# opkg install ~/helloworld_1.0-1_mips_24kc.ipk
Installing helloworld (1.0-1) to root...
Configuring helloworld.
# helloworld
This is my hello word!

同樣的,原本 OPKG 支援的指令,也可以用來查詢套件的細節:

# opkg info helloworld
Package: helloworld
Version: 1.0-1
Depends: libc
Status: install user installed
Architecture: mips_24kc
Installed-Time: 1553342567

參考資料:

Last updated