
Dalam artikel ini saya akan memberi tahu Anda apa yang manajer paket serupa dalam struktur internal, algoritma operasi, dan apa perbedaan mendasar mereka. Saya melihat manajer paket yang dirancang untuk pengembangan di bawah iOS / OS X, tetapi konten artikel dengan beberapa asumsi berlaku untuk yang lain.
Varietas manajer ketergantungan
- Manajer ketergantungan sistem - instal utilitas yang hilang di sistem operasi. Misalnya, Homebrew .
- Manajer ketergantungan bahasa - mengumpulkan sumber yang ditulis dalam salah satu bahasa pemrograman ke dalam program yang dapat dieksekusi akhir. Misalnya, pergi membangun .
- Manajer ketergantungan proyek - mengelola ketergantungan dalam konteks proyek tertentu. Yaitu, tugas mereka termasuk mendeskripsikan dependensi, mengunduh, memperbarui kode sumber mereka. Ini, misalnya, Cocoapods .
Perbedaan utama di antara mereka adalah siapa yang mereka “layani”. Sistem MH untuk pengguna, MH proyek untuk pengembang, dan MH bahasa untuk keduanya.
Selanjutnya, saya akan mempertimbangkan manajer ketergantungan proyek - kami paling sering menggunakannya, dan mereka lebih mudah dimengerti.
Skema proyek saat menggunakan manajer dependensi
Pertimbangkan Cocoapods,
manajer paket populer.
Biasanya kita menjalankan perintah
instal pod install , dan kemudian dependency manager melakukan segalanya untuk kita. Pertimbangkan apa yang harus terdiri dari proyek agar tim ini dapat menyelesaikan dengan sukses.

- Ada kode kami di mana kami menggunakan ketergantungan ini atau itu, katakanlah, perpustakaan Alamofire .
- Dari file manifes, manajer dependensi tahu dependensi mana yang kami gunakan dalam kode sumber. Jika kita lupa menunjukkan perpustakaan di sana, ketergantungan tidak akan ditetapkan, dan proyek pada akhirnya tidak akan dirakit.
- File kunci - file format tertentu yang dihasilkan oleh manajer dependensi, yang mencantumkan semua dependensi yang berhasil diinstal dalam proyek.
- Kode ketergantungan adalah kode sumber eksternal yang manajer "tarik" dan yang akan dipanggil dari kode kami.
Ini tidak akan mungkin terjadi tanpa algoritma spesifik yang berjalan setiap kali setelah perintah install dependensi.
Semua 4 komponen terdaftar satu demi satu, sebagai komponen selanjutnya dibentuk berdasarkan yang sebelumnya.

Tidak semua manajer dependensi memiliki semua 4 komponen, tetapi dengan mempertimbangkan fungsi manajer dependensi, kehadiran semua adalah pilihan terbaik.
Setelah menginstal dependensi, semua 4 komponen pergi ke input dari kompiler atau juru bahasa, tergantung pada bahasa.

Saya juga menarik perhatian pada kenyataan bahwa pengembang bertanggung jawab atas dua komponen pertama - kami menulis kode ini, dan manajer dependensi untuk dua komponen lainnya - ini menghasilkan file dan mengunduh kode sumber dependensi.

Alur Kerja Manajer Ketergantungan
Dengan komponen yang sedikit banyak diurutkan, sekarang mari kita beralih ke bagian algoritmik dari Departemen Kesehatan.
Algoritme kerja tipikal terlihat seperti ini:
- Validasi proyek dan lingkungan. Objek yang disebut Analyzer bertanggung jawab untuk ini.
- Membangun grafik. Dari dependensi, Departemen Kesehatan harus membuat grafik. Objek Resolver melakukan ini.
- Mengunduh dependensi. Jelas, kode sumber dependensi harus diunduh agar kami dapat menggunakannya di sumber kami.
- Integrasi Ketergantungan. Fakta bahwa kode sumber dependensi terletak di direktori tetangga pada disk mungkin tidak cukup, jadi mereka masih harus dilampirkan ke proyek kami.
- Pembaruan ketergantungan. Langkah ini tidak dilakukan segera setelah langkah 4, tetapi jika perlu, tingkatkan ke versi baru perpustakaan. Ada beberapa kekhasan di sini, jadi saya memilihnya secara terpisah - lebih banyak tentang mereka nanti.
Validasi proyek dan lingkungan
Validasi termasuk memeriksa versi OS, utilitas tambahan yang diperlukan oleh manajer dependensi, serta menautkan pengaturan proyek dan file manifes: dari pemeriksaan sintaks ke pengaturan yang tidak kompatibel.
Sampel
podfilesource 'https://github.com/CocoaPods/Specs.git' source 'https://github.com/RedMadRobot/cocoapods-specs' platform :ios, '10.0' use_frameworks! project 'Project.xcodeproj' workspace 'Project.xcworkspace' target 'Project' do project 'Project.xcodeproj' pod 'Alamofire' pod 'Fabric' pod 'GoogleMaps' end
Kemungkinan peringatan dan kesalahan saat memeriksa podfile:
- Tidak ada ketergantungan yang ditemukan di salah satu repositori spec ;
- Sistem operasi dan versi tidak ditentukan secara eksplisit;
- Ruang kerja atau nama proyek tidak valid.
Membangun grafik ketergantungan
Karena dependensi yang disyaratkan oleh proyek kami mungkin memiliki dependensinya sendiri, dan dependensi tersebut mungkin memiliki dependensi atau sub-dependensi bersarang sendiri, manajer menggunakan versi yang benar. Secara skematis, semua dependensi sebagai hasilnya harus berbaris dalam
grafik asiklik yang diarahkan .

Konstruksi grafik asiklik terarah mengurangi masalah penyortiran topologis. Dia memiliki beberapa algoritma keputusan.
- Algoritma Kahn - enumerasi simpul, kompleksitas O (n).
- Algoritma Tarjan - berdasarkan pada pencarian yang mendalam, kompleksitas O (n).
- Algoritma Demucron adalah partisi grafik berlapis.
- Algoritma paralel menggunakan jumlah prosesor polinomial. Dalam hal ini, kompleksitasnya akan “jatuh” ke O (log (n) ^ 2)
Tugas itu sendiri adalah NP-lengkap, algoritma yang sama digunakan dalam kompiler dan pembelajaran mesin.
Hasil dari solusi adalah file kunci yang dibuat, yang sepenuhnya menggambarkan hubungan antara dependensi.

Masalah apa yang mungkin muncul ketika algoritma ini bekerja? Pertimbangkan sebuah contoh: ada proyek dengan dependensi A, B, E dengan dependensi bersarang C, F, D.

Dependensi A dan B memiliki dependensi yang sama C. Dan di sini C harus memenuhi persyaratan dependensi A dan B. Beberapa manajer dependensi memungkinkan instalasi versi terpisah jika perlu, tetapi cocoapod, misalnya, tidak. Oleh karena itu, dalam hal ketidakcocokan persyaratan: A memerlukan versi yang sama dengan 2.0 dari ketergantungan C, dan B membutuhkan versi 1.0, instalasi akan gagal. Dan jika dependensi A membutuhkan versi 1.0 dan lebih tinggi untuk versi 2.0, dan dependensi B versi 1.2 atau kurang ke 1.0, versi yang paling kompatibel untuk A dan B versi 1.2 akan diinstal. Jangan lupa bahwa situasi ketergantungan siklik dapat terjadi, bahkan jika tidak secara langsung - dalam hal ini, instalasi juga akan gagal.

Mari kita lihat tampilannya dalam kode manajer dependensi paling populer untuk iOS.
Kartago
typealias DependencyGraph = [Dependency: Set<Dependency>] public enum Dependency { /// A repository hosted on GitHub.com or GitHub Enterprise. case gitHub(Server, Repository) /// An arbitrary Git repository. case git(GitURL) /// A binary-only framework case binary(URL) } /// Protocol for resolving acyclic dependency graphs. public protocol ResolverProtocol { init( versionsForDependency: @escaping (Dependency) -> SignalProducer<PinnedVersion, CarthageError>, dependenciesForDependency: @escaping (Dependency, PinnedVersion) -> SignalProducer<(Dependency, VersionSpecifier), CarthageError>, resolvedGitReference: @escaping (Dependency, String) -> SignalProducer<PinnedVersion, CarthageError> ) func resolve( dependencies: [Dependency: VersionSpecifier], lastResolved: [Dependency: PinnedVersion]?, dependenciesToUpdate: [String]? ) -> SignalProducer<[Dependency: PinnedVersion], CarthageError> }
Penerapan Resolver ada di
sini , dan NewResolver di
sini , Analyzer tidak demikian.
Cocoapods
Implementasi algoritma konstruksi grafik dialokasikan ke
repositori terpisah. Berikut ini adalah implementasi dari
grafik dan
Resolver . Di
Analyzer, Anda dapat menemukan bahwa ia memeriksa konsistensi versi cocoapods dari sistem dan file kunci.
def validate_lockfile_version! if lockfile && lockfile.cocoapods_version > Version.new(VERSION) STDERR.puts '[!] The version of CocoaPods used to generate ' \ "the lockfile (#{lockfile.cocoapods_version}) is "\ "higher than the version of the current executable (#{VERSION}). " \ 'Incompatibility issues may arise.'.yellow end end
Dari sumbernya Anda juga dapat melihat bahwa Analyzer menghasilkan target untuk dependensi.
File kunci cocoapods khas terlihat seperti ini:
PODS: - Alamofire (4.7.0) - Fabric (1.7.5) - GoogleMaps (2.6.0): - GoogleMaps/Maps (= 2.6.0) - GoogleMaps/Base (2.6.0) - GoogleMaps/Maps (2.6.0): - GoogleMaps/Base SPEC CHECKSUMS: Alamofire: 907e0a98eb68cdb7f9d1f541a563d6ac5dc77b25 Fabric: ae7146a5f505ea370a1e44820b4b1dc8890e2890 GoogleMaps: 42f91c68b7fa2f84d5c86597b18ceb99f5414c7f PODFILE CHECKSUM: 5294972c5dd60a892bfcc35329cae74e46aac47b COCOAPODS: 1.4.0
Bagian PODS mencantumkan dependensi langsung dan bersarang yang menunjukkan versi, kemudian checksumnya dihitung secara terpisah dan bersama-sama dan versi cocoapods yang digunakan untuk instalasi ditunjukkan.
Unduh Ketergantungan
Setelah berhasil membuat grafik dan membuat file kunci, manajer dependensi mulai mengunduhnya. Tidak harus berupa kode sumber, itu juga bisa merupakan file yang dapat dieksekusi atau kerangka kerja yang dikompilasi. Juga, semua manajer dependensi umumnya mendukung kemampuan untuk menginstal pada jalur lokal.

Tidak ada yang rumit untuk mengunduhnya dari tautan (yang, tentu saja, Anda harus dapatkan dari suatu tempat), jadi saya tidak akan memberi tahu bagaimana pengunduhan itu sendiri terjadi, tetapi fokus pada masalah sentralisasi dan keamanan.
Sentralisasi
Secara sederhana, manajer dependensi memiliki dua cara saat mengunduh dependensi:
- Buka beberapa daftar dependensi yang tersedia dan dapatkan tautan unduhan berdasarkan nama.
- Kami harus secara eksplisit menentukan sumber untuk setiap ketergantungan dalam file manifes.
Manajer ketergantungan terpusat mengikuti jalur pertama, terdesentralisasi di jalur kedua.

Keamanan
Jika Anda mengunduh dependensi melalui https atau ssh, maka Anda dapat tidur dengan tenang. Namun, pengembang sering memberikan tautan http ke perpustakaan resmi mereka. Dan di sini kita mungkin menghadapi serangan
man-in-the-middle ketika penyerang memalsukan kode sumber, file yang dapat dieksekusi, atau kerangka kerja. Beberapa manajer dependensi tidak dilindungi dari ini, dan beberapa melakukannya sebagai berikut.
Homebrew
Memeriksa ikal pada versi OS X yang lebih lama.
def check_for_bad_curl return unless MacOS.version <= "10.8" return if Formula["curl"].installed? <<~EOS The system curl on 10.8 and below is often incapable of supporting modern secure connections & will fail on fetching formulae. We recommend you: brew install curl EOS end
Ada juga pemeriksaan
hash SHA256 saat mengunduh melalui http.
def curl_http_content_headers_and_checksum(url, hash_needed: false, user_agent: :default) max_time = hash_needed ? "600" : "25" output, = curl_output( "--connect-timeout", "15", "--include", "--max-time", max_time, "--location", url, user_agent: user_agent ) status_code = :unknown while status_code == :unknown || status_code.to_s.start_with?("3") headers, _, output = output.partition("\r\n\r\n") status_code = headers[%r{HTTP\/.* (\d+)}, 1] end output_hash = Digest::SHA256.digest(output) if hash_needed { status: status_code, etag: headers[%r{ETag: ([wW]\/)?"(([^"]|\\")*)"}, 2], content_length: headers[/Content-Length: (\d+)/, 1], file_hash: output_hash, file: output, } end
Dan
Anda juga dapat menonaktifkan pengalihan yang tidak aman ke http (variabel
HOMEBREW_NO_INSECURE_REDIRECT ).
Kartago dan Cocoapoda
Semuanya lebih sederhana di sini -
Anda tidak dapat menggunakan http pada file yang dapat dieksekusi.
guard binaryURL.scheme == "file" || binaryURL.scheme == "https" else { return .failure(BinaryJSONError.nonHTTPSURL(binaryURL)) }
def validate_source_url(spec) return if spec.source.nil? || spec.source[:http].nil? url = URI(spec.source[:http]) return if url.scheme == 'https' || url.scheme == 'file' warning('http', "The URL (`#{url}`) doesn't use the encrypted HTTPs protocol. " \ 'It is crucial for Pods to be transferred over a secure protocol to protect your users from man-in-the-middle attacks. '\ 'This will be an error in future releases. Please update the URL to use https.') end
Kode lengkap di
sini .
Manajer paket cepat
Saat ini, tidak ada yang terkait dengan keamanan dapat ditemukan, tetapi dalam proposal pengembangan ada menyebutkan singkat tentang
mekanisme untuk menandatangani paket menggunakan sertifikat.
Integrasi ketergantungan
Dengan integrasi, maksud saya menghubungkan dependensi ke proyek sedemikian rupa sehingga kita dapat menggunakannya secara bebas, dan mereka dikompilasi dengan kode aplikasi utama.
Integrasi dapat berupa manual (Carthage) atau otomatis (Cocoapods). Kelebihan dari yang otomatis adalah gerakan minimal dari pihak pengembang, tetapi banyak keajaiban dapat ditambahkan ke proyek.
Diff setelah menginstal dependensi dalam suatu proyek menggunakan Cocoapods Dalam hal manual, Anda, misalnya, mengikuti instruksi Carthage
ini , sepenuhnya mengendalikan proses penambahan dependensi ke proyek. Dapat diandalkan, tetapi lebih lama.
Pembaruan Ketergantungan
Anda dapat mengontrol kode sumber dependensi dalam proyek menggunakan versi mereka.
Ada 3 metode yang digunakan dalam manajer dependensi:
- Versi perpustakaan. Cara paling nyaman dan umum. Anda dapat menentukan versi dan interval tertentu. Ini adalah cara yang sepenuhnya dapat diprediksi untuk mendukung kompatibilitas dependensi, asalkan penulis membuat versi perpustakaan dengan benar.
- Cabang Saat memperbarui cabang dan memperbarui ketergantungan, kami tidak dapat memprediksi perubahan apa yang akan terjadi.
- Komit atau tag. Ketika perintah pembaruan dijalankan, dependensi dengan tautan ke komit atau tag tertentu (jika tidak diubah) tidak akan pernah diperbarui.
Kesimpulan
Dalam artikel tersebut saya memberikan pemahaman yang dangkal tentang struktur internal manajer ketergantungan. Jika Anda ingin tahu lebih banyak, Anda harus mempelajari kode sumber manajer paket. Cara termudah untuk menemukan yang ditulis dalam bahasa yang akrab. Skema yang dideskripsikan adalah tipikal, tetapi dalam manajer ketergantungan tertentu sesuatu mungkin hilang atau, sebaliknya, yang baru mungkin muncul.
Komentar dan diskusi dalam komentar dipersilahkan.