آشنایی با ابزار GNU make(1555 مجموع کلمات موجود در متن) (1836 بار مطالعه شده است) 
اگر تا به حال برنامهای را از کـد منبع آن کامپایل کرده باشـید حتـما به دستور make بـر خورد کـردهاید. make نـرم افزاری اسـت برای خودکار سازی فرآیند کامپایل نرم افزارهای بزرگ. وظیفه این برنامه تشخیص و کامپایل مجدد فایل های تغییر یافته اسـت. به این ترتیب فایلهایی که دست نخورده باقی مانده اند مجددا کامپایل نشده و مقدار قابل توجهی در زمان صرف جویی خواهد شد.
هر چند این ابزار بیشـتر توسـط برنامه نویسـان زبان سی بـه کار می رود، برای زبـانهای دیگر نـیـز قـابل استفاده است. البته این ابزار تنها مختص برنامه نویسان و کامپایل برنامه نیست و می تواند برای هر فرآیندی که در آن تعدادی فایل باید بـر اساس تغیـیـرات فایل های دیگر بـه روز شـوند، استـفاده گردد. بـه عنوان مثال برای تهیه پشتیبان از اطلاعات. به این صورت که فقط فایل های تغییر داده شده به آرشیو اضافه شوند تا از دوباره کاری جلوگیری شود.
اگر بخواهیم کمی تخـصـصی صحبت کنیم، make جزء دسـته نرم افزارهایی کـه بـا نام سیستمهـای خبـره[۱] شـناخته میشوند، قرار میگیرد.
مقدمه
make در سـال ۱۹۷۷ توسط استوارت فلدمن[۲] در آزمایشگاه های بل[۳] ساخته شد. در سال ۲۰۰۳ دکتر فلدمن برای ابداع این ابزار مفید جایزه [۴] ACM را دریافت کرد.
هم اکنون ابزارهای make زیادی وجود دارند که بعضی ها با استفاده از همان make اولیه نوشته شده اند و بعضی دیگر از ابتدا دوباره طراحی شده اند.
در این مقـاله قصد داریم ابزار GNU make را که توسط ریچارد استالمن[۵] و رونالد مک گراث[۶] پیاده سـازی و از نسـخه ۳.۷۶ به بعد توسط پاول اسمیت[۷] توسعه یافته است معرفی کنیم.
آماده سازی و راه اندازی make
برای استفاده از این ابزار باید فایلی با نام Makefile وجود داشته باشد تا به وابستگی بین فایل ها و نحوه به روز رسانی آنها را مشخص کند. این فایل می تـوانـد نـام های دیـگری نـیـز داشـته باشـد. GNU make به تــرتـیـب فایلی با یـکی از نـامهای Makefile, makefile, GNUmakefile را جستـجو می کـنـد. به صـورت مـعمـول بـهتر است شما یکی از نـامهای makefile و یا Makefile را انـتـخاب کنید. (نام دوم به خاطر آغاز شـدن با حـرف بـزرگ ارجحیت دارد زیـرا در کنـار فایلهای مهم دیگری مانند README و COPYING قرار خواهد گرفت) هـر چند شـما می توانید نام GNUmakefile را نیز برای این فایل در نظر بگیرید اما این نام توصیه نمیشود. به این دلیل که توسط دیگر نسخههای make قابل شناسایی نیست.
در صورت وجود یک Makefile شما میتوانید به راحتی وارد دایرکتوری که Makefile در آن وجود دارد شده و دستور make را در خط فرمان صادر نمایید. البته با ارسال پارامتر f- و نام فایل نیز میتوانید این کار را خارج از آن دایرکتوری انجام دهید. یک makefile ساده می تواند به شکل زیر تعریف شود:
Target ... : prerequisites ...
Command ...
...
Target فایـلی اسـت کـه بـاید به روز رسـانی شـود. (از این به بعد آن را هدف خواهیم نامید) در یک برنامه سی معمولا هدف یک فایـل object است کـه پـس از کامپایل شـدن به فایل های دیگر link میشود. prerequisites لیستی از پیش نیاز های Target است و command دستـور یا دستوراتی است کـه برای به روز رسانی Target استفاده میشود. دقت کنید که قبل از نوشتن هر دستور باید از کاراکتر Tab استفاده کنید. با یک مثال مطلب را بیشتر روشن می کنیم.
فرض کنید در حال طراحی نرم افزاری بـه زبـان سی با نام sample هستـیـم. فایلهای این نرم افزار عبارتند از utils.cpp defs.h و main.cpp فایـل utils.cpp از توابعی کـه در defs.h تعریف شده استـفاده میکـند. بنابراین utils.cpp به defs.h وابسته است یا به عبارت دیگر defs.h پیش نیاز utils.cpp است. Makefile میتواند به شکل زیر باشد:
sample: main.o utils.o
gcc main.o utils.o -o sample
main.o: utils.o main.cpp
gcc -c main.cpp
utils.o: utils.cpp defs.h
gcc -c utils.cpp
همانطور که مشاهده میکنید فایل utils.o به دو فایل defs.h و utils.cpp وابسته است. علت اینکه ما فایل utils.cpp را به عنوان پیش نیاز خودش ذکر کرده ایم این است که با تغییر این فایل نیز ما باید دوباره دستور کامپایل را اجرا کنیم.
همانطور که متوجه شدهاید هدف main.o به utils.o که خود نیز یک هدف است وابسته میباشد. در این حـالت با تغییر هر یـک از پیش نـیـازهای utils.o هر دو هدف main.o و utils.o نـیز باید بـه روز رسانی شوند. در واقع می تـوان گفت که پیش نیازهای utils.o پـیـش نیاز main.o نیز میباشـند. در پایان هر دو این object ها به یکدیگر link شده و فایل اجرایی sample را میسازند.
شاید تعجب کنید که چرا ترتیب کامپایل شدن این نرم افزار نمونه به صورت معکوس در Makefile نوشته شده است؟
دلیل آن نـحوه خوانـدن این فایـل تـوسـط make اسـت. make اولیـن هـدف را در Makefile می خـواند و سـعی در بـه روز رسانی آن (با توجه به پیش نیازها) میکند. در فایل نمونهٔ ما اولین هدف sample است که به دو فایل main.o و utils.o وابسته است. اگر پیش نیازها به روز نبودند، make به دنبال هدفی میگردد که به روز رسانی آنها را انجام میدهد.
برای این کار Makefile را از بـالا بـه پایین برای یافتـن این اهداف جستجو می کند. به این ترتیب می توانید مطمئن شوید که با هربار اجرای دستور make فایل sample و تمام پیش نیازهای آن در صورت لزوم به روز رسانی خواهند شد.
اهداف می توانند به صورت پارامتر نیز برای make ارسـال شونـد. بـه عنـوان مثال اکثر Makefile ها شامـل هدفی به نام clean هستند که کلیه object ها و فایلهای باینری ساخته شده را پاک می کند.
clean:
rm *.o
rm sample
در این مثال دو دستور برای clean در نظر گرفته شده است. و همانطور که مشاهده میکنید هدف clean به هیچ هدف یا فایل دیگری وابسته نیست. بنابراین همیشه قابل اجرا است. به این معنی کـه برای اجرا شدن شـرایط خاصی (مانند تغییر در پیش نیازها) نیاز ندارد. و از آنـجایی که هدفی به clean وابسـته نیست به صورت خودکار فراخوانده نخواهد شد. برای پاک کردن فایل های کامپایل شده می توانید دستور زیر را صادر کنید:
$make clean
پارامتر clean باعث میشود که make به جای به روز رسانی اولین هدف سعی در به روز رسانی clean نمایـد که منجر به پاک شدن کلیه فایلهای object و فایل sample خواهد شد.
متغیرها در Makefile
همانطور که گفتـیـم ابزار make بـرای خـودکـار سازی فرآیـنـدها به کـار گـرفته می شود. بـنابراین طبیعی است که در آن امکاناتی مانند متغییرها، حلقه ها، شرط ها و ... قرار گرفته باشد. خطوط زیر را در یک Makefile در نظر بگیرید:
main.o: utils.o main.cpp
gcc $(CFLAGS) -c main.cpp
CFLAGS یک متغیـیـر محیطی اسـت و به دو روش قابـل مقدار دهی است. اول اینکه قبل از صدور دستور make آن را به عنوان یک متغییر محیطی تعریف و مقدار دهی کنیم. به عنوان مثال با صادر کردن دستور زیر:
$ export CFLAGS=-O2
حـال بـا صـادر کـردن دستـور make، عبارت (CFLAGS)$ با O2- جایـگزین خواهـد شد. و اگر به دستـوری کـه در Makefile نـمونـه نـوشته شده تـوجه کنـیـد متوجه می شـویـد که O2- به عنوان یـک پـارامتـر به gcc ارسال میشود. (O2- بـاعث optimize شـدن فایـل خـروجی میگردد.) روش دوم مـقدار دهی این متـغیـیـر، ارسـال آن بـه عـنـوان پـارامتـر بـه make میباشد:
$ make CFLAGS=-O2
این دستور معادل دستور بالا است.
علاوه بر این متغییرهای محیطی، make قابلیت تعریف متغییر درونی را نیز در اختیار کاربر قرار میدهـد. از آنجایی که کار با متغییرها در make مبحثی گسترده میباشد، به تعریف تعدادی از متغییرهای ویژه بسنده کرده و برای اطلاعات بیشتر شما را به راهنماهای make ارجاع می دهیم.
فرض کنـیـد تـعدادی فایل در دایرکـتـوریهای مختلف داریم که پـیـش نیاز دیگر فایلها میباشنـد. Makefile زیـر را در نظر بگیرید:
main.o: main.cpp include/defs.h structure/list.h
gcc -c include/defs.h structure/list.h main.cpp
ایـن طرز نوشتـن فـایل علاوه بر ناخـوانا نمودن آن باعث بـه وجـود آمدن اشـکالات زیادی نیز خواهد شد چرا که به خـاطر سپردن تمام وابستـگیها و دایـرکتـوری حاوی آنها مشکل اسـت. هـمچنین شما مجبور به تکرار دوباره پیش نیازها برای دستور کامپایل هستـیـد که خود باعث بروز اشتـبـاه خـواهد شد. make برای ایـن کـار راه حلی را ارائه نـموده است و آن استفاده از متغییرهای خاص می باشد. با استفاده از این متغییرها Makefile به این صورت در خواهد آمد:
VPATH = include:structure
main.o: main.cpp defs.h list.h
 gcc -c $^ -o $@
ایـن طرز نوشتـن فـایل علاوه بر ناخـوانا نمودن آن باعث بـه وجـود آمدن اشـکالات زیادی نیز خواهد شد چرا که به خـاطر سپردن تمام وابستـگیها و دایـرکتـوری حاوی آنها مشکل اسـت. هـمچنین شما مجبور به تکرار دوباره پیش نیازها برای دستور کامپایل هستـیـد که خود باعث بروز اشتـبـاه خـواهد شد. make برای ایـن کـار راه حلی را ارائه نـموده است و آن استفاده از متغییرهای خاص می باشد. با استفاده از این متغییرها Makefile به این صورت در خواهد آمد:
نویسنده: آیدین غریب نواز
aidin.vf@gmail.com
پانویسها:
PDF Version
|