انجمنهای فارسی اوبونتو
کمک و پشتیبانی => راهنماها، نکات و ترفندها => نویسنده: fond در 30 اردیبهشت 1392، 03:09 قظ
-
خب این نوشته قراره یه توضیح مختصری درباره برنامه make باشه و ما فقط درباره فلسفه اصلی پشت این برنامه صحبت میکنیم و به پیاده سازی های اون مثل gnu make, imake, cmake, qmake کاری نداریم!
خیلی از مواقع پیش میاد که توی پروژه ها وضعیت یکسری از فایلها به یه سری فایل دیگه وابسته هست. مثلا توی نرمافزارها، اگه فایل هدر یا فایل کدمنبع تغییر کنه، باید فایلهای باینری برنامه دوباره کامپایل بشن.
برای مثال فرض کنید که یه نرمافزار متشکل از سه فایل به نامهای header.h, sourcecode.c و فایل اجرایی برنامه به نام program.o داریم. خب اینجا program.o وابسته به هر دو تا فایل header.h و sourcecode.c هست و اگه این فایلها تغییر کردند فایل program.o هم باید دوباره ساخته بشه. تشخیص این کار با نگاه کردن به تاریخ آخرین تغییر فایل انجام میشه. هر وقت که شما فایلی رو تغییر دادید، تاریخ آخرین تغییر اون فایل بروز میشه. برنامه make با مقایسه تاریخ این فایلها با هم میفهمه که آیا باید کارش رو انجام بده یا نه؟
برنامه make وقتی اجرا شد، دنبال یه فایلی به نام Makefile میگرده و اون رو میخونه و کارهاش رو با توجه به اون انجام میده.
خب این یه Makefile ساده هست:
CC= cc
CFLAGS= -O2 -pipe
program.o: sourcecode.c header.h
${CC} ${CFLAGS} -o program.o sourcecode.c
install:
install -s -m 555 program.o ${PREFIX}/bin
خب توی دو خط اول ما دو تا متغیر تعریف کردیم. برای تعریف از عملگر مساوی استفاده کردیم. استفاده از = باعث میشه مقدار قبلی متغیر پاک بشه و مقدار جدید جایگزین بشه. برای اینکه مقدار قبلی حفظ بشه و مقدار جدید به اون اضافه بشه، از =+ استفاده میکنیم:
CFLAGS+= -march=core2
البته روشهای دیگه هم وجود داره خارج از حوصله این نوشته کوتاه هست.
خط سوم یه dependency line هست. این خط مشخص میکنه که چه فایلی به کدوم فایلهای دیگه وابسته هست. توی این مثال گفتیم که program.o به فایلهای sourcecode.c و header.h وابسته هست. make از خودش میپرسه آیا تاریخ آخرین تغییر program.o از تاریخ آخرین تغییر فایلهای header.h و sourcecode.c قدیمیتر هست؟ اگر قدیمیتر بود یا فایل program.o اصلا وجود نداشت، اون موقع make خط بعدی رو اجرا میکنه.
خط بعدی یه فرقی با بقیه خطها داره: یه فضای خالی tab اولش وجود داره. تمام دستوراتی که قراره اجرا بشن باید با یه tab اول خط شروع بشن.
توی این مثال، اگر make با مقایسه تاریخ آخرین تغییر program.o با sourcecode.c و header.h به این نتیجه رسید که باید program.o رو کامپایل کنه، اونوقت خط بعدی اجرا میشه. این خط هم بسادگی برنامه رو کامپایل میکنه و خروجی رو با نام program.o ذخیره میکنه. حالا اگر بدون تغییر دادن دو تا فایل سورس کد دوباره make رو اجرا کنید، چون تاریخ آخرین تغییر فایلها برابر هست، دیگه خط آخر اجرا نمیشه. به همین دلیل باید ساعت سیستم همیشه درست باشه!
اما خط بعدی که اون هم یه dependency line هست یکم فرق داره. توی این خط ما بعد از : هیچ چیز ننوشتیم. این یعنی اینکه install به هیچ چیز وابسته نیست و همیشه دستور بعد از اون اجرا میشه.
make از دستورات شرطی هم پشتیبانی میکنه. از اونجا که این دستورات توی پیاده سازی های مختلف make با هم متفاوتند، ما اینجا از شبه کد استفاده میکنیم.
از اونجا که فرض بر اینه که خوانندگان با یه زبان برنامه نویسی آشنایی دارند، زیاد این دستورات رو توضیح نمیدم.
دستور if
با این دستور میشه عملیات تصمیم گیری رو بر اساس یه شرط خاصی انجام داد. اگر شرط درست باشه، دستورات بعدی اجرا میشن و اگه درست نبود اجرا نمیشن.
مثلا:
if !defined(PREFIX)
PREFIX= /usr
endif
توی مثال بالا اگه متغیر PREFIX تعریف نشده باشه، خط بعدی اجرا میشه و متغیر PREFIX تعریف میشه.
لطفا ادامش رو بعدا بخونید.
-
خب یک مثال دیگه از if:
if defined(WITH_DEBUG)
CFLAGS+= -g
endif
توی مثال بالا اگه کاربر متغیری به نام WITH_DEBUG رو تعریف کرده باشه، برنامه با آپشن g- کامپایل میشه تا کامپایلر کدهای اضافه رو توی باینری قرار بده و بعدا بشه راحتتر با gdb برنامه رو خطایابی کرد.
if exist(options.mk)
.include options.mk
endif
همینطور که پیش پردازنده c دستور include# رو داره میشه فایلها رو داخل هم درج کرد، Makefile ها رو هم میشه با دستور include درج کرد. مثال بالا چک میکنه که اگه فایلی به نام options.mk وجود داشت، اون رو توی Makefile اصلی درج میکنه.
مثالهای اینچنینی زیاده که باید شما به مستندات اون پیاده سازی از make ای که استفاده میکنید مراجعه کنید. فقط اینم بگم برای شرطهای میتونید از < و > و بقیه عملگرها مثل زبانهای برنامه نویسی استفاده کنید.
دستور for
مثل زبانهای برنامه نویسی Makefile ها هم از این دستور پشتیبانی میکنند. معمولا این دستور توی اکثر پیاده سازی ها شباهت زیادی به حلقه for پوسته sh داره.
قالب کلی (شبه کد)
for variable in expression
<make-rules>
endfor
توی مثال بالا variable یه متغیر دلخواه هست و expression هم یه تعداد مقدار دلخواه. متغیر variable توی هر بار اجرای حلقه یکی از مقادیر مشخص شده توسط expression رو به خودش میگیره.
یک مثال:
DOCS= README INSTALL CHANGES UPDATE UNINSTALL
for FILE in ${DOCS}
install -m 444 ${FILE} ${PREFIX}/doc
endfor
ما تا حالا اصلا دستور اصلی make رو که توی خط فرمان استفاده میکنیم رو بررسی نکردیم. برای این کار کافیه وارد پوشه ای بشید که Makefile توی اون قرار داره و بعد این دستور رو اجرا کنید:
make target
اینجا target همون چیزی هست که میخوایم بسازیم و سمت چپ عملگر : قرار داره. اگه دستور رو بدن آرگومان فراخوانی کنیم، make اولین target ای که توی Makefile تعریف شده رو اجرا میکنه.
برنامه make دایرکتوری جاری رو برای پیدا کردن فایل به نام Makefile جستجو میکنه. اما این فایل میتونه هر اسم دیگه ای هم داشته باشه. البته باید به make اطلاع داد که اسم این فایل چیزی غیر از Makefile هست:
make -f filename
make میتونه چند تا کار رو به صورت همزمان انجام بده. مثلا اگه شما یک cpu چند هسته ای داشته باشید، میتونید از هر هسته برای انجام یک کار استفاده کنید. انجام این کار باعث میشه عملیات با سرعت بسیار بیشتری انجام بشه و صرفه جویی قابل ملاحظه ای توی وقت انجام میشه. مخصوصا موقع کامپایل برنامه ها. برای اینکه به make بگیم چند تا کار رو همزمان انجام بده، باید از گزینه j- استفاده کنیم:
make -j5 build
که توی مثال بالا make سعی میکنه همزمان 5 تا کار رو با هم انجام بده.
-D
این آپشن هم برای تعریف یه متغیر استفاده میشه و میتونیم باهاش بدون نیاز به ویرایش Makefile یه متغیری رو تعریف کنیم. مثلا:
make -DWITH_DEBUG build
که این متغیر WITH_DEBUG برابر با 1 مقدار دهی میشه و Makefile بر اساس اون تصمیم گیری میکنه. (به اولین مثال مراجعه کنید)
خب هر چند من در مورد خیلی چیزها توضیح ندادم و فرض بر این بوده که خواننده با برنامه نویسی تا حدودی آشنا هست، اما خواهش میکنم اگر جایی احتیاج به توضیح بیشتر داره و یا کلا اشتباه هست، حتما من رو در جریان بگذارید.
این هم یه Makefile نمونه. لطفا سعی نکنید این رو اجرا کنید، از اون الگو بگیرید. چون پیاده سازی های مختلفی از make هست احتمال اینکه کار نکنه بسیار زیاده و تازه ما از شبه کد استفاده کردیم (توی دستورات شرطی)
if !defined(PREFIX)
PREFIX= /usr
endif
CC= cc
CFLAGS= -O2 -pipe
DOCS= README INSTALL CHANGES UPDATE UNINSTALL
if defined(WITH_DEBUG)
CFLAGS+= -g
endif
program.o: sourcecode.c header.h
${CC} ${CFLAGS} -o program.o sourcecode.c
install:
install -s -m 555 program.o ${PREFIX}/bin
for FILE in ${DOCS}
install -m 444 ${FILE} ${PREFIX}/doc
endfor
-
عالی است. اما انقریب این پستها هم میان دیگر مطالب مفید انجمن گم خواهند شد. وبگاهی ندارید برای انتشارشون؟
-
ممکنه توضیح بدین dependency به چه معناست؟
متفاوت با includeهاست؟
-
خب بخش دوم هم اضافه شد. اگه جایی اشکالی داره یا احتیاج به توضیح بیشتری داره خواهش میکنم حتما بهم بگید.
عالی است. اما انقریب این پستها هم میان دیگر مطالب مفید انجمن گم خواهند شد. وبگاهی ندارید برای انتشارشون؟
سلام. ممنون. من حقیقتش هیچ وبلاگ و وبسایتی ندارم. اما بنظرم بهترین جا واسه اینجور مطالب ویکی باشه چون بقیه هم میتونن ویرایش کنن اما من اصلا آشنایی ندارم با ویکی ها.
اگه از دوستان کسی زحمت انتقال این مطلب به ویکی رو بکشه من ممنون میشم.
ممکنه توضیح بدین dependency به چه معناست؟
متفاوت با includeهاست؟
dependency line خطی هست که میگه یه فایل به چه فایلهای دیگه ای وابسته هست. مثلا این یه dependency line هست:
program.o: sourcecode.c header.h
این خط دو قسمت داره که با : از هم جدا شدند. قسمت سمت چپ target هست. این خط میگه که فایل program.o به فایلهای sourcecode.c و header.h وابسته هست و اگر این فایلها تغییر کردن، باید فایل program.o هم مجددا کامپایل بشه. (یا هر عمل دیگه ای)
-
ممنون دوست عزیز.
من تقریبا هیچ مرجعی به این خوبی و کاملی برای نوشتن Makefile و کار با make ندیده بودم.
کلی چیز یاد گرفتم ;)
-
ببخشید من متوجه نشدم دستور make کارش چیه؟زبان برنامه نویسی یا ساخت فایل اجرایی؟
-
ببخشید من متوجه نشدم دستور make کارش چیه؟زبان برنامه نویسی یا ساخت فایل اجرایی؟
مدیریت روی کامپایل سورسها.
پست اول رو با دقت بخون ، به خوبی توضیح دادند.
-
من هم وقتی برای نخستین بار میخواستم Makefile بنویسم خیلی به سختی تونستم یه راهنمای ساده براش پیدا کنم. ممنون از این نوشتهٔ آموزشی.
-
dependency line خطی هست که میگه یه فایل به چه فایلهای دیگه ای وابسته هست. مثلا این یه dependency line هست:
program.o: sourcecode.c header.h
این خط دو قسمت داره که با : از هم جدا شدند. قسمت سمت چپ target هست. این خط میگه که فایل program.o به فایلهای sourcecode.c و header.h وابسته هست و اگر این فایلها تغییر کردن، باید فایل program.o هم مجددا کامپایل بشه. (یا هر عمل دیگه ای)
مشکل من اینجاست که وقتی ما میخواهیم فایل program.o را بسازیم باید از یک فایل برای نمونه program.c اون رو بسازیم. در فایل program.c هم نیازمندیها قبلا Include شدن. پس ما چه نیازی به مشخص کردن depenency داریم؟
راستش من معنای dependency رو متوجه نمیشم.
-
ببخشید من متوجه نشدم دستور make کارش چیه؟زبان برنامه نویسی یا ساخت فایل اجرایی؟
مدیریت روی کامپایل سورسها.
پست اول رو با دقت بخون ، به خوبی توضیح دادند.
آخه کامپایل کردن که دیگه if و شرط نداره دیگه
dependency line خطی هست که میگه یه فایل به چه فایلهای دیگه ای وابسته هست. مثلا این یه dependency line هست:
program.o: sourcecode.c header.h
این خط دو قسمت داره که با : از هم جدا شدند. قسمت سمت چپ target هست. این خط میگه که فایل program.o به فایلهای sourcecode.c و header.h وابسته هست و اگر این فایلها تغییر کردن، باید فایل program.o هم مجددا کامپایل بشه. (یا هر عمل دیگه ای)
مشکل من اینجاست که وقتی ما میخواهیم فایل program.o را بسازیم باید از یک فایل برای نمونه program.c اون رو بسازیم. در فایل program.c هم نیازمندیها قبلا Include شدن. پس ما چه نیازی به مشخص کردن depenency داریم؟
راستش من معنای dependency رو متوجه نمیشم.
همین فایل program.c که دارید مییگید را باید مشخص کنیم
-
از همه دوستانی که علاقه نشون دادن و باعث دلگرمی شدن تشکر میکنم :D
در مورد dependency باید بگم، برای make فرقی نمیکنه که فایلها کدمنبع برنامه باشن، یه سری مستندات باشن، فایل html باشن یا هر چیز دیگه. توی هر پروژه ای که یه سری فایل به یه سری فایل دیگه وابسته باشند میشه از make برای مدیریت و خودکار سازی کارها استفاده کرد.یعنی مهم این رابطه ای هست که بین فایلها برقراره.
همونطور که شما گفتید، قبلا یه سری از وابستگی ها (مثل فایل هدر header.h) توی فایل program.c درج (include) شدن، درسته؟ خب حالا فرض کنید که ما فایل header.h رو تغییر دادیم. با تغییر دادن این فایل، نیاز به rebuild کردن کدوم فایل هست؟ program.o یا program.c؟ مسلما program.o چون program.c در واقع به header.h وابسته نیست. هر چند که header.h توی فایل program.c درج شده اما program.c به اون وابسته نیست. چون با تغییر header.h این program.o هست که باید مجددا کامپایل بشه و ما بعد از اینکه header.h رو تغییر دادیم نیازی نیست تا تغییری توی program.c هم ایجاد کنیم.
-
ببخشید من متوجه نشدم دستور make کارش چیه؟زبان برنامه نویسی یا ساخت فایل اجرایی؟
هیچکدوم. ساده بخوام بگم، توی هر پروژه ای که وضعیت یه سری فایل به یه سری فایل دیگه وابسته باشه، میشه از make استفاده کرد. یعنی با ایجاد تغیر توی یک فایل، مجبور باشیم یه سری فایل دیگه رو هم تغییر بدیم. مثلا توی مستندات، اگه ما یه ماکرویی تعریف کرده باشیم، هر وقت که تغییری توی اون ماکرو ایجاد شد، تمام فایلهایی که از اون ماکرو استفاده کردن باید مجددا بازسازی بشن.