در این قسمت از آموزش RTOS به ادامه معرفی و آموزش ابزار GNU Make می پردازیم، با ما همراه باشید.
متغیرها همواره کاربردهای زیادی در برنامهنویسی داشتهاند. بهطورکلی شما هر الگوریتمی را که بخواهید با یکزبان برنامهنویسی اجرایی کنید، بدون شک به متغیرها برخورد میکنید. ازآنجاکه ما در Makefile در حال نوعی برنامهنویسی برای مدیریت کامپایل پروژمون هستیم، متغیرها کمک بسیار زیادی به ما میکنند؛ اما معنا و مفهوم متغیر در GNU Make با آن چیزی که در زبانهای برنامهنویسی میشناسیم، کمی متفاوت است.
برای مثال شما میتوانید بهجای اینکه در جایجای Makefileتون از gcc در Recipeها استفاده کنید، یک متغیر تعریف و مقدار آن را gcc قرار دهید. در این صورت اگر روزی روزگاری تصمیم گرفتید بهجای gcc مثلاً با کامپایلر clang پروژتون رو کامپایل کنید، کافی است تنها مقدار آن متغیر را از gcc به clang تغییر دهید. در ادامه بامعنا و مفهوم متغیرها در Make بیشتر آشنا میشویم. پس با سیسوگ همراه باشید.
متغیرها در GNU make
بر اساس داکیومنتهای GNU، متغیر نامی است که در یک Makefile برای نمایش رشتهای از متن تعریف شده که مقدار متغیر نامیده میشود. این مقادیر معمولاً با استفاده از متغیرها در Target یا Prerequisite(s) یا Recipe ها جایگزین میشوند. در برخی از نسخههای Make، متغیرها ماکرو نیز نامیده میشوند. |
تعریف متغیر در Makefile
برای تعریف یک متغیر در Makefile ابتدا باید بدانیم از چه کاراکترهایی برای نامگذاری میتوانیم استفاده کنیم و سپس اینکه چه مقادیری به یک متغیر میتوانیم اختصاص دهیم:
نام گذاری متغیر
نامگذاری متغیر باید از قواعد زیر پیروی کند:
- نام متغیر میتواند هر مجموعهای از کاراکترها باشد که حاوی ” : “یا” # “یا ” = ” یا فضای خالی نباشد.
- با اینحال، نام متغیرهایی که شامل کاراکترهایی غیر از حروف، اعداد و ” _ ” هستند، باید بادقت در نظر گرفته شوند، زیرا در برخی پوستههای ترمینال نمیتوان آنها را از طریق محیط به یک sub-make منتقل کرد.
- نام متغیرهایی که با “.” و یک حرف بزرگ شروع میشوند ممکن است در نسخههای بعدی Make به معنای خاصی باشند.
- نکته : GNU Make به حروف بزرگ و کوچک متغیرها حساس است. برای مثال “foo” و “Foo” و “FOO” سه متغیر متفاوت هستند.
محتوای متغیر
متغیرها میتوانند فهرستی از نام فایلها، سوئیچهایی برای استفاده در دستورات کامپایلرها، برنامههایی برای اجرا، دایرکتوریهایی برای جستجوی سورس فایلها، فهرستهایی برای نوشتن خروجی یا هر چیز دیگری که میتوانید تصور کنید را نشان دهند.
نکته: هر فاصلهای که قبل از مقدار متغیر وجود داشته باشد، در نظر گرفته نمیشود.
جمع بندی
مثالهای زیر نمونههایی از یک متغیر مجاز را به نمایش میگذارند:
1 2 3 4 | cc = gcc switchcompiler = -o print = echo "Hello World\n" sourcefile = a.c b.c d.c e.c ... |
استفاده از متغیر در Makefile
به عبارت خیلی ساده پس از تعریف یک متغیر میتوان آن را به یکی از دو صورت زیر استفاده کرد یا بهعبارتدیگر به مقدارش ارجاع داد:
1 2 | var = metech name = $(var) or name = ${var} |
حالا میخواهم یک مثال بسیار کاربردی و ساده بزنم: فرض کنید ما تعداد پنج تا آبجکت فایل داریم که برای ساخت یک فایل اجرایی باید به یکدیگر لینک شوند.بنابراین Makefile با استفاده از متغیرها بهصورت زیر میشود:
1 2 3 4 | cc = gcc objects = main.o sum.o mine.o output : $(objects) $(cc) $(objects) -o program |
زمانی که ما از یک متغیر استفاده میکنیم، یعنی از () $ یا {}$ استفاده میکنیم، در واقع در حال جایگزینی نام متغیر با مقدار آن هستیم. به این کار در داکیومنتهای گنو Expanding یک متغیر گفته میشود. در Expanding یک متغیر، محتوای متغیر واو به واو جایگزین ارجاع متغیر میشود. |
نکته: زمانی که میگوییم محتوای متغیر حرفبهحرف جایگذاری میشود، یعنی ما متغیر را در هر قسمتی از Makefile میتوانیم استفاده کنیم:
1 2 3 4 | foo = c cc = g$(foo)$(foo) prog.o : prog.$(foo) $(cc) -$(foo) prog.$(foo) |
انواع متغیرها در GNU make
بر اساس داکیومنتهای گنو :”راههای مختلفی وجود دارد که یک متغیر در GNU make میتواند یک مقدار را دریافت کند. ما آنها را Flavor متغیرها مینامیم. Flavorها در نحوه برخورد با مقادیری که در Makefile تخصیص داده شدهاند و نحوه مدیریت آن مقادیر زمانی که متغیر بعداً مورداستفاده قرار میگیرد و گسترش مییابد، متفاوت میشوند. برایناساس متغیرها به دو گروه اصلی تقسیمبندی میشوند:
Recursively Expanded Variable
اولین Flavor متغیرها، حالت مقداردهی بازگشتی است. برای استفاده از این نوع مقداردهی باید متغیر در تعریف اولیه بهصورت زیر تعریف شود:
1 | varRec = metech |
در این حالت تنها زمانی متغیر مقداردهی میشود که از متغیر استفاده کنیم. هنگامی که از متغیر استفاده کنیم، Make برای مقدار متغیر شروع به اسکن کل فایل میکند و آخرین مقداری که به متغیر اختصاصدادهشده است را جایگزین متغیر میکند، یا بهعبارتدیگر فرض کنید دو متغیر a و b داشته باشیم که محتوای متغیر a بهعنوان مقدار متغیر b قرار گیرد، آنگاه اگر هر زمانی که از متغیر b استفاده میکنیم، یکبار مقدار متغیر a نیز خوانده شود و سپس مقدار متغیر b جایگزین شود، به این حالت Recursively Expantion گفته میشود. برای مثال:
1 2 3 4 5 6 7 | foo = $(bar) bar = $(ugh) ugh = Huh all: echo $(foo) .PHONY = all |
نتیجه Makefile بالا:
بنابراین، هنگام استفاده از متغیرها بهصورت بازگشتی، هر باری که متغیر فراخوانده میشود، یکبار کل فایل اسکن میشود و بهصورت بازگشتی (همانطور که در بالا گفتیم) مقدار متغیر جایگزین ارجاع آن میشود. این حالت استفاده از متغیرها تنها نوعی است که توسط بیشتر نسخههای make پشتیبانی میشود؛ اما دو مشکل بزرگ دارد:
شاید برای شما مفید باشد: آموزش AVR از 0 تا 100 کاملا رایگان
- همانطور که گفتیم، هنگام استفاده از متغیرها در این حالت هر بار فایل بهصورت کامل اسکن میشود تا متغیر Expand شود.حالا تازه درصورتیکه مقدار متغیر برابر با مقدار خروجی یک تابع (در قسمت بعد توابع به طور کامل توضیح داده میشوند) باشد، در هر بار استفاده از متغیر تابع یکبار اجرا میشود. در نتیجه سرعت اجرای make کند میشود.
- نمیتوان چیزی به انتهای متغیر در این حالت اضافه کرد، زیرا متغیر وارد یک حلقه بینهایت بازگشتی میشود: یعنی اگر بخواهیم بهصورت زیر چیزی به متغیر اضافه کنیم:
1 2 | foo = Huh foo = $(foo) Huh |
با این پیغام روبهرو میشویم:
البته برای حل این مشکل یک راهحل وجود دارد و آن هم در بخش چسباندن یک مقدار به مقدار فعلی متغیر توضیح داده شده است. اما بنا بر دلایل گفته شده نوع دوم از متغیرها وارد شدند.
Simply Expanded Variable
دومین Flavor متغیرها، حالت مقداردهی ساده است. در این حالت مقداردهی و اسکن فایل برای مقداردهی متغیر فقط یکبار، آن هم زمانی که متغیر تعریف میشود، انجام میشود؛ بنابراین اگر خروجی تابعی بهعنوان مقدار متغیر قرار گیرد، فقط یکبار آن هم زمان تعریف متغیر تابع اجرا میشود. برای استفاده از این حالت، متغیر باید بهصورت زیر تعریف شود:
1 2 3 | x := foo y := $(x) bar x := slm |
نتیجه Makefile بالا:
1 2 3 | x = foo y = foo bar x = slm |
همچنین مشکل چسباندن یک مقدار به مقدار قبلی متغیر نیز در این حالت رفع شده است:
1 2 3 | x = foo x = ${x} bar #Result is : x = foo bar |
Immediately Expantion Vs Referred Expantion
علاوه بر نوع تعریف متغیر که نحوه Expanding آن را تعیین میکند، جایی که متغیر را استفاده میکنیم نیز تعیینکننده نوع برخورد Make با آن متغیر است و در واقع بهتر است بگویم که بر نوع تعریف متغیر اولویت دارد. همانطور که در بخش قبل گفتیم Make به دو صورت عمل میکند:
- اگر متغیر از نوع Recursively تعریف شده باشد، آن موقع کل فایل را در هربار استفاده اسکن میکند. به این حالت اصطلاحاً Referred Expantion یا جایگزینی مقدار بهصورت تأخیری نیز گفته میشود.
- اگر متغیر از نوع Simply تعریف شده باشد آن موقع تنها یکبار در موقع تعریف متغیر فایل اسکن میشود و متغیر مقداردهی میشود. به این حالت نیز اصطلاحاً Immediate Expantion یا بهعبارتدیگر جایگزینی مقدار بهصورت فوری گفته میشود.
حال هر گاه ما از متغیر در سمت چپ علامت انتساب مقدار به متغیر یا در Target یا در Prerequisite(s) استفاده میکنیم، make به صورت فوری ارجاع متغیر را با مقدارش جایگزین میکند اما اگر از متغیر در سمت راست علامت انتساب مقدار به متغیر یا در Recipe استفاده شود، آنگاه make به صورت تاخیری ارجاع متغیر را با مقدارش جایگزین میکند.
بنابراین، بهصورت کلی:
تعریف متغیرهای چندخطی در GNU make
متغیرهای چندخطی متغیرهایی هستند که مقدار آنها در چند خط گسترش داده شده است. برای تعریف متغیرهای چندخطی میتوان بهصورت زیر عملکرد:
شاید برای شما مفید باشد: آموزش STM32 با توابع HAL و LL
برای مثال:
1 2 3 4 5 6 | define var = Hello World echo "salam" foo bar endef |
چسباندن یک مقدار به متغیر در GNU make
این کار بسیار ساده است. تنها کافی است از یک علامت بهصورت زیر استفاده کنیم:
1 2 3 | var = foo var += bar #Result is: var -> foo bar |
این روش هم برای متغیرهایی که بهصورت بازگشتی و هم متغیرهایی که بهصورت ساده تعریف شدهاند، جوابگوست.
مقداردهی مستقیم به متغیر با دستور make
در بین تنظیمات دستور make قالبی وجود دارد که در آن میتوان با مقداردهی مستقیم به یک متغیر، بدون تعریف آن در Makefile از آن متغیر استفاده کرد. برای مثال فرض کنید Makefile زیر را داریم:
1 2 3 | print: echo $(var) .PHONY : print |
در فایل بالا متغیر var تعریف نشده و به همین دلیل میتوان از دستور make بهصورت زیر برای مقداردهی به متغیر استفاده کرد:
1 | make var=bar |
حال نکتهای که وجود دارد این است که اگر متغیر در بخشی از Makefile مانند Recipe استفاده شده باشد، بهصورت Referred مقداردهی شده و مقداری که در دستور make به این متغیر اختصاص میدهیم بهعنوان آخرین مقدار متغیر در فایل شناخته میشود. اگر بخواهیم مقداری که توسط دستور make به یک متغیر اختصاص داده میشود، نادیده گرفته شود، کافی است آن متغیر را بهصورت override تعریف کنیم:
1 | override var = bar |
جمع بندی
در این قسمت به شرح موضوعاتی در زمینه متغیرها در make پرداختیم. با استفاده از اطلاعاتی که در این قسمت و قسمت بعدی (که آشنایی و کار با توابع است) به دست میآورید، میتوانید Makefileهای حرفهایتر و هوشمندتری بنویسید.
عالی بود.
سپاس.
بسیار کار سودمندی کردید.
ادامه بدید.