Jump to content

Recommended Posts

Posted (edited)

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.

Edited by PsySc0rpi0n

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted (edited)

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.

Edited by pwseo
Posted

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 $@.

Posted (edited)

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...

Edited by PsySc0rpi0n

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted (edited)

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!

Edited by PsySc0rpi0n

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted (edited)

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.

Edited by pwseo
Posted

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?

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted (edited)

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...

Edited by pwseo
Posted

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"...

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted

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.

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted

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

Posted (edited)

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).

Edited by pwseo
Posted

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?

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted (edited)

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?

Edited by PsySc0rpi0n

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.