sayfa başı

30 Haziran 2015 Salı

Beagle Bone Konsola Debug Portu ile Erişim




Beagle Bone Konsola Debug Portu ile Erişim


Amaç
BeagleBone Konsolune Erişim
2.1 PUTTY ile IP Üzerinden Erişim
2.2 PUT ile Seri Port Üzerinden Erişim
2.3 Board Üzerinde Bulunan Seri Port Pinleri İle Konsola Erişim


  1. Amaç

Bu dökümanda BeagleBone konsoluna nasıl erişileceği anlatılmaktadır.
  1. BeagleBone Konsolune Erişim

BeagleBone konsoluna erişmek içini üç farklı yöntem bulunmaktadır. PUTY ile IP üzerinden erişim, PUTY ile seri port(USB seri port) üzerinden erişim, board üzerinde olan seri port pinleri ile konsola erişim olara gruplandırabiliriz.

2.1 PUTTY ile IP Üzerinden Erişim

İlk olarak Putty bu linkten indirin. Daha sonra linux makine ile fiziksel teması kurmak için ürünle beraber gelen USB kabloyu bağlayın.
Usb kablo bağlandıktan sonra makine enerjisini buradan aldığı için açılmaya başlayacaktır. Bilgisayarımızın bu USB girişini seri port olarak görmesi için driver kurulması gerekir.


Windows 64 bit için:


Windows 32 bit için:


Linux için:
Bu script  /etc/udev/rules.d/ altında 73-beaglebone.rules isminde yeni bir dosya oluşturacaktır. Bu script koşmak gerekecektir.


Cihaz açıldıktan sonra ve Windos tarafında driver kurulumu bittikten sonra PUTTY çalıştıralım. Putty ile SSH üzerinden cihaza bağlanmak için cihazın IP adresi gerekmektedir. BeagleBone fabrika çıkışı olara USB girişine sanal bir static IP adresi vermektedir. Sanal IP diyorum çünkü ethernet kablosu bağlı olmadan makine IP almaz. Bu sanal IP 192.168.7.2. Host Name (or IP address) kısmına bu IP adresini yazalım. Port numarası olarak 22 kalmalı.
Bu ayarlar yapıldıktan sonra “Open” diyerek cihaza bağlanalım. Cihaza ilk bağlandığımızda güvenlik uyarısı verecektir. Yes diyerek devam edelim. Cihaza bağlandımızda ”login as” promp ile hangi kullanıcı olarak devam edeceğimizi soracaktır.”root” deyip cihazın konsoluna erişelim.
Tüm bu adımlardan sonra BeagleBone konsoluna erişmiş oluyoruz.

2.2 PUT ile Seri Port Üzerinden Erişim

Putty çalıştıralım ve “connection type” kısmından “Serial” seçelim. Daha sonra seri port için gerekli ayarları girelim. Doğru Com Port numarasını girdikten sonra baudrate olarak 115200 değerini girelim. Bu adımlardan sonra “Open” dedikten sonra BeagleBone(BB) konsoluna erişmiş olacağız. Enter tuşuna basarak hangi kullanıcı ile bağlandığımızı ve pwd komutu ile bulunduğumuz dizini görebiliriz.

2.3 Board Üzerinde Bulunan Seri Port Pinleri İle Konsola Erişim

Board üzerinde bulunan 6 pinli header konnektor(konsol seri portu) ile BeagleBone konsoluna erişebilinir.(Debug portu) Embedded linux kartlarda genellikle bu yol ile makine konsoluna erişim yapılmaktadır. Bu yüzden BB USB seri portunu değil de bu portu kullanacağız.
Bord üzerindeki debug portunun yerini ve gerekli olan TX, RX ve GND pinlerini gösteren resim incelendikten sonra bağlantılar yapılmalıdır. Burada USB-TTL UART dönnüştürücünüzün 5V ya da 3.3V çıkışlarını bağlanmamasına dikkat ediniz. Sadece TX, RX ve GND pinleri bağlanmalıdır.
USB-TTL UART dönüştürücümüzün TX pini BeableBone RX ile, RX pini BeagleBone TX pini ile bağlanmalıdır.
Bağlantılar yapıldıktan sonra seri port terminal programımız(SecureCRTPortable) açıp konsola erişebiliriz.


USB-TTL RS232 Dönüştürücü









22 Mart 2015 Pazar

Bir Dizindeki Tüm Dosyalara Ulaşmak

    Öncelikle Merhaba. Bu yazımda bir dizin üzerinde işlemler yapacağız. Ufak bir örnek üzerinden yapılan işlemleri açıklayacağız.
    İlk olarak dizin üzerinde işlem yapacağımızdan kullanacağımız başlık dosyasını taktim edeyim; #include <dirent.h> Örneğimize geçmeden önce bir klasör içerisinde bulunabilecek verilere bakalım.
  • Block Device 
  • Char Device
  • Directory
  • Pipe (FIFO)
  • Symbolic Link
  • Regular File
  • Unix Domain Socket
  • Unknown
    Bir klasör içerisindeki yukarıdaki gruplardan birine ait olan tüm dosyaları sıralayabiliriz. Örneğin Linux cihazımızn /dev/ dizini altında ne kadar Char Device türünde dosya varsa ekrana yazalım.
 
#include <stdio.h>
#include <string.h>
#include <dirent.h>


int main(void)
{
     DIR *dir;
     struct dirent *dirent;

     dir = opendir("/dev/");

     if (dir)
     {
        while (NULL != (dirent = readdir(dir)))
          {
               if (DT_CHR == dirent->d_type)
               {
                       printf(">> %s \n", dirent->d_name);
                }
          }
     }

    return 0;
}
 
    İlk olarak opendir ile istenilen dizine gittik, daha sonra readdir ile buradaki tüm verileri (dizin, char device, regular file) ayırt etmeden okuduk. Örneğimizde Char Device istendiği için if sogusu içinde DT_CHR ile sadece char device dosya isimlerini ekrana basmış oldu(Not: standart output consol olarak düşünüldü).
Diğer dosya türü sorgusu için <dirent.h> başlık dosyasında tanımlanan makroları kullanabilirsiniz. 

       DT_BLK       Block Device.
       DT_CHR      Character Device.
       DT_DIR        Directory(klasör).
       DT_FIFO      Pipe (FIFO).
       DT_LNK      Symbolic link.
       DT_REG      Regular file(sıradan dosyalar, text, zip, odt...)
       DT_SOCK     This is a UNIX domain socket.
       DT_UNKNOWN  The file type is unknown


Faydalanacağını düşündüğünüz kişilere iletin, eksik ya da yanlış olduğunu düşündüğünüz yerleri belirtin.


Şimdi Yazmaya Devam ....




14 Mart 2015 Cumartesi

Adım Adım Linux Driver Yazıyoruz -3-

     Yolu buraya düşen herkse ilk olarak Merhaba. Bugün yazdığımız modülü derleyip çalıştıracağız. Bir önceki yazımda makefile oluşturacağımzı bahsetmiştim. Bu yazımda da Makefile hakkında kısa bir yazı yazmıştım. Bu yazılardan sonra kaldığımız yerden devam edelim.
    İlk olarak modülümüz derlememize yardımcı olacak Makefile kodunu aşağıda bulunmakta. Makefile dosyası ile .c uzantılı modül kodumuzu aynı dizine koyalım ve konsoldan o dizine gidelim. Derleme için tek yapmamız gereken konsola make yazıp entere basmak. Hepsi bu. Belki KERNEL_SOURCE := /usr/src/linux-headers-3.13.0-32-generic bağlı olarak hata alabilirsiniz. Onu da kendi PC bulunan ayara göre düzenleyerek çözebilirsiniz. Zaten /usr/src/ dizinine giderseniz orada linux-...... diye birşey görürsünüz. Gerisi kopyala yapıştır.

# Makefile – makefile of our first driver

# if KERNELRELEASE is not defined, we've been called directly from the command line.
# Invoke the kernel build system.
ifeq (${KERNELRELEASE},)
 KERNEL_SOURCE := /usr/src/linux-headers-3.13.0-32-generic
 PWD := $(shell pwd)
default:
 ${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} modules

clean:
 ${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} clean

# Otherwise KERNELRELEASE is defined; we've been invoked from the
# kernel build system and can use its language.
else
 obj-m := zafer.o //derlenecek .c dosyasının ismi verilmeli
endif
    Derleme işleminden sonra elimizde .ko uzantılı bir dosya oluştu. Bundan sonra yapmamız gereken ilk iş ya seriport üzerinden ya da tftp ile bu dosyayı cihaza aktarmamız olacak. Bu adımı da başarılı bir şekilde yaptığımızı varsayarak sıradaki işlemlere geçelim.

    Cihazımıza .ko uzantılı kernel objesini  yükledik. Peki bu dosyayı nasıl çalıştıracağız. Tabiki normal çalıştırılabilir dosyalar gibi ./isim.ko şeklinde çalıştıramayız. Modül dosyalarını çalıştırmak ya da durdurmak için tanımlanmış komutlar vardır. Özetle onlara bir göz atalım.
  • lsmod: yüklenmiş modül dosyalarını listeler
  • insmod: modülü Kernel yükler yani çalışmasını sağlar
  • rmmod: Kernel yüklenen modülü geri alır. Çalışmasını durdurur.
    Şu bilgiyi hatırlayalım: yüklenmiş modüller major numarası ve isimleri ile /proc/devices/ dizini altında görülür. Yazdığımız modülün Kernel yüklenmesi yani çalışır duruma gelmesi için insmod komutunu koşalım $insmod modül_ismi koşalımŞimdi modülümüz Kernel yüklenmiş oldu. Teyit etmek için cat /proc/devices/ komutunu kullanalım. Verdiğimiz isim ve major numarası ile cihazımız tam karşımızda olması lazım. Major numarasını bir yere not edelim. Kısa bir süre bu numara bizim için önemli olacak.
    Madem insmod ile modülü yükledim, o zaman /dev/ altında normal cihazlar gibi görüp kullanmam gerekir diyenlere bunun şimdilik imkansız olduğunu söylemek gerekir. Cihaz dosyasını /dev/ altında oluşturma işini şimdilik elle yapacağız. 
             sudo mknod /dev/isim c 250 0
   Buradaki isim bağımsız olarak verebileceğimz bir isim, istediğiniz isimi verebilirsiniz. c char device file. 250 hani kaydettiğimiz major numarası vardı ya işte o değer, 0 ise cihaza atayacağımız minor numarası. Bu adımdan sonra ls -l /dev/ yaptığıızda /dev/ altında yeni oluşan dosyayı görebiliriz.


Otomatik Cihaz dosyasını(Device File) Oluşturma

    Bir önceki adımda cihaz dosyasını(Device File) mknod komutu yardımıyla elle oluşturduk. Şimdiki adımımız bu işlemi otomatik olarak yapma.
    2.4 Kernel versiyonna kadar device file oluşturma işi devfs ait uygun API ile Kernel tarafından otomatik olarak yapılırdı. Kernel' ın gelişmesiyle geliştiriciler cihaz dosyasının kullanıcı katmanına ait bir olduğunu düşünerek bunun kullanıcılar tarafından yapılmasına karar verdiler.
    <linux/device.h> altındaki fonksiyonlara bakarak bu işlemi gerçekleştirelim.
struct class *cl;

if (IS_ERR(cl = class_create(THIS_MODULE, MY_DRIVER_CLASS_NAME)))
{
 rv = PTR_ERR(cl);
 goto err_class_create;
}

if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, MY_DRIVER_DEVICE_FILE_NAME)))
{
 rv = PTR_ERR(dev_ret);
 goto err_device_create;
}
    Bu kod parçasını init fonksiyomuz içine koyarak cihaz dosyasını otomatik olarak oluşturmuş oluruz. IS_ERR makrosu ile fonksiyonların geri dönüş değerleri test edildi. Her ne kadar C kullananlar goto kullanmayı sevmese de burada goto iyi bir tercih. “Linux Device Drivers” kitabinda sayfa 33' de goto için yazılanlar aynen size aktarıyorum. "We normally hate to use goto, but in our opinion, this is one situation where it is useful."  Bu cümlenin ardından verdikleri örnek de şu şekilde:
int __init my_init_function(void)
{
   int err;

   /* registration takes a pointer and a name */
   err = register_this(ptr1, "skull");
   if (err) goto fail_this;
   err = register_that(ptr2, "skull");
   if (err) goto fail_that;
   err = register_those(ptr3, "skull");
   if (err) goto fail_those;
   return 0; /* success */
   fail_those: unregister_that(ptr2, "skull");
   fail_that: unregister_this(ptr1, "skull");
   fail_this: return err; /* propagate the error */
}
    Peki IS_ERR hata olduğunu bildirir ve goto nun gösterdiği yere gidersek burada ne yapacağız. 
err_device_create:
 class_destroy(cl);
err_class_create:
 cdev_del(&c_dev);
    Burada bir şeye dikkat çekmek isterim. İlk sorguya ait hata işleme yeri ikinci sorguya ait hata işleme yerinden daha sonra yazılmış yani en son adım hata oluşursa kendinden önceki tüm adımlarda yapılan işlemler hatalıymış gibi onlar da geri alınmalı. Hataları ele alma yeri init fonksiyonun sonunda return hemen önce olmalı. Ayrıca exit fonksiyonumuzda da bu durumları ele almalıyız. Bu kadar sözel açıklamadan sonra kodun son halini görerek anlatılanların resmine bakalım.
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>

static dev_t dev;          //dev device number
static struct cdev c_dev;   //character device structure
static struct class *cl;   //device class


static int __init myModule_init(void) /* Constructor */
{
 int retVal = -EACCES;
 struct device *dev_ret;

 if ((retVal = alloc_chrdev_region(&dev, 0, 1, "zafer")) < 0)
 {
  goto err_alloc_dev_region;
 }

 if (IS_ERR(cl = class_create(THIS_MODULE, "chardriver")))
 {
  retVal = PTR_ERR(cl);
  goto err_class_create;
 }

        /*Burada verdigimiz isim /dev/ altinda gorulur "myDevice"*/
 if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, "myDevice"))) 
 {
  retVal = PTR_ERR(dev_ret);
  goto err_device_create;
 }
 return 0;

err_device_create:
 class_destroy(cl);
err_class_create:
 cdev_del(&c_dev);
err_cdev_add:
 unregister_chrdev_region(dev, 1);
err_alloc_dev_region:
 return retVal;
}

static void __exit myModule_exit(void) /* Destructor */
{
 cdev_del(&c_dev);
 device_destroy(cl, dev);
 class_destroy(cl);
 unregister_chrdev_region(dev, 1);
 printk(KERN_INFO "myModule unregistered");
}

module_init(myModule_init);
module_exit(myModule_exit);

MODULE_AUTHOR("Zafer Satilmis");
MODULE_DESCRIPTION("First Character Driver");
    Artık modülümüz hazır. /dev/ altında da cihaz dosyamızı görür durumdayız. Bundan sonraki adım kullanıcı katmanında bu cihaz dosyası ile cihazı kontrol etmek. Bunun için son adım olan dosya operasyonları kodunu yazacağız. Dosya operasyonları ile cihazımız üzerinde okuma, yazma işlemleri yapacağız. ioctl(input/output control) ile cihaza kendimizin tanımladığı komutları vereceğiz. Bu işlemler sonucunda tam kullanışlı bir iş yapmış olacağız. 

Faydalanacağını düşündüğünüz kişilere iletin, eksik ya da yanlış olduğunu düşündüğünüz yerleri belirtin.


Şimdi Yazmaya Devam ....




10 Mart 2015 Salı

Makefile



    Adım Adım Linux Driver Yazıyoruz -2- başlıklı yazımın sonunda ilk kernel modülümüzü oluşturmuştuk. Bundan sonraki işlem modülü derlemek için Makefile oluşturmak. Fakat burada ezbere bir Makefile kullanarak Makefile konusunu es geçmek istemedim. Yeri gelmişken Makefile ile ilgili bir yazı hazırlamak istedim. Makefile ile ilgili bu yazım tamamen giriş düzeyinde. En azından Makefile ile ilgili ufak da olsa bir bilgimiz olsun.

Makefile

[Illustration]
 Makefile projemizdeki kaynak kodları hızlı ve kolayca derleme için kullandığımız bir dosyadır. Makefile dosyası işlemlerin nasıl yapılacağını gösteren kural tanımlamalarından oluşmaktadır. Makefile özel bir dosyadır ve ismi büyük harfle başmalıdır ve "Makefile" şeklinde olmalıdır.

1- Basit Düzeyde Makefile Oluşturma


hedef: bağımlılıklar
<TAB> komut
<TAB> komut
<TAB> ...

   Burada <TAB> kullanmak zorunludur. Bir satır boşluk bırakmak GNU make için zorunlu olmamakla birlikte bazı Unix sürümleriyle uyumluluk için boşluk bırakılması gereklidir. 
  İlk satırda hedefin oluşturulmasında etkili(bağımlı) dosyalar birbirinden boşluk ile ayrılarak sırasıyla yazılır. Etkili(bağımlı) dosyalar hedefin oluşturulmasında kullanılacak olan dosyalardır. Eğer bağımlı dosyalarda herhangi birinin değiştirilme tarihi hedefin tarihinden daha yeni ise hedef yeniden oluşturulur. Bu sınama ile hedefin tekrar tekrar oluşturulması engellenmiş olunur. hedef ve bağımlı dosyalar belirtildikten sonra bir alt starırda <TAB> ile başlanarak komutlar yazılır. 

keypad: keypad.c
      gcc keypad.c -o output_file_name

 Örnekte hedefe verilen isim keypad, bağımlı dosya keypad.c. Komut kısmında da bu hedefe yönelik kod yazılmıştır.(kodun zorunlu olan bir <TAB> içerinde yazıldığına dikkat ediniz.) 
Hedeflerin diğer bir özelliği de tek başlarına işletilebilir olmasıdır. Örneğin elimizde aşağıdaki gibi yazılmış bir makefile dosyası bulunsun:

dest1: file.c
      #commands
dest2: file.c
      #commands
dest3: file.o
      #dommands
  Konsoldan Makefile bulunduğu dizine geçtikten sonra “make dest1”, ya da “make dest2” gibi sadece o hedefi çalıştıracak komut verebiliriz. Eğer hiç bir hedef belirtmeden sadece make komutunu çalıştırırsak işlem ilk hedefi çalıştırır.     Genelde Makefile dosyalarında ilk hedefe diğer hedefleri bağlayabiliriz ve bu sayede tüm hedefler de çağrılmış olur. Bu yüzden ilk hedefe çoğunlukla “all” ismi verilir ve konsoldan bu hedef “make all” yerine sadece “make” yazılarak kısa bir yazımla çağrılmış olur. Makefile kurallarına ufak bir uygulama üzerinden anlatalım.

CC            = gcc
CFLAGS  = -O2 -Wall -pedantic
LIBS         = -lm -lnsl

test: test.o
    $(CC) $(CFLAGS) $(LIBS) -o test test.o

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

clean:
    rm -f test *.o

install: test
    cp test /usr/bin

 Dosyanın başında yaptığımız ilk işlemler atama işlemleridir. Makefile içinde bu şekilde atama işlemi yapabiliriz. Kullanımı ise koddan da görüleceği gibi $(DEGISKEN).  Makefile yazılırken kullanılacak derleyici, derleyiciye ait komutlar ve kütüphane komutları bu şekilde tanımlanır. Bu sayede tek bir noktadan kontrol oluşturulur. 

 Makefile dosyasında çoğunlukla clean ve install hedefleri bulunur. Neredeyse her makefile içinde bu amaçlı çalışan hedefler bulunmaktadır. Hedef ismi yaygın olarak “clean” olarak kullanılsa da makefile yazan kişi istediği ismi verebileceği gibi bu hedef altında başka yardımcı kodları da koşturabilir. Örneğin obje dosyalarının silinmesi yanında derleme sonrası oluşan çalıştırılabilir çıktıyı ya da .so uzantılı dosyaları da sildirebilir. Burada yazanın proje içindeki gereksinimine göre işlem yapması gerekir. Ayrıca versiyon kontrol programları ile proje commit edilmeden önce bu komut kullanılarak repostory bulunmaması gereken dosyaları da bu şekilde temizlediğimizi göz önüne almak gerekir. Tüm bu isteklere cevap verecek bir “clean” hedefi yazmak gerekir.
 Örnek makefile dosyasında clean komutu için bağımlılık listesi olmadığına dikkat çekmek gerekir. Çünkü bağımlı listedeki belirtilen dosyada herhangi bir değişiklik olduğunda çağrılan hedef çalışır. Fakat biz clean hedefine her seferinde koşulsuz olarak çalıştırmak istediğimizden bağımlılık listesi clean komutu için kullanılmaz.

 Clean komutunu kullanırken şu hata durum da oluşabilir: proje dosyaları içince “clean” adında bir dosya var olması durumunda siz clean komutunu çalıştırmak isterseniz bunu yapamazsınız. make clean komutuna karşılık “make: `clean' is up to date.” cevabını alırsınız. İşte bu hataya karşı “.PHONY: clean” komutunu makefile dosyanıza eklemeniz gerekir. Aslında bu komutu tüm bağımlılık listesi olmayan hedefler için kullanmalıyız.
 “install” hedefi ise projenin derlenmesi sonucunda oluşan sonuç dosyanın nereye kopyalanması gerektiğini gösterir. Bu adım da bir makefile dosyasında bulunması gerekir. Çünkü kullanıcı sonuç dosyasının nerede oluşacağını bilemez. Ayrıca bu hedefin hangi dizine kopyalama yapacağı konsola çıktı olarak da verilmelidir.

2-Orta Düzeyde Makefile

 Basit Düzeyde Makefile başlığı altında verilen makefile dosyası uygulamada kullanılmayacak kadar basit düzeyde. Zaten uygulamalarda projelerde yüzlerce hatta binlerce dosya bulunabilir. Peki bu büyüklükteki projeler için nasıl bir makefile yazmak gerekir.

CC = g++
CFLAGS = -O2 -Wall -pedantic
LIBS = -lnsl -lm
INCLUDES = -I/usr/local/include/custom

all: server client

server: ortak.o server.o list.o que.o \
            data.o hash.o
    $(CC) $(CFLAGS) $(LIBS) -o server ortak.o server.o \
            list.o que.o data.o hash.o

client: ortak.o client.o
    $(CC) $(CFLAGS) $(LIBS) -o client ortak.o client.o

ortak.o: ortak.cpp ortak.h
    $(CC) $(CFLAGS) $(INCLUDES) -c ortak.cpp

server.o: server.cpp server.h ortak.h
    $(CC) $(CFLAGS) $(INCLUDES) -c server.cpp

client.o: client.cpp client.h ortak.h
    $(CC) $(CFLAGS) $(INCLUDES) -c client.cpp

list.o: list.cpp list.h
    $(CC) $(CFLAGS) $(INCLUDES) -c list.cpp

que.o: que.cpp que.h
    $(CC) $(CFLAGS) $(INCLUDES) -c que.cpp

data.o: data.cpp data.h
    $(CC) $(CFLAGS) $(INCLUDES) -c data.cpp

hash.o: hash.cpp hash.h
    $(CC) $(CFLAGS) $(INCLUDES) -c hash.cpp

install: client server
    mkdir -p /usr/local/bin/test
    cp client /usr/local/bin/test
    cp server /usr/local/bin/test

uninstall:
    rm -rf /usr/local/bin/test

clean:
    rm -f *.o server client

.PHONY: clean
 Yukarıdaki makefile soyut kurallar kullanılmadan yazılmıştır. Proje sayıda dosyadan oluşmasına rağmen makefile yazımı yorucu ve zaman alıcı bir görünüm sunmaktadır ki bu basit projenini 50 dosyadan oluştuğunu düşünürsek 50 adet dosyanın derlenmesini tek tek bildirmek gerekecektir. Bu durum hem zaman kaybı hemde hatalara açıktır. Orta düzeyeki projelerde bile yukarıdaki örnek biçiminde makefile yazmaktan kaçınmalıyız.
 Bu fikirle hareket edilirse çözüm olarak soyut kurallar (abstract rules) imdadımıza yetişir. Bu soyut kurallar bize örneğin tüm .bet uzantılı dosyalardan pratik ve hatasız bir biçimde nasıl .son uzantılı dosyalar oluşturulacak sorununa  yardımcı olur.
.bet.son:
    komutlar
    komutlar
   ...
 Yukarıdaki özet kullanım .bet kaynak dosya .son hedef dosyayı gösterir ve dikkat edilirse bir bağımlılık listesi kullanılmadığı dikkati çeker. Daha kapsamlı bir örneğe geçmeden önce burada bilmemiz gereken bazı değişkenler var.

·      $< Değiştiği zaman hedefin yeniden oluşturulması gereken bağımlılıkları gösterir.
·      $@ Hedefi temsil eder.
·      $^ Geçerli kural için tüm bağımlılıkları temsil eder.
 Bir örnek ile bu değişlenlerin nasıl kullanıldığına bakalım.
.c.o:
       $(CC) -o $@ -c $< $(LIBS)
       @echo ">> $<: compiled $@ created"

all: $(OBJECT_FILE)
        
       gcc -o $(OUTPUT_FILE) $^ $(LIBS)
       @echo "Link done"

 .c.o hedef yazımı yukarıdaki .bet ile .son karşılık gelir yani kaynak dosyaların uzantısı .c hedef dosyanın uzantısı .o olmalı bildrimini yapmış olduk.
 $(CC) -o $@ -c $< $(LIBS) bu satırda kaynak dosyadan hedef dosyatı oluşturacak komutu işleriz. $@ ve $< anlarmları kapalı da olsa aslında basit bir ifadesi vardır. $@ burada oluşturulacak hedef dosyanın ismini içerirken $< ise kaynak dosyanın ismini içerir. Zaten komut bir .c(source) dosyasından .o(obje) dosyasının oluşmasını sağlıyor. Bunun açık yazılmış hali şu şekilde olurdu:
$(CC) -o mayfile.o -c mayfile.c $(LIBS). Peki neden bu şekilde a
çık değilde yukarıdaki gibi kapalı yazım yaptık. Çok sayıda dosyadan oluşan projelerde kapalı yazım ile dosyaların tek tek derlenme komutunu biz değil bu kapalı yazım yapar. Yani işin güzel tarafı bu kapalı yazım ile projede ne kadar .c(source) dosyası var ise o kadar .o(obje) dosyasını tek bir satır komut ile oluşturmuş oluruz.
İşin tam olarak nasıl bu kadar basitçe yapıldığını anlamak için  all: hedefini incelememiz gerekir. Çünkü .c.o ile all hedefleri birbirleriyle yardımlaşarak çalışırlar. All hedefine ve altında işlenen komutlara bir bakalım. 
all: $(OBJECT_FILE)
       $(CC) -o $(OUTPUT_FILE) $^ $(LIBS)
       @echo "Link done"

 Kodu inceleyenin ilk dikkatini çekmesi gereken nokta all hedefinin bir bağımlığının olması. All hedefi $(OBJECT_FILE) bağımlıdır. Yani all hedefinin altında yazılmış olan $(CC) -o $(OUTPUT_FILE) $^ $(LIBS) komutunun işlenebilmesi için $(OBJECT_FILE) ile belirtilen dosyalardan en az birinin değişmiş olması gerekir. OBJECT_FILE bir değişiklik ya da eksikli olduğunda ilk olarak bu ihtiyaçlar giderilir.
 OBJECT_FILE tanımlaması makefile dosyasının başında yapılmalıdır. Örneğin şu şekilde olabilir:
OBJECT_FILE = test.o test2.o test3.o test4.o test5.o. Bu durumda all hedefi aslında şu şekilde yazılmış olur: all: test.o test2.o test3.o test4.o test5.o.
 Artık all hedefi ile .c.o hedeflerinin yardımlaşarak çalışmasını daha basit bir şekilde açıklayabiliriz. All hedefi bağımlılık listesindeki her bir elemanı kontrol eder, eğer bir bağımlılığı bulamaz ya da güncel değil ise o bağımlılığın oluşması için .c.o hedefine çağrı yapar. .c.o hedefide üstüne düşen görevi yaparak all hedefinin ihtiyacı olan dosyayı üretir. İşin pratikliğini şu örnek ile daha da iyi anllayabiliriz: all hedefine bağlı dosyaların hepsinin değiştiğini ya da silindiğini var sayarsak tüm dosyalar sırasıyla otomatik bir şekilde oluşturulacaktır. İşte bu durum makefile yazana büyük bir kolaylık sağlar. Farkettiyseniz projemizde kullandığımız dosya isimleri sadece tek bir noktada geçiyor;
OBJECT_FILE = test.o test2.o test3.o test4.o test5.o. Art
ık projeye eklenen her bir dosyanın ismini bu listeye eklemek yeter.
 Aşağıdaki örnek kod ile projenizdeki tüm dosyları derleyip obje dosyası oluşturabilir ve daha sonrasında link ederek çıktı dosyanızı almayı sağlayabilirsiniz.

CC = gcc
CFLAGS = -O2 -Wall
LIBS = -lm -lnsl
OBJECT_FILE = test.o test2.o test3.o test4.o test5.o

OUTPUT_FILE_NAME = pinkFloyd

.PHONY: clean
.SUFFIXES: .c .o

.c.o:
       @$(CC) -o $@ -c $< $(LIBS)
       @echo "   -[CC]                     $< compiled"


all: $(OBJECT_FILE)
        
       @$(CC) -o $(OUTPUT_FILE_NAME) $^ $(LIBS)
       @echo "   ---------------------------------------------------------------------"
       @echo "   |-- Linking is successful. Output file name: $(OUTPUT_FILE) was created --|"
       @echo "   ---------------------------------------------------------------------"

       @##cp $(OUTPUT_FILE_NAME) /home/zafer/Desktop/
       @#@echo "output file $(OUTPUT_FILE) copyed to Desktop"

clean:
       @rm -f $(OUTPUT_FILE_NAME) *.o
       @echo "      ---------------------------------------------------------"
       @echo "      |-- All object file and outputfile: $(OUTPUT_FILE) deleted --|"
       @echo "      ---------------------------------------------------------\n"

Not: .SUFFIXES: .c .o ile kullanılacak uzantılar tanımlanır.

Çalıştırılan kodun terminalde görünmemesini istiyorsanız ilgili kodun başına @ koymanız yeterli. Bunu yapmaz iseniz çalıştrılacak komut ilk olarak komut satırına yazılır daha sonra çalışır. Bu da komut satırının kirlenmesine sebep olur ve ekranı takip etmeyi zorlaştırır.

3-İleri Düzeyde Makefile

 İleri düzeyde makefile yazabilmek için yukarıda kullandığımız komutlardan fazlasını bilmek gerekir. Örneğin bir önceki örnekte clean hedefinde kullandığımız
   rm -f $(OUTPUT_FILE_NAME) *.o komutu gerçek uygulamalarda yetersiz kalacaktır. Sizinde görebileceğiniz gibi silme işlemi sabit bir dizinde -içinde olduğumuz dizin- sadece obje dosyalarını silmeye yöneliktir.
 Gerçek uygulamalarda projeler birden fazla hatta çok sayıda klasörlerden oluşur. Doğal olarak da obje dosyalarının tek bir klasörde bulunmasını beklemek anlamsız olur. Yapılması gereken bu düzene uyum sağlayarak tüm klasörleri dolaşıp derleyicinin oluşturduğu obje dosyalarını silmektir.
 Tüm klasörleri dolaşıp içlerindeki obje dosyasını silmek göze çok zahmetli görünsede bu işi find komutu ile yapabiliriz. Bu komut istenilen öğeyi komutun çağrıldığı dizinden başlayarak aramaya başlar. İstenilen ögeyi bulduktan sonra tek yapmamız gereken bunu rm komutuna parametre olarak geçmektir. Hatta istenirse hangi dizinde silme yapıldığını  komut terminaline de yazdırarak kullanıcıyı bilgilendirmiş oluruz. Tüm bu anlatılanlara uyan örnek kullanım aşağıdaki gibi olur.
       @find -name '*.o' -exec rm -f {} \; -exec echo " >> "{} "    Deleted" \;
       @rm -f $(OUTPUT_FILE) *.o
       @echo "\n    ---------------------------------------------------------"
       @echo "    |-- All object file and outputfile: $(OUTPUT_FILE) deleted --|"
       @echo "    ---------------------------------------------------------\n"


 Ana konumuzu kaçırmamak için Makefile konusunu şimdilik burada sonlandırıyorum. İleri düzeyde makefile konusuna daha sonra devam edeceğim.

    Faydalanacağını düşündüğünüz kişilere iletin, eksik ya da yanlış olduğunu düşündüğünüz yerleri belirtin.


Şimdi Yazmaya Devam ....