GPL,  Makefiles (GNU make), kompilleerimine (GNU CC) ja install

teejuht

                        1. Sissejuhatus
                        2. GNU GPL
                        3. Makefile
                        4. make
                        5. kompillaator gcc
                        6. install
                        7. patchfile: patch ja diff
 

1. Sissejuhatus
 

Kõige parem koht make'i kohta info saamiseks on nii irooniline kui see ka isiklikult minu jaoks pole programm 'info' :)
Seda programmi ma kunagi üsna alguses sattusin käppima ja mulle tundus ta tõeliselt ebameeldiva ja segasena. Aga näe, nüüd meeldib ta mulle nii mis kole: nii oma ülesehituse kui ka tavaliselt temaga vähemalt slackware puhul kaasas oleva info poolest (igasugu GNU asjad).
 

2. GNU General Public Licence

Rääkides Linux'i all kasutatavast tarkvarast peaks korrektse esituse puhul tõenäoliselt alustama GNU General Public Licence (GPL) ' ist. Selge see, et järgnev esitus pole korrektne, küll aga märgin ma oma arusaamise piirides midagi selle listsensi kohta ära.

Tavaliselt tarkvara tootja annab kasutajale õiguse oma tarkvara kasutada teatud tingimustel. Reeglina ei tohi kasutaja tarkvara ei muuta ega edasi jagada.

GNU GPL aga annab kasutajale teada tema vabadusest tarkvara edasi jagada ja seda muuta. Keskseks sellise listsensi all jagatava tarkvara allikaks on Free Software Foundation (FSF).

Kõneldes vabast tarkvarast mõeldakse ennekõike mitte seda, et teda levitatakse tasuta vaid seda, et tarkvara levitatakse nn. source'dena ning, et kasutajaid julgustatakse tarkvara täiendama. Samuti annab GPL teada, et inimesed võivad oma originaalse tarkvara loomisel kasutada muu GPL'i all levitatava tarkvara osi kusjuures inimesed võivad oma produkte ka müüa. Igal juhul, kas te omatehtud programme müüte edasi või jagate niisama peate te levitama neid sourcedena või vähemalt tegema need kõigile soovijatele kättesaadavaks.

Ning lõpuks, GPL'i all levitatavatel programmidel puudub warranty, st. et keegi (isegi mitte programmi autor) ei taga 100 %, et programm töötab. Niisiis, kasutades GPL'i all levitatavaid programme tegutsete te omal riskil ja vastutate ise kõigi võijmalike tagajärgede eest. Kui te ka ei vastuta pole teil kedagi võimalik süüdistada.
 

3. GNU Makefiles

Kui hankida tarkvara ja reeglina hangitakse ta nö. source'dena, siis selleks, et seda kasutama hakata on ta vaja kõigepealt ära kompilleerida.

Kompilleerimiseks on teil vaja

- GNU C kompillaatorit koos asja juurde kuuluvate failidega (headerid, C library'd assemblerid, linkerid jne.)
soovitatakse verisooni 2.7.2.3

- on vaja konkreetse tarkvara source tree'd koos Makefile'ga. Tihtipeale sede Makefile ei panda lihtsalt kaasa vaid pannakse kaasa mõnevõrra üldisem variant - Makefile.in millest samuti kaasapandud nn. configure script'iga tehakse kohapealseid olusid ja nõudmisi arvestades Makefile valmis.

Aeg on rääkida miks selline salapärane asi nagu Makefile üldse vajalik on. Võiks ju teha nii, et nt. pragrammi GIMP levitada üheainsa suure source failina ning kompilleerimiseks anda üks käsk

hash# gcc -o gimp gimp.c

Eks saaks ka nii teha, aga see poleks eriti optimaalne, väga mitmetest seisukohtadest.

Mis tehakse tavaliselt on see, et GIMP'i valmistajad, eeldavad, et teil on kohapeal olemas GIMP'i kompileerimiseks ja tööks vajalikud library'd (gtk ja libc5). Need libraryd on tõenäoliselt sobivad ka veel paljude teise progogrammide kompilleerimiseks.

Ja source esitatakse tree' na ehk kataloogistruktuurina. Võimalik, et igas alamkataloogis on oma Makefile.

Niiisiis, Makefile ülesanne on näidata kohalikule kompillaatorile milliste kohalike library'te olemasolu ja teenuseid vajatakse ning seda kuidas source tree on organiseeritud. Samuti milliste nn. võtemetega millised source sektsioonid kokku lasta. Lõpuks, Makefile'is on näidatud ka ära kuhu kompilleeritud programm installeerida ja mida teha kui midagi valesti läheb. Tavaliselt annab kas Makefile või gcc häid vihjeid kui ka kompilleerimine tervikuna ebaõnnestub. Nii saate teiste käest abi küsida ja mitte selgitada oma probleemi vaid sõnadega 'ei läinud tööle'.

Makefile on iseenesest tavaline script, süntaks on tal spetsiifiline, aga sarnane bash shellile Makefile ise ei ole käivitatav. Makefile' i nö. käivitab programm make. Ja sealt käivituvad programmid gcc ja install ja saab kasutada enamust teie masinas olevad muid programme.

Minu esituses kasutavav lähenemine on valmis programmi tarvitamise seisukohast. make'i ja Makefile tegelik funktsioon on siiski veidi teise rõhuasetusega. Nimelt,  make omab sisemist andmebaasi kõnealuse tarkvara kompilleerimise kohta. Ja kui te oleksite ise tarkvara arendaja, siis tehes muutuse suure projekti mõnda faili tõenäoliselt tuleb teil osa tollest failist nö. sõltuvaid faili samuti ümber kompilleerida. Millised need on on ehk raske iga kord meeles pidada. Siin tulebki make appi. Ta vaatab oma sisemisest andmebaasist järele failide viimase kompilleerimise aja ja selle järgi otsustab.

Ehk oleks korrektne esitada kõik make ja Makefiledega seonduv programmeerija seisukohalt, aga kuivõrd mina pole programmerija, siis ma seda teha ei oska. Küll aga mulle meeldib programme kasutada ja kuna neid levitatakse source'dena siis pole muud teed kui tahes-tahtmata tuleb nad ise binary'teks kompilleerida. Enamasti tahes :) Ma siiski soovitan vähemalt C ja bash shelliga end niipalju kurssi viia kui selle kodulehkülje muudes sektsioonides on esitatud. Kui puudub igasugune ettekujutus programmerimisest, siis läheb kogu lugu asjatult segaseks.
 
 

make ja tema script fail Makefile

Esialgu tutvume Makefile'i üldise struktuuriga. Asjasse võib suhtuda nii, et see on nagu bash shelli script ainult et süntaks on erinev ja käivitamiseks tuleb anda korraldus:

bash# make -f makefile_nimi sektsiooni_nimi

Muuseas, kui anda käsk  'make sektsiooni_nimi' siis eeldatakse,et eksisteerib fail nimega 'Makefile'.

Teeme mõne väikese katse: looge fail m1 ja kasutage nagu all näidatud:

m1:

#see on lihtsalt keeruliselt näide
esimene:
       echo see on esimene sektsioon
teine:
       @echo see on teine sektsioon
kolmas:
       @echo see on viimane sektsioon
 
 

bash# make -f m1 kolmas
see on viimane sektsioon
bash#

bash# make -f m1 esimene
echo see on esimene sektsioon
see on esimene sektsioon
bash#

bash# make -f m1 kolmas teine
see on viimane sektsioon
see on teine sektsioon
bash#
 
 

Selgitus:

võti -f näitab milline on makefile nimi (vaikimisi on see Makefile, proovige!)
@ märgi lisamine/ärajätmine - ta kirjutab ka käsu rea enda või ei kirjuta.
praktiline on ütelda, et iga sektsiooni käsud peavad algama kindlast ühe tabulaatoriga.
# on kommentaar
kui te sektsiooni nime ei näita täidetakse esimese sektsiooni käsud

Tõsi, antud juhul me ei kompilleeri veel midagi.
 
 

Teise näitena vaatame kuidas kasutada ka mõnda muud tuntud bash shelli käsku:

Looge tühi kataloog ja minge sinna sisse ning proovige midagi sellist:

m2:

loo:
        mkdir kataloog{1,2,3,4,5}
        ls -l

unloo:
        -rmdir kataloog{1,2,3,4,5}

clean:
        rm *~
 

Katsetage kusjuures pange tähele, et

- märk käsu ees, et hoiab ära make lõppemise veaga (vaadake seda 'echo $?' ga) vaid lubab vaatamata sellele jätkata.

Viimane sektsioon on backup'id epuhastamiseks.
 

Niisiis, nüüd peaks selgeks saama, et näiteks kernelit tehes antavad käsud

make mrproper, make clean, make dep ja make zImage  jne viitavad Makefile erinevatele sektsioonidele.

Reeglina pannakse source'desse sisse ikka clean sektsioon, et vajadusel ära koristada poolikud/valmistehtud object failid (.o).
 

Muutujad:
 

Makefile'is on võimalik kasutada ka muutujaid. Osa muutujaid on varem valmisdefineeritud (automaatselt nt. MAKELEVEL, MAKE) ja seda peab arvestama.

See näide illustreerib, kuidas muutujate moodi asju saab kasutada nii käskude kui ka käsudega tegeldavate objektide väärtuste hoidmiseks.

m3:

REMOVE=rm -fr
DIRNAME = tartu
FILENAMES = kesklinn, tamme, ropka, anne, veeriku
list:
        @echo $(FILENAMES)

loo_kataloogid:
        mkdir $(DIRNAME)

loo_failid:
        cd $(DIRNAME); touch $(FILENAMES)

rm_kataloogid:
        rm -fr $(DIRNAME)

rm_k:
        $(REMOVE) $(DIRNAME)
 

Siin vajad rõhutamist see, et kui anda shelli käske siis tuleb nad anda reas!
 

No iseenesest mõista on kõige olulisekmaks kasutatavaks käsuks gcc - GNU CC kompillaator.
 
 

gcc

Kopillaatori võtmetest pole ma tõenäoliselt kõige õigem mees kõnelema, aga olgu mõned nimetatud lisaks '-o' le  C sektsioonis.

pre-precessor

-E

aga kõige parema ülevaate saate samuti eespool mainitud info'st. (GCC ja CPP - C Pre-Processor)
GNU CC'ga kompillerides käivitatakse kõigepealt nn. pre-protsessor.

Selle tegevuse tulemusena lahendatakse ära

- käsurea osade võtmetega näidatud asjad (nt. -D ehk define'd) ja
- #include' ga viidatud header failid

selle võtme kasutamisel kompilleerimist ei toimu. Võite proovida:

prep.c

#define a 5
#define b 6
main () {
printf("tere %d %d\n", a, b);
}

bash# gcc -E prep.c
# 1 "prep.c"
 

main () {
printf("tere %d %d\n", 5 , 6 );
}
bash#

või omate kahte faili:

includetav:

/* deklaratsioonid */
int a=4, b=5;
char c;

prog.c:

#include<stdio.h>
main() {

#include "includetav"

printf("tere %d\n", a+b);
}

Samuti annab pre - protsessor kummalised tingimuste kontrollimise võimalused:

#include<stdio.h>
main() {
#ifdef TERE_IMRE
printf("tere Imre\n");
#else
printf("tere mitte-Imre\n");
#endif
}

Ja milline binary kompilleerub sõltub sellest kuidas kompilleeriti!

Proovige:

bash# gcc -o prog.b prog.c
bash# prog.b
tere mitte-Imre
bash#

ja nii:

bash# gcc -DTERE_IMRE -o prog.b prog.c
bash# prog.b
tere Imre
bash#

-D võtme ime seisneb selles, et #define'd võib teha kompillaatori käsureal ilma sekkumata .c faili. Aga Makefile ja make on just igasuguste gcc argumentide peale mihkel.
 

target

Kompilaator on nii tark, et tunneb erinevate protsessorite käsustikke ja oskab neile vastavaid binarey'd luua. x86  arvutite jaoks on võti '-m486'.

Aga seda võtit pole vaja tavaliselt anda juhul kui te kompilleerite oma masina peal sama masina või temaga sama tüüpi masina peal kasutamiseks mõeldud programme. gcc teeb selle milise masinaga on vaikimisi tegemis kindalks sele järgi millises kataloogis ta ise elab (/usr/local/lib/gcc-lib/MACHINE/VERSION).
 

optimizing

Kompilleerimisel räägitakse ka optimiseerimisest. Praktiliselt tähendab see seda, et enam optimiseeritud programm on väiksem ja töötab kiiremini kuid võtab kompilleerimiseks rohkem aga.

Võtmeks on -O või
-O1 optimiseeritud
-O2 rohkem optimiseeritud
-O0 optimiseerimata
 

debugging

-d

warnings

-W
 

define (marcos)

-DKASUTA_LOCK_FAILE_KATALOOGIS_TMP

Siin on üks naide mis on realiseeritud kahel moel:

- kõik määrangud on tehtud programmi source' s endas

votmega.c

#include<stdio.h>
#define mitu 5
main()
{
printf("Tere, palun %d piletit Tartusse, me lahme Ylikooli\n", mitu);
}

kompilleerimine:

bash# gcc -Dmitu=5 -o votmega.b votmega.c
 

- osa määranguid näidatakse kompilleerides; see on suurte multiplatvorm asjade puhul eelistatavam

votmega.c

#include<stdio.h>
main()
{
printf("Tere, palun %d piletit Tartusse, me lahme Ylikooli\n", mitu);
}

kompilleerimine:

bash# gcc -Dmitu=5 -o votmega.b votmega.c
 

See on asjasse mitte puutuv näide aga illustreerib kuidas saab kasutada define't:

#include<stdio.h>
#define kirjuta printf
#define korda for
#define tee_seni_kui while
#define tagasta return
#define taisarv int
#define pea main

pea () {
taisarv i;
kirjuta ("tere\n");
korda (i = 1; i < 5 ; i++)
{
kirjuta ("ahoi %d\n", i);
}
tee_seni_kui (i > 0) {
kirjuta ("mine ise ka kooli!\n");
i--;
}
tagasta 0;
}
 
 

include filies (library's)

-I

enne toodud programmi võiks kokku lasta ka nt. nii:

main()
{
printf("Tere, palun %d piletit Tartusse, me lahme Ylikooli\n", mitu);
}

kompilleerimine:

bash# gcc -Dmitu=5 -I/usr/include -o votmega.b votmega.c

aga sel pole suur mõtet kuna gcc otsib header file'e nii kui nii standartsetest kohtadest nagu '/usr/include'
 

(vt. ka C programmeerimise lõpust juttu shared library'te tegemise ja kasutamise kohta)

install

See programm käivitatakse tavaliselt mitmel korral makefile poolt kui annate käsu  'make install'.

Aga vaatame teda eraldi Makefile'st.

Olge kataloogis kus on fail 'tere.b' ja kataloog 'sinna'.

proovige:

bash# install -m 770 -o imre -g users tere.b sinna

- m määrab faili õigused
- o määrab omaniku (owner)
- g määrab grupi (group)

Muuseas, kui katalogi sinna polnud enne olemas siis luuakse antud määrangutele vastav fail.

Install on huvitav selle poolest, et ta teeb vajaduse ka kõik tee peal olevad kataloogid kui tarvis. Erinevalt cp' st.

nt:

bash# install -m 777 -o imre -g users -d a/b/c/d
 

Niisiis, see on oluline programm ka muidu ettemääratud õigustega kataloogistruktuuri ja sinna failide paigutamiseks.
 

patch'id (in. k. paigad või lapid)

Nagu märgitud jagatakse linux'i tarkvara source'dena mis iseenesest on tekstifailid. Kujutleme, et mingi tarkvara nt. kerneli juures teatud aja jooksul leitud üles mingi hulk vigu või on lihtsalt inimestele tulnud uusi ideid ning soovitakse neid kokku võtta ja kasutajatele pakkuda. Minu teada tegeleb kerneliga isiklikult Linus Trovalds ning tema nõusolekul ilmuvad ka nn. uued nn. patchlevelid ja versioonid. Konkreetselt kerneli puhul on numeratsioon selline, et nt. 2.0.36 tähendab, et patchlevel on 36 ja versioon on 2.0. Juba kasutasimegi sõna patch!

Kernel tervikuna on ca 10 MB. Aga upgradedes patchlevelite vahel tõenäoliselt sisseviidud muutused ei haara kernelit kogu mahus. Enamus on sama. Ja seda asjaolu kasutataksegi ära.

Niisiis, et oma olemasolevast 2.0.35 kerneli source'st teha 2.0.36 source't on teil vaja muutusi ehk patchfaili (seda nimetatakse ka vahel diff failiks kuivõrd fail tehakse progammiga diff; teiselpoolt on ka põhjust faili kutsuda patch failiks kuivõrd ta rakendatakse programmiga patch). Kerneli patchfail on ca 300 kB suur.

Rakendamine toimub selliselt:

- Eeldame, et teil on olemas lahtipakitud kerneli 2.0.35 source tree '/usr/src/linux/' all.
- Olete tõmbanud ära faili  patch.2.0.36.gz ja see asub kataloogis '/usr/src/'

nüüd tuleb anda korraldus:

bash# gunzip -c patch.2.0.36.gz   |   patch -p0

Nende käskude tähendus on järgmine:

-c gunzip'i järel tähendab, et ta pakib lahti standard väljundisse (muuseas kui kasutate Netscape't, siis too asmastab ise gz faile lahti pakkida)

- p0 patch'i võtmena tähendab, et kataloogistruktuuris asutakse kerneli source'i tipus
 

Muuseas, kui soovite nt. kernel 2.0.33 patch'ida 2.0.36 peale, siis tuleb hankida kõik puudu olevad vahepealsed patch'id!
 

Selge on vist see, et kogu patchimise mõte on kergendada tarkvara kohaletõmbamist üle suhteliselt väikese kiirusega arvutivõrgu. Teisalt ka see, et tarkvara arhiividel kulub vähem ruumi hoidmaks efektiivselt erinevate kernelite source'sid.

Jutt oli küll kernelist kuid patch'itakse ka muud tarkvara ja keegi ei keela teil oma tekste või programmi source'sid patch'ida.

Tavaliselt patchitakse tekstifaile.
 

kuidas patch faile teha?

Võib teha käsitsi aga parem on selleks kasutada diff-utils'i programmi diff. See on standartsete distributsioonidega enamasti ühes.

olgu teil samas kataloogi kaks faili:

vana.ver:

1
2
3
4
5

uus.ver

a
1
2
4
5
b
c
d
 

Nagu näha uus versioon erineb selle poolest, et autor on otsustanud lisada algusse a ja lõppu b, c, d tähed ning eemaldada sümboli 3.:)
 

andke käsk

bash# diff -u vana.ver uus.ver
--- vana.ver    Thu Mar 11 15:03:27 1999
+++ uus.ver     Thu Mar 11 15:03:38 1999
@@ -1,5 +1,9 @@
+a
 1
 2
-3
 4
 5
+b
+c
+d
bash#

Mida see tähendab?

kõige ees on kirjas  millist faili võrreldi (---) millise failiga (+++). Edaspidi kasutatakse - ja + märke samas tähenduses.

-1,5 - vanas oli viis rida
+1,9 - uues on 9 rida

ja edasised - ja + märgid näitavad erinevust:

Et vanast vailist saada uut tuleb:

+ järel olev sümbol tuleb vanasse juurde panna
- järel olev sümbol vanast ära võtta

Kuidas ikkagi sisemiselt diff tegutseb jääb saladuseks. Aga keegi ei keela teil uusimas diff'i source't mis on saadaval :)

Niisiis, et luua vana.ver failile patchi mille abil saab vana uueks teha tuleb suunata see väljund faili. Muuseas selle väljundiga oskab programm patch vana.ver'i uuesks teha.

bash# diff -u vana.ver uus.ver > patch.vana.uueks

Ja patch rakendamine toimub nii:

bash# patch < patch.vana.uueks

Patch eeldab antud juhul, et käesolevas kataloogis on olemas nii vana.ver kui ka patch fail. Tekib muudetud vana.ver fail (NB! nimi jääb endiselt vana.ver kuid sisu muutub! Ja tegelik vana.ver salvestatakse faili vana.ver.orig). Kui segaseks jääb see, et kuidas patch.vana.uueks oskab rakenduda just vana.ver'io peale, siis vaadake patch'i - seal on üleval see kirjas.

Ja muidugi saate teha ka tagurpidi st.  patchida uut vanaks. Ei diff ega patch tea kumb fail on sisuliselt uuem kumb vanem :)

Ja lõpuks, patchida saab ka terveid kataloogistruktuure, proovige.
 
 
 

ggg