Um momento
Aula 08
Cursos / SDL - Desenvolvimento de Jogos - libsdl SDL2
Compilação Conveniente do Jogo com o make Através do Makefile

Summary

Resumo da Aula sobre Makefile

Introdução

Na aula de hoje, aprendemos a criar um Makefile para facilitar o processo de compilação usando o comando make. O objetivo é simplificar a compilação de programas que têm múltiplos arquivos fonte e dependências.

Compilação Sem Makefile

  • A compilação manual de cada arquivo CPP usando g++ requer listar todos os arquivos e bibliotecas, o que se torna inviável em projetos grandes.
  • O comando make facilitará a recompilação apenas dos arquivos que mudaram, economizando tempo.

Estrutura do Makefile

  • O Makefile consiste em definir alvos, que são seguidos de pré-requisitos e comandos a serem executados.

Exemplos de Comandos e Estrutura

labirinto: main.o jogo.o
	g++ -o labirinto main.o jogo.o -lSDL2

main.o: main.cpp
	g++ -c main.cpp

jogo.o: jogo.cpp
	g++ -c jogo.cpp

Benefícios do Makefile

  • Apenas recompila os arquivos que foram modificados, ao invés de compilar todos os arquivos do zero, economizando tempo e recursos.

Organização do Projeto

  • Para melhor organização, o executável e arquivos de objeto foram movidos para uma pasta build.
  • Um alvo build é criado para gerar a pasta, se ela não existir.

Estrutura Final do Makefile

build: 
	mkdir build

labirinto: build/labirinto

build/labirinto: build/main.o build/jogo.o
	g++ -o build/labirinto build/main.o build/jogo.o -lSDL2

build/main.o: main.cpp
	g++ -c main.cpp -o build/main.o

build/jogo.o: jogo.cpp
	g++ -c jogo.cpp -o build/jogo.o

Copiando Arquivos Necessários

  • Para que o programa funcione corretamente, foi desenvolvido um alvo para copiar o arquivo mapa_do_jogo.txt para a pasta build.

Comando para Copiar o Mapa

build/mapa_do_jogo: mapa_do_jogo.txt
	cp mapa_do_jogo.txt build/mapa_do_jogo.txt

Considerações Finais

  • Aprendemos como construir um Makefile que organiza a compilação de um projeto em C++.
  • O uso de Makefile separa o código-fonte dos arquivos compilados e executáveis, tornando a estrutura do projeto mais limpa.
  • Sugestões para melhorias incluem a criação de uma pasta src para os arquivos fonte.

Agradecemos pela participação e até a próxima aula!

Video Transcript

Olá pessoal, estamos de volta com mais uma aula, vamos aprender a criar um makefile para poder facilitar o comando de compilação. Então você vê que toda hora que eu tenho que compilar aqui no meu caso eu compilo o programa na linha de comando com o G++, que é parte do GNU Compiler Collection, GCC. Você note que para cada arquivo CPP eu tenho que adicionar essa lista aqui, tenho que fazer os link, o link, o linkar com a opção, né? E finalmente ele gera o arquivo A.out. Bem, o nosso projeto é bem curto, bem pequeno, mas ao longo do tempo quando você tem uma coisa que realmente está na improdução, né? O projeto normalmente é grande, muitos arquivos, muitas bibliotecas para linkar e tal e tal. Então fica muito inconveniente toda vez que eu ter que rodar o comando e ter que listar todos os arquivos e todas as bibliotecas para linkar. É claro que eu estou usando, porque isso vai facilitar para mim porque eu estou usando a linha de comando. É claro que você pode usar o build system da sua IDE e tal e tal, mas no meu caso como eu estou deixando, eu deixo tudo simples e uso a linha de comando, eu vou usar o comando make e através do makefile ele vai detectar se houve alguma mudança nos arquivos e vai recompilar de acordo com aquelas mudanças. Então o benefício do makefile vai ser que a gente vai determinar como os arquivos serão gerados e o make vai detectar também se, por exemplo, você compilou tudo de uma vez, aí você só mudou um arquivo. Na maneira que eu estou fazendo agora, sempre eu tenho que recompilar tudo do zero, né? Mas isso num projeto realmente grande demora para caramba. Então o make vai detectar, opa, você só mudou esse arquivo aqui, então eu só vou recompilar esse arquivo. Todos os outros arquivos de objetos já foram gerados e não há mudança. Tudo que eu tenho que fazer é regenerar o arquivo principal, o programa, o executável. Vamos lá, vamos começar aqui. Eu vou criar um arquivo chamado makefile, primeira leita que vai usc-lo. Agora a maneira que o makefile funciona é o seguinte, você tem que definir o alvo, tá? Seguir de dois pontos, seguido dos pré-requisitos. Aí de baixo você põe uma tab e o comando para poder você gerar esse alvo. Note que não é a espécie de tab, tá? Então o alvo vai ser o que a gente precisa, né? Para poder fazer o nosso processo aqui de compilação. Bem, o alvo principal é o que? É o programa, né? O programa final executável. Então para isso eu vou dar um nome, por exemplo, eu posso chamar de labirinto. Esse vai ser o alvo. Mas para poder eu criar o labirinto vai fazer o quê? Bem, tem uns pré-requisitos, né? Precisa de umas coisas. Mas antes disso eu só vou dar o comando aqui para você ver o equivalente. Então eu tenho o comando aqui no lado esquerdo, que é o do terminal g++, main.cpp, jogo.cpp, praço sdli2, né? Isso vai gerar o executável, tá? Bem, então ele depende do arquivo main e depende da existência também do arquivo jogo, tá? Então os pré-requisitos aqui do labirinto será o quê? Será o main e o jogo, mas não é o arquivo de código fonte, vai ser o arquivo de objeto, tá? Então quando a gente compila alguma coisa, ele gera o arquivo .o, que é o objeto, né? E com esse objeto a gente pega todos os objetos e linka, né? Para poder gerar o executável. Então para esse processo aqui do makefile funcionar direitinho a gente vai, em vez de compilar tudo de uma vez assim, para o executável a gente vai separar em partes e vai compilar o arquivo de objeto para cada arquivo cpp, tá? Então na verdade aqui a gente vai precisar com o pré-requisito main.o e o jogo.o, que serão os arquivos de objetos e nós iremos gerar. Então para cada pré-requisito aqui você tem que definir outro, outra entrada de algo, né? No caso o main.o dois pontos, esse é o alvo, corresponde a esse aqui, tá? Então para o main.o pré-requisito é que o arquivo main.cpp exista, tá? Então a existência do main.cpp é o pré-requisito. Mas como é que gera o main.o? Para poder gerar isso você tem que compilar com o g mais mais, no meu caso. O main.cpp, mas o problema aqui é que para poder nomear dessa maneira aqui e não compilar para executar você tem que usar a opção traça c, tá? Só para gerar o arquivo de objeto. A mesma coisa aqui para o jogo, o jogo.o depende do que? Do jogo.cpp existir para poder gerar o jogo.aj mais mais para c, o jogo.cpp. Eu estou pensando aqui, não sei se ele vai gerar o arquivo de objeto já baseado no nome ou se eu tenho que especificar com a opção traço o. Isso que eu estou pensando agora, eu vou ver, vamos ver, tá? Então... Deixa eu ver o negócio aqui. Isso é esse então, vamos ver aqui. Eu vou fechar aqui e vou salvar o makefile. Deixa eu só ver se eu não esqueci nada, main.o, jogo.o, g mais mais. Então eu esqueci o nome aqui do executável, eu vou botar traço o labirinto para, em vez de criar a.out, ele vai nomear labirinto que é realmente o nome do alvo, tá? Vamos lá. E em vez de cpp, tá certo? Como a gente criou os arquivos de objeto, a gente vai usar o main.o e o jogo.o, tá? Agora eu vou dar só falar make na linha de comando aqui no lado esquerdo. Deixa eu ver se ele realmente criou. Então, você vê que o make executou o comando g mais mais traço c, main.cpp gerou o arquivo main.o que está aqui. Depois fez o g mais mais traço c, jogo.cpp gerou o jogo.o que está aqui. E finalmente ele gerou o alvo principal que é o labirinto g mais mais traço o labirinto, main.o, jogo.o, traço l, sdl2. E você não haja que apareça aqui o labirinto, o executável de cpp.barra labirinto, labirinto, ele é executa o jogo, tá? Tá bom? Então o que acontece aqui? Então você define o alvo principal chamado labirinto que vai ser o nome do executável que será gerado. Ele depende de duas coisas, depende do main.o existe e do jogo.o existe. Bem, antes de executar esse comando aqui debaixo, ele primeiro vai ver e vai resolver essa dependência do main.o. Então vamos lá para o alvo do main.o.o, linha 4, main.o para poder você gerar o main.o.o. Como é que faz? Primeiro precisa da existência dependência do arquivo main.cpp. Então já existe? Sim. Então tudo certo, vamos executar o comando, ele roda os g++, traço c, main.cpp e ele gera o arquivo main.o. Então nem precisa usar o traço o que ele automaticamente deduz o nome do arquivo de saída que é main.o, né? É baseado no nome antes do cpp. Então fez isso, note que é o primeiro comando aqui na linha de comando. Depois que fizer isso ele volta lá para a lista de dependências do labirinto. Agora vai para o jogo.o que é a segunda dependência. Então vamos resolver essa dependência, vamos lá para a linha de alvo do jogo.o. Então o jogo.o depende do arquivo jogo.cpp existir. Já existe? Sim. Tudo certo, então vamos rodar o comando, você note que o comando roda como o segundo comando na... Aqui no terminal. Depois que fazer isso ele vai gerar o arquivo jogo.o. Então as dependências do labirinto, desses alvos já estão satisfeitas, esses dois arquivos agora existem. Então ele finalmente executa o comando g++, traço o labirinto, main.o, jogo.o, traço lsdl2. E isso vai gerar que ele executa o chamado labirinto e vai por fim terminar o processo. Agora quando eu tinha falado, por exemplo, se eu mudar alguma coisa no main.cpp, por exemplo, eu vou adicionar um negócio aqui. Olha que o que acontece se eu dar makef novo. Você note que ele só rodou dois comandos, vez de três, né? Isso porque ele nota que, opa, você só modificou o arquivo main.cpp. O arquivo jogo.cpp não foi modificado, então eu vou usar aquilo que já tinha gerado antes, para poder economizar tempo, né? Então com isso vai ser muito tempo economizado quando você tem um projeto grande com vários arquivos. Se eu até rodar o makef de novo, você vê que ele já fala que o labirinto já está mais atualizado. Isso é, nem precisou compilar nada porque ele já fez a compilação, então ele detecta isso. Detecta que já fez compilação e não há nenhuma mudança no código ponte para poder começar o processo de compilação de novo, tá? Então eu vou reverter esse comentário. Então está tudo certo, tudo legal, mas está muita coisa aqui, muita tralha no mesmo direitório do código ponte, né? Então normalmente a gente separa esses arquivos compilados do próprio código ponte, né? Então normalmente é parte chamada build, ordist, qualquer nome aí. Então para poder fazer isso eu vou fazer o seguinte, no makefile aqui eu quero criar uma parte chamada build. E dentro dessa pasta eu vou colocar todos os arquivos de objeto e o executável. Então como fazer isso? Bem, deixa eu só remover os arquivos aqui para você ver o negócio. Então vou remover todos os arquivos.o e vou remover o labirinto que é o executável e o a.out. Então só temos o código ponte. Tá bom? Agora vamos ver aqui. Então mais uma coisa que eu quero fazer. Então o labirinto, em vez de ser na mesma pasta, eu vou botar aqui build barra, tá? Então vai ser um arquivo que vai parecer dentro da pasta build. Agora build não existe ainda, então eu preciso fazer uma dependência aqui chamada build, tá? Então essa dependência do build, primeiro você adiciona o alvo aqui, build, como é que... O que é que build depende? Bem, o build depende na verdade nada, porque não existe ainda. Então para poder criar o build você vai fazer o comando para criar a pasta, que é o mkdir... dir, né? mkdir, makedir... build. Então quando ele bate aqui no build bar labirinto, ele vai, primeiro, resolver a dependência do build, vai bater aqui no build, vai rodar o comando para criar a pasta. Pronto, vai criar a pasta e já existe. Aí ele vai para o próximo. Bem, no próximo eu quero modificar isso, né? Eu não quero mais no mesmo diretor, então vou botar buildbarra main.o e buildbarra jogo.o. E para resolver essas dependências você vai lá e modifica o nome, que é o alvo mudou, né? Mudou para buildbarra main.o e mudou buildbarra jogo.o. Agora vamos ver aqui. Bem, o diretório presente tem main.cpp, então tá certo. g++, tá certo, tudo certo. Agora eu acho que tem um detalhe aqui. Eu não sei se ele vai criar na mesma pasta ou no build. Eu acho que a gente vai precisar usar o traço.o. Mas vamos ver já já. Voltando aqui na dependência, tem um comando. Bem, o comando vai gerar o executava agora no build barra labirinto, tá? E o main.o e o jogo.o tá dentro do build barra main.o e buildbarra jogo.o. Agora o negócio aqui que eu acho que não vai dar certo é o que a gente vai precisar do traço.o aqui nas linhas 8 e 11. Vamos ver. Eu vou dizer a e-mail aqui no terminal. A você note que ele já gerou o main.o e o jogo.o na mesma pasta que tá errado, né? Então por isso que eu tinha falado antes. Então vou remover esses caras.o e vou modificar aqui e vou adicionar o traço.o buildbarra main.o. E no mesmo comando para poder criar o buildbarra jogo.o. Eu vou botar traço.o, output, né? Build barra jogo.o. Por isso eu vou rodar a make de novo e você note que ele primeiro gerou o buildbarra main.o depois gerou o jogo ou buildbarra jogo.o. Finalmente ele rodou o comando para gerar o executava labirinto na pasta build. Então tá tudo legal se eu rodar o buildbarra labirinto aqui. Ele funciona direitinho, tá? Ah, só um detalhe aqui que você pode ter problema. Se eu mudar direitório para pasta build e rodar o labirinto eu acho que vai ter problema no mapa, tá? E é, como eu tinha esperado. O problema é porque quando eu estava na pasta terrio de cima, o mapa do jogo estava aqui, né? Aí na hora do programa lá de rodar no jogo.cpp você note que quando ele carrega o mapa ele carrega do pasta, mesmo pasta que o executava estar, tá? Então esse é um problema e você vai ter. Então para poder consertar isso você pode fazer uma maneira aqui. Você pode copiar o arquivo mapa do jogo para a pasta build. Que vai ser uma espécie de compilação, mas a verdade é só a cópia do arquivo. Então o buildbarra labirinto também vai depender do mapa do jogo.txt. E aí você adiciona aqui embaixo o alvo mapa do jogo, dois pontos para a requisição do arquivo, né? Existir, tá? Na verdade eu errei, desculpa, eu tenho que botar buildbarra mapa do jogo, né? Porque isso que vai ser o gerado e esse alvo buildbarra mapa do jogo, ele vai depender do quê? Vai depender de a existência do arquivo mapa do jogo.txt na pasta-raiz do projeto, tá? Você vai rodar um meio que da pasta-raiz, tá certo? Detalhe. Para poder gerar essa arquiva é simples, é só copiar o arquivo. Então eu vou dar cpmapadojogo.txt no para destino buildbarramapadojogo.txt. Bem simples. Tá? Então vamos dar um make-new e ver como é que dá. Ah, eu estou na pasta-raiz, não tem que dar o cd.ponto para voltar para a pasta-raiz do projeto e falar a mente. Você note que agora tem a parte do mapa do jogo dentro do mesmo executável do labirinto, né? Então se eu cd para o build e rodar.barra labirinto, ele não vai ter mais esse problema, tá? Mas vou te dizer um negócio, se eu cd barra barra, se eu cd 2.2.pontos e dar o build barra labirinto aqui e eu tivesse, se eu remover o mapa do jogo aqui, a pasta-raiz também vai dar problema, né? Porque ele sempre vai ver de que pasta você rodou o programa. Da parte que você rodou o programa, na verdade, é que ele vai pegar o arquivo. Por isso que esse caminho aqui relativo é meio problemático, tá? Então deixa aí como uma péssica, para você ver se pode melhorar isso, porque pode dar problema se você rodar de uma pasta diferente. Tá bom? Então deixa eu reverter aqui. Pronto. Pronto. Então makefile. Tá bom? Então vamos avisar o que a gente fez nessa aula. Nós aprendemos a fazer o makefile para poder usar o prédio. Tá bom? Então vamos avisar o que a gente fez nessa aula. Nós aprendemos a fazer o makefile para poder usar o programa make, para poder facilitar a compilação na linha de comando. Make você faz a Alvos, que tem pré-requisitos, e depois de cada pré-requisito ser resolvido, ser... Ele executa o comando, né? Então tem sempre Alvo, dois pontos, pré-requisitos, e na nova linha, depois de um tab, tem o comando para poder gerar aquele Alvo. Nós fizemos um Alvo para poder gerar a pasta build, um Alvo para poder gerar a pasta build.mail.o, arquivo de objeto. Fizemos um Alvo para gerar o arquivo build.jogo.o, e finalmente um Alvo para gerar o arquivo build.mapa.jogo.txt. E simplesmente copia o mapa do jogo para a pasta build, que é a pasta executável, né? Então tudo vai para essa pasta build para a gente poder separar o que foi compilado, exáctável e tal do próprio código-fonte. Normalmente, código-fonte também é posto em uma pasta separada chamada src, por exemplo, mas eu deixei assim mesmo na pasta raiz. Se você quiser, pode adicionar a pasta src para os arquivos código-fonte lá. Se você fizer isso, você vai ter que mudar esse makefire para poder botar src antes de main.cpp, jg.cpp e tal, tá? Então por essa aula só, muito obrigado por assistir e até a próxima.
Nenhum comentário ainda (loading...)
Nenhum comentário ainda (loading...)
Gostou da aula? 😆👍
Apoie nosso trabalho com uma doação: