C ++ प्रोजेक्ट ऑर्गनाइजेशन (gtest, cmake और doxygen के साथ)


123

मैं सामान्य रूप से प्रोग्रामिंग में नया हूं इसलिए मैंने फैसला किया कि मैं C ++ में एक साधारण वेक्टर क्लास बनाकर शुरू करूंगा। हालाँकि मैं बाद में अपने वर्कफ़्लो को संशोधित करने की कोशिश करने के बजाय शुरू से ही अच्छी आदतों में शामिल होना चाहता हूँ।

मैं वर्तमान में केवल दो फ़ाइलें vector3.hppऔर vector3.cpp। यह परियोजना धीरे-धीरे बढ़ने लगेगी (इसे सामान्य लीनियर बीजगणित पुस्तकालय का अधिक हिस्सा बनाना) क्योंकि मैं हर चीज से अधिक परिचित हो गया हूं, इसलिए मैं बाद में जीवन को आसान बनाने के लिए एक "मानक" प्रोजेक्ट लेआउट अपनाना चाहूंगा। तो चारों ओर देखने के बाद मुझे hpp और cpp फ़ाइलों के आयोजन के बारे में जाने के दो तरीके मिल गए हैं, पहला:

project
└── src
    ├── vector3.hpp
    └── vector3.cpp

और दूसरा होना:

project
├── inc
   └── project
       └── vector3.hpp
└── src
    └── vector3.cpp

आप किसकी सिफारिश करेंगे और क्यों?

दूसरी बात मैं अपने कोड के परीक्षण के लिए Google C ++ टेस्टिंग फ्रेमवर्क का उपयोग करना चाहूंगा क्योंकि यह उपयोग करने में काफी आसान लगता है। क्या आप इसे मेरे कोड के साथ बंडल करने का सुझाव देते हैं, उदाहरण के लिए inc/gtestया contrib/gtestफ़ोल्डर में? यदि बंडल किया गया है, तो क्या आप fuse_gtest_files.pyसंख्या या फ़ाइलों को कम करने के लिए स्क्रिप्ट का उपयोग करने का सुझाव देते हैं , या इसे छोड़ रहे हैं? यदि बंडल नहीं है तो यह निर्भरता कैसे नियंत्रित की जाती है?

जब लेखन परीक्षण की बात आती है, तो ये आम तौर पर कैसे व्यवस्थित होते हैं? मैं प्रत्येक वर्ग ( test_vector3.cppउदाहरण के लिए) के लिए एक cpp फ़ाइल रखने की सोच रहा था, लेकिन सभी एक बाइनरी में संकलित हैं ताकि वे सभी आसानी से चल सकें?

चूँकि gtest लाइब्रेरी आमतौर पर cmake और मेक का उपयोग करके निर्मित होती है, मैं सोच रहा था कि यह मेरी परियोजना के लिए भी इस तरह से बनाया जाएगा? अगर मैंने निम्नलिखित प्रोजेक्ट लेआउट का उपयोग करने का निर्णय लिया है:

├── CMakeLists.txt
├── contrib
   └── gtest
       ├── gtest-all.cc
       └── gtest.h
├── docs
   └── Doxyfile
├── inc
   └── project
       └── vector3.cpp
├── src
   └── vector3.cpp
└── test
    └── test_vector3.cpp

कैसे देखना होगा CMakeLists.txtताकि यह या तो सिर्फ पुस्तकालय या पुस्तकालय और परीक्षणों का निर्माण कर सके? इसके अलावा, मैंने काफी कुछ प्रोजेक्ट्स देखे हैं जिनमें एक buildऔर एक binडायरेक्टरी है। क्या बिल्ड बिल्ड निर्देशिका में होता है और फिर बायनेरिज़ बिन निर्देशिका में चले गए? क्या परीक्षण और पुस्तकालय के लिए बायनेरिज़ एक ही जगह पर रहेंगे? या यह संरचना के लिए इसे और अधिक समझ में आता है:

test
├── bin
├── build
└── src
    └── test_vector3.cpp

मैं अपने कोड के दस्तावेज़ के लिए doxygen का उपयोग करना चाहूंगा। क्या यह संभव है कि यह स्वचालित रूप से सेमीक के साथ चले और बनाये?

इतने सारे प्रश्नों के लिए क्षमा करें, लेकिन मुझे C ++ पर एक पुस्तक नहीं मिली है जो इस प्रकार के प्रश्नों का संतोषजनक उत्तर देती है।


6
महान सवाल है, लेकिन मुझे नहीं लगता कि यह स्टैक ओवरफ्लो के प्रश्नोत्तर प्रारूप के लिए एक अच्छा फिट है । मैं एक जवाब में बहुत दिलचस्पी रहा हूँ, हालांकि। +1 &
फ़ेव

1
ये बहुत से प्रश्न हैं। यह बेहतर हो सकता है कि इसे कई छोटे प्रश्नों में विभाजित करें और एक दूसरे से लिंक करें। वैसे भी अंतिम भाग का जवाब देने के लिए: सीएमके के साथ आप अपनी src निर्देशिका के अंदर और बाहर निर्माण करना चुन सकते हैं (मैं बाहर की सिफारिश करूंगा)। और हाँ आप CMake के साथ doxygen का स्वतः उपयोग कर सकते हैं।
mistapink

जवाबों:


84

सी ++ बिल्ड सिस्टम एक काली कला का एक सा है और पुराने प्रोजेक्ट में अधिक अजीब सामान आप पा सकते हैं इसलिए यह आश्चर्यजनक नहीं है कि बहुत सारे प्रश्न सामने आते हैं। मैं एक-एक करके प्रश्नों के माध्यम से चलने की कोशिश करूँगा और C ++ पुस्तकालयों के निर्माण के संबंध में कुछ सामान्य बातों का उल्लेख करूँगा।

निर्देशिकाओं में हेडर और सीपीपी फ़ाइलों को अलग करना। यह केवल तभी आवश्यक है जब आप एक ऐसे घटक का निर्माण कर रहे हों, जिसे वास्तविक अनुप्रयोग के विपरीत पुस्तकालय के रूप में उपयोग किया जाना चाहिए। आपके शीर्षलेख उपयोगकर्ताओं को आपके द्वारा ऑफ़र किए गए और इंस्टॉल किए जाने के साथ सहभागिता करने का आधार हैं। इसका मतलब है कि उन्हें एक उपनिर्देशिका में होना चाहिए (कोई भी शीर्ष-स्तर में बहुत सारे हेडर समाप्त नहीं करना चाहता है /usr/include/) और आपके हेडर को इस तरह के सेटअप के साथ खुद को शामिल करने में सक्षम होना चाहिए।

└── prj
    ├── include
       └── prj
           ├── header2.h
           └── header.h
    └── src
        └── x.cpp

अच्छी तरह से काम करता है, क्योंकि इसमें पथ शामिल हैं और आप आसान टारगेटिंग का उपयोग कर सकते हैं।

निर्भरता को कम करना: मुझे लगता है कि यह काफी हद तक निर्भरता का पता लगाने और कॉन्फ़िगर करने के लिए निर्माण प्रणाली की क्षमता पर निर्भर करता है और एक कोड पर आपका कोड कितना निर्भर है। यह इस बात पर भी निर्भर करता है कि आपके उपयोगकर्ता कितने सक्षम हैं और उनके प्लेटफॉर्म पर इंस्टॉल करने के लिए निर्भरता कितनी आसान है। CMake find_packageGoogle टेस्ट की स्क्रिप्ट के साथ आता है । इससे चीजें बहुत आसान हो जाती हैं। मैं केवल आवश्यक होने पर बंडल के साथ जाऊंगा और अन्यथा इससे बचूंगा।

कैसे बनाएं: इन-सोर्स बिल्ड से बचें CMake सोर्स-बिल्ड से बाहर आसान बनाता है और यह जीवन को बहुत आसान बनाता है।

मुझे लगता है कि आप अपने सिस्टम के लिए परीक्षण चलाने के लिए CTest का उपयोग करना चाहते हैं (यह GTest के लिए बिल्ड-इन सपोर्ट के साथ भी आता है)। निर्देशिका लेआउट और परीक्षण संगठन के लिए एक महत्वपूर्ण निर्णय होगा: क्या आप उपप्रोजेक्ट के साथ समाप्त होते हैं? यदि ऐसा है, तो आपको CMakeLists सेट करते समय कुछ और काम करने की आवश्यकता है और अपने सबप्रोजेक्ट्स को उपनिर्देशिकाओं में विभाजित करना चाहिए, प्रत्येक को अपनी includeऔर srcफाइलों के साथ। हो सकता है कि उनका अपना डॉक्सिन भी चलता हो और आउटपुट (कई डॉक्सीजेन प्रोजेक्ट्स का संयोजन संभव हो, लेकिन आसान या सुंदर नहीं)।

आप कुछ इस तरह से समाप्त करेंगे:

└── prj
    ├── CMakeLists.txt <-- (1)
    ├── include
       └── prj
           ├── header2.hpp
           └── header.hpp
    ├── src
       ├── CMakeLists.txt <-- (2)
       └── x.cpp
    └── test
        ├── CMakeLists.txt <-- (3)
        ├── data
           └── testdata.yyy
        └── testcase.cpp

कहाँ पे

  • (1) निर्भरताएँ, प्लेटफ़ॉर्म विवरण और आउटपुट पथ कॉन्फ़िगर करता है
  • (2) आपके द्वारा बनाई जा रही लाइब्रेरी को कॉन्फ़िगर करता है
  • (3) परीक्षण निष्पादनयोग्य और परीक्षण-मामलों को कॉन्फ़िगर करता है

यदि आपके पास उप-घटक हैं तो मैं सुझाव दूंगा कि एक और पदानुक्रम जोड़ा जाए और प्रत्येक उप-परियोजना के लिए ऊपर के पेड़ का उपयोग किया जाए। तब चीजें मुश्किल हो जाती हैं, क्योंकि आपको यह तय करने की आवश्यकता है कि क्या उप-घटक उनकी निर्भरता को खोजते हैं और कॉन्फ़िगर करते हैं या यदि आप शीर्ष स्तर में ऐसा करते हैं। यह मामला-दर-मामला आधार पर तय किया जाना चाहिए।

Doxygen: आपके द्वारा doxygen के कॉन्फ़िगरेशन नृत्य के माध्यम से जाने में कामयाब होने के बाद, add_custom_commandडॉक्टर लक्ष्य का उपयोग करने के लिए CMake का उपयोग करना तुच्छ है ।

यह है कि मेरी परियोजनाएं कैसे समाप्त होती हैं और मैंने कुछ इसी तरह की परियोजनाएं देखी हैं, लेकिन निश्चित रूप से यह सब ठीक नहीं है।

परिशिष्ट कुछ बिंदु पर आप एक config.hpp फ़ाइल उत्पन्न करना चाहेंगे जिसमें एक संस्करण परिभाषित हो और शायद कुछ संस्करण नियंत्रण पहचानकर्ता (एक Git हैश या SVN संशोधन संख्या) के लिए परिभाषित हो। CMake के पास उस जानकारी को खोजने और फाइलों को उत्पन्न करने के लिए मॉड्यूल हैं। आप configure_fileटेम्पलेट फ़ाइल में वेरिएबल्स को बदलने के लिए सीएमके का उपयोग कर सकते हैं CMakeLists.txt

यदि आप पुस्तकालयों का निर्माण कर रहे हैं, तो आपको संकलक के बीच अंतर प्राप्त करने के लिए एक निर्यात परिभाषित की आवश्यकता होगी, उदाहरण के __declspecलिए MSVC और visibilityGCC / clang पर विशेषताएँ।


2
अच्छा जवाब है, लेकिन मैं अभी भी इस बात पर स्पष्ट नहीं हूं कि आपको अपनी हेडर फ़ाइलों को एक अतिरिक्त प्रोजेक्ट नाम की उपनिर्देशिका में डालने की आवश्यकता क्यों है: "/prj/include/prj/foo.hpp", जो मुझे बेमानी लगती है। सिर्फ "/prj/include/foo.hpp" क्यों नहीं? मैं मान रहा हूं कि आपके पास इंस्टॉलेशन-टाइम पर इंस्टॉलेशन डायरेक्शंस को री-जिग करने का अवसर होगा, इसलिए जब आप इंस्टॉल करते हैं, तो आपको <INSTALL_DIR> /include/prj/foo.hpp मिलता है, या सीमेक के लिए यह मुश्किल है?
विलियम पेने

6
@William वास्तव में CPack के साथ करना मुश्किल है। इसके अलावा, आपकी स्रोत फ़ाइलों के अंदर कैसे शामिल होंगे? यदि वे एक स्थापित संस्करण पर सिर्फ "शीर्ष लेख" हैं तो "usr / शामिल करें / शामिल करें / prj /" केवल "/ usr / शामिल करें" के विपरीत पथ में शामिल होने की आवश्यकता है।
अपराह्न

37

स्टार्टर के रूप में, निर्देशिका के कुछ पारंपरिक नाम हैं जिन्हें आप अनदेखा नहीं कर सकते हैं, ये यूनिक्स फाइल सिस्टम के साथ लंबी परंपरा पर आधारित हैं। य़े हैं:

trunk
├── bin     : for all executables (applications)
├── lib     : for all other binaries (static and shared libraries (.so or .dll))
├── include : for all header files
├── src     : for source files
└── doc     : for documentation

कम से कम शीर्ष स्तर पर, इस मूल लेआउट से चिपके रहना शायद एक अच्छा विचार है।

शीर्ष लेख फ़ाइलों और स्रोत फ़ाइलों (cpp) को विभाजित करने के बारे में, दोनों योजनाएँ काफी सामान्य हैं। हालांकि, मैं उन्हें एक साथ रखना पसंद करता हूं, यह फाइलों को एक साथ रखने के लिए दिन-प्रतिदिन के कार्यों पर अधिक व्यावहारिक है। इसके अलावा, जब सभी कोड एक शीर्ष-स्तरीय फ़ोल्डर के अंतर्गत होते हैं, अर्थात, trunk/src/आप देख सकते हैं कि शीर्ष स्तर पर, अन्य सभी फ़ोल्डर (बिन, lib, शामिल, डॉक्टर, और शायद कुछ परीक्षण फ़ोल्डर), इसके अलावा आउट-ऑफ-सोर्स बिल्ड के लिए "बिल्ड" डायरेक्टरी, सभी फ़ोल्डर हैं जिनमें बिल्ड प्रक्रिया में उत्पन्न फ़ाइलों से अधिक कुछ नहीं है। और इस प्रकार, केवल src फ़ोल्डर को बैकअप नियंत्रण प्रणाली / सर्वर (जैसे Git या SVN) के तहत रखने की आवश्यकता होती है, या बहुत बेहतर।

और जब यह आपके हेडर फाइल को गंतव्य प्रणाली पर स्थापित करने की बात आती है (यदि आप अंततः अपनी लाइब्रेरी को वितरित करना चाहते हैं), तो, सीएमके में फाइलें स्थापित करने के लिए एक कमांड है ("स्थापित करें" करने के लिए, "स्थापित करें" लक्ष्य बनाएं) आप /usr/include/निर्देशिका में सभी हेडर डालने के लिए उपयोग कर सकते हैं । मैं सिर्फ इस उद्देश्य के लिए निम्नलिखित cmake मैक्रो का उपयोग करता हूं:

# custom macro to register some headers as target for installation:
#  setup_headers("/path/to/header/something.h" "/relative/install/path")
macro(setup_headers HEADER_FILES HEADER_PATH)
  foreach(CURRENT_HEADER_FILE ${HEADER_FILES})
    install(FILES "${SRCROOT}${CURRENT_HEADER_FILE}" DESTINATION "${INCLUDEROOT}${HEADER_PATH}")
  endforeach(CURRENT_HEADER_FILE)
endmacro(setup_headers)

जहाँ SRCROOTएक cmake वैरिएबल है जिसे मैंने src फ़ोल्डर में सेट किया है, और INCLUDEROOTcmake वैरिएबल है जिसे मैं हेडर में जाने के लिए जहाँ भी कॉन्फ़िगर करता हूँ। बेशक, ऐसा करने के कई अन्य तरीके हैं, और मुझे यकीन है कि मेरा तरीका सबसे अच्छा नहीं है। मुद्दा यह है कि हेडर और स्रोतों को विभाजित करने का कोई कारण नहीं है, क्योंकि केवल हेडर को लक्ष्य प्रणाली पर स्थापित करने की आवश्यकता है, क्योंकि यह बहुत आसान है, विशेष रूप से सीएमके (या सीपीैक) के साथ, हेडर को बाहर निकालने और कॉन्फ़िगर करने के लिए। उन्हें एक अलग निर्देशिका में स्थापित किए बिना स्थापित किया जाए। और यही मैंने ज्यादातर पुस्तकालयों में देखा है।

उद्धरण: दूसरे मैं अपने कोड के परीक्षण के लिए Google C ++ टेस्टिंग फ्रेमवर्क का उपयोग करना चाहूंगा क्योंकि यह उपयोग करने में काफी आसान लगता है। क्या आप इसे मेरे कोड से जोड़ने का सुझाव देते हैं, उदाहरण के लिए "inc / gtest" या "contrib / gtest" फ़ोल्डर में? यदि बंडल किया गया है, तो क्या आप संख्या या फ़ाइलों को कम करने के लिए fuse_gtest_files.py स्क्रिप्ट का उपयोग करने का सुझाव देते हैं, या जैसा है वैसा ही छोड़ देते हैं? यदि बंडल नहीं है तो यह निर्भरता कैसे नियंत्रित की जाती है?

अपने पुस्तकालय के साथ निर्भरता को बंडल न करें। यह आम तौर पर एक बहुत ही भयानक विचार है, और मैं हमेशा इसे नफरत करता हूं जब मैं एक पुस्तकालय बनाने की कोशिश कर रहा हूं जो ऐसा किया था। यह आपका अंतिम उपाय होना चाहिए, और नुकसान से सावधान रहना चाहिए। अक्सर, लोग अपनी लाइब्रेरी के साथ निर्भरता को या तो बंडल करते हैं, क्योंकि वे एक भयानक विकास के वातावरण (जैसे, विंडोज) को लक्षित करते हैं, या क्योंकि वे केवल पुस्तकालय (निर्भरता) के एक पुराने (अस्वीकृत) संस्करण का समर्थन करते हैं। मुख्य नुकसान यह है कि आपकी बंडल की गई निर्भरता उसी लाइब्रेरी / एप्लिकेशन के पहले से इंस्टॉल किए गए संस्करणों के साथ टकरा सकती है (उदाहरण के लिए, आपने gtest को बंडल किया है, लेकिन आपके लाइब्रेरी का निर्माण करने की कोशिश करने वाले व्यक्ति के पास पहले से ही स्थापित gtest का नया (या पुराना) संस्करण है, फिर दोनों टकरा सकते हैं और उस व्यक्ति को बहुत बुरा सिरदर्द दे सकते हैं)। इसलिए, जैसा कि मैंने कहा, इसे अपने जोखिम पर करें, और मैं केवल अंतिम उपाय के रूप में कहूंगा। अपनी लाइब्रेरी को संकलित करने में सक्षम होने से पहले कुछ निर्भरताएं स्थापित करने के लिए लोगों से पूछना आपके बंडल पर निर्भरता और मौजूदा प्रतिष्ठानों के बीच संघर्ष को सुलझाने की कोशिश करने की तुलना में बहुत कम बुराई है।

उद्धरण: जब लेखन परीक्षण की बात आती है, तो ये आम तौर पर कैसे व्यवस्थित होते हैं? मैं प्रत्येक वर्ग (उदाहरण के लिए test_vector3.cpp) के लिए एक cpp फ़ाइल रखने की सोच रहा था, लेकिन सभी एक बाइनरी में संकलित हैं ताकि वे सभी आसानी से एक साथ चल सकें?

प्रति वर्ग (या कक्षाओं और कार्यों के छोटे सामंजस्यपूर्ण समूह) प्रति एक सीपीपी फ़ाइल मेरी राय में अधिक सामान्य और व्यावहारिक है। हालाँकि, निश्चित रूप से, उन सभी को केवल एक बाइनरी में संकलित न करें ताकि "वे सभी एक साथ चल सकें"। यह एक बहुत बुरा विचार है। आमतौर पर, जब कोडिंग की बात आती है, तो आप चीजों को उतना ही विभाजित करना चाहते हैं जितना कि ऐसा करना वाजिब है। यूनिट-परीक्षणों के मामले में, आप सभी परीक्षणों को चलाने के लिए एक बाइनरी नहीं चाहते हैं, क्योंकि इसका मतलब है कि आपके पुस्तकालय में किसी भी चीज़ में थोड़ा सा भी बदलाव होने पर उस इकाई-परीक्षण कार्यक्रम के निकट पुनर्मिलन होने की संभावना है , और वह बस कुछ ही मिनटों / घंटों के लिए इंतजार कर रहे थे। बस एक साधारण योजना से चिपके रहें: 1 इकाई = 1 इकाई-परीक्षण कार्यक्रम। फिर,

उद्धरण: चूँकि gtest लाइब्रेरी आमतौर पर cmake और मेक का उपयोग करके निर्मित होती है, मैं सोच रहा था कि यह मेरी परियोजना के लिए भी समझ में आएगा? यदि मैंने निम्नलिखित प्रोजेक्ट लेआउट का उपयोग करने का निर्णय लिया है:

मैं इस लेआउट का सुझाव दूंगा:

trunk
├── bin
├── lib
   └── project
       └── libvector3.so
       └── libvector3.a        products of installation / building
├── docs
   └── Doxyfile
├── include
   └── project
       └── vector3.hpp
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

├── src
   └── CMakeLists.txt
   └── Doxyfile.in
   └── project                 part of version-control / source-distribution
       └── CMakeLists.txt
       └── vector3.hpp
       └── vector3.cpp
       └── test
           └── test_vector3.cpp
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

├── build
└── test                        working directories for building / testing
    └── test_vector3

यहाँ ध्यान देने योग्य कुछ बातें। सबसे पहले, आपकी src निर्देशिका की उप-निर्देशिकाओं में आपकी शामिल निर्देशिका की उप-निर्देशिकाओं को प्रतिबिंबित करना चाहिए, यह सिर्फ चीजों को सहज रखने के लिए है (साथ ही, अपने उप-निर्देशिका संरचना को यथोचित रूप से सपाट (उथले) रखने की कोशिश करें, क्योंकि फ़ोल्डरों के गहरे घोंसले के शिकार) अक्सर किसी अन्य चीज की तुलना में अधिक परेशानी होती है)। दूसरा, "शामिल" निर्देशिका सिर्फ एक अधिष्ठापन निर्देशिका है, इसकी सामग्री बस वही है जो हेडर src निर्देशिका से बाहर निकाले जाते हैं।

तीसरा, CMake सिस्टम को स्रोत उप-निर्देशिकाओं में वितरित करने का इरादा है, न कि शीर्ष स्तर पर एक CMakeLists.txt फ़ाइल के रूप में। यह चीजों को स्थानीय रखता है, और यह अच्छा है (चीजों को स्वतंत्र टुकड़ों में विभाजित करने की भावना में)। यदि आप एक नया स्रोत, एक नया हेडर, या एक नया परीक्षण कार्यक्रम जोड़ते हैं, तो आपको केवल एक और छोटे और सरल CMakeLists.txt फ़ाइल को किसी अन्य चीज को प्रभावित किए बिना, उप-निर्देशिका में संपादित करना है। यह आपको आसानी से निर्देशिकाओं का पुनर्गठन करने की भी अनुमति देता है (CMakeLists स्थानीय हैं और उप-निर्देशिकाओं में निहित हैं)। शीर्ष स्तर के CMakeLists में अधिकांश शीर्ष-स्तरीय कॉन्फ़िगरेशन होते हैं, जैसे कि गंतव्य निर्देशिकाओं को सेट करना, कस्टम कमांड (या मैक्रोज़), और सिस्टम पर स्थापित पैकेज खोजना। निचले स्तर के CMakeLists में हेडर, स्रोतों की केवल सरल सूची होनी चाहिए,

उद्धरण: CMakeLists.txt को कैसे देखना होगा ताकि यह या तो सिर्फ पुस्तकालय या पुस्तकालय और परीक्षणों का निर्माण कर सके?

मूल उत्तर यह है कि सीएमके आपको विशेष रूप से "सभी" (जो कि "मेक" टाइप करने पर बनाया गया है) से कुछ लक्ष्यों को बाहर करने की अनुमति देता है, और आप लक्ष्यों के विशिष्ट बंडल भी बना सकते हैं। मैं यहां सीएमके ट्यूटोरियल नहीं कर सकता, लेकिन यह स्वयं के द्वारा पता लगाने के लिए काफी सीधा है। इस विशिष्ट मामले में, हालांकि, अनुशंसित समाधान, निश्चित रूप से, सीरेस्ट का उपयोग करने के लिए है, जो कि कमांड का एक अतिरिक्त सेट है जिसका उपयोग आप कई लक्ष्यों (कार्यक्रमों) को पंजीकृत करने के लिए CMakeLists फाइलों में कर सकते हैं जिन्हें इकाई के रूप में चिह्नित किया गया है- परीक्षण। तो, सीएमके सभी परीक्षणों को एक विशेष श्रेणी के बिल्ड की श्रेणी में रखेगा, और ठीक वही है जो आपने पूछा था, इसलिए, समस्या हल हो गई।

Quote: इसके अलावा, मैंने काफी कुछ प्रोजेक्ट देखे हैं जिनमें एक बिल्ड विज्ञापन एक बिन निर्देशिका है। क्या बिल्ड बिल्ड निर्देशिका में होता है और फिर बायनेरिज़ बिन निर्देशिका में चले गए? क्या परीक्षण और पुस्तकालय के लिए बायनेरिज़ एक ही जगह पर रहेंगे? या इसे इस तरह से संरचना करने के लिए अधिक समझ में आता है:

स्रोत ("आउट-ऑफ-सोर्स" बिल्ड) के बाहर एक बिल्ड डायरेक्टरी होना वास्तव में एकमात्र समझदारी की बात है, यह इन दिनों वास्तविक मानक है। तो, निश्चित रूप से, स्रोत निर्देशिका के बाहर एक अलग "बिल्ड" डायरेक्टरी है, जैसा कि सीएमके लोग सलाह देते हैं, और जैसा कि मैंने कभी भी किया है, हर प्रोग्रामर करता है। बिन निर्देशिका के लिए, ठीक है, यह एक सम्मेलन है, और शायद यह एक अच्छा विचार है कि मैं उससे चिपके रहूं, जैसा कि मैंने इस पोस्ट की शुरुआत में कहा था।

Quote: मैं अपने कोड को डॉक्यूमेंट करने के लिए doxygen का इस्तेमाल करना चाहूंगा। क्या यह संभव है कि यह स्वचालित रूप से सेमीक के साथ चले और बनाये?

हाँ। यह संभव से अधिक है, यह भयानक है। इस बात पर निर्भर करता है कि आप कैसा फैंस हासिल करना चाहते हैं, इसकी कई संभावनाएं हैं। CMake के पास Doxygen (यानी find_package(Doxygen)) के लिए एक मॉड्यूल है, जो आपको कुछ फ़ाइलों पर Doxygen चलाए जाने वाले लक्ष्यों को पंजीकृत करने की अनुमति देता है। यदि आप अधिक फैंसी चीजें करना चाहते हैं, जैसे कि Doxyfile में संस्करण संख्या को अपडेट करना, या स्रोत फ़ाइलों के लिए दिनांक / लेखक टिकटों को स्वचालित रूप से दर्ज करना और इसी तरह, यह सभी CMake कुंग-फू के साथ संभव है। आम तौर पर, ऐसा करने में शामिल होगा कि आप एक स्रोत Doxyfile (उदाहरण के लिए, "Doxyfile.in" जो कि मैं ऊपर फ़ोल्डर लेआउट में रखता हूं) को रखा जाना है, जिसमें सीएमके के पार्सिंग कमांड को ढूंढना और प्रतिस्थापित करना है। में अपने शीर्ष स्तर CMakeLists फ़ाइल , आप CMake कुंग फू कि cmake-Doxygen एक साथ के साथ कुछ फैंसी काम करता है में से एक इस तरह के टुकड़े मिल जाएगा।


तो main.cppजाना चाहिए trunk/bin?
उगनियस मल्कस

17

परियोजना की संरचना करना

मैं आम तौर पर निम्नलिखित का पक्ष लूंगा:

├── CMakeLists.txt
|
├── docs/
│   └── Doxyfile
|
├── include/
│   └── project/
│       └── vector3.hpp
|
├── src/
    └── project/
        └── vector3.cpp
        └── test/
            └── test_vector3.cpp

इसका मतलब है कि आपके पास अपने पुस्तकालय के लिए एपीआई फाइलों का एक बहुत स्पष्ट रूप से परिभाषित सेट है, और संरचना का मतलब है कि आपके पुस्तकालय के ग्राहक क्या करेंगे

#include "project/vector3.hpp"

कम स्पष्ट के बजाय

#include "vector3.hpp"


मैं / src ट्री की संरचना को पसंद करता / करती हूं जिसमें ट्री शामिल है, लेकिन यह व्यक्तिगत पसंद है। हालाँकि, यदि आपकी परियोजना में / / परियोजना / उप शामिल करने के लिए उपनिर्देशिका सम्‍मिलित है, तो यह आम तौर पर / src वृक्ष के अंदर मेल खाने में मदद करेगा।

परीक्षणों के लिए, मैं उनके द्वारा परीक्षण की गई फ़ाइलों के लिए उन्हें "पास" रखने का पक्ष लेता हूं, और यदि आप / src के भीतर उपनिर्देशिकाओं के साथ अंत करते हैं, तो यह दूसरों के लिए एक बहुत आसान प्रतिमान है कि वे किसी दिए गए फ़ाइल के परीक्षण कोड को खोजना चाहते हैं।


परिक्षण

दूसरी बात मैं अपने कोड के परीक्षण के लिए Google C ++ टेस्टिंग फ्रेमवर्क का उपयोग करना चाहूंगा क्योंकि यह उपयोग करने में काफी आसान लगता है।

Gtest वास्तव में उपयोग करने के लिए सरल है और अपनी क्षमताओं के मामले में काफी व्यापक है। यह अपनी क्षमताओं का विस्तार करने के लिए बहुत आसानी से गमॉक के साथ इस्तेमाल किया जा सकता है, लेकिन गमॉक के साथ मेरे अपने अनुभव कम अनुकूल हैं। मैं यह मानने के लिए काफी तैयार हूं कि यह मेरी अपनी कमियों के कारण हो सकता है, लेकिन gmock परीक्षण बनाने के लिए अधिक कठिन हो जाता है, और बहुत अधिक नाजुक / बनाए रखना मुश्किल होता है। Gmock ताबूत में एक बड़ी कील यह वास्तव में स्मार्ट संकेत के साथ अच्छा नहीं खेलता है।

यह एक बहुत बड़े प्रश्न का बहुत ही तुच्छ और व्यक्तिपरक उत्तर है (जो कि वास्तव में SO पर नहीं है)

क्या आप इसे मेरे कोड से जोड़ने का सुझाव देते हैं, उदाहरण के लिए "inc / gtest" या "contrib / gtest" फ़ोल्डर में? यदि बंडल किया गया है, तो क्या आप संख्या या फ़ाइलों को कम करने के लिए fuse_gtest_files.py स्क्रिप्ट का उपयोग करने का सुझाव देते हैं, या जैसा है वैसा ही छोड़ देते हैं? यदि बंडल नहीं है तो यह निर्भरता कैसे नियंत्रित की जाती है?

मैं CMake के ExternalProject_Addमॉड्यूल का उपयोग करना पसंद करता हूं । यह आपको अपने रिपॉजिटरी में gtest स्रोत कोड रखने, या कहीं भी स्थापित करने से बचा जाता है। यह आपके बिल्ड ट्री में स्वचालित रूप से डाउनलोड और निर्मित होता है।

यहां दिए गए बारीकियों से निपटने के लिए मेरा जवाब देखें ।

जब लेखन परीक्षण की बात आती है, तो ये आम तौर पर कैसे व्यवस्थित होते हैं? मैं प्रत्येक वर्ग (उदाहरण के लिए test_vector3.cpp) के लिए एक cpp फ़ाइल रखने की सोच रहा था, लेकिन सभी एक बाइनरी में संकलित हैं ताकि वे सभी आसानी से एक साथ चल सकें?

अच्छी योजना।


इमारत

मैं सीएमके का प्रशंसक हूं, लेकिन आपके परीक्षण संबंधी सवालों के साथ, एसओ शायद इस तरह के एक विषय पर राय मांगने के लिए सबसे अच्छी जगह नहीं है।

CMakeLists.txt को कैसे देखना होगा ताकि यह या तो सिर्फ पुस्तकालय या पुस्तकालय और परीक्षणों का निर्माण कर सके?

add_library(ProjectLibrary <All library sources and headers>)
add_executable(ProjectTest <All test files>)
target_link_libraries(ProjectTest ProjectLibrary)

लाइब्रेरी एक लक्ष्य "प्रोजेक्टलाइड्स" के रूप में दिखाई देगी, और टेस्ट सूट "प्रोजेक्टटेस्ट" के रूप में। लाइब्रेरी को टेस्ट एक्स की निर्भरता के रूप में निर्दिष्ट करके, टेस्ट एक्स का निर्माण स्वचालित रूप से लाइब्रेरी को फिर से बनाने का कारण होगा अगर यह तारीख से बाहर है।

इसके अलावा, मैंने काफी कुछ प्रोजेक्ट्स देखे हैं जिनमें बिल्ड एड बिन डायरेक्टरी है। क्या बिल्ड बिल्ड निर्देशिका में होता है और फिर बायनेरिज़ बिन निर्देशिका में चले गए? क्या परीक्षण और पुस्तकालय के लिए बायनेरिज़ एक ही जगह पर रहेंगे?

CMake "आउट-ऑफ-सोर्स" बिल्ड की सिफारिश करता है, यानी आप प्रोजेक्ट के बाहर अपनी खुद की बिल्ड डायरेक्टरी बनाते हैं और वहां से CMake चलाते हैं। बिल्ड फ़ाइलों के साथ अपने स्रोत के पेड़ को "प्रदूषित" करने से बचता है, और यदि आप वीसीएस का उपयोग कर रहे हैं तो यह बहुत वांछनीय है।

आप यह निर्दिष्ट कर सकते हैं कि बायनेरिज़ को एक बार निर्मित या अलग निर्देशिका में कॉपी किया गया है, या वे किसी अन्य निर्देशिका में डिफ़ॉल्ट रूप से बनाए गए हैं, लेकिन आमतौर पर इसकी कोई आवश्यकता नहीं है। सीएमके अपनी परियोजना को स्थापित करने के लिए व्यापक तरीके प्रदान करता है यदि वांछित है, या अन्य सीएमके परियोजनाओं के लिए अपनी परियोजना की प्रासंगिक फाइलों को "ढूंढना" आसान बना देता है।

Gtest परीक्षणों को खोजने और निष्पादित करने के लिए CMake के स्वयं के समर्थन के संबंध में , यह काफी हद तक अनुचित होगा यदि आप अपने प्रोजेक्ट के हिस्से के रूप में gtest का निर्माण करते हैं। FindGtestमॉड्यूल वास्तव में इस मामले में जहां gtest अलग से अपनी परियोजना के बाहर बनाया गया है में इस्तेमाल किया जा बनाया गया है।

CMake अपना स्वयं का परीक्षण ढांचा (CTest) प्रदान करता है, और आदर्श रूप से, प्रत्येक gtest मामले को CTest मामले के रूप में जोड़ा जाएगा।

हालाँकि, सबसे GTEST_ADD_TESTSबड़े FindGtestमामलों के रूप में आसान cestest मामलों की अनुमति देने के लिए प्रदान की गई मैक्रो कुछ हद तक इस बात की कमी है कि यह gtest के मैक्रो के अलावा TESTऔर किसी के लिए काम नहीं करती है TEST_FValue- या टाइप parameterised परीक्षण का उपयोग TEST_P, TYPED_TEST_Pआदि सब पर नियंत्रित नहीं कर रहे हैं।

समस्या का एक आसान समाधान नहीं है जो मुझे पता है। Gtest मामलों की एक सूची प्राप्त करने का सबसे मजबूत तरीका ध्वज के साथ परीक्षण exe को निष्पादित करना है --gtest_list_tests। हालाँकि, यह केवल एक बार exe बनने के बाद किया जा सकता है, इसलिए CMake इसका उपयोग नहीं कर सकता है। जो आपको दो विकल्पों के साथ छोड़ देता है; सीएमके को परीक्षणों के नामों को कम करने के लिए C ++ कोड को पार्स करने की कोशिश करनी चाहिए (यदि आप सभी gtest macros, टिप्पणी-आउट परीक्षण, अक्षम परीक्षण) या परीक्षण के मामलों को हाथ में लेना चाहते हैं, तो चरम पर (गैर-तुच्छ)। CMakeLists.txt फ़ाइल।

मैं अपने कोड के दस्तावेज़ के लिए doxygen का उपयोग करना चाहूंगा। क्या यह संभव है कि यह स्वचालित रूप से सेमीक के साथ चले और बनाये?

हां - हालांकि मुझे इस मोर्चे पर कोई अनुभव नहीं है। CMake FindDoxygenइस उद्देश्य के लिए प्रदान करता है ।


6

अन्य (उत्कृष्ट) उत्तरों के अलावा, मैं एक संरचना का वर्णन करने जा रहा हूं जो मैं अपेक्षाकृत बड़े पैमाने पर परियोजनाओं के लिए उपयोग कर रहा हूं ।
मैं Doxygen के बारे में उपकथा को संबोधित नहीं करने जा रहा हूं, क्योंकि मैं अन्य उत्तरों में कही गई बातों को दोहराऊंगा।


दलील

प्रतिरूपकता और स्थिरता के लिए, परियोजना को छोटी इकाइयों के एक सेट के रूप में आयोजित किया जाता है। स्पष्टता के लिए, उन्हें एक्स = ए, बी, सी, ... के साथ यूनिटएक्स नाम दें, (लेकिन उनका कोई भी सामान्य नाम हो सकता है)। यदि आवश्यक हो तो समूह इकाइयों की संभावना के साथ इस संरचना को दर्शाने के लिए निर्देशिका संरचना का आयोजन किया जाता है।

उपाय

मूल निर्देशिका लेआउट निम्नलिखित है (इकाइयों की सामग्री बाद में विस्तृत है):

project
├── CMakeLists.txt
├── UnitA
├── UnitB
├── GroupA
   └── CMakeLists.txt
   └── GroupB
       └── CMakeLists.txt
       └── UnitC
       └── UnitD
   └── UnitE

project/CMakeLists.txt निम्नलिखित शामिल हो सकते हैं:

cmake_minimum_required(VERSION 3.0.2)
project(project)
enable_testing() # This will be necessary for testing (details below)

add_subdirectory(UnitA)
add_subdirectory(UnitB)
add_subdirectory(GroupA)

और project/GroupA/CMakeLists.txt:

add_subdirectory(GroupB)
add_subdirectory(UnitE)

और project/GroupB/CMakeLists.txt:

add_subdirectory(UnitC)
add_subdirectory(UnitD)

अब विभिन्न इकाइयों की संरचना (उदाहरण के लिए, UnitD को लें)

project/GroupA/GroupB/UnitD
├── README.md
├── CMakeLists.txt
├── lib
   └── CMakeLists.txt
   └── UnitD
       └── ClassA.h
       └── ClassA.cpp
       └── ClassB.h
       └── ClassB.cpp
├── test
   └── CMakeLists.txt
   └── ClassATest.cpp
   └── ClassBTest.cpp
   └── [main.cpp]

विभिन्न घटकों के लिए:

  • मुझे एक ही फ़ोल्डर में स्रोत ( .cpp) और हेडर ( .h) होना पसंद है। यह एक डुप्लिकेट निर्देशिका पदानुक्रम से बचा जाता है, रखरखाव आसान बनाता है। स्थापना के लिए, हेडर फ़ाइलों को केवल फ़िल्टर करने के लिए यह (विशेष रूप से सीएमके के साथ) कोई समस्या नहीं है।
  • निर्देशिका की भूमिका UnitDबाद की फाइलों सहित अनुमति देने पर है #include <UnitD/ClassA.h>। इसके अलावा, इस इकाई को स्थापित करते समय, आप केवल निर्देशिका संरचना की प्रतिलिपि बना सकते हैं। ध्यान दें कि आप अपनी स्रोत फ़ाइलों को उपनिर्देशिकाओं में भी व्यवस्थित कर सकते हैं।
  • मुझे READMEयह बताने के लिए एक फ़ाइल पसंद है कि यूनिट क्या है और इसके बारे में उपयोगी जानकारी निर्दिष्ट करें।
  • CMakeLists.txt बस शामिल हो सकते हैं:

    add_subdirectory(lib)
    add_subdirectory(test)
  • lib/CMakeLists.txt:

    project(UnitD)
    
    set(headers
        UnitD/ClassA.h
        UnitD/ClassB.h
        )
    
    set(sources
        UnitD/ClassA.cpp
        UnitD/ClassB.cpp    
        )
    
    add_library(${TARGET_NAME} STATIC ${headers} ${sources})
    
    # INSTALL_INTERFACE: folder to which you will install a directory UnitD containing the headers
    target_include_directories(UnitD
                               PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                               PUBLIC $<INSTALL_INTERFACE:include/SomeDir>
                               )
    
    target_link_libraries(UnitD
                          PUBLIC UnitA
                          PRIVATE UnitC
                          )

    यहां, ध्यान दें कि CMake को यह बताने के लिए आवश्यक नहीं है कि हम उन निर्देशिकाओं को शामिल करना चाहते हैं UnitAऔर UnitC, क्योंकि यह उन इकाइयों को कॉन्फ़िगर करते समय पहले से ही निर्दिष्ट था। इसके अलावा, PUBLICसभी लक्ष्य बताएंगे जो इस बात पर निर्भर करते हैं UnitDकि उन्हें स्वचालित रूप से UnitAनिर्भरता को शामिल करना चाहिए , जबकि UnitCतब ( PRIVATE) की आवश्यकता नहीं होगी ।

  • test/CMakeLists.txt (यदि आप इसके लिए GTest का उपयोग करना चाहते हैं तो नीचे देखें):

    project(UnitDTests)
    
    add_executable(UnitDTests
                   ClassATest.cpp
                   ClassBTest.cpp
                   [main.cpp]
                   )
    
    target_link_libraries(UnitDTests
                          PUBLIC UnitD
    )
    
    add_test(
            NAME UnitDTests
            COMMAND UnitDTests
    )

GoogleTest का उपयोग करना

Google टेस्ट के लिए, सबसे आसान यह है कि इसका स्रोत आपके स्रोत निर्देशिका में कहीं मौजूद है, लेकिन आपको वास्तव में इसे स्वयं वहां जोड़ने की आवश्यकता नहीं है। मैं इसे स्वचालित रूप से डाउनलोड करने के लिए इस परियोजना का उपयोग कर रहा हूं , और मैं एक फ़ंक्शन में इसके उपयोग को सुनिश्चित करने के लिए लपेटता हूं कि यह केवल एक बार डाउनलोड किया जाता है, भले ही हमारे पास कई परीक्षण लक्ष्य हों।

यह सीएमके समारोह निम्नलिखित है:

function(import_gtest)
  include (DownloadProject)
  if (NOT TARGET gmock_main)
    include(DownloadProject)
    download_project(PROJ                googletest
                     GIT_REPOSITORY      https://github.com/google/googletest.git
                     GIT_TAG             release-1.8.0
                     UPDATE_DISCONNECTED 1
                     )
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Prevent GoogleTest from overriding our compiler/linker options when building with Visual Studio
    add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL)
  endif()
endfunction()

और फिर, जब मैं अपने एक परीक्षण लक्ष्य के अंदर इसका उपयोग करना चाहता हूं, तो मैं निम्नलिखित पंक्तियों को इसमें जोड़ दूंगा CMakeLists.txt(यह ऊपर दिए गए उदाहरण के लिए है test/CMakeLists.txt):

import_gtest()
target_link_libraries(UnitDTests gtest_main gmock_main)

अच्छा "हैक" आप Gtest और cmake के साथ वहाँ किया था! उपयोगी! :)
तानियाँ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.