MAC 328 Algoritmos em Grafos

Este projeto foi elaborado pelo professor Francisco Reverbel

Segundo Exercício-Programa. Make: Parte I

OK, let's be straight about it,
the syntax of make is really stupid.
If you use spaces where you're supposed to use tabs or vice versa,
your makefile blows up. And the error messages are really confusing.

Fonte: Running Linux, Matt Welsh and Lar Kaufman


Objetivos

  1. Manipulação da representação de um grafo através de listas de adjacência.
  2. Treino no uso de algumas funções básicas do Stanford GraphBase.
  3. Familiarização com o processo de compilação usando a biblioteca libgb.
    (Veja os Makefiles em http://www.ime.usp.br/~coelho/grafos/sgb/exemplos/.)

Descrição

Introdução

Neste exercício-programa você fará um documento em CWEB que deverá charmar-se Make.w ou um programa em C. Este documento ou programa utilizará a biblioteca do SGB para a criação e recuperação de um `grafo de dependências', como descrito a seguir.

O seu programa deverá chamar-se Make. O `M' maiúsculo em Make é para diferenciar o seu programa do utilitário make do UNIX. Da mesma maneira que o make, o seu programa, ao ser chamado na linha de comando sem opção alguma, lerá um arquivo de nome MakeFile contendo informações de dependências e comandos para reconstrução (rebuilding comandos) de objetos. O `F' maiúsculo em MakeFile é para não confundir a entrada do Make com o arquivo Makefile; que você muito provavelmente estará utilizando.

Formato de um MakeFile

Um MakeFile consiste de uma seqüência de `regras' com o seguinte formato:
  <target> :  <dependências>
         <comando1>
         <comando2>
         ...

Um target é usualmente o nome de um arquivo que é gerado por um programa; exemplos de targets são arquivos executáveis (bin ou .o). Um target também pode ser o nome de uma ação, tal como `clean'.

Uma dependência é um arquivo que é usado para criar o target. Um target pode depender de vários arquivos.

Um comando é uma ação que Make pode mandar que seja executada. Um regra pode ter mais que um comando, cada um em uma linha. Nota: deve existir um caractere TAB ('\t') no início de cada linha.

Exemplo de um MakeFile

A seguir está um exemplo típico de um arquivo MakeFile que seu programa Make deve ser capaz de tratar (os números das linhas foram colocada apenas para efeito de referência, eles não fazem parte do MakeFile):

1   meuprog: meuprog.o  fila.o  etc.o
2         gcc meuprog.o fila.o etc.o -o meuprog 
3
4   meuprog.o:  meuprog.c  minhasdefs.h  fila.h   etc.h
5         gcc -c meuprog.c
6
7   fila.o:  fila.c minhasdefs.h  fila.h
8         gcc -c fila.c
9
10  etc.o: etc.c  minhasdefs.h  etc.h
11        gcc -c etc.c
12
13  clean:
14        rm -f meuprog.o fila.o etc.o
Este exemplo ilustra o formato do arquivo MakeFile que o seu programa deve tratar:

Linhas 1, 4, 7, 10 e 13 fornecem informações de dependência. Em uma linha de dependência o nome de um target files aparece primeiro, seguido por dois pontos (`:'). Depois dos dois pontos segue-se uma lista (possivelmente vazia) de arquivos dos quais o target depende. A Linha 7, por exemplo, diz que o target file fila.o depende dos arquivos fila.c, minhasdefs.h e fila.h.

Uma linha de dependência é seguida por uma ou mais linhas com comandos (rebuild comandos) que criam ou atualizam o target file especificado na linha de dependência. O primeiro caracere de um linha contendo um comando deve ser um TAB (`\t'); uma, aparentemente idêntica, seqüência de espaços não pode ser usada no lugar do TAB. Linhas 2, 5, 8, 11 e 14 contém comandos. A Linha 8, por exemplo, diz que o comando

meu_prompt> gcc -c fila.c

deverá ser executado para reconstruir o target fila.o a partir dos arquivos fila.c, minhasdefs.h e fila.h. Neste particular MakeFile, cada linha de dependência é seguida por apenas uma linha de comando. Seu programa, entretanto, deve ser capaz de tratar o caso em que várias linhas de comando seguem uma linha de dependência.

As linhas em branco (linhas 3, 6, 9 e 12) são ignoradas. Estas linhas podem estar presente apenas para facilitar a legibilidade do MakeFile, mas elas não são necessária. Um Makefile similar, mas sem linhas em branco, também deve ser aceito pelo seu Make.

Grafo de dependências

As linhas de dependências em um MakeFile (ou Makefile) definem um grafo de dependências: um grafo onde os vértices correspondem a targets e arquivos de dependências, e os arcos representam a dependência entre estes targets e arquivos. Sempre que um arquivo arquivo1 depende de um arquivo arquivo2 o correspondente grafo de dependências deve conter um arco do vértice de nome arquivo1 ao vértice de nome arquivo2. A Linha 7 do nosso exemplo de MakeFile acima diz que o respectivo grafo de dependências contém arcos do vértice de nome fila.o ao vértice de nome fila.c, ao vértice de nome minhasdefs.h, e ao vértice fila.h.

Um ciclo em um grafo é caminho fechado. Mais precisamente, um ciclo é uma seqüência de vértices <v1,v2,...,vk> tal que

existe um arco de vi a vi+1 (i=1,...,k-1) e um arco de vk a v1.

Um grafo de depêndencias não deve conter ciclos. Isto, entretanto, não é obstáculo para que um grafo de dependência correspondente a um arquivo MakeFile tenha circuitos. Isto apenas significa que o Make deverá ser capaz de verificar a presença ou não de circlos no grafo de dependências. A existência de ciclos deve ser considerada pelo Make como um erro na especificação do MakeFile. Detecção de ciclos será o objeto de estudo do Exercício-Programa 3. (Por enquanto não se preocupe com isto.)

O que Make deverá fazer

O programa Make poderá ser chamado em uma linha de comando do shell de duas maneiras diferentes:

meu_prompt> Make

Chamado desta maneira Make deverá procura no diretório corrente um arquivo de no MakeFile e processá-lo da seguinte forma:

  1. (Contrói o grafo de dependências) Make lê o arquivo MakeFile, que tem o formato descrito acima, e contrói o correspondente grafo de dependências usando o módulo GB_GRAPH do SGB.
  2. (Salva o grafo) Após a criação do grafo de dependências na representação interna do SGB, Make converte este grafo para a representação simbólica (texto) do SGB e salva está representação no diretório corrente em um arquivo de nome MakeFile.gb. Tudo isto corresponde a uma simples chamada da rotina gb_save do módulo GB_SAVE do SGB.
meu_prompt> Make -g

Chamado desta forma Make deve procurar no diretório corrente um arquivo de nome MakeFile.gb e processá-lo da seguinte maneira:

  1. (Restaura o grafo de dependências)  Make lê o arquivo Makefile.gb, que contém um grafo de dependências no formato simbólico (texto) do SGB, e converte este grafo para a representação interna do SGB. Tudo isto corresponde a uma simples chamada da rotina restore_graph do módulo GB_SAVE do SGB.
  2. (Recria o MakeFileMake percorre o grafo de dependências, produz e envia para a saída padrão (stdout) o MakeFile que gerou este grafo de dependências. Assim, ao percorrer o grafo de dependências Make envia para a saída padrão algo parecido com o exemplo de MakeFile acima, possivelmente sem as linhas em branco ou trocando alguma das regras de lugar; esta ordem é irrelevante, ela depende de como o seu Make gerou o grafo.

Representação interna de um grafo de dependências

O seu programa deve usar a biblioteca do SGB para a representação interna de um grafo de dependências. Você usará os módulos GB_GRAPH e GB_SAVE. Veja exemplos de como utilizareste módulos em
http://www.ime.usp.br/~coelho/grafos/sgb/exemplos/

Um vértice v, apontador para um registro ou estrutura do tipo Vertex, deve conter:

Por enquanto acho que é só isto que precisamos de um Vertex. No Exercício-programa 3 precisaremos de guardar mais informações.

Fazendo a busca por um vértice

O seu programa fará várias vezes a busca por um vértice, dado o seu nome. As seguintes rotinas podem ser usadas para isto. Elas fazem parte do módulo GB_GRAPH do SGB.

Important: Utility fields u and v of each vertex are reserved for use by the search routine when hashing is active. You can crash the system if you try to fool around with these values yourself, or if you use any subroutines that change those fields. The first two characters in the current graph's util_types field should be VV if the hash table information is to be saved by GB_SAVE.

Warning: Users of this hash scheme must preserve the number of vertices g->n in the current graph g. If g->n is changed, the hash table will be worthless, unless hash_setup is used to rehash everything.

Caution: This hash coding scheme is system-dependent, because it uses the system's character codes. If you create a graph on a machine with ASCII code and save it with GB_SAVE, and if you subsequently ship the resulting text file to some friend whose machine does not use ASCII code, your friend will have to rebuild the hash structure with hash_setup before being able to use hash_lookup successfully.

Problema com alocação dinâmica de vértices

Como devemos alocar o grafo já que o número de vértices não é fixado logo de cara? A estrutura do SGB permite que arcos sejam alocados dinamicamente, mas não permite que o mesmo seja feito com vértices. Uma possibilidade é fazer um tipo de realloc; podemos chamar de gb_alloc. Que tal? Por favor, proponham suas soluções para este problema.


Entrega e Prazos

  1. Este exercício-programa vale 15 pontos.
  2. Como sempre, somente entregue seu programa se o mesmo não apresentar erros de compilação. O processo de compilação aqui deve ser entendido como todo o processo cweave, latex, ctangle (se você fez em CWEB) e gcc.
  3. Junto com o seu programa você deve entregar um Makefile de tal forma que
    meu_prompt> make Make.tex
    gere o documento Make.tex correspondente ao seu EP2,
    meu_prompt> make Make.dvi
    produza o Make.dvi correspondente ao seu EP2,
    meu_prompt> make Make.c
    crie o arquivo Make.c correspondente ao seu EP2, e
    meu_prompt> make Make
    produza o executável de nome Make correspondente ao seu EP2.
  4. O Panda não costuma aceitar a entrega de vários arquivos. Por isto, você deve criar um arquivo ep2.tar.gz com todo o seu trabalho. Espera-se que
    meu_prompt>tar -xvf ep2.tar
    crie um diretório que tenha o seu login na rede Linux como nome. Neste diretório devem estar todos os arquivos do seu EP2, inclusive o Makefile que você usou. Se você achar necessários coloque junto um arquivo 00-leia-me, onde você descreve algo que achar necessário: como limitações e bugs no seu programa
  5. O seu EP2 deve ser depósitado no panda até o 24:00 do dia 24 MAR 2003 (dois dias antes da primeira prova).

[Página principal de algoritmos em grafos. | Exercícios-programas]
Last modified: Tue Mar 25 17:14:45 EST 2003