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.

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:
#include <GL/glut.h> void initFunc() { glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); } void reshapeFunc(int width, int height) { float aspect = 0.0f; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); height = (height == 0 ? 1 : height); aspect = (float) width / (float) height; gluPerspective(45.0f, aspect, 0.01f, 100.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void displayFunc() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(2.0f, 2.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); glLineWidth(2.0f); glColor3f(0.15f, 0.45f, 0.15f); glutWireCube(1.0f); glColor3f(0.5f, 1.0f, 0.5f); glutSolidCube(1.0f); glutSwapBuffers(); } int main(int count, char* args[]) { glutInit(&count, args); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(800, 600); glutInitWindowPosition(50, 100); glutCreateWindow("OpenGL Cube"); glutReshapeFunc(reshapeFunc); glutDisplayFunc(displayFunc); glutIdleFunc(displayFunc); initFunc(); glutMainLoop(); return 0; }
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.
Executando-se a aplicação, temos a Figura 3 como resultado.

Figura 3: Aplicação OpenGL executando
Conclusão
|
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 |



