Mais um artigo da série sobre o OpenWRT.
artigo anterior, apresentei o problema da compilação de um novo programa para o OpenWRT. Neste vamos colocar a mão na massa.
Você tem duas opções para montar o ambiente para gerar seus pacotes: recompilar todo o OpenWRT ou utilizar o SDK disponível no mesmo local onde estão as firmwares. O primeiro caso vai utilizar mais espaço e demorar mais mas traz a vantagem de isolar problemas do seu ambiente de problemas do seu pacote. Afinal de contas, se compilou todo o OpenWRT e não o meu pacote, o problema provavelmente é comigo. Fora isto, os fontes locais podem facilitar o uso destes como inspiração ou modelo para seu próprio pacote. Problemas de compilação cruzada se repetem aos montes e espiar o vizinho ajuda muito. Também, se seu desejo é integrar o pacote de volta ao OpenWRT para que outros usuários também possam aproveitar seu trabalho, o ideal é trabalhar em conjunto com todo o OpenWRT. A segunda opção é mais econômica. Serve mais para compilações isoladas. Como é o caso deste exemplo, foi optar por esta alternativa.
artigo anterior, apresentei o problema da compilação de um novo programa para o OpenWRT. Neste vamos colocar a mão na massa.
Vou pegar um exemplo simples: o figlet. Ele faz isto:
$ figlet LuizLuca
_ _ _
| | _ _(_)___| | _ _ ___ __ _
| | | | | | |_ / | | | | |/ __/ _` |
| |__| |_| | |/ /| |__| |_| | (_| (_| |
|_____\__,_|_/___|_____\__,_|\___\__,_|
_ _ _
| | _ _(_)___| | _ _ ___ __ _
| | | | | | |_ / | | | | |/ __/ _` |
| |__| |_| | |/ /| |__| |_| | (_| (_| |
|_____\__,_|_/___|_____\__,_|\___\__,_|
Fantástico, não é? (nem sei como vivi sem ele até hoje) O que o programa faz não importa neste caso.
Você tem duas opções para montar o ambiente para gerar seus pacotes: recompilar todo o OpenWRT ou utilizar o SDK disponível no mesmo local onde estão as firmwares. O primeiro caso vai utilizar mais espaço e demorar mais mas traz a vantagem de isolar problemas do seu ambiente de problemas do seu pacote. Afinal de contas, se compilou todo o OpenWRT e não o meu pacote, o problema provavelmente é comigo. Fora isto, os fontes locais podem facilitar o uso destes como inspiração ou modelo para seu próprio pacote. Problemas de compilação cruzada se repetem aos montes e espiar o vizinho ajuda muito. Também, se seu desejo é integrar o pacote de volta ao OpenWRT para que outros usuários também possam aproveitar seu trabalho, o ideal é trabalhar em conjunto com todo o OpenWRT. A segunda opção é mais econômica. Serve mais para compilações isoladas. Como é o caso deste exemplo, foi optar por esta alternativa.
Vou pegar a família ar71xx, muito comum em roteadores, para este exemplo. O SDK desta família está aqui. Baixe de descompacte:
$ wget https://downloads...OpenWRT-SDK-ar71xx-for-linux-...tar.bz2
$ tar -xjvf OpenWRT-SDK...tar.bz2
$ cd OpenWrt-SDK-...
Tem um problema nesta versão do SDK. Ele está com o uso do ccache ativado. Caso isto seja um problema para você, desative-o editando o arquivo Config-build.in nesta parte:
config CCACHE
bool
default n
Estamos prontos para criar o primeiro pacote. crie um subdiretório em package. No caso, criarei o figlet (OpenWrt-SDK-.../package/figlet/. Dentro deste criaremos um arquivo Makefile descrevendo o pacote. Makefile? Mas isto não é usado depois na compilação? Sim, o OpenWRT (ab)usou o make para montar todo seu ambiente de compilação. Este Makefile não é o mesmo gerado pelo ./configure mas um padrão do OpenWRT que descreve as informações do seu pacote, como compilar, instalar e metainformações, assim como ocorre nos arquivos spec do RPM. Sim, pode ser feio, nem lembra um Makefile comum, mas funciona. Fora o Makefile existem outros arquivos opcionais. Abaixo do subdiretório "patches" ficam eventuais correções para consertar ou adaptar problemas nos fontes do programa. Caso necessite adicionar algum arquivo não gerado pelo ou presente nos fontes, como scripts de disparo de serviços, estes podem ficam em um outro subdiretório "files". E os fontes? Isto o OpenWRT baixa para você. Você só precisa dizer de onde no Makefile.
O arquivo Makefile do exemplo deste artigo ficaria em package/figlet/Makefile. Inicialmente é assim (em destaque o que alterei):
# # Copyright (C) 2017 Luiz Angelo Daros de Luca
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=figlet
PKG_VERSION:=2.2.5
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=ftp://ftp.figlet.org/pub/figlet/program/unix/
PKG_MD5SUM:=d88cb33a14f1469fff975d021ae2858e
PKG_MAINTAINER:=Luiz Angelo Daros de Luca <luizluca@gmail.com>
PKG_LICENSE:=BSD-3-Clause
PKG_LICENSE_FILES:=LICENSE
PKG_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
define Package/figlet
SECTION:=xxx
CATEGORY:=Utilities
TITLE:=FIGlet
URL:=http://www.figlet.org/
MAINTAINER:=Claudio Matsuoka <cmatsuoka@gmail.com>
DEPENDS:=
endef
define Package/figlet/description
FIGlet is a program for making large letters out of ordinary text
endef
# Eh obrigatorio ser tabs antes das linhas abaixo
define Package/figlet/install
$(INSTALL_DIR) $(1)/usr/bin
$(CP) $(PKG_INSTALL_DIR)/usr/bin/* $(1)/usr/bin
$(INSTALL_DIR) $(1)/usr/share/figlet/
$(CP) $(PKG_INSTALL_DIR)/usr/share/figlet/* $(1)/usr/share/figlet/
endef
$(eval $(call BuildPackage,figlet))
O cabeçalho do arquivo, comentado, é meramente informativo. Os PKG_* descrevem dados para os pacotes gerados por este Makefile (sim, pode ser mais de um mas não é o caso deste exemplo). Os nomes são bem auto-explicativos. NAME é o nome do pacote, VERSION (do programa) e RELEASE (do pacote), em conjunto, formarão a versão do pacote. SOURCE_URL/SOURCE indicam de onde baixar e o MD5SUM o hash do arquivo. O campo MAINTAINER é o "dono" deste pacote. O PKG_INSTALL pede para que o "make install" seja executado após a compilação.
O "define Package/figlet" efetivamente declara o pacote. Em especial, observe o campo DEPENDS, que possui uma sintaxe própria e é usado para definir as dependências entre os pacotes. Este pacote de exemplo não tem dependências conhecidas (até este momento). "Package/figlet/description" é apenas um texto descritivo.
A compilação ocorre em uma série de etapas.
- Build/Prepare: descompacta os fontes, e aplica os patches (Build/Patches)
- Build/Configure: roda o ./configure (ou equivalente)
- Build/Compile: roda o make
- Build/Install: se PKG_INSTALL for 1, roda o "make install", instalando em $(PKG_INSTALL_DIR)
O "define Package/figlet/install" é um passo importante. Ele preenche a árvore que formará os arquivos do pacote. Este caminho é passado como argumento $(1). Então, se criar um $(1)/usr/yyy, o pacote será o conteúdo /usr/yyy. Normalmente o Package/xxx/install é uma sequência de chamadas ao comando install, que é equivalente uma composição dos comandos mkdir, cp, chmod, chown. Se o "make install" foi utilizado, o Package/xxx/install irá copiar arquivos de $(PKG_INSTALL_DIR) para $(1). Ao invés de usar diretamente o comando install ou ainda o cp/mkdir, o recomendado é utilizar o INSTALL_DIR, INSTALL_BIN, INSTALL_CONFIG, INSTALL_DATA (definidos em https://github.com/openwrt/openwrt/blob/master/rules.mk#L257)
$ make
Collecting package info: done
#
# configuration written to .config
#
make[1] world
make[2] package/compile
make[3] -C package/figlet compile
make -r world: build failed. Please re-run make with V=s to see what's going on
Erro?! Mas o que aconteceu? O OpenWRT não mostra o erro por padrão. Como sugerido, rode novamente com o V=s para ver o que está acontecendo (vou omitir linhas não relevantes):
$ make V=s
...
mips-openwrt-linux-uclibc-ld -o figlet figlet.o zipio.o crc.o inflate.o utf8.o
mips-openwrt-linux-uclibc-ld: warning: cannot find entry symbol __start; defaulting to 00000000004000b0
figlet.o:OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2/build_dir/target-mips_34kc_uClibc-0.9.33.2/figlet-2.2.5/figlet.c:293: undefined reference to `stderr'
figlet.o:OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2/build_dir/target-mips_34kc_uClibc-0.9.33.2/figlet-2.2.5/figlet.c:293: undefined reference to `stderr'
figlet.o:OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2/build_dir/target-mips_34kc_uClibc-0.9.33.2/figlet-2.2.5/figlet.c:293: undefined reference to `fprintf'
...
Não achando stderr? fprintf? __start? Isto é da libc! Quem já programou um pouco de C sabe que normalmente não precisamos indicar manualmente a libc para o ligador (linker). E por que ele não achou, então? Bem, o figlet é um dos exemplos onde o desenvolvedor optou por não usar o ./configure (autoconf). Ele escreveu o Makefile manualmente. O problema é que algumas coisas são classicamente configuradas no configure, como o prefixo do programa e parâmetros do compilador e do ligador. Outro problema é assumir coisas que nem sempre são verdades. O problema acima, por exemplo, ocorre porque o ligador (ld) não está ligando a libc ao programa caso isto não seja solicitado explicitamente. E por que funciona para o desenvolvedor? Como ele não viu isto? Na maioria dos casos, o ld disponível no Linux adiciona a libc por padrão e não é necessário explicitá-la. No caso do OpenWRT, o linker não faz isto. Se usasse o ./configure, provavelmente o ligador seria configurado apropiadamente (adicionando a libc). Como já enfrentei este problema anteriormente, conheço uma solução de contorno: usar o gcc como ligador. Isto pode ser feito substituindo a variável ambiente LD do make pelo compilador do alvo. Só adicionar isto ao Makefile anterior:
MAKE_FLAGS += \
LD="$(TARGET_CC)"
E compilar novamente:
$ make V=s
...
cp figlet chkfont figlist showfigfonts /home/luizluca/Downloads/OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2/build_dir/target-mips_34kc_uClibc-0.9.33.2/figlet-2.2.5/ipkg-install/usr/local/bin
...
cp: cannot stat '/home/luizluca/Downloads/OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2/build_dir/target-mips_34kc_uClibc-0.9.33.2/figlet-2.2.5/ipkg-install/usr/bin/*': No such file or directory
Erro novamente?! Ao menos ele compilou. Por que ele não achou os arquivos? Porque ele instalou o figlet abaixo de /usr/local e não de /usr, como é o padrão do OpenWRT (e da maioria das distribuições). Esta configuração do prefixo é, na maioria dos casos, configurada pelo script ./configure. Se quisesse, poderia alterar o nosso Makefile para usar o /usr/local. Porém, este não é o caminho padrão para este tipo de arquivo. Novamente, a opção por criar manualmente o Makefile prejudicou a compilação cruzada. Caso usasse o ./configure, o OpenWRT iria definir o prefixo automaticamente. Solução? A mesma usada anteriormente: outra variável ambiente para reconfigurar o Makefile do figlet.
MAKE_FLAGS += \
LD="$(TARGET_CC)" \
prefix="$(CONFIGURE_PREFIX)"
E agora sim:
$ make
Collecting package info: done
#
# configuration written to .config
#
make[1] world
make[2] package/compile
make[3] -C package/figlet compile
make[2] package/index
Sucesso! Seu pacote estará em bin/ar71xx/packages/base/figlet_2.2.5-1_ar71xx.ipk.
Copiando para um roteador e instalado. Não é que ele funciona?
$ scp bin/ar71xx/packages/base/figlet_2.2.5-1_ar71xx.ipk root@router:/tmp
$ ssh root@router
root@router:~# opkg install /tmp/figlet_2.2.5-1_ar71xx.ipk
Installing figlet (2.2.5-1) to root...
Configuring figlet.
root@router:~# figlet "Funciona!"
_____ _ _
| ___| _ _ __ ___(_) ___ _ __ __ _| |
| |_ | | | | '_ \ / __| |/ _ \| '_ \ / _` | |
| _|| |_| | | | | (__| | (_) | | | | (_| |_|
|_| \__,_|_| |_|\___|_|\___/|_| |_|\__,_(_)
O Makefile final ficou:
# # Copyright (C) 2017 Luiz Angelo Daros de Luca
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=figlet
PKG_VERSION:=2.2.5
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=ftp://ftp.figlet.org/pub/figlet/program/unix/
PKG_MD5SUM:=d88cb33a14f1469fff975d021ae2858e
PKG_MAINTAINER:=Luiz Angelo Daros de Luca <luizluca@gmail.com>
PKG_LICENSE:=BSD-3-Clause
PKG_LICENSE_FILES:=LICENSE
PKG_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
MAKE_FLAGS += \
LD="$(TARGET_CC)" \
prefix="$(CONFIGURE_PREFIX)"
define Package/figlet
SECTION:=xxx
CATEGORY:=Utilities
TITLE:=FIGlet
URL:=http://www.figlet.org/
MAINTAINER:=Claudio Matsuoka <cmatsuoka@gmail.com>
DEPENDS:=
endef
define Package/figlet/description
FIGlet is a program for making large letters out of ordinary text
endef
# Eh obrigatorio ser tabs antes das linhas abaixo
define Package/figlet/install
$(INSTALL_DIR) $(1)/usr/bin
$(CP) $(PKG_INSTALL_DIR)/usr/bin/* $(1)/usr/bin
$(INSTALL_DIR) $(1)/usr/share/figlet/
$(CP) $(PKG_INSTALL_DIR)/usr/share/figlet/* $(1)/usr/share/figlet/
endef
$(eval $(call BuildPackage,figlet))
Nem sempre preparar um pacote de algo mais simples será mais fácil. Às vezes, a simplificação do programa pode levar o desenvolvedor a não utilizar práticas padrão de desenvolvimento. Nem cheguei a tratar dependências de bibliotecas. Quando o programa fonte é bem feito, normalmente é questão de adicionar a biblioteca necessária na lista de dependências e pronto. O OpenWRT, inclusive, verifica automaticamente se você não esqueceu de uma delas.
Se fizer algo interessante para o OpenWRT e quiser compartilhar, o desenvolvimento dos pacotes "extras" do OpenWRT está bem produtivo. Ele migrou para https://github.com/openwrt/packages.
Se precisar de uma ajuda, crie um tópico no fórum do blog ou no tópico deste artigo.
Até a próxima.
Atualização em 2017-05-11: adicionado informações sobre etapas de compilação e mais informação sobre o Package/xxx/install; Adicionado o PKG_INSTALL para evitar chamada dentro do Package/xxx/install.
Atualização em 2017-05-11: adicionado informações sobre etapas de compilação e mais informação sobre o Package/xxx/install; Adicionado o PKG_INSTALL para evitar chamada dentro do Package/xxx/install.
Este comentário foi removido pelo autor.
ResponderExcluirEste comentário foi removido pelo autor.
ResponderExcluirtem como eu personalizar a pagina de login?
ResponderExcluirSim, provavelmente você vai querer editar este arquivo:
Excluir/usr/lib/lua/luci/view/sysauth.htm
Ou outro arquivo neste diretório.
Olá. Parabéns pelo posts!
ResponderExcluirGostaria de uma ajuda de como configurar o trecho "Define Package/XXXXX/install.
Quero fazer o cross compile de um projeto (https://github.com/beba-eu/beba-switch) que deve ser compilado com autotoolse não sei como adaptar este trecho.
Olá Peterson,
ExcluirTranquilo. Tinha uns passos implícitos que eu intencionalmente não tinha comentado. Agora incluí no texto. Também mudei a chamada explícita "$(call Build/Install/Default)" dentro do Package/figlet/install, que poderia ser a fonte da sua confusão.
No lugar, adicionei o PKG_INSTALL:=1, que faz essa chamada após a compilação.
Veja se as mudanças irão te ajudar.
Você vai ter algo como:
define Package/beba-switch/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/beba-xxx $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/beba-yyy $(1)/usr/bin
$(INSTALL_DIR) $(1)/usr/share/beba
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/beba/LEIAME $(1)/usr/share/beba/README
endef
O resultado será o pacote beba-switch com os arquivos:
/usr/bin/beba-xxx
/usr/bin/beba-yyy
/usr/share/beba/README
Se tiver mais dúvidas, é melhor abrir um tópico no fórum do blog (links no topo ou fim da página).
Sempre é bom um feedback para ir melhorando os posts, mesmo os antigos.
Tentei conforme disse mas embora não dê erro, não gera nenhum ipk.
ResponderExcluirAinda não peguei a lógica do que devo "linkar" no trecho "package install"
Atualizando: Acho que de tanto quebrar a cabeça peguei a lógica. Estou alterando o "install" e depois posto se consegui com sucesso.
ResponderExcluirObrigado.
Peterson. pode ser só confusão nos nomes. Eu sugiro que comece por um Makefile simples que funcione (como meu exemplo)
Excluire vá ajustando. Qq coisa, posta um tópico no fórum, que é bem mais simples de responder e acompanhar
Tentei postar o tópico no fórum após sua 1a recomendação sem sucesso. Acesso o link mas não vejo onde criar um novo tópico.
ResponderExcluirSobre a minha compilação, identifiquei que um arquivo do pacote é deletado durante o processo e na sequência ocorre o erro pela falta deste arquivo (/ledesdk/build_dir/target-mips_24kc_musl-1.1.16/beba/lib/backtrace.o).
Não consigo identificar o motivo do delete e também não consigo forçar o arquivo na pasta.
Teria ideia no motivo da falha ou um workaround para resolver? Log parcial do erro abaixo:
make[3]: Entering directory '/home/plopes/ledesdk/package/beba'
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/plopes/ledesdk/build_dir/target-mips_24kc_musl-1.1.16/beba:beba -Wformat .......... ARCH="mips" ;
make[4]: Entering directory '/home/plopes/ledesdk/build_dir/target-mips_24kc_musl-1.1.16/beba'
make all-recursive
make[5]: Entering directory '/home/plopes/ledesdk/build_dir/target-mips_24kc_musl-1.1.16/beba'
make[6]: Entering directory '/home/plopes/ledesdk/build_dir/target-mips_24kc_musl-1.1.16/beba'
depbase=`echo lib/backtrace.o | sed 's|[^/]*$|.deps/&|;s|\.o$||'`;\
ccache_cc -DHAVE_CONFIG_H -I. -g -I ./include -I ./lib -DOFL_LOG_VLOG -DOFL_LOG_VLOG -DNETPDLDIR='"/usr/share/openflow"' -I/home/plopes/ledesdk/staging_dir/target-mips_24kc_musl-1.1.16/usr/include -I/home/plopes/ledesdk/staging_dir/target-mips_24kc_musl-1.1.16/include -I/home/plopes/ledesdk/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/usr/include -I/home/plopes/ledesdk/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/include/fortify -I/home/plopes/ledesdk/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/include -Wstrict-prototypes -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/plopes/ledesdk/build_dir/target-mips_24kc_musl-1.1.16/beba:beba -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro -Wall -Wextra -Wno-free-nonheap-object -Wno-sign-compare -Wpointer-arith -Wdeclaration-after-statement -Wformat-security -Wunused-parameter -Wstrict-aliasing -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -Wno-override-init -MT lib/backtrace.o -MD -MP -MF $depbase.Tpo -c -o lib/backtrace.o lib/backtrace.c &&\
mv -f $depbase.Tpo $depbase.Po
lib/backtrace.c:39:22: fatal error: execinfo.h: No such file or directory
compilation terminated.
Peterson, era para aparecer embutido, mas vai o link direto. http://luizluca-blogspot.1115460.n5.nabble.com
ExcluirSó clicar em novo tópico.
Abre lá que removo o comentário daqui. Saída de comandos deixam os comentários meio poluídos.