Untuk utiliti Mac natif yang kecil, mulakan dengan pustaka statik apabila kod dihantar bersama fail boleh laksana. Pilih pustaka dinamik apabila komponen masa jalan yang berasingan berguna, dan rangka kerja apabila anda memerlukan pembungkusan bundle Apple. Panduan ini membina pembantu C++ yang sama dengan kedua-dua cara.

Tugas khususnya ialah membungkus pengiraan grid yang boleh digunakan semula untuk utiliti desktop macOS setempat. Contoh ini menggunakan input sintetik dan arahan terminal supaya anda dapat melihat kompilasi, pemautan dan pemuatan secara berasingan sebelum memilih format untuk projek aplikasi.

Apakah sebenarnya yang anda pilih?

Terdapat dua keputusan: cara kod dipautkan dan cara hasilnya dibungkus. Pustaka statik, yang biasanya berakhir dengan .a, ialah arkib fail objek. Pustaka dinamik, yang berakhir dengan .dylib, kekal sebagai imej masa jalan yang berasingan. Rangka kerja ialah bundle yang menyusun pustaka dan bahan berkaitan. Apple menerangkan format asas ini dalam Pengenalan kepada Pustaka Apple.

Rangka kerja juga boleh mengandungi pustaka statik. Apple menyokong penciptaan rangka kerja statik dalam Xcode 15 dan versi lebih baharu, termasuk sumber. Oleh itu, akhiran .framework sahaja tidak menentukan sama ada pustaka dipautkan secara statik atau dinamik. Lihat Mencipta rangka kerja statik.

Gunakan panduan keputusan ini untuk pembantu milik aplikasi yang anda bina daripada kod sumber:

Keperluan anda Pilihan awal Tanggungjawab yang timbul
Satu fail boleh laksana dan pembantu dibina semula bersama-sama Statik .a Pautkan semula fail boleh laksana apabila pembantu berubah
Pembantu yang kekal sebagai komponen masa jalan berasingan Dinamik .dylib Sediakan pustaka dan laluan carian masa jalan yang berfungsi
Komponen Xcode dengan pengepala awam dan sumber yang dibundel Rangka kerja Pilih pemautannya secara berasingan dan konfigurasikan keahlian sasaran
Varian binari untuk berbilang platform Apple XCFramework Bina dan bungkus varian platform yang diperlukan

Ini ialah cadangan praktikal berdasarkan format yang didokumenkan. Cadangan ini tidak menentukan pilihan yang paling pantas. Untuk utiliti grid ini, arkib statik ialah pilihan awal yang paling sesuai: hanya ada satu klien, tiada bundle sumber dan tiada keperluan untuk menggantikan pustaka berasingan.

XCFramework membungkus varian binari; ia tidak menggantikan keputusan antara statik dengan dinamik. Apple membenarkan rangka kerja dan pustaka statik atau dinamik dalam format pengedaran tersebut. Lihat Mencipta bundle rangka kerja binari berbilang platform.

Semak keserasian sebelum memilih bekas

Nama fail atau seni bina CPU yang sepadan sahaja tidak mencukupi. macOS, Mac Catalyst dan iOS ialah platform binaan yang berbeza. Pustaka arm64 yang dikompilasi untuk satu platform tidak boleh dipautkan begitu sahaja ke dalam fail boleh laksana platform lain. Apple membezakan platform daripada seni bina secara jelas dalam Pengenalan kepada Pustaka Apple.

Untuk tugas ini, pastikan input berikut konsisten: sasaran macOS, seni bina CPU yang diperlukan, versi minimum macOS dan tetapan bahasa/masa jalan yang serasi. Hasil Mac universal memerlukan kepingan kebergantungan yang sepadan bagi setiap seni bina yang disokong. Membina binari macOS universal oleh Apple menerangkan pembinaan semula pustaka serta fail boleh laksana.

Keserasian C++ memerlukan perhatian tersendiri. Memilih -std=c++17 menetapkan mod bahasa; ia tidak menjamin keserasian binari antara komponen yang dibina secara berasingan. LLVM mendokumenkan jaminan ABI dan pilihan konfigurasi vendor untuk libc++, termasuk tetapan yang mengubah susun atur jenis. Untuk kebergantungan milik aplikasi, pilihan lalai yang berguna ialah membinanya semula dengan rantaian alat aplikasi dan tetapan yang konsisten. Lihat Jaminan ABI libc++.

Contoh ini mendedahkan satu fungsi melalui pengepala yang serasi dengan C, sambil melaksanakannya dalam C++. Pengisytiharan extern \"C\" yang dilindungi menetapkan pautan bahasa C untuk kompilasi C++. Ia tidak menukar kelas C++ sewenang-wenangnya menjadi antara muka C atau menjadikan binari mudah alih merentas platform. Draf kerja C++ mentakrifkan sempadan ini dalam Spesifikasi pemautan.

Bina satu pembantu sebagai pustaka statik dan dinamik

Anda memerlukan Mac dengan alat pembangun Apple dan SDK macOS. Xcode penuh merangkumi alat baris perintah; Apple turut menyediakan pakej Alat Baris Perintah yang berasingan. Jika kedua-duanya belum dipasang, jalankan xcode-select --install dan selesaikan pemasang sebelum meneruskan. Lihat Memasang alat baris perintah.

Skrip lengkap di bawah mencipta direktori sementara baharu dan hanya menggunakan fail kod sumber sintetik. Jalankannya dalam Terminal. Skrip ini secara jelas menyasarkan seni bina persekitaran shell semasa dan macOS 11.0 sebagai contoh versi minimum penggunaan. Ini latihan untuk satu seni bina; gunakan versi minimum lain jika projek anda memerlukannya.

Saya telah memeriksa contoh ini tetapi tidak dapat menjalankan binaan Mac: persekitaran penyelidikan menggunakan Linux dan tiada clang++. Hasil yang dipaparkan ialah hasil yang dijangka, bukannya output ujian yang direkodkan.

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

Kedua-dua pelaksanaan sepatutnya mencetak Grid cells: 12. Pengiraan ini sengaja ringkas: tiga baris didarab dengan empat lajur. Kontrak input pembantu ini mengecualikan dimensi yang hasil darabnya tidak muat dalam int; contoh ini tidak melaksanakan API pengesahan input umum.

Perkara yang ditunjukkan oleh binaan statik

Panggilan -c menghasilkan grid.o tanpa memautkan fail boleh laksana. Arahan arkib membungkus objek itu ke dalam libGrid.a; pilihan r, c dan s meminta penyisipan/penggantian, penciptaan arkib dan indeks simbol. Clang mendokumenkan peringkat kompilasi dalam panduan arahan pengompilnya, dan LLVM menerangkan operasi arkib dalam llvm-ar — pengarkib LLVM.

Klien dipautkan menggunakan laluan eksplisit arkib. Ini menjelaskan input yang dipilih apabila kedua-dua versi statik dan dinamik tersedia. Senarai arkib yang dijangka mengandungi grid.o, manakala senarai kebergantungan fail boleh laksana tidak sepatutnya menunjukkan kebergantungan masa jalan pada libGrid.a.

Pemautan statik di sini terpakai pada pembantu Grid. Ini tidak bermakna fail boleh laksana yang terhasil bebas daripada pustaka masa jalan macOS; periksa entri yang selebihnya yang dilaporkan oleh otool -L. Apple membezakan pemaut pada masa binaan daripada pemuat masa jalan dalam Pengenalan kepada Pustaka Apple.

Perkara yang ditunjukkan oleh binaan dinamik

Panggilan -dynamiclib mencipta pustaka berasingan. Nama pemasangannya ialah @rpath/libGrid.dylib. Fail boleh laksana merekodkan laluan jalan @loader_path/lib yang, bagi entri laluan jalan fail boleh laksana ini, merujuk kepada direktori lib yang bersebelahan. Clang menyenaraikan pilihan pemacu yang berkaitan dalam rujukan argumen baris perintahnya.

Output otool -L yang dijangka bagi klien dinamik menyertakan @rpath/libGrid.dylib; output arahan muat sepatutnya mengandungi entri LC_RPATH dengan @loader_path/lib. Apple mendokumenkan cara nama pemasangan dan laluan jalan fail boleh laksana berfungsi bersama dalam Pustaka yang Bergantung pada Laluan Jalan.

Ini memberi anda ujian pembungkusan yang nyata: pindahkan seluruh direktori build/dynamic secara bersama-sama sambil mengekalkan susun atur dalamannya, kemudian jalankan fail boleh lakasananya dari lokasi baharu. Jika hanya fail boleh laksana dipindahkan, kebergantungan Grid akan tertinggal. Pemautan yang berjaya mengesahkan bahawa input binaan telah ditemukan; ia tidak mengesahkan bahawa pakej masa jalan lengkap.

Bilakah pembantu patut dijadikan rangka kerja?

Pilih rangka kerja apabila struktur bundlenya memenuhi keperluan projek—contohnya, apabila komponen memerlukan pengepala awam dan sumber yang diurus bersama dalam Xcode. Untuk pembantu ini yang hanya membuat pengiraan, rangka kerja menambah kerja pembungkusan tanpa memenuhi keperluan tambahan.

Untuk rangka kerja statik dalam Xcode 15 atau versi lebih baharu, Apple mendokumenkan urutan ini:

  1. Pilih File > New > Target, pilih macOS dan gunakan templat Framework.
  2. Pilih Build Settings sasaran baharu dan tukar Mach-O Type kepada Static Library.
  3. Tambahkan fail pelaksanaan pada Compile Sources dan sumber pada Copy Bundle Resources.
  4. Tambahkan pengepala yang dieksport pada fasa binaan Headers dan sertakan pengepala awam dalam pengepala payung.
  5. Tambahkan rangka kerja itu pada aplikasi yang menggunakannya dan ikuti konfigurasi pembenaman yang didokumenkan Apple.

Untuk rangka kerja statik yang mengandungi sumber, Apple menetapkan Embed & Sign. Xcode tidak menyertakan binari utama yang sudah dipautkan dalam bundle terbenam, tetapi mengekalkan bahan bundle. Ikuti Mencipta rangka kerja statik dan jangan anggap semua rangka kerja statik sebagai arkib yang tidak perlu dibenamkan.

Jika anda mengedarkan varian binari, gunakan XCFramework. Bagi .a kendiri yang sedia ada, Apple mendokumenkan input -library dan -headers untuk xcodebuild -create-xcframework; Apple menasihatkan supaya arkib itu tidak disamarkan secara manual sebagai rangka kerja. Xcode penuh diperlukan untuk xcodebuild. Lihat Mencipta bundle rangka kerja binari berbilang platform dan Memasang alat baris perintah.

Bagaimanakah Swift mengubah keputusan ini?

Swift menambah keperluan penggunaan modul dan evolusi pada keputusan pemautan asas. Kestabilan ABI, kestabilan modul dan evolusi pustaka menangani isu keserasian yang berbeza; bekas .framework tidak mengaktifkannya secara automatik.

Panduan Swift menyatakan bahawa rangka kerja yang dibina dan diedarkan bersama kliennya secara umumnya tidak sepatutnya mengaktifkan evolusi pustaka. Untuk rangka kerja binari yang dibina dan dikemas kini secara berasingan, tentukan sokongan evolusi sejak keluaran pertama. Dalam Xcode, BUILD_LIBRARY_FOR_DISTRIBUTION mengaktifkan evolusi pustaka dan kestabilan modul. Pilihan ini mempengaruhi ciri prestasi dan perubahan API yang dibenarkan, seperti yang diterangkan dalam Evolusi Pustaka dalam Swift.

Pastikan jaminan tersebut dalam skop platform yang didokumenkan. Dokumentasi Swift menerangkan keserasian binari merentas pengompil pada platform Apple; jangan luaskan janji itu kepada setiap platform yang disokong Swift atau menganggapnya sebagai kebenaran untuk membuat sebarang perubahan API. Pengepala serasi C dalam contoh ini juga bukan persediaan modul Swift yang lengkap.

Diagnosis kegagalan binaan mengikut urutan berlakunya

Gunakan peringkat kegagalan untuk mengenal pasti pemeriksaan seterusnya:

Gejala Pemeriksaan pertama Tindakan seterusnya
Pengompil tidak dapat menemukan grid.h Pengepala wujud di bawah include Semak direktori kerja dan -Iinclude
Rujukan kepada fungsi Grid tidak ditakrifkan Pelaksanaan dan pengisytiharan sepadan Pastikan arkib atau pustaka dinamik yang dimaksudkan berada dalam arahan pemautan
Ketidakpadanan seni bina atau platform Kepingan kebergantungan sebenar dan platform binaan Bina semula kebergantungan untuk sasaran macOS klien
Fail boleh laksana dipautkan tetapi tidak dapat memuatkan Grid Kebergantungan dan laluan jalan yang direkodkan Bandingkan otool -L, otool -l dan susun atur direktori berpakej
Binaan Intel dan Apple silicon berkelakuan berbeza Liputan seni bina setiap kebergantungan Periksa setiap binari dan uji seni bina yang dimaksudkan

panduan arahan pengompil Clang memisahkan kompilasi daripada pemautan. Pustaka yang Bergantung pada Laluan Jalan Apple menerangkan penyelesaian masa jalan, manakala Membina binari macOS universal mendokumenkan pemeriksaan seni bina dengan lipo -archs. Perbezaan ini menjadikan urutan diagnosis berguna: mengubah laluan pengepala tidak dapat membaiki pustaka masa jalan yang hilang.

Bagi utiliti grid sintetik ini, kekalkan arkib statik sehingga keperluan pembungkusan atau masa jalan yang nyata mewajarkan pilihan lain. Jika keperluan itu timbul, kekalkan pengiraan kecil yang sama sebagai semakan binaan, kemudian sahkan pustaka yang dipilih, keserasian sasaran dan susun atur direktori akhir secara berasingan.

Sumber