Faça o seu trabalho com o make!

ArticleCategory: []

Software Development

AuthorImage:[Here comes a small picture of you]

[Wilbert Berendsen]

TranslationInfo:[Info concerning writer(s) and translator(s)]

original in nl Wilbert Berendsen

en to pt Bruno Sousa

AboutTheAuthor:[a short biography about the author]

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!

Abstract:[a small summary/description of this article]

Este artigo feito mostra como o make trabalha e como pode ser usado para mais coisas que não só desenvolvimento de software.

ArticleIllustration:[illustration]

[Illustration]

ArticleBody:[The real article: put the text and html-codes here]

Introdução

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.

Exemplo: Construindo um site

Pretendemos construir um site que é mantido por pessoas diferentes. O Jan toma conta de duas páginas e mantém-nas. O Piet toma conta da sua aparência.

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.html
Por 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.html
Este 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.

Primeiro encontro com o make

O manual de informação do make da GNU é um documento fantástico. Contudo, desde o início que é focado num ambiente de programação. Por esta razão que estou a indicar as funções do make num sentido extenso:
    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.

Sintaxe do Makefile

O Makefile pode ser criado com um editor e é algo do género:
# 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.

Um Makefile para o nosso exemplo

Para o nosso exemplo, o makefile seria algo como:

# 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 !

Optimizando o Makefile

Variáveis

Graças às variáveis que o Makefile pode ser muito simplificado. As variáveis são definidas do seguinte modo :
variable = value
Referimo-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 !

Regras modelo

As `Regras Modelo' permitem-nos utilizar o mesmo conjunto de comandos para diferentes destinos.

Se as regras modelo são utilizadas, a sintaxe da linha altera-se; é adicionado um modelo extra :

Multiple targets: pattern : prerequisite prerequisite ...
	command
O 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/% : %
	commands
Se 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
	commands
O 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 ?

Variáveis Automáticas

Felizmente, o make define algumas variáveis por si. Algumas destas variáveis são chamadas de variáveis automáticas. Estas variáveis contém, durante a execução dos comandos (correctamente: mesmo antes da execução dos comandos) o valor do destino e/ou pré-requisitos.

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 end
Isto 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 !

Últimas pequenas optimizações

Nós preferíamos mencionas os documentos no DOCS, sem ter de incluir o directório completo. Isto pode ser feito como se segue (nós alteramos o DOCS no princípio do makefile no TEXTS):
TEXTS = index.html  offer.html  yetanotherfile.html

# Please change nothing below this line;-)
# -------------------------------------------------------------
DOCS =  $(addprefix $(TARGETDIR)/,$(TEXTS))

all: $(DOCS)

# and so on
O 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: all
Agora 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 end
Arquive 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 !

Notas Finais

Claro que é possível modificar este exemplo para outras situações.

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 !

Dicas

Mais informação

Mais informação em como o make trabalha e todas as outras possibilidades podem ser encontradas no `Manual Make da GNU'. Pode ler este manual no seu sistema Linux com o seguinte comando:
info make
Claro 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!