در توسعه نرمافزار، ساخت ابزاری برای ساختن اتوماتیک فایلهای اجرایی از کد منبع است. فایلی که Makefile(سیسیتم یونیکسی) خوانده میشود روند کامپایل قطعات پروژه را بر اساس پیشنیازهای آن معلوم میکند. اگر چه ابزارهای بسیار زیادی برای توسعه مداوم و اجرا کردن تستها در محیطهای برنامهنویسی موجود است، اما MakeFile به صورت گسترده و مخصوصاً در سیستمهای مبتنی بر یونیکس (مانند ubuntu ,kubuntu,deepin)استفاده میشود.
پارادایم برنامهنویسی | ماکرو، اعلانی |
---|---|
طراحی شده توسط | Stuart Feldman |
ظهوریافته در | ۱۹۷۶ |
زبان پیادهسازی | سی |
سیستمعامل | شبهیونیکس، اینفرنو |
Makefile | |
پیادهسازیهای بزرگ | |
BSD, GNU, nmake | |
گویش | |
BSD make, GNU make, Microsoft nmake | |
تأثیر گذاشته بر | |
Ant, Rake, MSBuild و others |
مقدمه
قویترین ابزار برنامهنویسان برای جلوگیری از پیچیدگیهای بیش از حد در پروژهها استفاده از روش برنامهنویسی ماژولار (Modular) است. در این روش بخشهای مختلف پروژه به صورت ماژولهای مجزا قابل کامپایل هستند؛ ولی از طرفی هم عملیات کامپایل تکتک ماژولها و چسباندن نهایی آنها به یکدیگر، فرایندی زمانبر و پراشتباه و درد سر ساز است است.
خوشبختانه ابزار ساخت برای تسهیل این مراحل و خودکارسازی این فرایند ساخته شدهاست. روند کامپایل این ماژولها در فایلی بهنام Makefile تعریف میشوند.
در یک نگاه کلی، ساخت فقط برای کامپایل برنامهها استفاده نمیشود. شما میتوانید در هر کاری که در آن نیاز به بروزرسانی بعضی فایلها متناسب با تغییر در فایلهای دیگر است از ساخت استفاده کنید.
بهطور مثال معمولاً در یک برنامه رایانهای نیاز است از کدهای منبع فایلهای آبجکت و سپس از این فایلهای آبجکت، فایل اجرایی ساخته شود. وقتی شما برنامهای مینویسید، باید برای آن یک Makefile هم درست کنید و سپس برای کامپایل و نصب آن از ساخت کمک میگیرید.
ساخت بهطور خودکار تشخیص میدهد چه قسمتهایی از یک برنامهٔ بزرگ به چه صورت باید کامپایل شوند. همچنین میتوان از آن برای کامپایل برنامهها به هر زبانی که نوشته شدهاند استفاده کرد، بهشرط آنکه کامپایلر آن زبان توانایی اجرا از طریق پوسته (Shell) را داشتهباشد.
قابل ذکر است که ابزار ساخت بهطور گستردهای بهخصوص در سیستمهای مبتنی بر یونیکس استفاده میشود.
قابلیتهای ساخت
- ساخت این امکان را برای کاربر نهایی فراهم میکند که حتی بدون داشتن اطلاعات فنی زیاد بتواند برنامه را کامپایل و نصب کند. زیرا شما جزئیات کامپایل و نصب برنامه را در Makefile نوشتهاید و دیگر نیازی نیست که کاربر آن هارا بنویسد.
- وقتی از بین کدهای منبع یک پروژه تعدادی از آنها تغییر کند، ساخت بهطور اتوماتیک تشخیص میدهد چه فایلهایی باید بروز شوند. این امکان زمانی مفید خواهد بود که تعداد کدهای منبع یک برنامه زیاد است و در صورت اعمال تغییرات بر روی چند تای آنها نیاز به کامپایل تمامی کدهای منبع و هدر دادن زمان و منابع نیست. فقط فایلهایی بروز میشوند که بهطور مستقیم یا غیرمستقیم به تغییرات داده شده ربط داشته باشند. ساخت از طریق آخرین زمان تغییر فایلها تشخیص میدهد که چه قسمتهایی باید دوباره بروز شوند.
- ساخت وابسته به زبان برنامهنویسی خاصی نیست. برای ساخت فایلهای مورد نیاز برای اجرای برنامه، در Makefile دستوری قرار دارد که در پوسته (Shell) اجرا میشود. بهطور مثال این دستورات میتوانند کامپایلری را برای ایجاد فایل آبجکت، لینکر را برای تولید فایل اجرایی، ar را برای به روز کردن یک کتابخانه (Library) یا صفحهآرایی مستندات به وسیلهٔ TeX یا Makeinfo اجرا کنند.
- ساخت فقط برای ساخت یک برنامه به کار نمیرود. میتوان از آن برای کنترل روند نصب یا حذف یک برنامه نیز استفاده کرد.
نگاهی دقیق تر به Makefile
محتویات Makefile
اجزای Makefile شامل: قوانین صریح، قوانین غیرصریح، متغیرها، رهنمونها و توضیحات.
- یک قانون صریح (Explicit Rule) معلوم میکند چه زمانی و چگونه ، فایل(ها) باید ساخته شوند. به فایلهایی که باید ساخته شوند هدف (Target) گفته میشود و ممکن است وابسته به فایل (های) دیگری باشد. همچنین دستورهایی برای ساخت فایل هدف نوشته میشوند.
- قوانین غیرصریح (Implicit Rule) معلوم میکنند چه وقت و چطور باید دستهای از فایلها که بر اساس نامشان دستهبندی شدهاند ساخته شوند.
- متغیرها برای نسبت دادن مقادیر متنی به نامی مشخص است؛ و برای رجوع به آن مقدار متنی، میتوان از نام متغیر استفاده کرد. یک مثال نوشتن نام تمام فایلهای آبجکت مورد استفاده در برنامه درون یک متغیر با نام objects است. (برای رجوع سادهتر)
- رهنمونها فرامینی هستند برای اجرای کارهای خاص که در هنگام پردازش Makefile مورد استفاده قرار میگیرند. مانند:
-- خواندن محتویات Makefile دیگری در دل یک Makefile.
-- تصمیمگیری (بر اساس مقدار درون یک متغیر) برای پردازش کردن یا نکردن قسمتی از Makefile.
-- مقدار دهی به متغیرها از طریق متنی که شامل چندین خط است.
- با نوشتن کارکتر # میتوان توضیحاتی در Makefile نوشت که در عمل پردازش نمیشوند و فقط باعث خواناتر شدن Makefile برای انسان خواهند شد.
Ruleها
Ruleها در Makefile مشخص میکنند که برای ساخت یک Target چه دستوراتی باید اجرا شود. همچنین میتوان برای ساخت هدف، فهرستی از پیشنیازها (Dependencies) را نیز تعریف کرد. در این فهرست باید تمام فایلهای مورد نیاز (کدمنبع یا هر نوع فایل دیگری) که برای ساخت هدف مورد نیاز است تعریف شود.
# Comments use the hash symbol
target: dependencies
command 1
command 2
.
.
.
command n
معمولاً Target نام فایلی است که قرار است توسط یک برنامه ایجاد شود. مثلاً فایلهای آبجکت(object) یا اجرایی. همچنین میتواند نام کاری باشد که قرار است اجرا شود. مثل تمیز کردن یک پروژه (Clean)
پیشنیازها (Dependencies) فایلهای ورودیای هستند که برای ساختن هدف مورد نیازند که به هر تعدادی میتوانند باشند. در صورت تغییر در هر یک از این پیشنیازها، هدف باید دوباره ساخته شود. البته هدفهایی هم هستند که نیاز به پیشنیازی ندارند. بهطور مثال هدفی مانند Clean که باید بعضی از فایلها را پاک کند.
فرمانهای (Command) هم کارهایی هستند که Make برای ساخت هدف انجام خواهد داد. برای ساخت یک هدف ممکن است چندین دستور نیاز باشد که باید در خطوط جداگانه نوشته شوند. توجه کنید که حتماً باید در ابتدای دستورات یک کارکتر تب (TAB) برای فاصله گذاری صحیح قرار دهید.
متغیرها کارها را ساده میکنند
مثال زیر را در نظر بگیرید:
edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o
چنین قواعدی در Makefile، بهشدت مستعد خطا هستند. اگر نیاز به اضافه کردن فایل آبجکت (objdect)جدیدی به پروژه باشد، ما باید در تمام مکانهای مورد نیاز آن را اضافه کنیم. میتوان بوسیلهٔ متغیرها این کار را سادهتر و مطمئنتر انجام داد. متغیرها این امکان را میدهند تا بتوانیم متنی را در آنها ذخیره کنیم و در چندین مکان مورد استفاده قرار دهیم.
تقریباً تمام Makefileها متغیری با نامی شبیه به objects , OBJECTS , objs , OBJS , obj یا OBJ دارند که فهرستی از نام تمام آبجکتهای مورد نیاز در پروژه را در خود نگه داشتهاست.
برای مثال:
ما میتوانیم برای مثال خود متغیری بنام objects را با نوشتن چنین خطی در Makefile تعریف کنیم:
objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
از این به بعد هر کجا نیاز به نوشتن فهرستی از فایلهای آبکجت مورد استفاده بود، میتوان از متغیر تعریف شده به صورت (objects)$ استفاده کرد.
edit : $(objects)
cc -o edit $(objects)
Makefile چطور پردازش میشود؟
بهطور پیشفرض، Make با اولین Target موجود در Makefile شروع میکند (اولین هدفی که با نقطه شروع نشده است). این هدف Default Goal نامیده میشود. (درصورت نیاز میتوان این هدف پیشفرض را توسط خط فرمان تغییر داد)
وقتی شما دستور make را در خط فرمان مینویسید، برنامهٔ Make در پوشه فعلیای که در آن قرار دارید فایلی بهنام Makefile را میخواند و شروع به پردازش اولین هدف موجود در آن میکند (Default Goal). اما قبل از اینکه دستورات موجود در این هدف اجرا شوند، Make باید تمام پیشنیازهای مربوط به آن هدف را پردازش کند. هر یک از این پیشنیازها نیز برای خود Rule دارند که برای ساخت آنها پردازش خواهد شد.
بهطور مثال در یک برنامه کامپایل دوباره یک فایل آبجکت زمانی اتفاق میافتد که فایل سورس یا هیدر مربوط به آن بعد از ساخت فایل آبجکت تغییر کرده باشند یا اساساً فایل آبجکت هنوز ساخته نشدهباشد.
به این ترتیب است که قواعد (Rules) دیگر موجود در Makefile پردازش و اجرا میشوند. زیرا آنها پیشنیازهای ساخت هدف اصلی هستند. روشن است که اگر یک قاعده جزء پیشنیازهای هدف اصلی نباشد پردازش نخواهد شد. مگر اینکه بهطور صریح از Make بخواهید آن را پردازش کند (مثلاً با دستور make target-name)