মেক ফাইল ব্যবহার করে ডিরেক্টরি তৈরি করুন


101

আমি মেকফাইলগুলিতে খুব নতুন এবং আমি মেকফিল ব্যবহার করে ডিরেক্টরি তৈরি করতে চাই। আমার প্রকল্প ডিরেক্টরিটি এর মতো

+--Project  
   +--output  
   +--source  
     +Testfile.cpp  
   +Makefile  

আমি সমস্ত অবজেক্ট এবং আউটপুট সংশ্লিষ্ট আউটপুট ফোল্ডারে রাখতে চাই। আমি ফোল্ডার কাঠামো তৈরি করতে চাই যা সংকলনের পরে এই জাতীয় হবে।

+--Project
   +--output
     +--debug (or release)
       +--objs
         +Testfile.o
       +Testfile (my executable file)
   +--source
     +Testfile.cpp
   +Makefile

আমি বেশ কয়েকটি বিকল্প দিয়ে চেষ্টা করেছি, তবে সফল হতে পারিনি। দয়া করে মেক ফাইল ব্যবহার করে ডিরেক্টরিগুলি তৈরি করতে আমাকে সহায়তা করুন। আমি আপনার বিবেচনার জন্য আমার মেকফিল পোস্ট করছি।

#---------------------------------------------------------------------
# Input dirs, names, files
#---------------------------------------------------------------------
OUTPUT_ROOT := output/

TITLE_NAME := TestProj 

ifdef DEBUG 
    TITLE_NAME += _DEBUG
else
ifdef RELEASE
    TITLE_NAME += _RELEASE
endif
endif


# Include all the source files here with the directory tree
SOURCES := \
        source/TestFile.cpp \

#---------------------------------------------------------------------
# configs
#---------------------------------------------------------------------
ifdef DEBUG
OUT_DIR     := $(OUTPUT_ROOT)debug
CC_FLAGS    := -c -Wall
else
ifdef RELEASE
OUT_DIR     := $(OUTPUT_ROOT)release
CC_FLAGS    := -c -Wall
else
$(error no build type defined)
endif
endif

# Put objects in the output directory.
OUT_O_DIR   := $(OUT_DIR)/objs

#---------------------------------------------------------------------
# settings
#---------------------------------------------------------------------
OBJS = $(SOURCES:.cpp=.o)
DIRS = $(subst /,/,$(sort $(dir $(OBJS))))
DIR_TARGET = $(OUT_DIR)

OUTPUT_TARGET = $(OUT_DIR)/$(TITLE_NAME)

CC_FLAGS +=   

LCF_FLAGS := 

LD_FLAGS := 

#---------------------------------------------------------------------
# executables
#---------------------------------------------------------------------
MD := mkdir
RM := rm
CC := g++

#---------------------------------------------------------------------
# rules
#---------------------------------------------------------------------
.PHONY: all clean title 

all: title 

clean:
    $(RM) -rf $(OUT_DIR)

$(DIR_TARGET):
    $(MD) -p $(DIRS)

.cpp.o: 
    @$(CC) -c $< -o $@

$(OBJS): $(OUT_O_DIR)/%.o: %.cpp
    @$(CC) -c $< -o $@

title: $(DIR_TARGET) $(OBJS)

আগাম ধন্যবাদ. আমি যদি কোন ভুল করে থাকে তবে দয়া করে আমাকে গাইড করুন।

উত্তর:


89

এটি এটি করবে - ইউনিক্সের মতো পরিবেশ অনুমান করে।

MKDIR_P = mkdir -p

.PHONY: directories

all: directories program

directories: ${OUT_DIR}

${OUT_DIR}:
        ${MKDIR_P} ${OUT_DIR}

এটি শীর্ষ-স্তরের ডিরেক্টরিতে চালিত করতে হবে - বা $ {OUT_DIR of এর সংজ্ঞাটি যেখানে এটি চালানো হয়েছে তার তুলনায় সঠিক হতে হবে। অবশ্যই, আপনি যদি পিটার মিলারের " রিকার্সিভ মেক কনসারডেড ক্ষতিকারক " কাগজের মূল নির্দেশগুলি অনুসরণ করেন তবে আপনি যেভাবেই শীর্ষ-স্তরের ডিরেক্টরিতে মেক চালাবেন।

আমি এই মুহূর্তে এই (আরএমসিএইচ) নিয়ে খেলছি। এটি একটি সফটওয়্যার স্যুট যা আমি পরীক্ষার ক্ষেত্র হিসাবে ব্যবহার করছি তাতে কিছুটা অভিযোজন প্রয়োজন। স্যুটটিতে এক ডজন পৃথক প্রোগ্রাম রয়েছে যা উত্স সহ 15 টি ডিরেক্টরিতে ছড়িয়ে রয়েছে, এর কয়েকটি ভাগ করে নিয়েছে। তবে কিছুটা যত্ন সহকারে এটি করা যায়। ওও, এটি কোনও নবজাতকের পক্ষে উপযুক্ত নাও হতে পারে।


মন্তব্যে উল্লিখিত হিসাবে, 'ডিরেক্টরিগুলি' নির্দেশের জন্য 'mkdir' কমান্ড তালিকাভুক্ত করা ভুল। মন্তব্যে যেমন উল্লেখ করা হয়েছে, 'আউটপুট / ডিবাগ কীভাবে তৈরি করতে হয় না তা' ঠিক করার অন্যান্য উপায় রয়েছে যার ফলস্বরূপ। একটি হ'ল 'ডিরেক্টরি' লাইনের উপর নির্ভরতা অপসারণ করা। এটি কাজ করে কারণ 'mkdir -p' তৈরি করার জন্য বলা সমস্ত ডিরেক্টরি যদি ইতিমধ্যে বিদ্যমান থাকে তবে ত্রুটি তৈরি করে না। অন্যটি দেখানো মেকানিজম, যা ডিরেক্টরিটি উপস্থিত না থাকলে কেবল তৈরি করার চেষ্টা করবে। 'সংশোধিত হিসাবে' সংস্করণটি গত রাতে আমার মনে ছিল - তবে উভয় কৌশলই কাজ করে (এবং উভয়ই সমস্যা থাকে যদি আউটপুট / ডিবাগ উপস্থিত থাকে তবে ডিরেক্টরি পরিবর্তে একটি ফাইল হয়)।


ধন্যবাদ জোনাথন যখন আমি চেষ্টা করেছিলাম যে আমি একটি ত্রুটি পেয়েছি "মেক: *** টার্গেট output/debug', needed by ডিরেক্টরিগুলি তৈরি করার কোনও নিয়ম নেই ।" থামুন। তবে আমি এখন তা নিয়ে চিন্তা করব না। বুনিয়াদি বিধি বদ্ধ থাকবে stick :)। গাইড করার জন্য আপনাকে ধন্যবাদ। এবং আমি কেবল শীর্ষ স্তরের ডিরেক্টরি থেকে "মেক" চালাচ্ছি।
যাবেজ

কেবল ডিরেক্টরিগুলির পিছনে থাকা $ {OUT_DIR delete মুছুন: তারপরে এটি কাজ করা উচিত।
ডক ব্রাউন

এটি প্রয়োগের জন্য আপনার কমান্ড-লাইন থেকে ডিরেক্টরিগুলি ব্যবহার করার ক্ষেত্রে প্রতিটি সম্ভাব্য কেস ধরা দরকার। directories
তদাতিরিক্ত

@ এমটালেেক্সান এই কয়েকটি উত্তরের মধ্যে কী কী আছে তা ব্যাখ্যা করার জন্য আপনি বেশ কয়েকটি মন্তব্য সরবরাহ করেছেন তবে আপনি বিকল্প উত্তর প্রস্তাব করেন নি। এই সমস্যার জন্য আপনার সমাধান শুনে উদ্বিগ্ন।
স্যামুয়েল

@ সামুয়েল আমি সমাধান না দিয়েই সমস্যার কথা উল্লেখ করেছি কারণ আমি একই জিনিসটি খুঁজছিলাম এবং এর সমাধান কখনও পাইনি। আমি কেবল আদর্শ সমাধানের চেয়ে কম সমাধানের বাইরে এসে পড়েছিলাম।
mtalexan

135

আমার মতে, ডিরেক্টরিগুলি আপনার মেকফিলের লক্ষ্য হিসাবে বিবেচনা করা উচিত নয়, প্রযুক্তিগত বা নকশার দিক থেকে। আপনার ফাইলগুলি তৈরি করা উচিত এবং যদি কোনও ফাইল তৈরির জন্য কোনও নতুন ডিরেক্টরি প্রয়োজন হয় তবে প্রাসঙ্গিক ফাইলটির জন্য নিয়মিতভাবে ডিরেক্টরিটি ডিরেক্টরিতে তৈরি করুন।

আপনি যদি কোনও সাধারণ বা "প্যাটার্নযুক্ত" ফাইলটিকে লক্ষ্য করে থাকেন তবে কেবলমাত্র makeঅভ্যন্তরীণ ভেরিয়েবলটি ব্যবহার করুন $(@D), তার অর্থ "বর্তমান টার্গেটটি যে ডিরেক্টরিটি লক্ষ্যবস্তুতে থাকে সেটিকে" $@লক্ষ্য করে সাথে থাকে)। উদাহরণ স্বরূপ,

$(OUT_O_DIR)/%.o: %.cpp
        @mkdir -p $(@D)
        @$(CC) -c $< -o $@

title: $(OBJS)

তারপরে, আপনি কার্যকরভাবে একই করছেন: সবার জন্য ডিরেক্টরি তৈরি করুন $(OBJS), তবে আপনি এটি কম জটিল উপায়ে করবেন।

একই অ্যাপ্লিকেশনটিতে (ফাইলগুলি লক্ষ্যমাত্রা, ডিরেক্টরিগুলি কখনই নয়) বিভিন্ন অ্যাপ্লিকেশনে ব্যবহৃত হয়। উদাহরণস্বরূপ, gitরিভিশন কন্ট্রোল সিস্টেম ডিরেক্টরি সংরক্ষণ করে না।


দ্রষ্টব্য: আপনি যদি এটি ব্যবহার করতে চলেছেন তবে কোনও সুবিধামত পরিবর্তনশীল প্রবর্তন করতে এবং makeএর সম্প্রসারণের বিধিগুলি ব্যবহার করতে কার্যকর হতে পারে ।

dir_guard=@mkdir -p $(@D)

$(OUT_O_DIR)/%.o: %.cpp
        $(dir_guard)
        @$(CC) -c $< -o $@

$(OUT_O_DIR_DEBUG)/%.o: %.cpp
        $(dir_guard)
        @$(CC) -g -c $< -o $@

title: $(OBJS)

12
ফাইলগুলির সাথে ডিরেক্টরি প্রয়োজনীয়তার সাথে লিঙ্ক করা আমার মতে আরও ভাল বিকল্প, আপনার সমাধানেও উল্লেখযোগ্য অসুবিধা রয়েছে যে mkdir প্রক্রিয়াটি পুনরায় তৈরি করা প্রতিটি ফাইলের জন্য মেক ফাইল দ্বারা ডাকা হবে, যার বেশিরভাগটি তৈরি করার প্রয়োজন হবে না ডিরেক্টরি আবার। উইন্ডোজের মতো নন-লিনাক্স বিল্ড সিস্টেমের সাথে অভিযোজিত হলে, এটি আসলে এমকেডির কমান্ডের সমতুল্য -p সমপরিমাণ না থাকায়, এবং তাত্পর্যপূর্ণ শেল আহ্বান ন্যূনতম আক্রমণাত্মক নয় বলে ওভারহেডের একটি বিশাল পরিমাণ উভয়ই ঘটায়।
mtalexan

4
এমকেডিরকে সরাসরি কল করার পরিবর্তে, ডিরেক্টরিটি ইতিমধ্যে উপস্থিত থাকলে ডিরেক্টরি তৈরি করার চেষ্টা এড়াতে আমি নিম্নলিখিতগুলি করেছি: shell (শেল [! -D $ (@ ডি)] && mkdir -p $ (@ ডি))
ব্র্যাডি

26

বা, KISS।

DIRS=build build/bins

... 

$(shell mkdir -p $(DIRS))

এটি মেকফিল পার্স করার পরে সমস্ত ডিরেক্টরি তৈরি করবে।


4
আমি এই পদ্ধতির পছন্দ করি কারণ ডিরেক্টরিগুলি পরিচালনা করতে কমান্ডগুলি সহ আমার প্রতিটি লক্ষ্যকে বিশৃঙ্খলা করতে হবে না।
কেন উইলিয়ামস

4
আমার কেবল ডিরেক্টরি তৈরি করার প্রয়োজন ছিল যদি এটি বিদ্যমান না থাকে। এই উত্তরটি আমার সমস্যার জন্য উপযুক্ত।
জেফ পাল

শুধু তাই নয়, এটি প্রতিটি ডিরেক্টরিতে সংশোধিত টাইমস্ট্যাম্পগুলিকে অপ্রয়োজনীয় বিল্ড স্টেপ ট্রিগার করা থেকে বাধা দেয়। এটির উত্তরটি হওয়া উচিত
এমিলিমেটার

6
এই উত্তম: $(info $(shell mkdir -p $(DIRS)))ছাড়া $(info ...), আউটপুট mkdirকমান্ড Makefile নামক মধ্যে আটকানো হবে , শ্রেষ্ঠ সময়ে সিনট্যাক্স ত্রুটি সৃষ্টি করে। $(info ...)কল নিশ্চিত করে যে ক) ত্রুটি (যদি থাকে) ব্যবহারকারীতে দৃশ্যমান হয়, এবং খ) যে কিছুই ফাংশন কল প্রসারিত করে।
মাস্টার

10

makeইন এবং নিজে থেকে, ডিরেক্টরি লক্ষ্যগুলি ফাইল টার্গেটের মতোই পরিচালনা করে। সুতরাং, এটি নিয়ম লিখতে সহজ:

outDir/someTarget: Makefile outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

এটির সাথে একমাত্র সমস্যাটি হ'ল ডিরেক্টরি টাইমস্ট্যাম্প নির্ভর করে যা ফাইলগুলি ভিতরে করা হয় তার উপর। উপরের নিয়মগুলির জন্য, এটি নিম্নলিখিত ফলাফলের দিকে নিয়ে যায়:

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget

এটি আপনি যা চান তা অবশ্যই নয়। আপনি যখনই ফাইলটি স্পর্শ করবেন তখন আপনি ডিরেক্টরিটিও স্পর্শ করেন। এবং যেহেতু ফাইলটি ডিরেক্টরিের উপর নির্ভর করে, ফলস্বরূপ ফাইলটি পুরানো বলে মনে হয়, এটি পুনরায় তৈরি করতে বাধ্য করে।

যাইহোক, আপনি ডিরেক্টরিটির টাইমস্ট্যাম্প উপেক্ষা করার জন্য মেককে বলে সহজেই এই লুপটি ভেঙে ফেলতে পারেন । ডিরেক্টরিটি কেবলমাত্র আদেশের পূর্বশর্ত হিসাবে ঘোষণা করে এটি করা হয়:

# The pipe symbol tells make that the following prerequisites are order-only
#                           |
#                           v
outDir/someTarget: Makefile | outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

এটি সঠিকভাবে ফলন দেয়:

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
make: 'outDir/someTarget' is up to date.

টিএল; ডিআর:

ডিরেক্টরি তৈরি করতে একটি বিধি লিখুন:

$(OUT_DIR):
    mkdir -p $(OUT_DIR)

এবং ভিতরে থাকা স্টাফের লক্ষ্যমাত্রা কেবলমাত্র ডিরেক্টরি আদেশের উপর নির্ভর করে:

$(OUT_DIR)/someTarget: ... | $(OUT_DIR)

কোন ভাঙা ওএস / এফএসে আপনি touchপিতামাতার ডিরেক্টরিতে কোনও স্ট্যাটাল ডেটা সংশোধন করতে দেখেছেন ? এটা আমার অজ্ঞান করে তোলে। একটি দির এমটাইম কেবল এটি উপস্থিত ফাইলের উপর নির্ভর করে। আমি আপনার সমস্যা পুনরুত্পাদন করতে পারে না।
জোহান বুলি

@ জোহানবুলি দেবিয়ান
মাস্টার -

এবং আপনি কি এইরকম ভাঙ্গা আচরণের জন্য কোনও বাগ পূরণ করেছেন?
জোহান বুলি

6

স্বীকৃত এক সহ সমস্ত সমাধানের কিছু নিজস্ব সমস্যা আছে যা তাদের নিজ নিজ মন্তব্যে বলেছে। দ্বারা @ জনাথন-Leffler গৃহীত উত্তর ইতিমধ্যে বেশ ভাল কিন্তু প্রভাব যে অপরিহার্য না অগত্যা (সময় অনুক্রমে নির্মিত হবে ভাগে ভাগ করা লাগবে না make -jউদাহরণস্বরূপ)। তবে কেবল চলন্ত directoriesথেকে পূর্বশর্ত allথেকে programযে রান AFAICT উপর, provokes পুনঃনির্মাণ। নিম্নলিখিত সমাধানটিতে সেই সমস্যা নেই এবং এএএএফআইএসস যেমন ইচ্ছা তেমন কাজ করে।

MKDIR_P := mkdir -p
OUT_DIR := build

.PHONY: directories all clean

all: $(OUT_DIR)/program

directories: $(OUT_DIR)

$(OUT_DIR):
    ${MKDIR_P} $(OUT_DIR)

$(OUT_DIR)/program: | directories
    touch $(OUT_DIR)/program

clean:
    rm -rf $(OUT_DIR)

4

আমি স্রেফ একটি মোটামুটি যুক্তিসঙ্গত সমাধান নিয়ে এসেছি যা আপনাকে ফাইলগুলি তৈরির জন্য সংজ্ঞা দিতে দেয় এবং ডিরেক্টরিগুলি স্বয়ংক্রিয়ভাবে তৈরি হতে দেয়। প্রথমে একটি ভেরিয়েবল নির্ধারণ ALL_TARGET_FILESকরুন যা আপনার মেকফিলটি তৈরি হবে এমন প্রতিটি ফাইলের ফাইলের নাম ধারণ করে। তারপরে নিম্নলিখিত কোডটি ব্যবহার করুন:

define depend_on_dir
$(1): | $(dir $(1))

ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED
$(dir $(1)):
    mkdir -p $$@

$(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1
endif
endef

$(foreach file,$(ALL_TARGET_FILES),$(eval $(call depend_on_dir,$(file))))

এখানে কিভাবে এটা কাজ করে. আমি একটি ফাংশন সংজ্ঞায়িত করি depend_on_dirযা একটি ফাইলের নাম নেয় এবং একটি নিয়ম উত্পন্ন করে যা ফাইলটি সেই ডিরেক্টরিটিতে অন্তর্ভুক্ত করে এবং তারপরে প্রয়োজনে ডিরেক্টরিটি তৈরি করার জন্য একটি বিধি সংজ্ঞা দেয়। তারপরে আমি প্রতিটি ফাইলের নাম এবং ফলাফলের foreachজন্য callএই ফাংশনটি ব্যবহার করি eval

নোট করুন যে আপনার জিএনইউ মেকের একটি সংস্করণ দরকার যা সমর্থন করে eval, যা আমি মনে করি সংস্করণটি ৩.৮১ এবং এর চেয়ে বেশি এর বেশি।


একটি ভেরিয়েবল তৈরি করা যা "আপনার মেকফাইল তৈরি করবে এমন প্রতিটি ফাইলের ফাইলের নাম" ধারণ করে এক ধরণের প্রয়োজনের প্রয়োজন - আমি আমার শীর্ষ-স্তরের লক্ষ্যগুলি, তারপরে কীসের উপর নির্ভর করে সেগুলি সংজ্ঞায়িত করতে চাই। সমস্ত টার্গেট ফাইলের একটি সমতল তালিকা মেকফাইল নির্দিষ্টকরণের শ্রেণিবিন্যাসের প্রকৃতির বিরুদ্ধে যায় এবং লক্ষ্য ফাইলগুলি রানটাইম কম্পিউটারের উপর নির্ভর করে তখন (সহজেই) সম্ভব হয় না।
কেন উইলিয়ামস

3

আপনি যে নবাগত, এই বলার পরে আমি এইটি করার চেষ্টা করব না। এটি অবশ্যই সম্ভব, তবে অযথা আপনার মেকফিলটিকে জটিল করে তুলবে। আপনি মেকিং করতে আরও স্বাচ্ছন্দ্য না হওয়া অবধি সহজ উপায়গুলিতে আটকে থাকুন।

এটি বলেছিল, উত্স ডিরেক্টরি থেকে আলাদা ডিরেক্টরি তৈরি করার একটি উপায় হ'ল ভিপিএটিএইচ ; আমি প্যাটার্ন বিধি পছন্দ


3

ওএসের স্বাধীনতা আমার পক্ষে গুরুত্বপূর্ণ, সুতরাং mkdir -pএটি কোনও বিকল্প নয়। আমি এই ক্রিয়াকলাপগুলি evalতৈরি করেছি যা অভিভাবক ডিরেক্টরিতে পূর্বশর্ত সহ ডিরেক্টরি লক্ষ্য তৈরি করতে ব্যবহার করে। make -j 2নির্ভরতা সঠিকভাবে নির্ধারিত হওয়ার কারণে এটির সুবিধা রয়েছে যা ইস্যু ছাড়াই কাজ করবে।

# convenience function for getting parent directory, will eventually return ./
#     $(call get_parent_dir,somewhere/on/earth/) -> somewhere/on/
get_parent_dir=$(dir $(patsubst %/,%,$1))

# function to create directory targets.
# All directories have order-only-prerequisites on their parent directories
# https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html#Prerequisite-Types
TARGET_DIRS:=
define make_dirs_recursively
TARGET_DIRS+=$1
$1: | $(if $(subst ./,,$(call get_parent_dir,$1)),$(call get_parent_dir,$1))
    mkdir $1
endef

# function to recursively get all directories 
#     $(call get_all_dirs,things/and/places/) -> things/ things/and/ things/and/places/
#     $(call get_all_dirs,things/and/places) -> things/ things/and/
get_all_dirs=$(if $(subst ./,,$(dir $1)),$(call get_all_dirs,$(call get_parent_dir,$1)) $1)

# function to turn all targets into directories
#     $(call get_all_target_dirs,obj/a.o obj/three/b.o) -> obj/ obj/three/
get_all_target_dirs=$(sort $(foreach target,$1,$(call get_all_dirs,$(dir $(target)))))

# create target dirs
create_dirs=$(foreach dirname,$(call get_all_target_dirs,$1),$(eval $(call make_dirs_recursively,$(dirname))))

TARGETS := w/h/a/t/e/v/e/r/things.dat w/h/a/t/things.dat

all: $(TARGETS)

# this must be placed after your .DEFAULT_GOAL, or you can manually state what it is
# https://www.gnu.org/software/make/manual/html_node/Special-Variables.html
$(call create_dirs,$(TARGETS))

# $(TARGET_DIRS) needs to be an order-only-prerequisite
w/h/a/t/e/v/e/r/things.dat: w/h/a/t/things.dat | $(TARGET_DIRS)
    echo whatever happens > $@

w/h/a/t/things.dat: | $(TARGET_DIRS)
    echo whatever happens > $@

উদাহরণস্বরূপ, উপরের চালনাটি তৈরি করবে:

$ make
mkdir w/
mkdir w/h/
mkdir w/h/a/
mkdir w/h/a/t/
mkdir w/h/a/t/e/
mkdir w/h/a/t/e/v/
mkdir w/h/a/t/e/v/e/
mkdir w/h/a/t/e/v/e/r/
echo whatever happens > w/h/a/t/things.dat
echo whatever happens > w/h/a/t/e/v/e/r/things.dat

1

Https://www.oreilly.com/library/view/managing-projects-with/0596006101/ch12.html দেখুন

REQUIRED_DIRS = ...
_MKDIRS := $(shell for d in $(REQUIRED_DIRS); \
             do                               \
               [[ -d $$d ]] || mkdir -p $$d;  \
             done)

$(objects) : $(sources)

আমি যেমন উবুন্টু ব্যবহার করি, আমার মেকফিলের শীর্ষেও এটি যুক্ত করা দরকার:

SHELL := /bin/bash # Use bash syntax
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.