छोटी नेटिव Mac यूटिलिटी के लिए, जब कोड एक्ज़ीक्यूटेबल के साथ शिप होता है, तो स्टैटिक लाइब्रेरी से शुरुआत करें। अलग रनटाइम कंपोनेंट उपयोगी हो तो डायनामिक लाइब्रेरी चुनें, और Apple की बंडल पैकेजिंग चाहिए तो फ़्रेमवर्क चुनें। यह गाइड एक ही C++ हेल्पर को दोनों तरीकों से बनाती है।
ठोस काम एक लोकल macOS डेस्कटॉप यूटिलिटी के लिए दोबारा इस्तेमाल होने वाली ग्रिड गणना को पैकेज करना है। उदाहरण में कृत्रिम इनपुट और टर्मिनल कमांड इस्तेमाल किए गए हैं, ताकि ऐप प्रोजेक्ट के लिए फ़ॉर्मैट चुनने से पहले आप कंपाइलेशन, लिंकिंग और लोडिंग को अलग-अलग देख सकें।
आप असल में क्या चुन रहे हैं?
यहाँ दो फ़ैसले हैं: कोड कैसे लिंक होगा और उसका डिलिवरेबल कैसे पैकेज होगा। स्टैटिक लाइब्रेरी, जिसका नाम आम तौर पर .a पर खत्म होता है, ऑब्जेक्ट फ़ाइलों का एक आर्काइव होती है। डायनामिक लाइब्रेरी, जिसका नाम .dylib पर खत्म होता है, एक अलग रनटाइम इमेज के रूप में रहती है। फ़्रेमवर्क एक बंडल है, जो लाइब्रेरी और उससे जुड़ी सामग्री को व्यवस्थित करता है। Apple इन मूल फ़ॉर्मैट का वर्णन An Apple Library Primer में करता है।
फ़्रेमवर्क में स्टैटिक लाइब्रेरी भी हो सकती है। Apple Xcode 15 और उसके बाद के संस्करणों में स्टैटिक फ़्रेमवर्क बनाने का समर्थन करता है, जिनमें संसाधन भी शामिल हैं। इसलिए केवल .framework प्रत्यय से यह तय नहीं होता कि लाइब्रेरी स्टैटिक रूप से लिंक होती है या डायनामिक रूप से। Creating a static framework देखें।
सोर्स से बनाए गए ऐप के अपने हेल्पर के लिए यह निर्णय-सहायक इस्तेमाल करें:
| आपकी ज़रूरत | शुरुआती विकल्प | इससे जुड़ी ज़िम्मेदारी |
|---|---|---|
| एक एक्ज़ीक्यूटेबल और साथ में दोबारा बनाया जाने वाला हेल्पर | स्टैटिक .a |
हेल्पर बदलने पर एक्ज़ीक्यूटेबल को फिर से लिंक करें |
| ऐसा हेल्पर जो अलग रनटाइम कंपोनेंट बना रहे | डायनामिक .dylib |
लाइब्रेरी उपलब्ध कराएँ और रनटाइम लुकअप पाथ सही रखें |
| सार्वजनिक हेडर और बंडल किए गए संसाधनों वाला Xcode कंपोनेंट | फ़्रेमवर्क | इसकी लिंकिंग अलग से चुनें और टारगेट मेंबरशिप कॉन्फ़िगर करें |
| कई Apple प्लेटफ़ॉर्म के लिए बाइनरी वेरिएंट | XCFramework | ज़रूरी प्लेटफ़ॉर्म वेरिएंट बनाएँ और पैकेज करें |
ये दस्तावेज़ीकृत फ़ॉर्मैट से निकली व्यावहारिक सिफ़ारिशें हैं। इनसे यह तय नहीं होता कि प्रदर्शन के लिहाज़ से कौन बेहतर है। इस ग्रिड यूटिलिटी के लिए स्टैटिक आर्काइव स्वाभाविक शुरुआती विकल्प है: इसका एक ही क्लाइंट है, कोई संसाधन बंडल नहीं है और अलग लाइब्रेरी को बदलने की ज़रूरत नहीं है।
XCFramework बाइनरी वेरिएंट पैकेज करता है; यह स्टैटिक और डायनामिक के बीच के फ़ैसले की जगह नहीं लेता। Apple इस वितरण फ़ॉर्मैट में स्टैटिक या डायनामिक फ़्रेमवर्क और लाइब्रेरी, दोनों की अनुमति देता है। Creating a multiplatform binary framework bundle देखें।
कंटेनर चुनने से पहले संगतता जाँचें
फ़ाइल का नाम या CPU आर्किटेक्चर मेल खाना काफ़ी नहीं है। macOS, Mac Catalyst और iOS अलग-अलग बिल्ड प्लेटफ़ॉर्म हैं। किसी एक प्लेटफ़ॉर्म के लिए कंपाइल की गई arm64 लाइब्रेरी को दूसरे प्लेटफ़ॉर्म के एक्ज़ीक्यूटेबल से सीधे लिंक नहीं किया जा सकता। Apple An Apple Library Primer में प्लेटफ़ॉर्म और आर्किटेक्चर के बीच का स्पष्ट अंतर बताता है।
इस काम के लिए ये इनपुट एक-दूसरे के अनुरूप रखें: macOS टारगेट, ज़रूरी CPU आर्किटेक्चर, macOS का न्यूनतम संस्करण और भाषा/रनटाइम की संगत सेटिंग्स। यूनिवर्सल Mac डिलिवरेबल के लिए हर समर्थित आर्किटेक्चर की निर्भरता में मेल खाने वाले स्लाइस चाहिए। Apple की Building a universal macOS binary गाइड में लाइब्रेरी और एक्ज़ीक्यूटेबल, दोनों को फिर से बनाने की जानकारी है।
C++ संगतता पर भी अलग से ध्यान दें। -std=c++17 चुनने से भाषा का मोड तय होता है; इससे अलग-अलग बनाए गए कंपोनेंट के बीच बाइनरी संगतता प्रमाणित नहीं होती। LLVM, libc++ के ABI गारंटी और वेंडर कॉन्फ़िगरेशन विकल्पों का दस्तावेज़ देता है, जिनमें कुछ सेटिंग्स टाइप के लेआउट बदलती हैं। ऐप की अपनी निर्भरता के लिए, ऐप के टूलचेन और एक-जैसी सेटिंग्स से उसे फिर से बनाना एक उपयोगी डिफ़ॉल्ट है। libc++’s ABI Guarantees देखें।
उदाहरण में एक फ़ंक्शन को C-संगत हेडर के ज़रिए उपलब्ध कराया गया है, जबकि उसका कार्यान्वयन C++ में है। गार्ड किया गया extern \"C\" डिक्लेरेशन C++ कंपाइलेशन के लिए C भाषा लिंकिंग निर्दिष्ट करता है। इससे मनमानी C++ क्लासें C इंटरफ़ेस में नहीं बदलतीं और न ही बाइनरी प्लेटफ़ॉर्मों के बीच पोर्टेबल बनती है। C++ वर्किंग ड्राफ़्ट Linkage specifications में इस सीमा को परिभाषित करता है।
एक हेल्पर को स्टैटिक और डायनामिक, दोनों लाइब्रेरी के रूप में बनाएँ
आपको Apple के डेवलपर टूल और macOS SDK वाला Mac चाहिए। पूर्ण Xcode में कमांड-लाइन टूल शामिल होते हैं; Apple अलग से Command Line Tools पैकेज भी देता है। अगर इनमें से कुछ भी इंस्टॉल नहीं है, तो xcode-select --install चलाएँ और आगे बढ़ने से पहले इंस्टॉलर पूरा करें। Installing the command-line tools देखें।
नीचे दी गई पूरी स्क्रिप्ट एक नई अस्थायी डायरेक्टरी बनाती है और केवल कृत्रिम सोर्स फ़ाइलें इस्तेमाल करती है। इसे Terminal में चलाएँ। यह मौजूदा शेल वातावरण के आर्किटेक्चर और उदाहरण के तौर पर macOS 11.0 को डिप्लॉयमेंट का न्यूनतम संस्करण मानकर स्पष्ट रूप से टारगेट करती है। यह एक आर्किटेक्चर का अभ्यास है; आपके प्रोजेक्ट को दूसरा न्यूनतम संस्करण चाहिए तो उसे इस्तेमाल करें।
मैंने उदाहरण का निरीक्षण किया, लेकिन Mac बिल्ड चला नहीं सका: रिसर्च वातावरण Linux पर चलता है और इसमें clang++ नहीं है। दिखाए गए नतीजे अपेक्षित नतीजे हैं, दर्ज किया गया टेस्ट आउटपुट नहीं।
bash <<'SH'
set -eu
work_dir=$(mktemp -d "${TMPDIR:-/tmp}/mac-library-demo.XXXXXX")
cd "$work_dir"
printf 'Example directory: %s\n' "$work_dir"
mkdir include src build
mkdir -p build/static build/dynamic/lib
cat > include/grid.h <<'EOF'
#ifndef GRID_H
#define GRID_H
#ifdef __cplusplus
extern "C" {
#endif
/* Inputs: positive dimensions whose product fits in int. */
int grid_cells(int rows, int columns);
#ifdef __cplusplus
}
#endif
#endif
EOF
cat > src/grid.cpp <<'EOF'
#include "grid.h"
int grid_cells(int rows, int columns) {
return rows * columns;
}
EOF
cat > src/main.cpp <<'EOF'
#include "grid.h"
#include <cstdio>
int main() {
const int cells = grid_cells(3, 4);
std::printf("Grid cells: %d\n", cells);
return cells == 12 ? 0 : 1;
}
EOF
sdk_path=$(xcrun --sdk macosx --show-sdk-path)
build_arch=$(uname -m)
case "$build_arch" in
arm64|x86_64) ;;
*) printf 'Unexpected architecture: %s\n' "$build_arch" >&2; exit 1 ;;
esac
xcrun --sdk macosx clang++ \
-std=c++17 -arch "$build_arch" -isysroot "$sdk_path" \
-mmacosx-version-min=11.0 -Iinclude \
-c src/grid.cpp -o build/grid.o
xcrun --sdk macosx ar rcs build/static/libGrid.a build/grid.o
xcrun --sdk macosx clang++ \
-std=c++17 -arch "$build_arch" -isysroot "$sdk_path" \
-mmacosx-version-min=11.0 -Iinclude \
src/main.cpp build/static/libGrid.a -o build/static/grid-demo
xcrun --sdk macosx clang++ \
-arch "$build_arch" -isysroot "$sdk_path" \
-mmacosx-version-min=11.0 -dynamiclib build/grid.o \
-Wl,-install_name,@rpath/libGrid.dylib \
-o build/dynamic/lib/libGrid.dylib
xcrun --sdk macosx clang++ \
-std=c++17 -arch "$build_arch" -isysroot "$sdk_path" \
-mmacosx-version-min=11.0 -Iinclude \
src/main.cpp build/dynamic/lib/libGrid.dylib \
-Wl,-rpath,@loader_path/lib -o build/dynamic/grid-demo
./build/static/grid-demo
./build/dynamic/grid-demo
xcrun --sdk macosx ar t build/static/libGrid.a
xcrun --sdk macosx otool -L build/static/grid-demo
xcrun --sdk macosx otool -L build/dynamic/grid-demo
xcrun --sdk macosx otool -l build/dynamic/grid-demo
xcrun --sdk macosx lipo -archs build/dynamic/grid-demo
SH
दोनों बार चलाने पर Grid cells: 12 प्रिंट होना चाहिए। यह गणना जानबूझकर छोटी रखी गई है: तीन पंक्तियाँ गुणा चार कॉलम। हेल्पर के घोषित इनपुट अनुबंध में ऐसे आयाम शामिल नहीं हैं जिनका गुणनफल int में न समा सके; यह उदाहरण सामान्य इनपुट-वैलिडेशन API लागू नहीं करता।
स्टैटिक बिल्ड क्या दिखाता है
-c वाला कमांड grid.o बनाता है, एक्ज़ीक्यूटेबल को लिंक किए बिना। आर्काइव कमांड उस ऑब्जेक्ट को libGrid.a में पैकेज करता है; इसके r, c और s विकल्प इंसर्शन/रिप्लेसमेंट, आर्काइव बनाने और सिंबल इंडेक्स बनाने का अनुरोध करते हैं। Clang कंपाइलेशन के चरणों का वर्णन अपनी compiler command guide में करता है, और LLVM आर्काइव ऑपरेशनों की जानकारी llvm-ar — LLVM archiver में देता है।
क्लाइंट, आर्काइव के स्पष्ट पाथ का इस्तेमाल करके लिंक होता है। इससे दोनों स्टैटिक और डायनामिक वर्ज़न मौजूद होने पर चुना गया इनपुट स्पष्ट रहता है। अपेक्षित आर्काइव लिस्टिंग में grid.o होना चाहिए, जबकि एक्ज़ीक्यूटेबल की डिपेंडेंसी लिस्टिंग में libGrid.a पर रनटाइम निर्भरता नहीं होनी चाहिए।
यहाँ स्टैटिक लिंकिंग केवल Grid हेल्पर पर लागू होती है। इसका अर्थ यह नहीं कि बना हुआ एक्ज़ीक्यूटेबल macOS की रनटाइम लाइब्रेरी पर निर्भर नहीं है; otool -L की बाकी प्रविष्टियाँ देखें। Apple An Apple Library Primer में बिल्ड-टाइम लिंकर और रनटाइम लोडर के बीच अंतर बताता है।
डायनामिक बिल्ड क्या दिखाता है
-dynamiclib वाला कमांड एक अलग लाइब्रेरी बनाता है। इसका इंस्टॉल नेम @rpath/libGrid.dylib है। एक्ज़ीक्यूटेबल का रिकॉर्ड किया हुआ रन पाथ @loader_path/lib है, जो इस एक्ज़ीक्यूटेबल की रन-पाथ प्रविष्टि के लिए पास की lib डायरेक्टरी की ओर इशारा करता है। Clang अपने command-line argument reference में संबंधित ड्राइवर विकल्प सूचीबद्ध करता है।
डायनामिक क्लाइंट के लिए अपेक्षित otool -L आउटपुट में @rpath/libGrid.dylib शामिल होता है; लोड-कमांड आउटपुट में @loader_path/lib के साथ एक LC_RPATH प्रविष्टि होनी चाहिए। Apple Run-Path Dependent Libraries में बताता है कि इंस्टॉल नेम और एक्ज़ीक्यूटेबल के रन पाथ साथ मिलकर कैसे काम करते हैं।
इससे एक ठोस पैकेजिंग टेस्ट मिलता है: पूरी build/dynamic डायरेक्टरी को साथ में किसी दूसरी जगह ले जाएँ, उसका आंतरिक लेआउट जस का तस रखें, फिर नई जगह से उसका एक्ज़ीक्यूटेबल चलाएँ। केवल एक्ज़ीक्यूटेबल ले जाने पर Grid की निर्भरता पीछे छूट जाएगी। लिंक सफल होना यह दिखाता है कि बिल्ड इनपुट मिल गए; इससे यह साबित नहीं होता कि रनटाइम पैकेज पूरा है।
हेल्पर को फ़्रेमवर्क कब बनाना चाहिए?
जब फ़्रेमवर्क की बंडल संरचना प्रोजेक्ट के काम आए, तो उसे चुनें—उदाहरण के लिए, जब कंपोनेंट को Xcode में एक साथ प्रबंधित किए जाने वाले सार्वजनिक हेडर और संसाधनों की ज़रूरत हो। केवल गणना करने वाले इस हेल्पर के लिए, कोई अतिरिक्त ज़रूरत पूरी किए बिना फ़्रेमवर्क पैकेजिंग का काम बढ़ाता है।
Xcode 15 या उसके बाद के संस्करण में स्टैटिक फ़्रेमवर्क के लिए Apple यह क्रम बताता है:
File > New > Targetचुनें, macOS चुनें औरFrameworkटेम्पलेट इस्तेमाल करें।- नए टारगेट की
Build Settingsचुनें औरMach-O TypeकोStatic Libraryमें बदलें। - कार्यान्वयन फ़ाइलें
Compile Sourcesमें और संसाधनCopy Bundle Resourcesमें जोड़ें। - एक्सपोर्ट किए गए हेडर
Headersबिल्ड फ़ेज़ में जोड़ें और सार्वजनिक हेडर अम्ब्रेला हेडर में शामिल करें। - फ़्रेमवर्क को इस्तेमाल करने वाले ऐप में जोड़ें और Apple के दस्तावेज़ीकृत एम्बेडिंग कॉन्फ़िगरेशन का पालन करें।
संसाधन वाले स्टैटिक फ़्रेमवर्क के लिए Apple Embed & Sign निर्दिष्ट करता है। Xcode पहले से लिंक किए गए मुख्य बाइनरी को एम्बेड किए गए बंडल से हटा देता है और बंडल की सामग्री बनाए रखता है। हर स्टैटिक फ़्रेमवर्क को ऐसा आर्काइव न मानें जिसे एम्बेड करने की ज़रूरत नहीं; इसके बजाय Creating a static framework का पालन करें।
अगर आप बाइनरी वेरिएंट वितरित करते हैं, तो XCFramework इस्तेमाल करें। मौजूदा स्टैंडअलोन .a के लिए Apple xcodebuild -create-xcframework में -library और -headers इनपुट का दस्तावेज़ देता है; वह आर्काइव को हाथ से फ़्रेमवर्क का रूप देने की सलाह नहीं देता। xcodebuild के लिए पूर्ण Xcode ज़रूरी है। Creating a multiplatform binary framework bundle और Installing the command-line tools देखें।
Swift से फ़ैसला कैसे बदलता है?
Swift, मूल लिंकिंग फ़ैसले में मॉड्यूल इस्तेमाल और इवोल्यूशन की ज़रूरतें जोड़ता है। ABI स्थिरता, मॉड्यूल स्थिरता और लाइब्रेरी इवोल्यूशन अलग-अलग संगतता चिंताओं का समाधान करते हैं; .framework कंटेनर इन्हें अपने आप सक्षम नहीं करता।
Swift के मार्गदर्शन के अनुसार, क्लाइंट के साथ बनाए और वितरित किए जाने वाले फ़्रेमवर्क में आम तौर पर लाइब्रेरी इवोल्यूशन सक्षम नहीं करना चाहिए। अलग से बनाए और अपडेट किए जाने वाले बाइनरी फ़्रेमवर्क के लिए, पहली रिलीज़ से ही इवोल्यूशन समर्थन का फ़ैसला करें। Xcode में BUILD_LIBRARY_FOR_DISTRIBUTION लाइब्रेरी इवोल्यूशन और मॉड्यूल स्थिरता सक्षम करता है। इन विकल्पों से प्रदर्शन की विशेषताएँ और API में अनुमत बदलाव प्रभावित होते हैं, जैसा कि Library Evolution in Swift में बताया गया है।
इन गारंटियों को उनके दस्तावेज़ीकृत प्लेटफ़ॉर्म दायरे में रखें। Swift का दस्तावेज़ Apple प्लेटफ़ॉर्म पर कंपाइलरों के बीच बाइनरी संगतता का वर्णन करता है; इस वादे को Swift के हर समर्थित प्लेटफ़ॉर्म तक न फैलाएँ और इसे मनमाने API बदलावों की अनुमति न समझें। इस उदाहरण का C-संगत हेडर भी पूरा Swift मॉड्यूल सेटअप नहीं है।
बिल्ड विफल होने पर उसी क्रम में समस्या का पता लगाएँ
विफलता का चरण देखकर अगली जाँच चुनें:
| समस्या | पहली जाँच | अगला कदम |
|---|---|---|
कंपाइलर को grid.h नहीं मिल रहा |
हेडर include के अंदर मौजूद है |
वर्किंग डायरेक्टरी और -Iinclude जाँचें |
| Grid फ़ंक्शन का रेफ़रेंस अनडिफ़ाइंड है | कार्यान्वयन और डिक्लेरेशन मेल खाते हैं | पुष्टि करें कि लिंक कमांड में अपेक्षित आर्काइव या डायनामिक लाइब्रेरी मौजूद है |
| आर्किटेक्चर या प्लेटफ़ॉर्म मेल नहीं खाता | निर्भरता के वास्तविक स्लाइस और बिल्ड प्लेटफ़ॉर्म | क्लाइंट के macOS टारगेट के लिए निर्भरता फिर से बनाएँ |
| एक्ज़ीक्यूटेबल लिंक होता है, लेकिन Grid लोड नहीं कर पाता | रिकॉर्ड की गई निर्भरता और रन पाथ | otool -L, otool -l और पैकेज की डायरेक्टरी संरचना की तुलना करें |
| Intel और Apple silicon बिल्ड अलग तरह से चलते हैं | हर निर्भरता का आर्किटेक्चर कवरेज | हर बाइनरी का निरीक्षण करें और अपेक्षित आर्किटेक्चर पर टेस्ट करें |
Clang की compiler command guide कंपाइलेशन और लिंकिंग को अलग-अलग रखती है। Apple की Run-Path Dependent Libraries रनटाइम रिज़ॉल्यूशन समझाती है, जबकि Building a universal macOS binary lipo -archs से आर्किटेक्चर जाँचने का तरीका बताती है। इन भेदों से समस्या-निवारण का क्रम उपयोगी बनता है: हेडर पाथ बदलने से रनटाइम लाइब्रेरी की कमी दूर नहीं होती।
इस कृत्रिम ग्रिड यूटिलिटी के लिए, किसी ठोस पैकेजिंग या रनटाइम ज़रूरत के आने तक स्टैटिक आर्काइव रखें। ऐसी ज़रूरत सामने आए तो उसी छोटी गणना को बिल्ड चेक के रूप में बनाए रखें, फिर चुनी गई लाइब्रेरी, टारगेट संगतता और अंतिम डायरेक्टरी लेआउट को अलग-अलग जाँचें।
स्रोत
- An Apple Library Primer — Apple Developer Technical Support.
- Creating a static framework — Apple.
- Creating a multiplatform binary framework bundle — Apple.
- Building a universal macOS binary — Apple.
- Installing the command-line tools — Apple.
- Run-Path Dependent Libraries — Apple Documentation Archive.
- clang — the Clang C, C++, and Objective-C compiler — LLVM Project.
- Clang command line argument reference — LLVM Project.
- llvm-ar — LLVM archiver — LLVM Project.
- libc++’s ABI Guarantees — LLVM Project.
- Linkage specifications — C++ Working Draft, rendered by eel.is.
- Library Evolution in Swift — Swift.org.