Published on

Makefile nedir ? Nasıl Kullanılır ?

Authors
  • avatar
    Name
    Alperen Önal
    Twitter

Makefile, kodların derlenme sürecini organize edilmesini kolaylaştıran bir araçtır.

Makefile aracı ilk olarak GNU projesi tarafından geliştirilmiştir.

Günümüzde bir proje oluşturduğumuzda derleme işlemleri için IDE tarafından makefile veya ilgili IDE'nin kullandığı aracın dosyaları otomatik olarak formatlanır/oluşturulur bu yüzden bu güne kadar doğrudan karşılaşmamış olabilirsiniz.

Makefile genellikle C/C++ için kullanılmaktadır. Diğer dillerde ise derleme işlemlerinin organizasyonu için makefile benzeri kendi araçlarını kullanırlar.

Örneğin Dev c/c++ IDE'sinde bir c++ projesi oluşturacağım :


Aşağıda görebileceğiniz gibi Dev IDE'sinin oluşturmuş olduğu .dev uzantılı dosya haricinde başka bir dosya mevcut değil.


Şimdi bu kodu derleyelim ve alttaki sonuca ulaşalım. Evet görebileceğimiz gibi makefile dosyası oluşturulmuş(.win uzantısını şimdilik es geçelim) ayrıca link aşaması için .o gibi nesne dosyaları oluşturulmuş ardından çalıştıralabilir bir .exe oluşturulmuş.


Neden Makefile kullanıyoruz ?

  1. Bağımlılık: Kodlarımız genellikle birbirlerine bağımlıdır. Örneğin, bir proje birden fazla kaynak dosyası içerir ve bu kaynak dosyaları diğer dosyalara bağımlıdır. Bu bağımlılıkların doğru şekilde ve zamanda işlenmesi büyük bir önem taşımaktadır. Ayrıca gereksiz yeniden derlemelerin önüne geçer, bir dosya değiştiği zaman tüm dosyaların yeniden derlenmesi yerine sadece değişen dosyayı yeniden derler. Bu, büyük projelerde büyük ölçüde zaman kazandırabilir.

  2. Otamasyon: Makefile, derleme ve link işlemlerini otomatikleştirir. Geliştirici yalnızca "make" komutunu çalıştırarak tüm projeyi linkleyebilir ve derleyebilir.

C projesi üzerinden "neden makefile kullanıyoruz?" sorusunu cevaplayalım.

1-) Makefile Kullanmadığımız senaryo :

  1. Öncelikle src ve include isimli 2 tane dizin oluşturuyorum.

  2. include içerisinde girip math.h isimli header dosyası oluşturuyorum.

  3. src içerisine girip main.c ve math.c isimli 2 adet c dosyası oluşturuyorum.

  4. oluşturmuş olduğum bu 3 dosyanın içeriğini aşağıdaki gibi dolduruyorum.

//math.h
#ifndef FUNCTIONS_H
#define FUNCTIONS_H

int sum(int, int);
#endif

//math.c
#include<stdio.h>
#include "../include/math.h"

int sum(int value1, int value2)
{
	return value1 + value2;

}
//main.c
#include<stdio.h>
#include "../include/math.h"

int main()
{
	int x = 5;
	int y = 3;

	int total = sum(x, y);

	printf("%d\n", total);

	return 0;
}

Evet gelelim şimdi derlemeye.

gcc ile main.c'yi derlemek istediğimde aşağıdaki hatayı alıyorum. Çünkü main.c içerisinde math.c'ye ait kod var ve math.c henüz derlenmemiş. Link(bağlanma) sırasında hata alıyoruz.

Bu sorunu çözmek için math.c dosyasının da .o uzantılı nesne dosyasına ihtiyacımız var yani math.c'yi de main.c ile birlikte derlemeliyiz.

gcc main.c math.c -o program yazarak hata almadan projemi derliyorum. Ve bulunduğum platforma göre bin çıktısını görebiliyorum(windowsda olduğum için .exe)

makefile kullanmadığımız senaryodaki bazı sıkıntılar :

  • Bağımlı olunan her kaynak kodu dosyasının derlenme sürecine manuel olarak eklenmesi gerekiyor.
  • Eğer herhangi bir .c dosyasında ufak bir değişiklik olsa bile tüm kaynak kodunu baştan derliyoruz.(örneğin bu örnekte main.c içerisindeki değişiklikler için math.c'nin de derlenmesine gerek yok.)

Bu sorunları manuel olarak halledebilsek de büyük projelerde bunu yapmamız imkansız ve saçma. Üstelik insani hatalar olması da çok olası.

2-)Makefile Kullandığımız Senaryo

  1. makefile dosyası oluşturup bağımlılıkları belirliyoruz.
  1. make veya belirlediğimiz komutu yazıyoruz.

Evet iş bitmiştir :)


Makefile Nasıl Kullanılır ?

Makefile oluşturmak için makefile isimli dosya oluşturmalıyız.

say_hello:
        echo "Hello World"

makefile dosyamızın bulunduğu dizinde make komutunu çalıştırdığımız zaman çıktımız aşağıdaki gibi olacaktır :

echo "hello world !"
hello world !

Bu örnekte say_hello bir target(hedef)'tir, echo "Hello World" bir recipe(tarif)'tir.

target + prerequisites + recipes = rule'dir.

Yani bu 3'lünün bir araya gelmesi bir rule(kural) oluşturur.

  • Target: İşlem veya işlemler dizisini temsil eder. Genellikle hedef dosya oluşturulmak istenen dosya adı olur. Örneğin bir çalıştırabilir dosyanı, derlenmiş obje dosyasını olabilir.

  • Prerequisite : Bir target'in oluşturulması için gerekli dosyalar veya işlemlerin listesidir. İlgili Target oluşturulmadan önce bu dosyaların mevcut ve güncel olması gerekmektedir. Eğer Prerquistes'deki dosyalardan biri değiştirrilmişse, make o dosyayı ve hedefi yeniden oluşturur.

  • Recipe : Bir target oluşturmak için gereken komutlar dizisidir. Hedef ve ön koşullardan sonra gelen komut satırlarıdır. Recipe satırları kesinlikle "tab" ile başlamalıdır.

Örneğin :
target: prerequisites
<TAB> recipe

veya

program: sub_target main.c
	recipes to create program

sub_target: utility.c
	recipes to create sub_target

Phony Target: Target'in bir dosya olması gerekli değildir. Target'in dosya olmadığı durumlarda bunlara "phony target" diyoruz. Örneğin bir phony target'in kullanım amacı genellikle dosya oluşturmak yerine belirli işlemleri gerçekleştirmektir.

  • Phony Target tanımlamak için .PHONY : phony_target_ismi atamasını gerçekleştirmeliyiz.
Örneğin :
.PHONY: clean

clean:
    rm -f *.o

@ belirteçinin kullanımı: Makefile'da yazdığımız tüm recipe'lar ekrana basılır. Eğer biz bunu istemiyorsak ilgili recipe satırının başına "@" karakterini koymalıyız.

Örneğin :

all: program

program: main.o math.o
	gcc -o program main.o math.o
	echo "the objects have been linked."

main.o: main.c
	gcc -c main.c
	echo "main.c has been complied."

math.o: math.c
	gcc -c math.c
	echo "math.c has been complied."

make yazdıktan sonra çıktısı :

PS C:\Users\Alperen\Desktop\my_c_project\src> make
gcc -c main.c
echo "main.c has been complied."
"main.c has been complied."
gcc -c math.c
echo "math.c has been complied."
"math.c has been complied."
gcc -o program main.o math.o
echo "the objects have been linked."
"the objects have been linked."

Aynı makefile'da bazı noktalara "@" karakterini koyduktan sonra deneyecek olursak :

all: program

program: main.o math.o
	gcc -o program main.o math.o
	@echo "the objects have been linked."

main.o: main.c
	@gcc -c main.c
	@echo "main.c has been complied."

math.o: math.c
	@gcc -c math.c
	@echo "math.c has been complied."

make yazdıktan sonra çıktısı :

PS C:\Users\Alperen\Desktop\my_c_project\src> make
"main.c has been complied."
"math.c has been complied."
gcc -o program main.o math.o
"the objects have been linked."

Default Target/Default Goal : make yazdığımızda çalışacak olan ilk target ilk yazdığımız target'dir. Bunun sebebi ilk target'in varsayılan hedef yani Default Goal olmasıdır. Ancak bunu değiştirebiliriz.

makefile'ımızın başına aşağıdaki satırı eklediğimizde bu varsayılan hedefi değiştirebiliriz.

.DEFAULT_GOAL := target_ismi

.DEFAULT_GOAL, yalnızca 1 target'i çalıştırabilir. Yani argüman olarak birden fazla target atayamazsınız. Bu yüzden eğer default goal'e bir phony target atarsanız sadece o targeti işleyecek ve diğer targetleri elleyemiyecektir. Bu yüzden bağımlılıkların olduğu yerlerde tercih ediyoruz.


all : all ile birden fazla target'i argüman olarak verebilir ve çalıştırabilirsiniz. Genellikle phony target'lar ile birlikte kullanarak bağımsızlık sorununu ortadan kaldırırız.

all: say_hello say_goodbye
say_hello:
	echo "hello"

say_goodbye:
	echo "goodbye"

make yazdıktan sonra çıktısı :

PS C:\Users\Alperen\Desktop\my_c_project\src> make
echo "hello"
"hello"
echo "goodbye"
"goodbye"

.PHONY: Makefile'da belirli hedeflerin dosya olmadığını ve yalnızca bir isim olarak kullanıldığını belirtmek için kullanılan özel bir hedeftir. PHONY, birden fazla target'i argüman olarak alabilir. Bu, özellikle hedef adları bir dosya adıyla aynı olduğunda önemlidir. .PHONY sayesinde, make komutu bu hedeflerin gerçek bir dosya olarak var olup olmadığını kontrol etmez; doğrudan o hedefin tarifini (recipe) çalıştırır.

PS C:\Users\Alperen\Desktop\my_c_project\src> make
echo "hello"
"hello"
echo "goodbye"
"goodbye"
PS C:\Users\Alperen\Desktop\my_c_project\src> make
echo "hello"
"hello"
echo "goodbye"
"goodbye"
  • clean hedefi, .o uzantılı dosyaları ve program adlı dosyayı silmek için kullanılır.

  • .PHONY: clean ifadesi sayesinde, clean hedefi gerçek bir dosya değil, sadece bir isim olarak kullanılır. Bu sayede, dizinde clean adlı bir dosya olsa bile make komutu bu dosyayı göz ardı eder ve clean tarifini çalıştırır.


Değişkenler : Bir değeri defalarca yazmak yerine tek bir yerde tanımlayarak onu kullanabilir ve istediğimizde değişebiliriz. Evet bunu değişkenler ile yapıyoruz. Değişkenler, makro benzeri bir işlev görür ve kodunuzu daha düzenli ve esnek hale getirir.

Bir değişkene değer atamak için 2 farklı yolumuz var.

  1. CC = gcc

CC değişkenine gcc değerini atadık. Makefile dosyamızda istediğimiz yerlere CC'yi yazarak gcc değerini kullanabiliriz.

  1. CC := gcc

Değişkenlerimizi, $(değişken_adı) veya ${değişken_adı} formatında kullanırız.

Örneğin :

CC = gcc
CFLAGS = -Wall -g
OBJS = main.o utils.o

program: $(OBJS)
	$(CC) $(CFLAGS) -o program $(OBJS)

main.o: main.c
	$(CC) $(CFLAGS) -c main.c

utils.o: utils.c
	$(CC) $(CFLAGS) -c utils.c

clean:
	rm -f *.o program

Yorum Satırı (#): Makefile dosyamızın içerisine yorum satırları ekleyebiliriz. Bunun için tek ihtiyacımız olan "#" karakterini ilgili satırın başına eklemek.

Örneğin :

# Bu makefile örnek bir C projesi için yazılmıştır

CC = gcc            # Derleyici olarak gcc kullanıyoruz
CFLAGS = -Wall -g   # Derleme sırasında uyarıları göster ve hata ayıklama bilgilerini ekle

# Tüm nesne dosyalarının listesi
OBJS = main.o utils.o

# Program hedefi ve nasıl derleneceği
program: $(OBJS)
	$(CC) $(CFLAGS) -o program $(OBJS)

# main.o dosyasını derlemek için kural
main.o: main.c
	$(CC) $(CFLAGS) -c main.c

# utils.o dosyasını derlemek için kural
utils.o: utils.c
	$(CC) $(CFLAGS) -c utils.c

# Temizlik için kural
clean:
	rm -f *.o program

Bu yazımız bu kadardı :) umarım faydalı olmuştur. Kaynakçadaki kaynaklardan araştırmaya devam edebilirsiniz.

KAYNAKÇA :

  • opensource.com/article/18/8/what-how-makefile

  • cs.colby.edu/maxwell/courses/tutorials/maketutor/

  • makefiletutorial.com/

  • tutorialspoint.com/makefile/why_makefile.htm