Ir para o conteúdo
PsySc0rpi0n

Makefiles - Dúvidas

Mensagens Recomendadas

PsySc0rpi0n

Boas...

Tenho estado a ler o artigo do pwseo que com todo o mérito foi escolhido para artigo de capa da 37ª edição da Revista a Programar aqui do site mas estou com algumas dúvidas.

Na página 9 da revista, o artigo tem o seguinte code:

SOURCES := $(wildcards *.c)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))

Depois na explicação deste código, diz que todos os files .c serão substituídos por ficheiros .o preservando o texto representado por %.

Então é como que se renomeássemos os files .c para files .o? Não estou a perceber o que se pretende aqui.

Se os files .c forem substituídos por files .o, ficamos sem os files .c para alterarmos mais tarde se houver necessidade.

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

PsySc0rpi0n,

A substituição é feita na lista de nomes contida na variável SOURCES, ou seja, não há alteração do nome de ficheiro nenhum no disco. No entanto, é criada uma nova lista (chamada OBJECTS) cujo conteúdo é igual à lista SOURCES mas com todas as terminações .c substituídas por .o.

Resumindo: não acontece nada aos ficheiros. Apenas pegamos numa lista de nomes terminados em .c e fazemos, a partir dela, uma lista com os mesmos nomes, mas terminados em .o.

Editado por pwseo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Na página 11, o que o comando sed faz é escrever num ficheiro o que ele lá tem à frente, certo? E aquilo é RegEx não é?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

O comando sed recebe texto do gcc e altera-o. Posteriormente, esse texto é guardado num ficheiro com a extensão .d. Na revista, ainda na página 11, tens um exemplo do output do gcc antes e depois de ser alterado pelo sed.

E sim, é regex. Mas o sed por si só não escreve nada para ficheiro nenhum... isso é feito graças ao operador de redireccionamento >, que obriga a que o output seja escrito no ficheiro representado pela variável $@.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Ok...Olha estou a ver pela net uns tutos sobre Regex e sobre o sed e só ainda não vi o que significa o parte da expressão. Vou aqui dizer o que entendi a ver se está certo.

1 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Comando substituir, usando o separador !.

2 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Encontrar no início da linha o caracter '('.

3 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Aqui não sei bem o significado do '.' e não sei se o '\+' é o caractere '+' ou se tem outro significado.

4 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Encontrar o caracter ')'.

5 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Encontrar os caracteres '.o:'

6 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Caractere de separação.

7 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Variável DEPDIR seguida da barra '/'

8 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Aqui também não sei. Só conheço o 'd' que é para apagar. Não sei se é para representar o caractere '1' seguido de '.d' ou se é para considerar '\1' uma parte e o '.o:' outra parte diferente.

9 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Variável OBJDIR seguida da barra '/'.

10 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Aqui é a mesma dúvida que em 8.

11 -

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Mesmo que em 6.

sed 's!^\(.\+\).o: !$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $@'

Enviar resultado para a variável $@ que é o nome do alvo...

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Entretanto eu editei a mensagem e agora fiquei naquela... O '!' pertence ao sed ou à expressão regular? É que o caractere a seguir ao 's' representa o caractere de separação a usar...

Estive a ver aqui.

http://www.tutorialspoint.com/unix/unix-regular-expressions.htm

Não consegui colocar o link na palavra "aqui". Quando clico em "Guardar Alterações" o link desaparece!

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Ao contrário do referido no link o HappyHippyHippo encontrou, o ponto de exclamação não tem significado nenhum nas expressões regulares, e neste caso específico nem sequer faz parte da expressão mas sim do comando passado ao sed: 's!padrao!substituição!'.

Por uma questão de hábito e conveniência, eu utilizo ! em vez de / nos comandos do sed (e do vim): 's!a!b!' é o mesmo que 's/a/b/' e que 's#a#b#' e que 's|a|b|', etc.

Mas Psysc0rpi0n, eu disse especificamente que essa parte fugia do âmbito do artigo, o que significa que foge do âmbito dos Makefiles propriamente ditos... Se te meteres agora a aprender a usar o sed, amanhã irás desviar-te para outro programa, depois de amanhã para ainda outro, e quando deres por ti, já saíste completamente do domínio dos Makefiles.

Tens que saber focar-te numa tarefa.

Editado por pwseo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Sim, eu li pwseo. Tens razão, mas é suposto eu saber construir Makefiles sem saber o que está neste parte do How To??? Ao colocar essa "expressão" a coisa funciona sempre? Eu penso que não porque também deve depender de como tenho a estrutura de directórios, ou não?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

O tutorial é sobre Makefiles, e sim, é suposto conseguires construí-los sem saber utilizar o sed. Aliás, muitos dos meus makefiles não utilizam o sed porque dizem respeito a pequenos projectos nos quais eu mesmo acrescento as dependências manualmente.

Para o tutorial, o que interessa é que o sed converte o texto main.o em deps/main.d obj/main.o. Para compreender o tutorial é só isto que precisamos de saber acerca da função do sed. Mais que isso, foge ao âmbito do tutorial e, mais ainda, foge ao âmbito desta thread.

Claro que ninguém te impede de aprenderes como funcionam as expressões regulares ou o sed (aliás, eu encorajo todos os programadores a fazerem isso mesmo!), mas não é suposto, num texto sobre makefiles (ou uma thread), estarmos a discutir a fundo um programa auxiliar.

De qualquer das formas, aqui fica uma breve explicação do comando:

Sempre que vês $(variavel), isso significa que esse texto vai ser substituído pelo valor da variável em questão. Esta substituição é feita pelo make, o que significa que o sed nunca vai ler "$(variavel)" em lado nenhum, mas sim o seu valor.

Os comandos \( e \) servem para criar um grupo. Nalguns motores de regex, podemos usar os parêntesis directamente sem barras, mas por defeito o sed exige as barras. Para que queremos criar grupos? para podermos utilizá-los mais tarde, com \1, \2, etc (pela ordem em que são criados).

O ponto final significa "qualquer carácter", e o código \+ (o sed exige a colocação da barra, por defeito) significa "um ou mais do especificado anteriormente": um ou mais de qualquer carácter, portanto.

O sed verá então o seguinte comando:

s!^\(.\+\).o:!deps/\1.d obj/\1.o:!

Isso fará com que ele isole o nome do ficheiro procure por um nome qualquer seguido de ".o", isola esse nome num grupo (o grupo 1, portanto), e substitui tudo isso por "deps/\1.d obj/\1.o", substituindo \1 pelo nome isolado anteriormente.

A parte que diz "> $@" não diz respeito ao sed nem ao make mas sim à shell, e indica que o output do sed deverá ser escrito no ficheiro representado por $@ (que varia consoante a regra em execução).

Em caso de mais dúvidas, sugiro que cries um tópico na secção apropriada, porque um tópico sobre Makefiles não pode incluir uma discussão detalhada sobre coisas que lhe são alheias...

Editado por pwseo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Ok, pwseo...

Bem, eu tentei seguir o How To mas não consegui fazer o Makefile...

Os meus programas, claro que não precisam de Makefiles nem tão pouco de vários files. Mas eu quero começar a saber como repartir com lógica os programas, sejam pequenos ou grandes. E quero também aprender a fazer os Makefiles...

Então, neste momento eu tenho apenas duas pastas, src e include.

Dentro de cada pasta tenho 3 e 2 ficheiros respectivamente.

O Makefile está assim:

CC := gcc
 6 CFLAGS := -Wall -Werror -Wextra -pedantic -std=c99 -lm
 7
 8 BINDIR := ./bin/
 9 OBJDIR := ./obj/
10 SRCDIR := ./src/
11 DEPDIR := ./depends/
12
13 SOURCES := $(wildcard $(SRCDIR)*.c)
14 OBJECTS := $(patsubst $(SRCDIR)%.c, $(OBJDIR)%.o, $(SOURCES))
15 DEPENDS := $(patsubst $(SRCDIR)%.c, $(DEPDIR)%.d, $(SOURCES))
16
17 $(BINDIR)datastore: $(OBJECTS) | $(BINDIR)
18	 @echo "Linking $@"
19	 @$(CC) -o $@ $^
20
21 $(OBJDIR)%.o: $(SRCDIR)%.c | $(OBJDIR)
22	 @echo "Compiling $*.c"
23	 @(CC) $(CFLAGS) -c -o $@ $<
24
25 $(BINDIR) $(OBJDIR):
26	 @mkdir $@
27
28 uninstall:
29	 @rm -f $(BINDIR)*
30	 @rm -f $(OBJDIR)*
31 .PHONY: uninstall

Ao fazer make, dá o seguinte erro:

Compiling fileman.c
/bin/sh: 1: Syntax error: word unexpected
make: ** [obj/fileman.o] Erro 2

Não sei ao que se refere o erro 2 nem qual é a "word unexpected"...

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Estive a ver agora que nem compilando directamente, está a dar.

Tenho a seguinte estrutura:

DataStore/include

DataStore/src

include/main.h

include/menus.h

src/main.c

src/fileman.c

src/listman.c

Estou a tentar compilar assim dentro da directoria DataStore:

gcc -c -o obj/main.o src/main.c

mas dá o seguinte erro:

src/main.c:1:26: fatal error: include/main.h: Arquivo ou diretório não encontrado
compilation terminated.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Uma vez que estás a utilizar uma directoria de "includes" tens que adaptar o teu Makefile para a incluir no comando de compilação, passando a flag -Idir ao gcc (substituis dir pela directoria dos includes). Omiti esta questão do tutorial porque para pequenos programas não é necessária esta separação de ficheiros .c e .h (e fundamentalmente, não quis complicar ainda mais as relações de dependências).

Quanto ao outro erro do "word unexpected", falta-te o cifrão antes de (CC) na regra que compila os ficheiros .c para .o

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Então qual a diferença do DEPENDS e DEPDIR para o -Idir??? Pensei que esta parte do DEPDIR e DEPENDS era para os includes...

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

DEPENDS é uma lista de ficheiros com a extensão .d que contêm as dependências para um ficheiro de mesmo nome, mas de extensão .c. DEPDIR é a directoria onde armazenamos esses ficheiros .d. Isto é um mecanismo artificial que estruturamos para que o Makefile seja capaz de "encontrar" as dependências sozinho.

Os ficheiros .h que tu tens na directoria de includes só serão encontrados pelo compilador se lhe passares a flag -Idir. Podes definir uma variável chamada INCDIR cujo valor seja a directoria dos includes e passar -I$(INCDIR) às invocações do gcc.

Reconheço que o título "Como lidar com #includes?" poderá induzir as pessoas em erro, mas isso só acontece se tentarem compilar um projecto com uma directoria separada para os includes, como no teu caso. E se estão a tentar isso, então já deveriam estar a par da flag -I, caso contrário nunca conseguiram compilar um projecto semelhante manualmente (e antes de usarmos o make, convém que saibamos utilizar manualmente o compilador que pretendemos automatizar).

Editado por pwseo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Há outra coisa que não percebi.

A ordem pela qual os comandos estão escritos, tem importância, certo?

Então e o comando/regra para criar as pastas vem depois das que criam as listas de objectos e dependências e etc? E também antes do comando/regra que gera o binário?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Ok... Mas continuo sem conseguir fazer a coisa...

Tenho mais perguntas...

1 - Quando temos mais que um source file (.c) temos que usar o DEPDIR e o DEPENDS, certo?

2 - A flag -I. indica para procurar na actual directoria '.' por files .h, certo?

3- Se tenho files .h na directoria include/, a flag passa a ser -I.include/, certo?

4 - A variável $(TARGET) não precisa de ser definida, pois não? O que tem ela e onde vai buscar o seu conteúdo?

5 - A mesma questão para $(LFLAGS)

6 - Explicar o $(@D) em @mkdir -p $(@D)...

As duas últimas questões vêm de um Makefile que estou a ver que não é da minha autoria... Estava só a tentar entender...

PS:

A acção de "linking" não é a criação dos files .o a partir dos files .c? E a compilação não é a criação dos executáveis a partir dos .c ou dos .o caso existam?

Editado por PsySc0rpi0n

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

1. Não. Podes gerir as dependências manualmente.

2. Sim, mais ou menos. A flag -I acrescenta a directoria especificada à path, que pode ser usada para encontrar (não só, mas também) ficheiros .h

3. Não. '.include' é uma directora por si só. O que tu queres é './include'

4. Qual é a variável $(TARGET)? Eu não a defini.

5. O mesmo que a anterior. Eu não defini nenhuma $(LFLAGS).

6. $(@D) é o mesmo que extrair a directoria de um caminho contido na variável $@ (ex.: se $@ for 'foo/bar.o', então $(@D) será 'foo')

Adenda (entretanto editaste o teu post):

Compilar é transformar o código-fonte (ficheiros .o) em instruções válidas para o processador (ficheiros .o), e linking é o passo final de juntar tudo num único executável, devidamente ligado às libs do sistema (se necessário). Para pequenos programas podemos fazer tudo de uma vez (ex.: gcc -o test test.c).

Se reparares bem, a flag -c (que resulta na criação de ficheiros .o) é descrita como impedindo a fase de linking, contradizendo o que pensavas.

Editado por pwseo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
Skun Fly

PS:

A acção de "linking" não é a criação dos files .o a partir dos files .c? E a compilação não é a criação dos executáveis a partir dos .c ou dos .o caso existam?

é isso.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

1. Não. Podes gerir as dependências manualmente.

2. Sim, mais ou menos. A flag -I acrescenta a directoria especificada à path, que pode ser usada para encontrar (não só, mas também) ficheiros .c

3. Não. '.include' é uma directora por si só. O que tu queres é './include'

4. Qual é a variável $(TARGET)? Eu não a defini.

5. O mesmo que a anterior. Eu não defini nenhuma $(LFLAGS).

6. $(@D) é o mesmo que extrair a directoria de um caminho contido na variável $@ (ex.: se $@ for 'foo/bar.o', então $(@D) será 'foo')

Adenda (entretanto editaste o teu post):

Compilar é transformar o código-fonte (ficheiros .o) em instruções válidas para o processador (ficheiros .o), e linking é o passo final de juntar tudo num único executável, devidamente ligado às libs do sistema (se necessário). Para pequenos programas podemos fazer tudo de uma vez (ex.: gcc -o test test.c).

Se reparares bem, a flag -c (que resulta na criação de ficheiros .o) é descrita como impedindo a fase de linking, contradizendo o que pensavas.

Agora não sei se te enganaste no que está a vermelho ou não. Se não te enganaste, estou ainda confuso.

2 - Em ficheiros .c ou ficheiros .h? Estamos a falar de includes, certo?

E então para guardar em INCLUDE os files .h faço assim:

INCDIR := -I./include/

????

4 e 5 - O Makefile que estava a ver se entendia era este - http://pastebin.com/dz3yNchE

é isso.

Pelo que percebi do que o pwseo disse, é ao contrário:

Compilar: .c e .h -> .o

Linkar: .o -> executável

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Tens razão, ali eu queria dizer ficheiros .h. Já corrigi, obrigado pelo reparo.

No ficheiro que estás a ver tem lá a definição de TARGET. LFLAGS será obtida das variáveis de ambiente no momento da execução.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
PsySc0rpi0n

Então, tentado resumir, que é para ver se consigo perceber o que se passa para o meu Makefile não estar a funcionar:

No Makefile tem que estar:

1 - As directorias que fazem parte do projecto e as que precisam de ser criadas, certo?

2 - As listas de files .c, listas de objectos e dependências e includes. (Se puderes, explica-me melhor as diferenças das dependências e dos includes)

3 - A regra/alvo que cria os ficheiros .o, a partir dos .c e dos .h.

4 - A regra/alvo que cria o file executável a partir dos files .o.

5 - A regra/alvo que apaga os files/directorias criadas...

É mais ou menos isto?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.