Skip to content

openwrt - makefile

概述

详细看英文原文

https://openwrt.org/docs/guide-developer/packages

https://openwrt.org/docs/guide-developer/package-policies

类似linux Kernel有自己的makefile体系,openwrt对于自己的package也有自己的编译脚本结构,这个是在openwrt植入自己的代码,或者将第三方的代码移植到openwrt的基础。

一个package下面一般有三个文件:

things remark
Makefile 这个文件是必须要的
这个Makefile指定了如下获取到源码,如果编译代码和安装文件
patches 这个是补丁所在目录,可选的
如果该package是第三方提供的,为了不影响原来的仓库,可以通过打补丁的方式来修复bug或者适配目标设备
files 这个文件是可选的,用来包含一些默认配置或者其他文件
src 不是必须的
有些package不是从第三方下载代码的,代码是直接从该目录下获取

Makefile

An OpenWrt source package Makefile contains a series of header variable assignments, action recipes and one or multiple OpenWrt specific signature footer lines identifying it as OpenWrt specific package Makefile.

The patches directory

The patches directory must be placed in the same parent directory as the Makefile file and may only contain patch files used to modify the source code being packaged.

Patch files must be in unified diff format and carry the extension .patch. The file names must also carry a numerical prefix to denote the order in which the patch files must be applied. Patch file names should be concise and avoid characters other than ASCII alphanumerics and hyphens.

Suitable patch file names could look like:

  • 000-patch-makefile.patch
  • 010-backport-frobnicate-crash-fix.patch
  • 999-add-local-hack-for-openwrt-compatibility.patch

It is recommended to use Quilt to manage source package patch collections.

The files directory

Static files accompanying a source package, such as OpenWrt specific init scripts or configuration files, must be placed inside a directory called files, residing within the same subdirectory as the Makefile. There are no strict rules on how such static files are to be named and organized within the files directory but by convention, the extension .conf is used for OpenWrt UCI configration files and the extension .init is used to denote OpenWrt specific init scripts.

The actual placement and naming of the resources within the files directory on the target system is controlled by the source package Makefile and unrelated to the structure and naming within the files directory.

The src directory

Some packages do not actually fetch their program code from an external source but bundle the code to be compiled and packages directly within the package feed. This is usually done for packages which are specific to OpenWrt and do not exist outside of their respective package feed.

Sometimes the src directory may also be used to supply additional code to the compilation process, in addition to the program code fetched from external sources.

If present, the OpenWrt build system will automatically copy the contents of the src directory verbatim to the compilation scratch directory (build directory) of the package, retaining the structure and naming of the files.

例子

helloworld

更详细直接看原文

https://openwrt.org/docs/guide-developer/helloworld/start

  • 在目录/home/buildbot/helloworld(或者其他目录)下创建一个helloworld.c文件, hellowold.c写入
#include <stdio.h>

int main(void)
{
    printf("\nHello, world!\n\n");
    return 0;
}

在linux下直接运行gcc命令,可以编译出在本地linux的可执行文件

gcc -c -o helloworld.o helloworld.c -Wall
gcc -o helloworld helloworld.o
  • 编写与openwrt兼容的Makefile

新建{openwrt根目录}/package/helloworld目录,在该目录新建Makefile文件

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/buildbot/helloworld

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

# 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

# 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))
  • 执行make menuconfig 可以在上面找到helloworld的配置,勾选helloworld package,保存并退出
make package/helloworld/compile V=s # 单独编译helloworld
make package/helloworld/{clean,compile} V=s # 先清除再重新编译

cmake-based package

更多示例,自行在openwrt根目录下搜索,找到该种类型的package

find package/ -type f -exec grep -inrH "cmake.mk" {} \; 2>/dev/null

下面以公司的主程序(pubmsg)进行展示。

pubmsg是cmake-based的package

如下所示,文件结构如下

{openwrt}/package/pubmsg
               |-----files
                      |-----pubmsg.conf
                      |-----pubmsg.init
               |-----src
                      |-----pubmsg.c
                      |-----CMakeLists.txt (pubmsg代码通过cmake来编译)
               |-----Config.in (make menuconfig中看到的选项)
               |-----Makefile (openwrt的Makefile)

CMakefiles.txt

该CMakefiles.txt会在openwrt的Makefile调用该cmake文件进行编译

cmake_minimum_required(VERSION 2.6)

PROJECT(pubmsg C)

INCLUDE(CheckFunctionExists)

FIND_PATH(uci_include_dir uci.h)
FIND_PATH(LIBEVENT_INCLUDE_DIR NAMES event.h)

FIND_LIBRARY(udev NAMES udev)
FIND_LIBRARY(evdev NAMES evdev)
FIND_LIBRARY(lzo2 NAMES lzo2)
FIND_LIBRARY(mosquitto NAMES mosquitto)
FIND_LIBRARY(urcu-cds NAMES urcu-cds)
FIND_LIBRARY(urcu-common NAMES urcu-common)
FIND_LIBRARY(urcu-memb NAMES urcu-memb)
FIND_LIBRARY(urcu NAMES urcu)
FIND_LIBRARY(curl NAMES curl)
FIND_LIBRARY(uci NAMES uci)
FIND_LIBRARY(mbedtls NAMES mbedtls)
FIND_LIBRARY(mbedx509 NAMES mbedx509)
FIND_LIBRARY(mbedcrypto NAMES mbedcrypto)
FIND_LIBRARY(ssl NAMES ssl)
FIND_LIBRARY(pthread NAMES pthread)
FIND_LIBRARY(ubox NAMES ubox)
FIND_LIBRARY(crypto NAMES crypto)
FIND_LIBRARY(event NAMES event)
FIND_LIBRARY(event_pthreads NAMES event_pthreads)

INCLUDE_DIRECTORIES(${uci_include_dir})
ADD_DEFINITIONS(-Os -Wall  --std=gnu99 -g3)
# 声明宏的默认值
OPTION(CONFIG_PUBMSG_G1_E_GRAPES "Yancy ESL PUBMSG_G1_E_GRAPES" 0)
OPTION(CONFIG_PUBMSG_G1_D_PRO_ADVANCED "Yancy ESL PUBMSG_G1_D_PRO_ADVANCED" 0)

# 该宏定义是从openwrt的Makefile文件传递进来的,需要在cmake中赋值定义才生效
add_compile_definitions(CONFIG_PUBMSG_G1_E_GRAPES=${CONFIG_PUBMSG_G1_E_GRAPES})
add_compile_definitions(CONFIG_PUBMSG_G1_D_PRO_ADVANCED=${CONFIG_PUBMSG_G1_D_PRO_ADVANCED})

message(STATUS "CONFIG_PUBMSG_G1_E_GRAPES = ${CONFIG_PUBMSG_G1_E_GRAPES}")
message(STATUS "CONFIG_PUBMSG_G1_D_PRO_ADVANCED = ${CONFIG_PUBMSG_G1_D_PRO_ADVANCED}")

set(CMAKE_VERBOSE_MAKEFILE ON)
aux_source_directory(. CURRENT_SRC)
aux_source_directory(./libdict/src DICT_SRC)
aux_source_directory(./libdfu DFU_SRC)
aux_source_directory(./libbase  BASE_SRC)
aux_source_directory(./libradio  RADIO_SRC)

message(STATUS "CURRENT_SRC = ${CURRENT_SRC}")
message(STATUS "DICT_SRC = ${DICT_SRC}")
message(STATUS "DFU_SRC = ${DFU_SRC}")
message(STATUS "BASE_SRC = ${BASE_SRC}")
message(STATUS "RADIO_SRC = ${RADIO_SRC}")

SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")

IF(APPLE)
  INCLUDE_DIRECTORIES(/opt/local/include)
  LINK_DIRECTORIES(/opt/local/lib)
ENDIF()

include_directories(./
./libdict/include
./libbase
./libdfu
./libradio
 )

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -std=gnu99")

# 重新定义__FILE__, 防止打印日志时,把绝对路径也打印出来,仅仅截取相对路径
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-builtin-macro-redefined -D'__FILE__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR})/,,$(abspath $<))\"'")


ADD_EXECUTABLE(${PROJECT_NAME} ${CURRENT_SRC} ${DFU_SRC} ${DICT_SRC} ${BASE_SRC} ${RADIO_SRC} )
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${urcu} ${pthread} ${ssl} ${lzo2} ${urcu-common} ${urcu-cds} ${urcu-memb} ${mbedtls} ${mbedx509} ${mbedcrypto}
${mosquitto} ${udev}  ${evdev} ${crypto} ${ubox} ${curl} ${uci} ${event} ${event_pthreads})

INSTALL(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION sbin)

Config.in

该文件需要在openwrt的Makefile中调用才会生效

该配置文件用于定义一些宏定义,用于配置该package的属性

if PACKAGE_pubmsg-g1-e-grapes

config PUBMSG_G1_E_GRAPES_DEBUG
        bool "Enable debug version build."
        default n
        help
           This option enables assert and gdb symbol.
           Disabled by default.

choice
        prompt "Firmware model selection"
        default PUBMSG_G1_E_GRAPES
        help
            Select the firmware model.

        config PUBMSG_G1_E_GRAPES
                bool "Yancy ESL g1-e-grapes"

        config PUBMSG_G1_D_PRO_ADVANCED
                bool "Yancy ESL g1-d-pro-advanced"

endchoice

endif

Makefile

#
# Copyright (C) 2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

include $(TOPDIR)/rules.mk

PKG_NAME:=pubmsg-g1-e-grapes

PKG_RELEASE:=1.0
PKG_LICENSE:=GPLv3
PKG_MAINTAINER:=Yancy

include $(INCLUDE_DIR)/package.mk
# 对于cmake-based的package,必须include cmake.mk,里面事先定义了一些函数
include $(INCLUDE_DIR)/cmake.mk

define Package/$(PKG_NAME)
  CATEGORY:=Thingoo
  DEPENDS:=+libmosquitto +liburcu  +libcurl +libuci  +libudev-fbsd +kmod-drv_regopt +liblzo +libevent2 +libevent2-pthreads
  TITLE:=Main program $(PKG_NAME)
  MENU:=1
  SUBMENU:=Grapes
endef

define Package/$(PKG_NAME)/description
  This package contains an main program to handle upstream and downstream
endef

# 调用上面提到的Config.in
define Package/$(PKG_NAME)/config
    source "$(SOURCE)/Config.in"
endef

define Build/Prepare
    mkdir -p $(PKG_BUILD_DIR)
    $(CP) ./src/*   $(PKG_BUILD_DIR)/
    $(CP) ./files/* $(PKG_BUILD_DIR)/
endef

TARGET_CFLAGS += $(if $(CONFIG_PUBMSG_G1_E_GRAPES_DEBUG), -ggdb3)
CMAKE_OPTIONS += $(if $(CONFIG_PUBMSG_G1_E_GRAPES_DEBUG),-DCMAKE_BUILD_TYPE=Debug)

# 如果在make menuconfig勾选相应的选项,追加到CMAKE_OPTIONS变量中
CMAKE_OPTIONS += $(if $(CONFIG_PUBMSG_G1_E_GRAPES),-DCONFIG_PUBMSG_G1_E_GRAPES=1)
CMAKE_OPTIONS += $(if $(CONFIG_PUBMSG_G1_D_PRO_ADVANCED),-DCONFIG_PUBMSG_G1_D_PRO_ADVANCED=1)

define Package/$(PKG_NAME)/install
    $(INSTALL_DIR) $(1)/usr/bin
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/pubmsg     $(1)/usr/bin
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/usr/bin/autopubmsg     $(1)/usr/bin
    $(INSTALL_DIR) $(1)/etc/init.d 
    $(INSTALL_BIN) $(PKG_BUILD_DIR)/etc/init.d/pubmsg $(1)/etc/init.d 
endef

define Package/$(PKG_NAME)/postinst
    #!/bin/sh  
        # check if we are on real system  
       if [ -z "$${IPKG_INSTROOT}" ]; then    
               echo "Enabling rc.d symlink for pubmsg" 
              chmod 777 /etc/init.d/pubmsg
             /etc/init.d/pubmsg enable  
       fi  
      exit 0
endef

define Package/$(PKG_NAME)/prerm
    #!/bin/sh  
        # check if we are on real system  
       if [ -z "$${IPKG_INSTROOT}" ]; then    
               echo "Removing rc.d symlink for pubMessage"    
                /etc/init.d/pubmsg disable  
        fi  
        exit 0 
endef


$(eval $(call BuildPackage,$(PKG_NAME)))

automake-based package

对于第三方package需要使用automake来编译的, 运行如下命令搜索,可以直接找到相关的例子

find package/ -type f -exec grep -inrH "CONFIGURE_ARGS" {} \; 2>/dev/null

比如: tcpdump的例子就比较典型

# vi package/network/utils/tcpdump/Makefile

#
# Copyright (C) 2007-2011 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

include $(TOPDIR)/rules.mk

PKG_NAME:=tcpdump
PKG_VERSION:=4.9.3
PKG_RELEASE:=3

PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=http://www.tcpdump.org/release/
PKG_HASH:=2cd47cb3d460b6ff75f4a9940f594317ad456cfbf2bd2c8e5151e16559db6410

PKG_BUILD_PARALLEL:=1

PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
PKG_LICENSE:=BSD-3-Clause
PKG_CPE_ID:=cpe:/a:tcpdump:tcpdump

PKG_INSTALL:=1

include $(INCLUDE_DIR)/package.mk

define Package/tcpdump/default
  SECTION:=net
  CATEGORY:=Network
  DEPENDS:=+libpcap
  TITLE:=Network monitoring and data acquisition tool
  URL:=http://www.tcpdump.org/
endef

define Package/tcpdump
  $(Package/tcpdump/default)
  VARIANT:=full
# 全功能版
endef

define Package/tcpdump-mini
  $(Package/tcpdump/default)
  TITLE+= (minimal version)
  VARIANT:=mini
# 阉割版
endef

# CONFIGURE_ARGS变量是运行.configure传递进去的参数,你需要添加什么参数,直接在这个变量追加即可
CONFIGURE_ARGS += \
        --without-cap-ng \
        --without-crypto

ifeq ($(CONFIG_IPV6),y)
# 如果选择配置了IPV6,则在编译时使能ipv6
CONFIGURE_ARGS += \
        --enable-ipv6
endif

TARGET_CFLAGS += -ffunction-sections -fdata-sections
TARGET_LDFLAGS += -Wl,--gc-sections

CONFIGURE_VARS += \
        BUILD_CC="$(TARGET_CC)" \
        HOSTCC="$(HOSTCC)" \
        td_cv_buggygetaddrinfo="no" \
        ac_cv_linux_vers=$(LINUX_VERSION) \
        ac_cv_header_rpc_rpcent_h=no \
        ac_cv_lib_rpc_main=no \
        ac_cv_path_PCAP_CONFIG=""

MAKE_FLAGS :=

ifeq ($(BUILD_VARIANT),mini)
# 对于阉割版追加的变量
  TARGET_CFLAGS += -DTCPDUMP_MINI
  CONFIGURE_ARGS += --disable-smb
  MAKE_FLAGS += TCPDUMP_MINI=1
endif

MAKE_FLAGS += \
        CCOPT="$(TARGET_CFLAGS)" INCLS="-I. $(TARGET_CPPFLAGS)"

define Package/tcpdump/install
        $(INSTALL_DIR) $(1)/usr/sbin
        $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/tcpdump $(1)/usr/sbin/
endef

Package/tcpdump-mini/install = $(Package/tcpdump/install)

$(eval $(call BuildPackage,tcpdump))
$(eval $(call BuildPackage,tcpdump-mini))

general package

普通使用原始Makefile的package

find package -type f -exec grep -inrH "CC=\"\$(TARGET_CC)\"" {} \; 2>/dev/null

以下以maccal为例子

#
# Copyright (C) 2011 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

include $(TOPDIR)/rules.mk

PKG_NAME:=maccalc
PKG_RELEASE:=1
PKG_LICENSE:=GPL-2.0

PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)

include $(INCLUDE_DIR)/package.mk

define Package/maccalc
  SECTION:=utils
  CATEGORY:=Utilities
  TITLE:=MAC address calculation
endef

define Package/maccalc/description
 This package contains a MAC address manipulation utility.
endef

define Build/Configure
endef

define Build/Compile
        $(MAKE) -C $(PKG_BUILD_DIR) \
                CC="$(TARGET_CC)" \
                CFLAGS="$(TARGET_CFLAGS) -Wall" \
                LDFLAGS="$(TARGET_LDFLAGS)"
endef

define Package/maccalc/install
        $(INSTALL_DIR) $(1)/usr/sbin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/maccalc $(1)/usr/sbin/
endef

$(eval $(call BuildPackage,maccalc))