آموزش کار با CMake - هفت خط کد انجمن پرسش و پاسخ برنامه نویسی

وبـــلاگ هــفت خــط کــد


آموزش های برنامه نویسی
۳۷۴ نفر آنلاین
۱۵۴ عضو و ۲۲۰ مهمان در سایت حاضرند

آموزش کار با CMake

+3 امتیاز

سلام. این پست را ایجاد کردم تا مطالبی را که در مورد CMake یاد می گیرم را به اشتراک بگذارم.دوستان هم اگر مایل بودید همکاری کنید.

فرض کنید فایل hello.cpp را به صورت زیر داریم و قصد دارم با cmake پروژه را بسازیم.

//hello.cpp
#include <cstdlib>
#include <iostream>
#include <string>
std::string hello_msg() 
{ 
  return std::string("Hello, CMake"); 
}
int main() {
   std::cout << hello_msg() << std::endl;
   return 0;
}

 

1- برای میک کردن با cmake ما نیاز به یک فایل به نام CMakeLists.txt داریم که آن را در روت پروژه ایجاد می کنیم.

۲- پایین ترین نسخه cmake که ما به آن وابسته هستیم.

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

در اینجا ما حداقل به نسخه ۳.۵ نیاز داریم .این بخش در هر فایل cmake باید مشخص کنیم.

۳- در این بخش نام پروژه و زبان های قابل  پشیبانی را مشخص می کنیم.

project(hello LANGUAGES CXX)

۴- در این مرحله باید تصمیم گرفت که آیا لینکر برای ما فایل اجرایی ایجاد کنه یا کتابخانه که هر دو حالت را توضیح میدم:

  • حالت فایل اجرایی :
add_executable(hello hello.cpp)

 

  • حالت کتابخانه :
add_library(hello
STATIC
hello.cpp
)

//یا

add_library(hello
SHARED
hello.cpp
)

 

سوال شده شهریور 4, 1399  بوسیله ی RED (امتیاز 494)   9 30 41
ویرایش شده شهریور 6, 1399 بوسیله ی 7khatcode
سلام؛ یکی از دوستان عزیز قبلاً دراین‌مورد اقدام کردند و ویدئوهای بسیار خوبی در این زمینه ضبط کرده‌اند که می‌تونید استفاده کنید :
http://myprogrammingworks.blog.ir/post/%D8%AF%D9%88%D8%B1%D9%87-%D9%88%DB%8C%D8%AF%DB%8C%D9%88%DB%8C%DB%8C-%D8%A2%D9%85%D9%88%D8%B2%D8%B4-cmake-%D8%A8%D9%87-%D8%B2%D8%A8%D8%A7%D9%86-%D9%81%D8%A7%D8%B1%D8%B3%DB%8C
همچنین یک جزوهٔ خیلی خلاصه که چند سال پیش در شرکت ارائه شد نیز در سایت موجوده :
http://bayanbox.ir/info/2191569396116101342/CMake-Tutorial
سلام اونو دیدم کامل نیست اینجا اگر خدا بخواد به کمک دوستان کامل همه جنبه هاشو بررسی کنیم.

5 پاسخ

+3 امتیاز

در این مرحله با CLI یعنی از طریق کامند توضیح میدم در انتها با cmake-gui هم توضیح میدم.

برای اینکه بتونیم پروژه را بیلد کنیم ابتدا باید configure و generation را انجام بدیم چون در این پروژه ساده ما عملا configure نداریم و فقط generation‌انجام میشه.از آنجایی که ما در os مشخص و پلتفرم مشخص بیلد می کنیم پس ترکیب های مختلفی میشه بیلد کرد پس بهتره برای هر ترکیبی را در فلدر خودش بیلد کنید تا با هم اشتباه نشه پس حتما توی فلدر بیلد کنید. 

سوپیچ کنید تو روت پروژه و فلدر مورد نظر را بسازید و عملیات میک را اجرا کنید.

mkdir build
cd build
cmake ..

همه تنظیمات به صورت پیش فرض بوده که هر چه جلوتر میریم سعی می کنم همه تنظیمات را ذکر کنم.

 

حالا پروژه ما تو فلدر Build ایجاد شده .داخل فلدر چندین فایل و فلدر وجود داره که برخی ها را توضیح میدم.

 

  • فلدر CMakeFiles :این فلدر فایل موقت که برای شناسایی os و کامپایلر مورد استفاده قرار می گیره
  • فایل cmake_install.cmake :این فایل قواعد مربوط به نصب را مدیریت می کنه در زمان نصب استفاده میشه.
  • فایل CMakeCache.txt :کس مربوط به پروژه می باشد چون هر بار مجبور نباشه اطلاعات را مجدد تولید کنه از فایل کش استفاده می کنه.

در این مرحله می تونیم توی ویژوال استدیو یا هر کامپایلر که مشخص کردیم پروژه را باز کنیم و بیلدش کنیم تا فایل اجرایی ساخته شه خود cmake هم می تونه این کار را برای ما انجام بده به صورت زیر:

cmake --build .

خوب فایل اجرایی ما در مسیر زیر ساخته شد.

.\hello\Debug\hello.exe

 

پاسخ داده شده شهریور 4, 1399 بوسیله ی RED (امتیاز 494)   9 30 41
ویرایش شده شهریور 4, 1399 بوسیله ی RED
+2 امتیاز

در این بخش در مورد کامنت گذاری در کد ، مقداردهی متغیر ها ،چاپ پیام ،دسترسی به متغیرها ، تعریف لیست و ساختار شرطی را توضیح میدم و در ادامه مثالی با مطالب مورد نظر ارایه میدم.

کامنت گذاری :

برای اینکار مثل خیلی از زبان های دیگر در ابتدای خط از # استفاده می کنیم.

#Add your comment here

 

مقداردهی متغیر :

جهت مقداردهی متغیرها از تابع set‌ استفاده می کنیم:

set(USE_LIBRARY OFF)

متغیری به نام USE_LIBRARY تعریف کردیم با مقدار OFF 

 

چاپ پیام :

در این زمینه cmake همانند سیستم های log‌ عمل می کنه یعنی پیام یک حالت را هم از کاربر دریافت می کنه به مثال زیر دقت کنید:

message(STATUS "This is CMake message.? ")

یکی از پرکاربرد ترین حالت هایی  همین حالت STATUS است البته از WARNING و FATAL_ERROR  میشه استفاده کرد

 

دسترسی به متغیرها :

message(STATUS "Compile sources into a library? ${USE_LIBRARY}")

با استفاده از {var_name}$‌ می تونیم به متغیرها دسترسی داشته باشیم.

 

استفاده از لیست :

جهت تعریف لیست از تابع list استفاده می کنیم جهت افزودن مقدار هم از شناسه APPEND .به مثال زیر دقت کنید:

list(APPEND _sources Message.hpp Message.cpp)

در مثال بالا لیستی با نام _sources ایجاد کردیم که دو مقدار  Message.hpp و Message.cpp را به آن اضافه کردیم.

 

عبارت شرطی :

جهت تعریف عبارت شرطی همچون اکثر زبان های اسکریپتی از if  به صورت زیر استفاده میشه:

if(شرط)
# کد شمادر صورت برقراری شرط

else()
#کد شما در صورت عدم برقرای شرط
endif()

 

پاسخ داده شده شهریور 4, 1399 بوسیله ی RED (امتیاز 494)   9 30 41
ویرایش شده شهریور 4, 1399 بوسیله ی RED
+2 امتیاز
  • جهت بدست آوردن نام فایل ها در یک مسیر و اختصاص دادن آنها به یک متغیر ازتابع aux_source_directory استفاده می کنیم.فرض کنیم سورس ها ما در دو فلدر قرار دارد به صورت زیر اسم آنها را بدست می آوریم:
aux_source_directory(. SOURCES1)
aux_source_directory(./src SOURCES2)
list (APPEND SOURCES
     ${SOURCES1}
     ${SOURCES2}
)

 

  • افزودن preprocessor به پروژه با تابع add_definitions به صورت زیر :
 add_definitions(-DOS_WIN)

 

  • برای تشخیص سیستم عامل جاری از متغیر {CMAKE_SYSTEM_NAME}$ استفاده می کنیم.

 

  •  جهت دستیابی به مسیر سورس از متغیر CMAKE_SOURCE_DIR  استفاده می کنیم.

 

  • جهت  دستیابی به مسیر بیلد از متغیر CMAKE_BINARY_DIR استفاده می کنیم.

 

  • جهت مقداردهی متغیر از تابع set‌ استفاده کنیم زمانیکه قصد داشته باشیم مقدار را ui دریافت کنیم از CACHE استفاده می کنیم.به صورت زیر:
set(path "" CACHE PATH  "Your Path")
set(str "" CACHE string  "Your String")

 

پاسخ داده شده شهریور 5, 1399 بوسیله ی RED (امتیاز 494)   9 30 41
ویرایش شده شهریور 8, 1399 بوسیله ی RED
+2 امتیاز
 
فلگ های کاربردی زمان استفاده از cmake از طریق CLI :
 
  • H- : مسیر فایل اصلی یا سرشاخه cmake را مشخص می کند.

 

  • B- : مسیر بیلد را مشخص می کنید.

 

  • G- : جنریتور موردنظر جهت عملیات  generation را مشخص می کند.

 

  • help-- :جهت پی بردن به لیست جنریتور های موجود استفاده می شود.

 

  • D- : جهت مقداردهی به متغیر چه از نوع منطقی ،رشته ،مسیر و غیره از این فلگ استفاده می کنیم.

 

  • config-- : جهت تغییر درconfiguration پروژه به حالت debug , release و غیره مورد استفاده قرار می گیرد.

 

برای مثال :

cmake .. -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Release -A x64

 

پاسخ داده شده شهریور 6, 1399 بوسیله ی RED (امتیاز 494)   9 30 41
ویرایش شده شهریور 9, 1400 بوسیله ی مصطفی ساتکی
+2 امتیاز

مسیر های کاربردی در CMake:

 

  • CMAKE_SOURCE_DIR : همانطور که قبلاْ هم گفتیم این مسیر به بالاترین مسیر در درخت سورس اشاره داره همان جایی که cmakelists.txt قرار داره و این مسیر در طول اجرای cmake تغییر نمی کنه.

 

  • CMAKE_BINARY_DIR :همانطور که قبلاْ هم گفتیم این مسیر به بالاترین مسیر درفلدر بیلد اشاره داره این مسیر در طول اجرای cmake تغییر نمی کنه.

 

  • CMAKE_CURRENT_SOURCE_DIR :وقتی که فلدر مروبط به cmaklists.txt توسط cmake مورد پردازش قرار می گیره این مسیر اشاره داره به مسیر سورس ، گاهی اوقات این مسیر توسط add_subdirectory() تغییر می کنه و پس از برگشت دوباره به مسیر های قبلی تغیر می کنه.

 

 

  • CMAKE_CURRENT_BINARY_DIR : :وقتی که فلدر مروبط به cmaklists.txt توسط cmake مورد پردازش قرار می گیره این مسیر اشاره داره به مسیر بیلد، گاهی اوقات این مسیر توسط add_subdirectory() تغییر می کنه و پس از برگشت دوباره به مسیر های قبلی تغیر می کنه.


مثالی براتون قرار میدم تا این قضیه روشن تر بشه :

//Top level CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(MyApp)
message("top: CMAKE_SOURCE_DIR = ${CMAKE_SOURCE_DIR}")
message("top: CMAKE_BINARY_DIR = ${CMAKE_BINARY_DIR}")
message("top: CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}")
message("top: CMAKE_CURRENT_BINARY_DIR = ${CMAKE_CURRENT_BINARY_DIR}")
add_subdirectory(mysub)
message("top: CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}")
message("top: CMAKE_CURRENT_BINARY_DIR = ${CMAKE_CURRENT_BINARY_DIR}")


//mysub/CMakeLists.txt

message("mysub: CMAKE_SOURCE_DIR = ${CMAKE_SOURCE_DIR}")
message("mysub: CMAKE_BINARY_DIR = ${CMAKE_BINARY_DIR}")
message("mysub: CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}")
message("mysub: CMAKE_CURRENT_BINARY_DIR = ${CMAKE_CURRENT_BINARY_DIR}")


//خروجی

top: CMAKE_SOURCE_DIR = /somewhere/src
top: CMAKE_BINARY_DIR = /somewhere/build
top: CMAKE_CURRENT_SOURCE_DIR = /somewhere/src
top: CMAKE_CURRENT_BINARY_DIR = /somewhere/build
mysub: CMAKE_SOURCE_DIR = /somewhere/src
mysub: CMAKE_BINARY_DIR = /somewhere/build
mysub: CMAKE_CURRENT_SOURCE_DIR = /somewhere/src/mysub
mysub: CMAKE_CURRENT_BINARY_DIR = /somewhere/build/mysub
top: CMAKE_CURRENT_SOURCE_DIR = /somewhere/src
top: CMAKE_CURRENT_BINARY_DIR = /somewhere/build

 

نکته ای را که باید مد نظر داشته باشید که مسیر های CMAKE_CURRENT_SOURCE_DIR و CMAKE_CURRENT_BINARY_DIR  با فراخوانی تابع include() تغییر نمی کنند.

 

  • CMAKE_CURRENT_LIST_DIR : کاملا شبیه به متغیر CMAKE_CURRENT_SOURCE_DIR  با این تفاوت که با فراخوانی تابع include()  مسیر موجود در این متغیر بروزرسانی می شود.

 

  •  CMAKE_CURRENT_LIST_FILE :مسیر مطلق فایلی که توسط cmake در حال پردازش است را در خود نگهداری می کند.
//CMakeLists.txt

add_subdirectory(subdir)
message("====")
include(subdir/CMakeLists.txt)

//subdir/CMakeLists.txt

message("CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}")
message("CMAKE_CURRENT_BINARY_DIR = ${CMAKE_CURRENT_BINARY_DIR}")
message("CMAKE_CURRENT_LIST_DIR = ${CMAKE_CURRENT_LIST_DIR}")
message("CMAKE_CURRENT_LIST_FILE = ${CMAKE_CURRENT_LIST_FILE}")


//خروجی
CMAKE_CURRENT_SOURCE_DIR = /somewhere/src/subdir
CMAKE_CURRENT_BINARY_DIR = /somewhere/build/subdir
CMAKE_CURRENT_LIST_DIR = /somewhere/src/subdir
CMAKE_CURRENT_LIST_FILE = /somewhere/src/subdir/CMakeLists.txt
====
CMAKE_CURRENT_SOURCE_DIR = /somewhere/src
CMAKE_CURRENT_BINARY_DIR = /somewhere/build
CMAKE_CURRENT_LIST_DIR = /somewhere/src/subdir
CMAKE_CURRENT_LIST_FILE = /somewhere/src/subdir/CMakeLists.txt

 

پاسخ داده شده شهریور 8, 1399 بوسیله ی RED (امتیاز 494)   9 30 41
ویرایش شده شهریور 8, 1399 بوسیله ی farnoosh
...