{"id":27542,"date":"2024-05-06T05:00:00","date_gmt":"2024-05-06T03:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=27542"},"modified":"2024-04-30T14:10:14","modified_gmt":"2024-04-30T12:10:14","slug":"jak-budowac-aplikacje-w-jezyku-c-c-za-pomoca-pliku-makefile","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/jak-budowac-aplikacje-w-jezyku-c-c-za-pomoca-pliku-makefile\/","title":{"rendered":"Jak budowa\u0107 aplikacje w j\u0119zyku C\/C++ za pomoc\u0105 pliku makefile"},"content":{"rendered":"\n<p>G\u0142\u00f3wnym celem artyku\u0142u jest pokazanie, w jaki spos\u00f3b mo\u017cna r\u0119cznie zbudowa\u0107 aplikacj\u0119 napisan\u0105 w j\u0119zyku C, korzystaj\u0105c z narz\u0119dzia GNU Make. Po jego przeczytaniu nie tylko poznacie struktur\u0119 pliku makefile, ale przede wszystkim zrozumiecie lepiej jego dzia\u0142anie. To daje mo\u017cliwo\u015b\u0107 <strong>dostosowywania gotowych szablon\u00f3w pod w\u0142asne projekty, <\/strong>a tak\u017ce pisania ich ca\u0142kowicie od zera. <\/p>\n\n\n\n<p>Adresatami tego wpisu s\u0105 g\u0142\u00f3wnie programi\u015bci system\u00f3w wbudowanych, kt\u00f3rzy maj\u0105 ju\u017c pewne <strong>do\u015bwiadczenie w tworzeniu oprogramowania w j\u0119zyku C\/C++<\/strong>, jednak do tej pory korzystali z gotowych rozwi\u0105za\u0144. Niemniej, artyku\u0142 bazuje na przyk\u0142adzie prostego projektu, dla kt\u00f3rego makefile tworzony jest krok po kroku \u2013 w zwi\u0105zku z tym <strong>nawet pocz\u0105tkuj\u0105ce osoby nie powinny czu\u0107 si\u0119 zagubione.<\/strong> W takim wypadku, lepsze zrozumienie struktury pliku makefile pozwoli r\u00f3wnie\u017c <strong>lepiej zrozumie\u0107 dzia\u0142anie CMake\u2019a<\/strong>, b\u0119d\u0105cego czym\u015b w rodzaju abstrakcji dla plik\u00f3w makefile.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Przygotowanie<\/strong><\/h2>\n\n\n\n<p>Tematyka tego wpisu skupia si\u0119 na praktycznym zagadnieniu, jakim jest budowanie programu napisanego w j\u0119zyku C. Zach\u0119cam wi\u0119c gor\u0105co nie tylko do przeczytania, ale r\u00f3wnie\u017c uruchamiania we w\u0142asnym zakresie omawianych przyk\u0142ad\u00f3w. B\u0119dziecie potrzebowali jedynie <strong>kompilatora j\u0119zyka C i dowolnego edytora<\/strong>.<\/p>\n\n\n\n<p>Poniewa\u017c wykorzystywany b\u0119dzie kompilator j\u0119zyka C, dzia\u0142aj\u0105cy jako aplikacja pow\u0142oki Unix, docelowo b\u0119dziemy potrzebowa\u0107 zainstalowanej dystrybucji systemu Linux. Je\u015bli jednak jeste\u015bcie u\u017cytkownikami Windowsa, nie przejmujcie si\u0119 \u2013 nie musicie zmienia\u0107 systemu lub instalowa\u0107 drugiego.<\/p>\n\n\n\n<p>Je\u015bli macie dost\u0119p do program\u00f3w takich jak VMware lub VirtualBox, to mo\u017cecie uruchomi\u0107 wirtualn\u0105 maszyn\u0119 z zainstalowanym systemem Linux, ale s\u0105 jeszcze prostsze rozwi\u0105zania np.:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cygwin,<\/li>\n\n\n\n<li>MinGW,<\/li>\n\n\n\n<li>WSL (Windows Subsystem for Linux).<\/li>\n<\/ul>\n\n\n\n<p>Dzi\u0119ki tym programom mo\u017cliwe jest dodanie do systemu Windows cz\u0119\u015bci funkcjonalno\u015bci systemu Linux w tym biblioteki standardu POSIX. W omawianych przyk\u0142adach b\u0119d\u0119 korzysta\u0142 z ostatniej opcji, ale bez wzgl\u0119du na to, kt\u00f3r\u0105 wybierzecie, r\u00f3wnie\u017c b\u0119dziecie w stanie zrealizowa\u0107 te dzia\u0142ania.<\/p>\n\n\n\n<p>Ostatni\u0105 rzecz\u0105, jakiej potrzebujemy, jest <strong>przyk\u0142adowy program<\/strong>. W artykule skupiamy si\u0119 na tworzeniu plik\u00f3w makefile, dlatego najlepiej, je\u015bli <strong>kod<\/strong>, kt\u00f3rego u\u017cyjemy b\u0119dzie <strong>maksymalnie prosty<\/strong>. Poni\u017cej zamieszczam przyk\u0142ad, kt\u00f3ry mo\u017cecie wykorzysta\u0107. Jest to bardzo prosty kalkulator, kt\u00f3ry posiada jedynie funkcj\u0119 dodawanie i odejmowanie dw\u00f3ch liczb.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2024\/04\/1.jpg\"><img decoding=\"async\" width=\"1024\" height=\"547\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2024\/04\/1-1024x547.jpg\" alt=\"Przyk\u0142ad prostego kalkulatora\" class=\"wp-image-27543\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2024\/04\/1-1024x547.jpg 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2024\/04\/1-300x160.jpg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2024\/04\/1-768x410.jpg 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2024\/04\/1.jpg 1207w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Ryc. 1 Przyk\u0142ad prostego kalkulatora<\/figcaption><\/figure>\n\n\n\n<p>W g\u0142\u00f3wnym pliku main.c \u00a0funkcje dodawania i odejmowania zosta\u0142y u\u017cyte na przyk\u0142adzie warto\u015b\u0107 na sta\u0142e zapisanych w kodzie. To wszystko czego potrzebujemy, aby przej\u015b\u0107 do kompilacji.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Tworzenie pliku makefile krok po kroku<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>R\u0119czna kompilacja programu<\/strong><\/h3>\n\n\n\n<p>Poniewa\u017c przedstawiony przyk\u0142ad jest bardzo prosty i zawiera tylko 3 pliki, aby go skompilowa\u0107, w zasadzie nie potrzebujemy pisa\u0107 pliku makefile. Mo\u017cemy uruchomi\u0107 kompilacje, r\u0119cznie podaj\u0105c list\u0119 plik\u00f3w.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ngcc main.c calculator.c -o calculator_app\n<\/pre><\/div>\n\n\n<p>Niestety, zazwyczaj programy, nawet te ma\u0142o skomplikowane, zawieraj\u0105 o wiele wi\u0119cej plik\u00f3w, a ich struktura jest znacznie bardziej z\u0142o\u017cona. Pliki \u017ar\u00f3d\u0142owe s\u0105 odseparowane od plik\u00f3w nag\u0142\u00f3wkowych i dodatkowo pogrupowane w r\u00f3\u017cnych katalogach. Chc\u0105c w ten sam spos\u00f3b uruchomi\u0107 kompilacj\u0119, komenda sta\u0142aby si\u0119 bardzo d\u0142uga i wymagaj\u0105ca. Na szcz\u0119\u015bcie <strong>rozwi\u0105zaniem tego problemu jest plik makefile.<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Definiowanie target\u00f3w<\/strong><\/h3>\n\n\n\n<p>Podobnie jak w przypadku r\u0119cznego uruchomienia kompilacji, plik makefile zawiera informacje, jakie pliki, w jaki spos\u00f3b i do jakiego miejsca maj\u0105 zosta\u0107 zbudowane, z tym, \u017ce s\u0105 one umieszczone w jednym pliku. Dzi\u0119ki temu docelowa komenda uruchamiaj\u0105ca budowanie aplikacji b\u0119dzie bardzo kr\u00f3tka.<\/p>\n\n\n\n<p>W g\u0142\u00f3wnym katalogu projektu stw\u00f3rzcie nowy plik o nazwie \u201eMakefile\u201d bez \u017cadnego rozszerzenia, po prostu sama nazwa, mo\u017ce by\u0107 napisana du\u017c\u0105 lub ma\u0142\u0105 liter\u0105. Plik ten musi zawiera\u0107 przynajmniej jedn\u0105 definicj\u0119 tak zwanego \u201etargetu\u201d. Mo\u017cna powiedzie\u0107, \u017ce jest to co\u015b w rodzaju nazwy operacji, kt\u00f3r\u0105 chcemy wykona\u0107. Poniewa\u017c proces kompilacji zwyczajowo nazywany jest \u201ebudowaniem\u201d, dlatego najcz\u0119\u015bciej spotkasz si\u0119 z nazw\u0105 \u201e<em>build<\/em>\u201d, ale tak naprawd\u0119 mo\u017ce ona by\u0107 dowolna np. \u201e<em>calculator<\/em>\u201d.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncalculator:\n\tgcc main.c calculator.c -o calculator_app\n\n<\/pre><\/div>\n\n\n<p><strong>Uwaga:<\/strong> Pami\u0119taj, \u017ce ka\u017cda kolejna nowa linia targetu powinna zaczyna\u0107 si\u0119 od jednego znaku tabulatora (nie spacji). Po zapisaniu pliku mo\u017cemy uruchomi\u0107 budowanie naszej aplikacji kalkulatora. Tym razem, zamiast wpisywa\u0107 pe\u0142n\u0105 komend\u0119, wystarczy, \u017ce uruchomimy program make, wskazuj\u0105c mu nazw\u0119 docelowego targetu.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nmake calculator\n<\/pre><\/div>\n\n\n<p>Liczba \u201etarget\u00f3w\u201d mo\u017ce by\u0107 dowolna. Przyk\u0142adowo, je\u015bli chcieliby\u015bmy skasowa\u0107 wynik kompilacji, mo\u017cemy stworzy\u0107 nowy target o nazwie \u201e<em>clean<\/em>\u201d, usuwaj\u0105cy okre\u015blone pliki. Podobnie mo\u017cemy stworzy\u0107 target \u201e<em>run<\/em>\u201d, odpowiadaj\u0105cy za uruchamiania zbudowanej aplikacji.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncalculator:\n    gcc main.c calculator.c -o calculator_app\n\nclean:\n    rm -rf calculator_app\n\nrun:\n    .\/calculator_app\n\n<\/pre><\/div>\n\n\n<p>Nie zawsze musimy podawa\u0107 nazw\u0119 targetu \u2013 samo polecenie \u201emake\u201d r\u00f3wnie\u017c zadzia\u0142a. Domy\u015blnie zostanie wykonany pierwszy napotkany target \u2013 w naszym przypadku b\u0119dzie to <em>calculator<\/em> \u2013 wi\u0119c wynik dzia\u0142ania b\u0119dzie dok\u0142adnie taki sam. Dlatego, pisz\u0105c pliki makefile, warto jako pierwszy target ustawi\u0107 ten, z kt\u00f3rego najcz\u0119\u015bciej b\u0119dziemy korzysta\u0107. <strong>Oszcz\u0119dzi nam to sporo pisania.<\/strong><\/p>\n\n\n\n<p>Jak na razie przedstawiony plik makefile w obecnej formie jest ma\u0142o przydatny dla \u201eprawdziwej aplikacji\u201d. Definiowanie target\u00f3w w postaci jednej d\u0142ugiej komendy zawieraj\u0105cej ca\u0142\u0105 list\u0119 plik\u00f3w b\u0119dzie zar\u00f3wno nieczytelne jak i uci\u0105\u017cliwe w utrzymaniu. Istniej\u0105 jednak <strong>odpowiednie mechanizmy<\/strong>, kt\u00f3re pozwalaj\u0105 podzieli\u0107 ten proces na etapy, a cz\u0119\u015b\u0107 z nich <strong>nawet zautomatyzowa\u0107<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Makefile target w postaci pliku<\/strong><\/h3>\n\n\n\n<p>Targetem mo\u017ce by\u0107 nie tylko \u201epusta nazwa\u201d, tak jak to zrobili\u015bmy do tej pory. Tak naprawd\u0119 makefile domy\u015blnie oczekuje, \u017ce b\u0119dzie to plik. Dzi\u0119ki temu mo\u017cemy rozdzieli\u0107 proces kompilacji na etapy zwi\u0105zane z kompilacj\u0105 pojedynczych plik\u00f3w wchodz\u0105cych w sk\u0142ad docelowej aplikacji.<\/p>\n\n\n\n<p>Stw\u00f3rzmy <strong>dodatkowe dwa targety<\/strong>, po jednym dla ka\u017cdego pliku \u017ar\u00f3d\u0142owego. Tym razem nie b\u0119dzie to tylko pusty alias, a nazwa pliku wynikowego. Plik main.c b\u0119dziemy budowa\u0107 do pliku wynikowego <em>main.o<\/em>, a calculator.c do <em>calculator.o<\/em>. <strong>Pliki z rozszerzeniem .o<\/strong>, to tzw. \u201epliki obiektowe\u201d (ang. object files). Bardzo skr\u00f3towo mo\u017cna przedstawi\u0107 je jako pliki wynikowe procesu kompilacji, kt\u00f3re jeszcze nie nadaj\u0105ce si\u0119 do uruchomienia.<\/p>\n\n\n\n<p>W tym przypadku docelowy target buduj\u0105cy wynikow\u0105 aplikacj\u0119 powinien odwo\u0142ywa\u0107 si\u0119 do nich, zamiast do plik\u00f3w \u017ar\u00f3d\u0142owych, tak jak mia\u0142o to miejsce wcze\u015bniej. Po wprowadzeniu tych modyfikacji makefile powinien wygl\u0105da\u0107 nast\u0119puj\u0105co:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncalculator:\n    gcc main.o calculator.o -o calculator_app\n\nmain.o:\n    gcc main.c -c main.o\n\ncalculator.o:\n    gcc calculator.c -c calculator.o\n\nclean:\n    rm -rf calculator_app\n\nrun:\n    .\/calculator_app\n\n<\/pre><\/div>\n\n\n<p>Niestety, gdy tym razem spr\u00f3bujemy zbudowa\u0107 aplikacj\u0119 poleceniem \u201emake calculator\u201d naszym oczom uka\u017ce si\u0119 b\u0142\u0105d m\u00f3wi\u0105cy o tym, \u017ce pliki main.o i calculator.o nie zosta\u0142y zlokalizowane. Wynika to z tego, \u017ce target \u201e<em>calculator<\/em>\u201d odwo\u0142uje si\u0119 do plik\u00f3w main.o i calculator.o, zanim zosta\u0142y utworzone. &nbsp;Musz\u0105 one zosta\u0107 uruchomione wcze\u015bniej.<\/p>\n\n\n\n<p>Zatem kolejno\u015b\u0107 powinna by\u0107 nast\u0119puj\u0105ca:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nmake main.o\nmake calculator.o\nmake calculator\n\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\"><strong>Makefile target z warunkami wst\u0119pnymi<\/strong><\/h3>\n\n\n\n<p>Aby unikn\u0105\u0107 wpisywania target\u00f3w w \u015bci\u015ble okre\u015blonej kolejno\u015bci, mo\u017cna u\u017cy\u0107 tzw. <strong>warunk\u00f3w wst\u0119pnych<\/strong> (ang. prerequisites). Daj\u0105 one mo\u017cliwo\u015b\u0107 przekazania kompilatorowi informacji o kolejno\u015b\u0107 budowania poszczeg\u00f3lnych element\u00f3w. W tym przypadku target calculator mo\u017ce zosta\u0107 wykonany, pod warunkiem, \u017ce wcze\u015bniej zostan\u0105 wykonane inne targety.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncalculator: calculator.o main.o\n    gcc main.o calculator.o -o calculator_app\n\n<\/pre><\/div>\n\n\n<p>Teraz mo\u017cemy od razu uruchomi\u0107 w\u0142a\u015bciwe polecenie bez informacji zwrotnej o b\u0142\u0119dzie. Efektem budowania aplikacji w ten spos\u00f3b b\u0119dzie nie tylko utworzenie pliku <strong>calculator_app, <\/strong>ale r\u00f3wnie\u017c pliku z rozszerzeniem .o.<\/p>\n\n\n\n<p>W takim razie, aby dobrze posprz\u0105ta\u0107 po procesie budowania, nale\u017ca\u0142oby rozszerzy\u0107 target \u201e<em>clean<\/em>\u201d o mo\u017cliwo\u015b\u0107 ich usuwania.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncalculator: calculator.o main.o\n    gcc main.o calculator.o -o calculator_app\n\nmain.o:\n    gcc main.c -c main.o\n\ncalculator.o:\n    gcc calculator.c -c calculator.o\n\nclean:\n    rm -rf calculator_app\n    rm -rf *.o\n\n<\/pre><\/div>\n\n\n<p>Niestety w dalszym ci\u0105gu makefile w takie formie nie jest zbyt u\u017cyteczny. Nie b\u0119dziemy przecie\u017c dla ka\u017cdego pliku r\u0119cznie tworzy\u0107 dedykowanego targetu, a p\u00f3\u017aniej jeszcze ich \u0142\u0105czy\u0107, aby zbudowa\u0107 ko\u0144cowy plik wykonywalny. <strong>Idealnie by\u0142oby, gdyby makefile robi\u0142 to automatycznie.<\/strong>\u00a0<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Automatyczne tworzenie listy plik\u00f3w \u017ar\u00f3d\u0142owych<\/h3>\n\n\n\n<p>Zanim przejdziemy do tworzenia pierwszego szablonu, musimy pozna\u0107 jego elementy sk\u0142adowe. S\u0105 to znaki spacjalne tj.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>$,<\/li>\n\n\n\n<li>@,<\/li>\n\n\n\n<li>%,<\/li>\n\n\n\n<li>^ ,<\/li>\n\n\n\n<li>&lt;.<\/li>\n<\/ul>\n\n\n\n<p>U\u017cywane w r\u00f3\u017cnych kombinacjach w pliku makefile pomagaj\u0105 w tworzeniu r\u00f3\u017cnego rodzaju szablon\u00f3w. Poni\u017cej przyk\u0142adowe zastosowanie:<\/p>\n\n\n\n<figure class=\"wp-block-table aligncenter\"><table><tbody><tr><td>$@<\/td><td>oznacza nazw\u0119 pliku b\u0119d\u0105cego stanowi\u0105cego target<\/td><\/tr><tr><td>$&lt;<\/td><td>nazwa pierwszej napotkanej zale\u017cno\u015bci<\/td><\/tr><tr><td>$^<\/td><td>nazwy wszystkich zale\u017cno\u015bci<\/td><\/tr><tr><td>%<\/td><td>dowolny ci\u0105g znak\u00f3w<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Dzi\u0119ki tym symbolom mo\u017cna stworzy\u0107 szablon, kt\u00f3ry b\u0119dzie odpowiada\u0142 za automatyczne tworzenie target\u00f3w dla ka\u017cdego pliku \u017ar\u00f3d\u0142owego.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n%.o: %.c\n    gcc -c $&amp;lt; -o $@\n\n<\/pre><\/div>\n\n\n<p><strong>Make zinterpretuje ten fragment w nast\u0119puj\u0105cy spos\u00f3b<\/strong>: dla ka\u017cdego pliku z rozszerzeniem .c znajduj\u0105cego si\u0119 w g\u0142\u00f3wnym katalogu projektu utworzy\u0142 plik z rozszerzeniem .o wed\u0142ug regu\u0142y okre\u015blonej w drugiej linii. Ta regu\u0142a to: nazwa napotkanej zale\u017cno\u015bci (czyli kolejnego napotkanego pliku .c), skompilowana do pliku o tej samej nazwie, ale z rozszerzeniem .o. Jest to dok\u0142adnie ten sam proces, kt\u00f3ry wykonywali\u015bmy wcze\u015bniej r\u0119cznie.<\/p>\n\n\n\n<p>Poniewa\u017c zosta\u0142 on zautomatyzowany, <strong>od teraz wi\u0119ksza liczba plik\u00f3w w projekcie nie b\u0119dzie ju\u017c stanowi\u0142a problemu.\u00a0<\/strong><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncalculator: calculator.o main.o\n    gcc main.o calculator.o -o calculator_app\n\nclean:\n    rm -rf calculator_app\n    rm -rf *.o\n\nrun:\n    .\/calculator_app\n\n%.o: %.c\n    gcc -c $&amp;lt; -o $@\n\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\"><strong>Zmienne<\/strong><\/h3>\n\n\n\n<p>Podobnie jak w przypadku r\u00f3\u017cnych j\u0119zyk\u00f3w programowania, makefile r\u00f3wnie\u017c pozwala na tworzenie zmiennych. Ich zalety s\u0105 dok\u0142adnie takie same \u2013 mo\u017cemy tworzy\u0107 czytelniejsz\u0105 struktur\u0119 i szybciej wprowadza\u0107 modyfikacje, edytuj\u0105c tylko jedno miejsca zamiast wielu. Aby utworzy\u0107 tak\u0105 zmienn\u0105, wystarczy dla dowolnego ci\u0105gu znaku u\u017cy\u0107 znaku r\u00f3wno\u015bci, przypisuj\u0105c zawarto\u015b\u0107, przyk\u0142adowo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">NAZWA_ZMIENNEJ = TRE\u015a\u0106 <\/pre>\n\n\n\n<p>Odwo\u0142anie do takiej zmiennej polega na u\u017cyciu jej nazwy w okr\u0105g\u0142ym nawiasie, poprzedzonym znakiem $, czyli przyk\u0142adowo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$(NAZWA_ZMIENNEJ)<\/pre>\n\n\n\n<p>W ramach \u0107wiczenia dodamy do aktualnego pliku trzy zmienne:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pierwsza \u2013 b\u0119dzie definiowa\u0142a nazw\u0119 pliku wynikowego,<\/li>\n\n\n\n<li>druga \u2013 b\u0119dzie przechowywa\u0142a list\u0119 plik\u00f3w (\u201eobject files\u201d),<\/li>\n\n\n\n<li>trzecia \u2013 b\u0119dzie zawiera\u0142a nazw\u0119 wykorzystywanego kompilatora.<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nAPP_NAME = calculator_app\nC_OBJ    = calculator.o main.o\nCC       = gcc\n\n<\/pre><\/div>\n\n\n<p>Teraz target calculator b\u0119dzie m\u00f3g\u0142 si\u0119 odwo\u0142ywa\u0107 do tych zmiennych w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncalculator: $(C_OBJ)\n    $(CC) $(C_OBJ) -o $(APP_NAME)\n\n<\/pre><\/div>\n\n\n<p><strong>Uwaga:<\/strong> zmienne mog\u0105 by\u0107 tworzone w dowolnym miejscu, jednak nie wcze\u015bniej ni\u017c miejsce, w kt\u00f3rym si\u0119 do niej odwo\u0142ujemy.<\/p>\n\n\n\n<p>Zazwyczaj <strong>wszystkie zmienne zebrane s\u0105 w jednym miejscu na pocz\u0105tku pliku<\/strong>, co znacznie u\u0142atwia ich odnalezienie i p\u00f3\u017aniejsz\u0105 modyfikacj\u0119. <\/p>\n\n\n\n<p>Za pomoc\u0105 zmiennych mo\u017cemy r\u00f3wnie\u017c tworzy\u0107 wyra\u017cenia warunkowe, kt\u00f3re umo\u017cliwi\u0105 sterowanie procesem kompilacji. Jako przyk\u0142ad zdefiniujmy zmienn\u0105 DEBUG, kt\u00f3ra w zale\u017cno\u015bci od warto\u015bci logicznej (true lub false) b\u0119dzie definiowa\u0142a, z jakich flag kompilacji b\u0119dziemy korzysta\u0107:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nDEBUG = true\nC_FLAGS = -w\n\nifeq ($(DEBUG), true)\nC_FLAGS += -g0\nelse\nC_FLAGS += -g3\nendif\n\n<\/pre><\/div>\n\n\n<p>Zastosowana zosta\u0142a kombinacja \u201e+=\u201d, poniewa\u017c nie powoduje ona nadpisania poprzedniej zawarto\u015bci zmiennej now\u0105 warto\u015bci\u0105, jak to ma miejsce przy samym znaku r\u00f3wno\u015bci, tylko rozszerzenie zawarto\u015bci o kolejn\u0105 warto\u015b\u0107.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Automatyzacja z wykorzystaniem dzikich kart<\/strong><\/h3>\n\n\n\n<p>Ostatni\u0105 rzecz\u0105, kt\u00f3r\u0105 potrzebujemy zmieni\u0107 w naszym pliku makefile, aby sta\u0142 si\u0119 w pe\u0142ni u\u017cyteczny dla du\u017cego projektu, jest lista plik\u00f3w wynikowych kompilacji. Jej zawarto\u015b\u0107 zale\u017cy od plik\u00f3w \u017ar\u00f3d\u0142owych znajduj\u0105cych si\u0119 w projekcie. Spr\u00f3bujmy i t\u0119 list\u0119 plik\u00f3w stworzy\u0107 w spos\u00f3b zautomatyzowany, wykorzystuj\u0105c <strong>dzik\u0105 kart\u0119<\/strong>. Dzikie karty mo\u017cna por\u00f3wna\u0107 do wbudowanych funkcji. Ich u\u017cycie wygl\u0105da podobnie jak zmiennych:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$(wildcard pattern...) <\/pre>\n\n\n\n<p>Ten ci\u0105g znak\u00f3w, w miejscu jego u\u017cycia, zostanie zast\u0105piony list\u0105 plik\u00f3w oddzielonych spacj\u0105, kt\u00f3rych nazwy spe\u0142niaj\u0105 podany wzorzec. Przyk\u0142adowo:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nC_SRC = $(wildcard *.c)\n<\/pre><\/div>\n\n\n<p>Ten ci\u0105g znak\u00f3w spowoduje, \u017ce zmienna C_SRC b\u0119dzie zawiera\u0142a list\u0119 plik\u00f3w o dowolnej nazwie z rozszerzeniem .c, czyli plik\u00f3w \u017ar\u00f3d\u0142owych. Czy tak jest, mo\u017cemy sprawdzi\u0107, wy\u015bwietlaj\u0105c zawarto\u015b\u0107 komend\u0105 \u201eecho\u201d:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncalculator: $(C_OBJ)\n    echo $(C_SRC)\n    $(CC) $(C_FLAGS) $(C_OBJ) -o $(APP_NAME)\n\n<\/pre><\/div>\n\n\n<p>Aby przekonwertowa\u0107 t\u0119 list\u0119 na pliki z rozszerzeniem .o, mo\u017cemy wykorzysta\u0107 inn\u0105 funkcj\u0119 o nazwie <strong>patsubst<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$(patsubst pattern,replacement,text)<\/pre>\n\n\n\n<p>Jej dzia\u0142anie polega na przeszukiwaniu podanego tekstu w celu znalezienia nazw spe\u0142niaj\u0105cych dany wzorzec i zamiany ich zgodnie z podan\u0105 regu\u0142\u0105. A wi\u0119c <strong>konwersj\u0119 listy plik\u00f3w \u017ar\u00f3d\u0142owych na list\u0119 obiekt\u00f3w<\/strong> mo\u017cna przeprowadzi\u0107 w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nC_OBJ = $(patsubst %.c, %.o, $(C_SRC))\n<\/pre><\/div>\n\n\n<p>Make przeszuka zmienn\u0105 <strong>C_SRC <\/strong>pod k\u0105tem plik\u00f3w o dowolnej nazwie zawieraj\u0105cej rozszerzenie .c i zast\u0105pi je t\u0105 sam\u0105 nazw\u0105 z rozszerzeniem .o. Musimy jedynie pami\u0119ta\u0107, aby najpierw stworzy\u0107 i uzupe\u0142ni\u0107 zmienn\u0105 C_SRC, a dopiero p\u00f3\u017aniej C_OBJ. Ko\u0144cowa wersja pliku makefile powinna wygl\u0105da\u0107 tak:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nAPP_NAME = calculator_app\nCC = gcc\nDEBUG = true\nC_FLAGS = -w\nC_SRC = $(wildcard *.c)\nC_OBJ = $(patsubst %.c, %.o, $(C_SRC))\n\nifeq ($(DEBUG), true)\nC_FLAGS += -g0\nelse\nC_FLAGS += -g3\nendif\n\ncalculator: $(C_OBJ)\n    $(CC) $(C_FLAGS) $(C_OBJ) -o $(APP_NAME)\n\nclean:\n    rm -rf calculator_app\n    rm -rf *.o\n\nrun:\n    .\/calculator_app\n\n%.o: %.c\n    gcc -c $&amp;lt; -o $@\n\n\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\"><strong>Podsumowanie<\/strong><\/h2>\n\n\n\n<p>Mam nadziej\u0119, \u017ce po tej lekturze rozumiecie ju\u017c, do czego s\u0142u\u017c\u0105 pliki makefile i jak z nich korzysta\u0107. Wiecie, jak u\u017cywa\u0107 zmiennych, definiowa\u0107 targety, czy odwo\u0142ywa\u0107 si\u0119 do wbudowanych funkcji w celu automatyzacji zada\u0144. Wyt\u0142umaczy\u0142em tak\u017ce, jak sterowa\u0107 procesem kompilacji.<\/p>\n\n\n\n<p>Wszystko to otwiera przed Wami zupe\u0142nie nowe mo\u017cliwo\u015bci. Nie musicie polega\u0107 wy\u0142\u0105cznie na zintegrowanych \u015brodowiska deweloperskich, aby zbudowa\u0107 napisan\u0105 przez Was aplikacj\u0119. Mo\u017cecie teraz:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>tworzy\u0107 w\u0142asne, zautomatyzowane \u015brodowiska do budowania i testowania projektu w oparciu o narz\u0119dzie CI\/CD,<\/li>\n\n\n\n<li>tworzy\u0107 kod, kt\u00f3ry b\u0119dzie m\u00f3g\u0142 by\u0107 budowany na r\u00f3\u017cne platformy sprz\u0119towe,<\/li>\n\n\n\n<li>pracowa\u0107 w metodyce TDD,<\/li>\n\n\n\n<li>korzysta\u0107 z innych edytor\u00f3w kodu.<\/li>\n<\/ul>\n\n\n\n<p>Opisywane w tym artykule narz\u0105dzie jest du\u017co bardziej z\u0142o\u017cone ni\u017c to, co zosta\u0142o tutaj przedstawione. Niemniej, mam nadziej\u0119, \u017ce przekazana wiedza stanowi solidn\u0105 podstaw\u0119 do dalszego zg\u0142\u0119biania tego tematu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>\u0179r\u00f3d\u0142a<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.gnu.org\/software\/make\/manual\/make.html\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >GNU make, manual<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/devhints.io\/makefile\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >Makefile<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/siipoland-my.sharepoint.com\/personal\/dbaldyga_sii_pl\/Documents\/Blog\/autorzy%20i%20teksty\/Karol%20Mika\/\u2022%09https:\/latedev.wordpress.com\/2014\/11\/08\/generic-makefiles-with-gcc-and-gnu-make\/\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >Generic Makefiles with GCC and GNU Make<\/a><\/li>\n<\/ul>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;27542&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;10&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 10)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Jak budowa\u0107 aplikacje w j\u0119zyku C\\\/C++ za pomoc\u0105 pliku makefile&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 10)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>G\u0142\u00f3wnym celem artyku\u0142u jest pokazanie, w jaki spos\u00f3b mo\u017cna r\u0119cznie zbudowa\u0107 aplikacj\u0119 napisan\u0105 w j\u0119zyku C, korzystaj\u0105c z narz\u0119dzia GNU &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/jak-budowac-aplikacje-w-jezyku-c-c-za-pomoca-pliku-makefile\/\">Continued<\/a><\/p>\n","protected":false},"author":630,"featured_media":27549,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[1546,1527,1512,563,1058],"class_list":["post-27542","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-przeglad-narzedzi","tag-c-2","tag-poradnik","tag-embedded","tag-cpp"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2024\/04\/Jak-budowac-aplikacje-w-jezyku-C-C-za-pomoca-pliku-makefile.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/27542"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/630"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=27542"}],"version-history":[{"count":3,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/27542\/revisions"}],"predecessor-version":[{"id":27548,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/27542\/revisions\/27548"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/27549"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=27542"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=27542"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=27542"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}