Página principal

Desenvolvimento C/Cmaismais no Linux para iniciantes

From CInLUG

Conteúdo

Introdução

Este artigo tem como objetivo fornecer uma configuração modular para o desenvolvimento de aplicações C/C++ de pequeno a médio porte no Linux, utilizando make e makedepend, sem que não haja nenhum conhecimento prévio nestas ferramentas. Nenhuma pretensão em explicar minuciosamente as ferramentas citadas ocorre neste artigo.

Ambiente

O ambiente de desenvolvimento consiste em um editor de textos e algum terminal. O editor será utilizado para a edição dos arquivos headers e de código fonte e o terminal para a compilação completa da aplicação. Para este artigo, estará sendo utilizado o gedit como editor de textos e o GNOME Terminal para a compilação da aplicação.

Configuração

Antes de iniciar o desenvolvimento da aplicação, é necessário configurar o ambiente de desenvolvimento. O primeiro passo é organizar uma hierarquia de diretórios. A Figura 1 mostra a hierarquia utilizada.

Imagem:Image_001.png
Figura 1: Hierarquia dos diretórios

O diretório bin conterá o arquivo executável da aplicação, que pode ter sido compilado nos modos debug ou release. O diretório dependencies conterá as dependências de compilação da aplicação. O sub-diretório include conterá os headers necessários para a compilação e o sub-diretório lib conterá as bibliotecas necessárias para a produção do executável. O diretório include conterá os headers da aplicação [[arquivos .h]. O diretório obj conterá os arquivos intermediários gerados a partir da compilação, os objetos. O diretório resources conterá os recursos necessários à aplicação, tais como arquivos de configuração, imagens, etc. O diretório scripts conterá os scripts necessários para compilação, implantação, etc. da aplicação. Nele ficará, por exemplo, o arquivo Makefile, responsável pelas regras de dependências. Por fim, o diretório src conterá todo o código fonte da aplicação [[arquivos .c ou .cpp].

Após a configuração dos diretórios, é necessário um breve entendimento das ferramentas make e makedepend. A ferramenta make toma decisões sobre quais comandos devem ser executados quando uma dependência é satisfeita. Já a ferramenta makedepend calcula tais dependências entre os headers da aplicação. Por exemplo, se um arquivo A.h inclui o arquivo B.h, então há uma regra de dependência entre A e B. Quando o arquivo B.h for alterado, o arquivo A.h deverá ser recompilado. O arquivo Makefile conterá os comandos a serem realizados para a geração do executável, bem como todas as regras de dependências para a compilação de toda a aplicação. O conteúdo proposto do arquivo é listado a seguir:

BINARY_NAME = app
ARCH = i686
CC = g++
SRC_EXTENSION = cpp
.SUFFIXES = .o .$(SRC_EXTENSION)
SOURCES_DIR = $(shell cd ../src && dirs -l)
RESOURCES_DIR = $(shell cd ../resources && dirs -l)
SCRIPTS_DIR = $(shell cd ../scripts && dirs -l)
SOURCES = $(shell cd $(SOURCES_DIR) && ls *.$(SRC_EXTENSION))
CONFIG_NAME = debug
MODE = -g
OBJECTS = $(SOURCES:.$(SRC_EXTENSION)=.o)
OBJECTS_DIR = $(shell cd ../obj/$(CONFIG_NAME) && dirs -l)
INCLUDE_DIR = $(shell cd ../include && dirs -l)
DEPENDENCIES_DIR = $(shell cd ../dependencies && dirs -l)
DEP_INCLUDE_DIR = $(DEPENDENCIES_DIR)/include
DEP_LIBRARIES_DIR = $(DEPENDENCIES_DIR)/lib
BINARIES_DIR = $(shell cd ../bin/$(CONFIG_NAME) && dirs -l)
DEPENDENCIES = $(foreach dep, $(OBJECTS), $(OBJECTS_DIR)/$(dep))
CFLAGS = $(MODE) -Wall -march=$(ARCH) -mtune=$(ARCH) -I$(INCLUDE_DIR) -I$(DEP_INCLUDE_DIR)
LDFLAGS = -L$(DEP_LIBRARIES_DIR)
CLEAN_COMMAND = rm -f *.o *~ *.bak $(BINARY_NAME)

.$(SRC_EXTENSION).o:
	$(CC) $(CFLAGS) -c $< -o $@

all:
	-@ make create-links
	-@ make $(BINARIES_DIR)/$(BINARY_NAME)
	-@ make remove-links

create-links:
	-@ $(foreach file, $(SOURCES), $(shell ln -fs $(SOURCES_DIR)/$(file) $(OBJECTS_DIR)/$(file)))

remove-links:
	-@ rm -rf $(OBJECTS_DIR)/*.$(SRC_EXTENSION)

$(BINARIES_DIR)/$(BINARY_NAME): $(DEPENDENCIES)
	$(CC) $(LDFLAGS) $(DEPENDENCIES) -o $(BINARIES_DIR)/$(BINARY_NAME)

clean:
	-@ $(CLEAN_COMMAND)
	-@ cd ../ && $(CLEAN_COMMAND)
	-@ cd ../bin/ && $(CLEAN_COMMAND)
	-@ cd ../bin/debug/ && $(CLEAN_COMMAND)
	-@ cd ../bin/release/ && $(CLEAN_COMMAND)
	-@ cd ../obj/debug/ && $(CLEAN_COMMAND)
	-@ cd ../obj/release/ && $(CLEAN_COMMAND)
	-@ cd ../src/ && $(CLEAN_COMMAND)
	-@ cd ../include/ && $(CLEAN_COMMAND)
	-@ make remove-links CONFIG_NAME=release
	-@ make remove-links CONFIG_NAME=debug

release:
	-@ make depend CONFIG_NAME=release
	-@ make CONFIG_NAME=release MODE=-O3

debug:
	-@ make depend CONFIG_NAME=debug
	-@ make CONFIG_NAME=debug MODE=-g

depend:
	-@ cd $(SOURCES_DIR) && makedepend -Y *.$(SRC_EXTENSION) -I$(INCLUDE_DIR) -p$(OBJECTS_DIR)/ -f$(SCRIPTS_DIR)/Makefile

O conteúdo acima não será explicado a fundo, porém algumas macros precisam ser alteradas para adequar o Makefile à aplicação a ser desenvolvida. Por exemplo, a macro BINARY_NAME conterá o nome do executável da aplicação. A macro ARCH conterá a arquitetura para a qual a aplicação será compilada, por exemplo, i386, i486, i586, i686, pentium4, athlon64, etc. A macro CC indicará qual o compilador a ser utilizado. Caso a aplicação seja escrita em C, gcc deve ser utilizado. Caso seja em C++, g++ deve ser utilizado. A macro SRC_EXTENSION é utilizada para indicar a extensão dos códigos fonte. Se a aplicação for escrita em C, SRC_EXTENSION = c, se a aplicação for escrita em C++, SRC_EXTENSION = cpp. Finalmente, a macro LD_FLAGS indica quais bibliotecas estão sendo utilizadas para geração do executável, logo, deve-se complementar a macro com as bibliotecas necessárias, na forma -lNOME_DA_BIBLIOTECA. Por exemplo, -lGL deve ser adicionado, caso a aplicação utilize a biblioteca OpenGL.

Desenvolvimento

Finalizada a configuração de todo o ambiente de desenvolvimento, é necessário reescrever parte do arquivo Makefile. Por exemplo, para desenvolver uma aplicação OpenGL em C, para a arquitetura i686, utilizando o freeglut como gerenciador de janelas, tem-se a seguinte configuração do arquivo Makefile:

BINARY_NAME = openGLApp
ARCH = i686
CC = gcc
SRC_EXTENSION = c
LDFLAGS = -L$(DEP_LIBRARIES_DIR) -lGL -lglut

Com o Makefile criado e satisfazendo as condições iniciais da aplicação, o seu desenvolvimento pode ser iniciado. O próximo passo é adicionar um arquivo main.c ao diretório src com o seguinte código:

  1. #include <GL/glut.h>
  2.  
  3. void initFunc() {
  4. glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
  5. glClearDepth(1.0f);
  6. glEnable(GL_DEPTH_TEST);
  7. glDepthFunc(GL_LEQUAL);
  8. }
  9.  
  10. void reshapeFunc(int width, int height) {
  11. float aspect = 0.0f;
  12. glViewport(0, 0, width, height);
  13.  
  14. glMatrixMode(GL_PROJECTION);
  15. glLoadIdentity();
  16.  
  17. height = (height == 0 ? 1 : height);
  18. aspect = (float) width / (float) height;
  19.  
  20. gluPerspective(45.0f, aspect, 0.01f, 100.0f);
  21.  
  22. glMatrixMode(GL_MODELVIEW);
  23. glLoadIdentity();
  24. }
  25.  
  26. void displayFunc() {
  27. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  28. glLoadIdentity();
  29.  
  30. gluLookAt(2.0f, 2.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
  31.  
  32. glLineWidth(2.0f);
  33. glColor3f(0.15f, 0.45f, 0.15f);
  34. glutWireCube(1.0f);
  35. glColor3f(0.5f, 1.0f, 0.5f);
  36. glutSolidCube(1.0f);
  37.  
  38. glutSwapBuffers();
  39. }
  40.  
  41. int main(int count, char* args[]) {
  42. glutInit(&count, args);
  43. glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
  44. glutInitWindowSize(800, 600);
  45. glutInitWindowPosition(50, 100);
  46. glutCreateWindow("OpenGL Cube");
  47. glutReshapeFunc(reshapeFunc);
  48. glutDisplayFunc(displayFunc);
  49. glutIdleFunc(displayFunc);
  50. initFunc();
  51. glutMainLoop();
  52. return 0;
  53. }
  54.  

Após isso, os seguintes comandos podem ser executados: make depend - para que as regras de dependência sejam criadas, mesmo que nenhum header esteja sendo utilizado. make - para compilar a aplicação e gerar o executável no modo padrão [[debug]. make debug - para calcular as dependências, compilar a aplicação e gerar o executável em modo debug. make release – para calcular as dependências, compilar a aplicação e gerar o executável em modo release. make clean - para remover arquivos de backup, arquivos intermediários e o executável.

Executando-se o comando make release, as dependências são calculadas, pois internamente uma das dependências é o make depend e logo em seguida o processo de compilação é iniciado. O executável mostrado na Figura 2 é gerado no diretório bin/release.

Imagem:Image_002.png
Figura 2: Executável gerado

Executando-se a aplicação, temos a Figura 3 como resultado.

Imagem:Image_003.png
Figura 3: Aplicação OpenGL executando

Conclusão

Imagem:Go-down.png

Esse artigo pode ser baixado nas seguintes extensões:

Aplicações C/C++ de pequeno a médio porte que utilizam a hierarquia de diretórios proposta, bem como o arquivo Makefile, podem ser desenvolvidas no Linux por iniciantes, não requerendo que eles possuam conhecimento em ferramentas específicas.

O desenvolvimento das aplicações se torna simples e não burocrático, requerendo do desenvolvedor que poucas linhas de comando sejam digitadas e executadas.

Autor: Pedro Leite

Imagem:Cc-small.png : Imagem:Cc-by.png Imagem:Cc-nc.png Imagem:Cc-sa.png
Atribuição-Compatilhamento pela mesma licença 2.5

Ferramentas pessoais
Vistas