Untuk utilitas Mac native yang kecil, mulailah dengan pustaka statis jika kodenya dikirim bersama executable. Pilih pustaka dinamis jika komponen runtime terpisah berguna, dan framework jika Anda memerlukan pengemasan bundle Apple. Panduan ini membangun helper C++ yang sama dengan kedua cara tersebut.

Tugas konkretnya adalah mengemas kalkulasi grid yang dapat digunakan kembali untuk utilitas desktop macOS lokal. Contoh ini menggunakan masukan sintetis dan perintah terminal agar Anda dapat melihat proses kompilasi, penautan, dan pemuatan secara terpisah sebelum memilih format untuk proyek aplikasi.

Apa sebenarnya yang Anda pilih?

Ada dua keputusan: cara kode ditautkan dan cara hasilnya dikemas. Pustaka statis, yang biasanya berakhiran .a, adalah arsip berisi berkas objek. Pustaka dinamis, yang berakhiran .dylib, tetap menjadi image runtime terpisah. Framework adalah bundle yang mengatur pustaka beserta materi terkait. Apple menjelaskan format dasar ini dalam Pengantar Pustaka Apple.

Framework juga dapat berisi pustaka statis. Apple mendukung pembuatan framework statis di Xcode 15 dan versi lebih baru, termasuk sumber daya. Karena itu, sufiks .framework saja tidak menentukan apakah pustaka ditautkan secara statis atau dinamis. Lihat Membuat framework statis.

Gunakan panduan keputusan ini untuk helper milik aplikasi yang Anda bangun dari kode sumber:

Kebutuhan Anda Pilihan awal Tanggung jawab yang muncul
Satu executable dan helper yang dibangun ulang bersama Statis .a Tautkan ulang executable saat helper berubah
Helper yang tetap menjadi komponen runtime terpisah Dinamis .dylib Sediakan pustaka dan jalur pencarian runtime yang berfungsi
Komponen Xcode dengan header publik dan sumber daya dalam bundle Framework Pilih penautannya secara terpisah dan atur keanggotaan target
Varian biner untuk beberapa platform Apple XCFramework Bangun dan kemas varian platform yang diperlukan

Ini adalah rekomendasi praktis yang diturunkan dari format yang didokumentasikan. Rekomendasi ini tidak menentukan format mana yang paling cepat. Untuk utilitas grid ini, arsip statis adalah pilihan awal yang wajar: hanya ada satu klien, tidak ada bundle sumber daya, dan tidak ada kebutuhan untuk mengganti pustaka terpisah.

XCFramework mengemas varian biner; format ini tidak menggantikan keputusan statis atau dinamis. Apple mengizinkan framework dan pustaka statis maupun dinamis dalam format distribusi tersebut. Lihat Membuat bundle framework biner multiplatform.

Periksa kompatibilitas sebelum memilih wadah

Nama berkas atau arsitektur CPU yang cocok saja tidak cukup. macOS, Mac Catalyst, dan iOS adalah platform build yang berbeda. Pustaka arm64 yang dikompilasi untuk satu platform tidak dapat begitu saja ditautkan ke executable platform lain. Apple secara eksplisit membedakan platform dari arsitektur dalam Pengantar Pustaka Apple.

Untuk tugas ini, pastikan masukan berikut konsisten: target macOS, arsitektur CPU yang diperlukan, versi minimum macOS, serta pengaturan bahasa/runtime yang kompatibel. Hasil Mac universal memerlukan slice dependensi yang sesuai untuk setiap arsitektur yang didukung. Membangun biner macOS universal dari Apple membahas pembangunan ulang pustaka selain executable.

Kompatibilitas C++ perlu diperhatikan tersendiri. Memilih -std=c++17 menentukan mode bahasa; pilihan itu tidak menjamin kompatibilitas biner antara komponen yang dibangun secara terpisah. LLVM mendokumentasikan jaminan ABI dan pilihan konfigurasi vendor untuk libc++, termasuk pengaturan yang mengubah tata letak tipe. Untuk dependensi milik aplikasi, pilihan awal yang baik adalah membangunnya ulang dengan toolchain aplikasi dan pengaturan yang konsisten. Lihat Jaminan ABI libc++.

Contoh ini mengekspos satu fungsi melalui header yang kompatibel dengan C, sementara implementasinya ditulis dalam C++. Deklarasi extern \"C\" yang dilindungi guard menetapkan C language linkage untuk kompilasi C++. Deklarasi ini tidak mengubah sembarang kelas C++ menjadi antarmuka C atau membuat biner dapat dipindahkan ke platform lain. Draf kerja C++ menjelaskan batasan ini dalam Spesifikasi linkage.

Bangun satu helper sebagai pustaka statis dan dinamis

Anda memerlukan Mac dengan alat pengembang Apple dan SDK macOS. Xcode lengkap menyertakan alat baris perintah; Apple juga menyediakan paket Command Line Tools mandiri. Jika keduanya belum terpasang, jalankan xcode-select --install dan selesaikan pemasangannya sebelum melanjutkan. Lihat Memasang alat baris perintah.

Skrip lengkap di bawah ini membuat direktori sementara baru dan hanya menggunakan berkas sumber sintetis. Jalankan di Terminal. Skrip secara eksplisit menargetkan arsitektur lingkungan shell saat ini dan macOS 11.0 sebagai contoh versi minimum deployment. Ini adalah latihan dengan satu arsitektur; gunakan versi minimum lain jika proyek Anda memerlukannya.

Saya telah memeriksa contohnya, tetapi tidak dapat menjalankan build Mac: lingkungan riset ini menggunakan Linux dan tidak memiliki clang++. Hasil yang ditampilkan adalah hasil yang diharapkan, bukan keluaran pengujian yang direkam.

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 eksekusi seharusnya mencetak Grid cells: 12. Perhitungannya sengaja dibuat sederhana: tiga baris dikalikan empat kolom. Kontrak masukan helper menyatakan bahwa dimensinya tidak boleh menghasilkan perkalian yang tidak muat dalam int; contoh ini tidak menerapkan API validasi masukan umum.

Yang ditunjukkan oleh build statis

Pemanggilan -c menghasilkan grid.o tanpa menautkan executable. Perintah arsip mengemas objek tersebut menjadi libGrid.a; opsi r, c, dan s meminta penyisipan/penggantian, pembuatan arsip, dan indeks simbol. Clang mendokumentasikan tahapan kompilasi dalam panduan perintah compilernya, dan LLVM menjelaskan operasi arsip dalam llvm-ar — pengarsip LLVM.

Klien ditautkan menggunakan jalur eksplisit ke arsip. Dengan demikian, masukan yang dipilih tetap jelas saat versi statis dan dinamis sama-sama tersedia. Daftar arsip yang diharapkan berisi grid.o, sementara daftar dependensi executable seharusnya tidak menunjukkan dependensi runtime pada libGrid.a.

Penautan statis di sini berlaku untuk helper Grid. Ini tidak berarti executable yang dihasilkan tidak bergantung pada pustaka runtime macOS; periksa entri lainnya yang dilaporkan oleh otool -L. Apple membedakan linker saat build dari loader saat runtime dalam Pengantar Pustaka Apple.

Yang ditunjukkan oleh build dinamis

Pemanggilan -dynamiclib membuat pustaka terpisah. Nama instalasinya adalah @rpath/libGrid.dylib. Executable mencatat run path @loader_path/lib, yang untuk entri run-path executable ini menunjuk ke direktori lib di sebelahnya. Clang mencantumkan opsi driver terkait dalam referensi argumen baris perintahnya.

Keluaran otool -L yang diharapkan untuk klien dinamis mencakup @rpath/libGrid.dylib; keluaran perintah load seharusnya berisi entri LC_RPATH dengan @loader_path/lib. Apple mendokumentasikan cara nama instalasi dan run path executable bekerja bersama dalam Pustaka yang Bergantung pada Run Path.

Ini memberi Anda pengujian pengemasan yang konkret: pindahkan seluruh direktori build/dynamic secara bersamaan dengan mempertahankan tata letak internalnya, lalu jalankan executable dari lokasi baru. Jika hanya executable yang dipindahkan, dependensi Grid akan tertinggal. Penautan yang berhasil memastikan masukan build ditemukan; itu tidak memastikan paket runtime sudah lengkap.

Kapan helper sebaiknya menjadi framework?

Pilih framework jika struktur bundlenya berguna bagi proyek—misalnya, jika komponen memerlukan header publik dan sumber daya yang dikelola bersama di Xcode. Untuk helper yang hanya melakukan kalkulasi ini, framework menambah pekerjaan pengemasan tanpa memenuhi kebutuhan tambahan.

Untuk framework statis di Xcode 15 atau versi lebih baru, Apple mendokumentasikan urutan berikut:

  1. Pilih File > New > Target, pilih macOS, lalu gunakan templat Framework.
  2. Pilih Build Settings pada target baru dan ubah Mach-O Type menjadi Static Library.
  3. Tambahkan berkas implementasi ke Compile Sources dan sumber daya ke Copy Bundle Resources.
  4. Tambahkan header yang diekspor ke fase build Headers dan sertakan header publik dalam umbrella header.
  5. Tambahkan framework ke aplikasi pengguna dan ikuti konfigurasi embedding yang didokumentasikan Apple.

Untuk framework statis yang berisi sumber daya, Apple menetapkan Embed & Sign. Xcode tidak menyertakan biner utama yang sudah ditautkan ke dalam bundle yang di-embed, tetapi tetap menyertakan materi bundle. Ikuti Membuat framework statis, jangan menganggap setiap framework statis sebagai arsip yang tidak perlu di-embed.

Jika Anda mendistribusikan varian biner, gunakan XCFramework. Untuk .a mandiri yang sudah ada, Apple mendokumentasikan masukan -library dan -headers untuk xcodebuild -create-xcframework; Apple menyarankan agar arsip tidak disamarkan secara manual sebagai framework. Xcode lengkap diperlukan untuk xcodebuild. Lihat Membuat bundle framework biner multiplatform dan Memasang alat baris perintah.

Bagaimana Swift mengubah keputusan ini?

Swift menambahkan persyaratan konsumsi modul dan evolusi pada keputusan penautan dasar. Stabilitas ABI, stabilitas modul, dan evolusi pustaka membahas persoalan kompatibilitas yang berbeda; wadah .framework tidak mengaktifkannya secara otomatis.

Panduan Swift menyatakan bahwa framework yang dibangun dan didistribusikan bersama kliennya umumnya tidak perlu mengaktifkan evolusi pustaka. Untuk framework biner yang dibangun dan diperbarui secara terpisah, tentukan dukungan evolusi sejak rilis pertama. Di Xcode, BUILD_LIBRARY_FOR_DISTRIBUTION mengaktifkan evolusi pustaka dan stabilitas modul. Pilihan ini memengaruhi karakteristik performa dan perubahan API yang diizinkan, sebagaimana dijelaskan dalam Evolusi Pustaka di Swift.

Jaga agar jaminan tersebut tetap dalam cakupan platform yang didokumentasikan. Dokumentasi Swift menjelaskan kompatibilitas biner lintas compiler di platform Apple; jangan memperluas jaminan itu ke setiap platform yang didukung Swift atau mengartikannya sebagai izin untuk melakukan perubahan API sembarangan. Header yang kompatibel dengan C dalam contoh ini juga bukan penyiapan modul Swift yang lengkap.

Diagnosis build yang gagal sesuai urutan prosesnya

Gunakan tahap kegagalan untuk menentukan pemeriksaan berikutnya:

Gejala Pemeriksaan pertama Tindakan berikutnya
Compiler tidak dapat menemukan grid.h Header tersedia di include Periksa direktori kerja dan -Iinclude
Referensi ke fungsi Grid tidak terdefinisi Implementasi dan deklarasi cocok Pastikan arsip atau pustaka dinamis yang dimaksud tercantum dalam perintah penautan
Ketidakcocokan arsitektur atau platform Slice dependensi yang sebenarnya dan platform build Bangun ulang dependensi untuk target macOS klien
Executable berhasil ditautkan tetapi tidak dapat memuat Grid Dependensi dan run path yang tercatat Bandingkan otool -L, otool -l, dan tata letak direktori paket
Build Intel dan Apple silicon berperilaku berbeda Cakupan arsitektur setiap dependensi Periksa setiap biner dan uji arsitektur yang dituju

panduan perintah compiler Clang memisahkan kompilasi dari penautan. Pustaka yang Bergantung pada Run Path dari Apple menjelaskan resolusi runtime, sedangkan Membangun biner macOS universal mendokumentasikan pemeriksaan arsitektur dengan lipo -archs. Pembedaan tersebut membuat urutan diagnosis ini berguna: mengubah jalur header tidak dapat memperbaiki pustaka runtime yang hilang.

Untuk utilitas grid sintetis ini, pertahankan arsip statis sampai ada kebutuhan pengemasan atau runtime konkret yang membenarkan pilihan lain. Jika kebutuhan itu muncul, gunakan kalkulasi kecil yang sama sebagai pemeriksaan build, lalu verifikasi pustaka yang dipilih, kompatibilitas target, dan tata letak direktori akhir secara terpisah.

Sumber