original in nl Wilbert Berendsen
en to pt Bruno Sousa
O Wilbert Berendsen é um músico profissional e um utilizador entusiasta do Linux. Desde que intensamente assemblou, pirateou o Z80. Nos dias de hoje utiliza o Linux para todo o seu trabalho de produção. Por entretimento, escreve artigos introdutórios e mantém um pequeno site em http://www.xs4all.nl/~wbsoft/. Viva o source aberto!
Quase todos os que utilizam o Linux já correram o programa make alguma vez. Ele faz o seu trabalho se um programa ou o Kernel são construídos a partir do código source, se um pacote está instalado, e por aí a fora. O Make é uma ferramenta muito importante para o desenvolvimento de software. Contudo, o make possui muitas mais possibilidades !
Neste Documento Veremos que o Make pode ser um utilitário muito poderoso nos trabalhos do dia-a-dia, como a escrita de artigos, a composição de livros, ou a composição de um site simpático. Durante a introdução outros 'truques Unix' serão abordados. No fim da história, outros mais truques serão apresentados utilizando-se o make. Por favor note: estamos a falar acerca do Linux, mas em princípio é possível utilizar o make em todos os sistemas operativos.
Precisamos de um sistema simples para separar o conteúdo da aparência, um do outro. Uma solução poderosa é: ler o conteúdo de uma base de dados, sempre que a página é pedida. Por exemplo, PHP e o ASP da Microsoft trabalham deste modo. Contudo, só temos a possibilidade de guardar pleno HTML (HyperText Markup Language). E além disso, o conteúdo não se altera tantas vezes que requeira a manutenção de uma base de dados eficiente.
Usando comandos simples, um site será construído.
Por exemplo, o Piet põe o cabeçalho do site no header.html e o rodapé do site no footer.html. O header.html pode parecer algo do género:
<html><!-- the header --> <head> <title>Piet and Jan productions</title> </head> <body bgcolor="white"> <table border="0" width="100%"><tr> <td bgcolor="#c040ff" valign="top"> This is our website<br> Some rubbish is written down here.<br> We are very interactive<br> so this is our telephone number:<br> <b>0123-456789</b> </td><td valign="top"> <!-- Put the contents here -->E este footer.html é o rodapé:
<!-- the footer --> </td></tr></table> </body></html>Por exemplo, os comandos unix para construir as páginas finais do index.html do Jan são:
cat header.html /home/jan/Docs/website/index.html echo -n '<hr>Last modification: ' date '+%A %e %B' cat footer.htmlPor favor reveja as páginas manuais acerca destes comandos. O ficheiro final, como resultado dos comandos acima, é transposto para a saída padrão, a qual é sacada para um ficheiro:
{ cat header.html /home/jan/Docs/website/index.html echo -n '<hr>Last modification: ' date '+%A %e %B' cat footer.html } > /home/piet/public_html/index.htmlEste procedimento pode ser substituído pelo outro ficheiro, offer.html. De facto, criámos uma pequena script que permite a construção do nosso site.
Contudo a execução deste comando não é muito prático. Podemos criar uma script da shell que é executada sempre que o Jan faça um update ao seu index. Contudo se o Piet decide alterar o cabeçalho ou o rodapé a script deveria ser executada na mesma ! Por outro lado se o Jan nada alterou num dia, a script não deverá ser executada. Estamos a utilizar o Linux, e queremos uma solução inteligente (leia-se: automática)!
É neste ponto que o make aparece.
make determines whether a set of commands should be executed, based on the time-stamp of the target-file and the time-stamps of the source files.Por outras palavras: se um dos ficheiros source, precisa de criar o ficheiro de destino e este é mais recente que o ficheiro source, então um conjunto de comandos são executados. O propósito destes comandos é actualizar o ficheiro de destino.
O ficheiro de destino é o 'target' e os ficheiros source são os `prerequisites' (primeiros pedidos). Os comandos são executados se um dos `prerequisites' é mais novo que o ficheiro de destino (ou se o destino não existe). Se todos os 'prerequisites' são mais velhos ou iguais ao destino, então os comandos não são executados e o destino é considerado actualizado.
No directório corrente de trabalho, um ficheiro devia ser criado com o nome de Makefile. Este ficheiro contém a informação necessária ao make para fazer o seu trabalho apropriadamente. Após termos o Makefile, a única coisa que temos de fazer é: digitar 'make' e os comandos necessários para criar os ficheiros de destino são executados automaticamente.
O Make é chamado com o comando
make target1 target2 ....
O destino (target) é opcional (se o destino não é especificado, é utilizado o primeiro destino que está no Makefile). O Make procura sempre no directório corrente pelo Makefile. É possível especificar mais do que um destino.
# This is an example of a Makefile. # Comments can be put after a hash (#). target: prerequisites command target: prerequisites commando # and so on and so on.Começamos pelo destino, seguido da marca (:) e os necessários 'prerequisites' (pré-requisitos). Na presença de muitos pré-requisitos é possível terminar a linha com a barra (\) e continuar na próxima linha.
Nas linhas seguintes, um ou mais comandos são apresentados. Cada linha é considerada um só comando. Se quer utilizar múltiplas linhas para um só comando têm de por as barras (\\) no fim da linha. O Make junta os comandos como se fossem escritos numa só linha. Nesta situação temos de separar os comandos com a marca (;) no sentido de prevenir os erros de execução da shell.
Nota: Os comandos devem ser indentados com o TAB e não com 8 espaços ! |
O make lê o Makefile e determina para cada destino (a começar pelo primeiro) se os comandos devem ser executados. Cada destino, juntamente com os pré-requisitos e regras, é denotado de 'regra'.
Se o make é executado sem argumentos, só o primeiro destino será executado.
# This Makefile builds Piets' and Jans' website, the potato-eaters. all: /home/piet/public_html/index.html /home/piet/public_html/offer.html /home/piet/public_html/index.html: header.html footer.html \ /home/jan/Docs/website/index.html { \ cat header.html /home/jan/Docs/website/index.html ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > /home/piet/public_html/index.html /home/piet/public_html/offer.html: header.html footer.html \ /home/jan/Docs/website/offer.html { \ cat header.html /home/jan/Docs/website/index.html ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > /home/piet/public_html/offer.html # the end
Agora, temos três destinos, 'all' e os ficheiros index.html e offer.html do site. A única função do destino 'all' e ter os outros como pré-requisitos. Estes são ambos testados. Porque 'all' em si mesmo não é o nome de um ficheiro, o destino 'all' será sempre executado. (Mais tarde introduziremos um modo mais elegante de definir destinos que não são ficheiros).
Se o cabeçalho e o rodapé foram modificados, ambas as páginas serão actualizadas. Se o Jan modifica uma das suas páginas, só a página modificada será actualizada. A execução do comando 'make' faz este trabalho !
Claro que o Makefile tem um senão : não é fácil de supervisionar. Felizmente, estão disponíveis muitos modos de tornar as coisas mais simples !
variable = valueReferimo-nos à variável com a expressão $(variável). Se incluirmos isto no Makefile, parecerá um pouco melhor :
# This Makefile builds Piets' and Jans' website, the potato-eaters. # Directory where the website is stored: TARGETDIR = /home/piet/public_html # Jans' directory: JANSDIR = /home/jan/Docs/website # Files needed for the layout: LAYOUT = header.html footer.html all: $(TARGETDIR)/index.html $(TARGETDIR)/offer.html $(TARGETDIR)/index.html: $(LAYOUT) $(JANSDIR)/index.html { \ cat header.html $(JANSDIR)/index.html ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(LAYOUT) $(JANSDIR)/offer.html { \ cat header.html $(JANSDIR)/index.html ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $(TARGETDIR)/offer.html # the endÉ um bom hábito utilizar letras maiúsculas para as variáveis. Agora é mais fácil de alterar, e.g. o directório de destino (target-directory).
Se o desejar, é possível de definir outro método para cada documento no quel quer por a correcta apresentação. O que devemos fazer se são muitos documentos a utilizar a mesma estrutura ? O Makefile tornar-se-ia muito grande, quando muitas repetições estão presentes. Isto também pode ser simplificado !
Se as regras modelo são utilizadas, a sintaxe da linha altera-se; é adicionado um modelo extra :
Multiple targets: pattern : prerequisite prerequisite ... commandO modelo é uma expressão que devia ser aplicável a todos os destinos. É utilizado o símbolo de percentagem para incluir parte das variáveis do nome de destino.
Um exemplo:
/home/bla/target1.html /home/bla/target2.html: /home/bla/% : % commandsSe o make lê isto, a linha é expandida para 2 linhas. Aqui o modelo determina que parte do nome de destino é incorporado no símbolo da percentagem.
O símbolo de percentagem no campo dos pré-requisitos representa a parte a ser copiada para o símbolo a percentagem.
O Make expande o dito em cima como:
/home/bla/target1.html: target1.html commands /home/bla/target2.html: target2.html commandsO símbolo da percentagem no modelo `/home/bla/%' obtém o destino `/home/bla/target1.html' com o valor `target1.html', expandindo então o pré-requisito `%' para `target1.html'.
Para o nosso site, a seguinte regra é incorporada:
$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: \ $(TARGETDIR)/% : $(JANSDIR)/% \ $(LAYOUT)Agora temos ainda um problema: Como usar as variáveis nos comandos ? Os comandos foram um pouco diferentes para ambos os destinos ?
A variável especial $< é utilizada para indicar o primeiro pré-requisito e a variável $@ expande sempre para o destino corrente.
Usando estas variáveis, é possível de generalizar a regra completa como se segue:
$(TARGETDIR)/index.html $(TARGETDIR)/offer.html: $(TARGETDIR)/% : \ $(JANSDIR)/% \ $(LAYOUT) { \ cat header.html $< ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $@E Eis que! Uma simples linha funciona para ambos os ficheiros !
Para Completar, o Makefile completo é apresentado, incluindo mais algumas optimizações:
# This Makefile builds Piets' and Jans' website, the potato-eaters. # Directory where the website is published: TARGETDIR = /home/piet/public_html # Jans' directory: JANSDIR = /home/jan/Docs/website # Files needed for the layout: LAYOUT = header.html footer.html # These are the webpages: DOCS = $(TARGETDIR)/index.html $(TARGETDIR)/offer.html # Please change nothing below this line;-) # ------------------------------------------------------------- all: $(DOCS) $(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT) { \ cat header.html $< ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $@ # the endIsto já começa a parecer como devia. Se mais algum documento é adicionado, é relativamente fácil de o incorporar no Makefile, utilizando a variável DOCS, sem muita edição.
No fim, a pessoa que mantém o Makefile deve, facilmente, ver como trabalha, sem ser necessário "repartir para compreender o seu funcionamento !
TEXTS = index.html offer.html yetanotherfile.html # Please change nothing below this line;-) # ------------------------------------------------------------- DOCS = $(addprefix $(TARGETDIR)/,$(TEXTS)) all: $(DOCS) # and so onO que nós vemos aqui é uma função especial do make: em vez de um nome da variável, é possível utilizar uma expressão completa entre parêntesis. Deste modo é possível modificar texto em numerosos modos.
O Comando especial $(addprefix prefix,list) adiciona a cada elemento na lista um prefixo. No nosso exemplo, a variável TARGETDIR contém este informação juntando a barra (/).
Os itens listados são separados com espaços. É por esta razão que não é boa ideia processar ficheiros cujo nome inclui espaços com o comando make.
Para concluir: no princípio, mencionámos que o destino 'all' não criava um ficheiro com o nome 'all' (esta linha não contém nenhum comando) e tem um resultado, este destino é sempre executado. Mas como fazer se <acidentalmentey> existir um ficheiro com este nome e mais recente que os outros ficheiros ...?
Existe um modo fácil de dizer ao make que um destino particular deve ser sempre executado e que este destino não se refere a um ficheiro no disco rígido. Para validar isto, o destino é marcado como 'phony' (irreal). Isto é como se segue :
.PHONY: allAgora o Makefile comleto é algo do género:
# This Makefile builds Piets' and Jans' website, the potato-eaters. # Directory where the website is published: TARGETDIR = /home/piet/public_html # Jans' directory: JANSDIR = /home/jan/Docs/website # Files needed for the layout: LAYOUT = header.html footer.html # These are the names of the webpages: TEXTS = index.html offer.html yetanotherfile.html # Please change nothing below this line;-) # ------------------------------------------------------ DOCS = $(addprefix $(TARGETDIR)/,$(TEXTS)) .PHONY: all all: $(DOCS) $(DOCS): $(TARGETDIR)/% : $(JANSDIR)/% $(LAYOUT) { \ cat header.html $< ;\ echo -n '<hr>Last modification: ' ;\ date '+%A %e %B' ;\ cat footer.html ;\ } > $@ # the endArquive este ficheiro e esqueça-o! Doravante, é possível manter as suas páginas web, talvez utilizando o crontab e separar apropriadamente o conteúdo da estrutura !
Como exemplo, o modo mais simples em que o documento gerado contém erros : se Jan termina acidentalmente o seu artigo com </body></html>, a maioria dos browsers não apresentarão o rodapé que o Piet fez. Se aplicarmos o grep, o perl ou o tcl, é possível de adicionar títulos do documento do Jan, de um modo inteligente, ao cabeçalho do site.
Claro que, o Jan pode editar somente texto e depois usar o comando sed para alterar as linhas escritas transformando os carriage returns em <P>:
sed -e 's/^\s*$/<p>/g'Ou ainda, O Jan pode escrever o seu texto em LyX e utilizar um programa como lyx2html, para converter o texto em html. Existem imensas possibilidades disponíveis !
Um outro modelo de construção também é possível.
Não considerámos como as figuras são transportadas (escaladas, convertidas, ou compressas) para o directório da web. É também possível a automatização deste processo !
Neste exemplo, o Piet deveria ter permissões de leitura no directório da Web do Jans. O interesse de separar estas tarefas é que as mesmas podem ser aplicadas numa organização enorme. É até possível ao Piet fazer login do outro lado do mundo, ou montar o seu directório de trabalho através do NFS. Os exemplos apresentados podem ser aplicados ao trabalho realizado por uma só pessoa.
Esperançosamente, ficou claro como os princípios do Makefile funcionam e como o seu trabalho diário se pode tornar mais fácil desde o momento em que escreve bons Makefiles !
Utilizando destinos 'phony' (.PHONY: destino), é mais fácil de agrupar funções simples. Um exemplo é a configuração do Kernel do Linux.
Editando make menuconfig começa-se a configuração com um menu interactivo. Editando make xconfig inicia-se a configuração com uma interface Tcl/Tk sobre o X.
Ambas em cima nada têm haver com a construção real do Kernel. São somente uma simples interface para as necessárias funções (como a configuração do Kernel).
Você deveria criar um Makefile com os seguintes destinos PHONY:
Deste modo, é possível de gerar HTML a partir de um ficheiro de texto e melhorar a estrutura nova obtida do ficheiro de HTML. Um exemplo:
TEMPLATE = layout1/Template1.txt /home/httpd/sales/sales.html: sales.html $(TEMPLATE) perl Scripts/BuildPage.pl -template $(TEMPLATE) $< > $@-new mv -f $@-new $@ sales.html: sales.txt aptconvert -toc $@ $<Observe como o ficheiro seria actualizado se o Template1.txt fosse alterado.
Se um comando é precedido de um '@', não é apresentado pelo comando make:
target: prerequisite @cc -o target prerequisiteSe um comando começa em '-', o processo make não termina se este comando gerar um erro (por exemplo apagar um ficheiro que não existe):
.PHONY: clean clean: -rm -r $(tempdir)Se você quer ver, de um certo modo, o que o make faz, e.g. make install, mas quer prevenir-se de que o comando é realmente executado, utiliza a opção -n na prompt:
wilbert@nutnix:~ > make -n install install -m 755 program /usr/local/bin install -m 644 program.1 /usr/local/man/man1 wilbert@nutnix:~ >
Se tem necessidade do símbolo do dólar ($) como parte de, e.g., um nome de ficheiro, um comando da shell, utilize-o duplamente ($$):
# A Makefile # Don't try this at home! :-) source = menu.txt help.txt target: $(source) for i in $(source) ;\ do \ if [ "$$i" = "menu.txt" ] ;\ then \ doThis $$i ;\ else \ doThat $$i ;\ fi ;\ done > targetO Make antes de enviar o comando para a shell a fim de ser executado, substitui os seus próprios valores e altera os símbolos duplos do dólar por símbolos com um só dólar.
info makeClaro que, é possível de ler o Manual Make da GNU no KDE ou GNOME com os browsers ou com o programa tkinfo.
Links para mais informação acerca do make:
Divirta-se!