Code Loading
هذا الفصل يغطي التفاصيل الفنية لتحميل الحزم. لتثبيت الحزم، استخدم Pkg
، مدير الحزم المدمج في جوليا، لإضافة الحزم إلى بيئتك النشطة. لاستخدام الحزم الموجودة بالفعل في بيئتك النشطة، اكتب import X
أو using X
، كما هو موضح في Modules documentation.
Definitions
تملك جوليا آليتين لتحميل الشيفرة:
- تضمين الشيفرة: على سبيل المثال
include("source.jl")
. يسمح التضمين لك بتقسيم برنامج واحد عبر ملفات مصدر متعددة. التعبيرinclude("source.jl")
يتسبب في تقييم محتويات الملفsource.jl
في النطاق العالمي للوحدة التي يحدث فيها استدعاءinclude
. إذا تم استدعاءinclude("source.jl")
عدة مرات، يتم تقييمsource.jl
عدة مرات. يتم تفسير المسار المضمن،source.jl
، بالنسبة للملف الذي يحدث فيه استدعاءinclude
. هذا يجعل من السهل نقل شجرة من ملفات المصدر. في REPL، يتم تفسير المسارات المضمنة بالنسبة للدليل الحالي،pwd()
. - تحميل الحزمة: على سبيل المثال
import X
أوusing X
. يسمح آلية الاستيراد بتحميل حزمة - أي مجموعة مستقلة وقابلة لإعادة الاستخدام من كود جوليا، مغلفة في وحدة - ويجعل الوحدة الناتجة متاحة بالاسمX
داخل الوحدة المستوردة. إذا تم استيراد نفس حزمةX
عدة مرات في نفس جلسة جوليا، فإنه يتم تحميلها فقط في المرة الأولى - في الاستيرادات اللاحقة، تحصل الوحدة المستوردة على مرجع إلى نفس الوحدة. ومع ذلك، لاحظ أنimport X
يمكن أن يحمل حزمًا مختلفة في سياقات مختلفة: يمكن أن تشيرX
إلى حزمة واحدة تحمل الاسمX
في المشروع الرئيسي ولكن يمكن أن تشير أيضًا إلى حزم مختلفة تحمل نفس الاسمX
في كل اعتماد. المزيد عن هذا أدناه.
إدراج الشيفرة بسيط وسهل للغاية: يقوم بتقييم ملف المصدر المعطى في سياق المتصل. تحميل الحزم مبني على إدراج الشيفرة ويخدم different purpose. بقية هذا الفصل تركز على سلوك وآليات تحميل الحزم.
حزمة package هي شجرة مصدر بتنسيق قياسي توفر وظائف يمكن إعادة استخدامها بواسطة مشاريع جوليا الأخرى. يتم تحميل الحزمة بواسطة عبارات import X
أو using X
. تجعل هذه العبارات أيضًا الوحدة المسماة X
- التي تنتج عن تحميل كود الحزمة - متاحة داخل الوحدة التي تحدث فيها عبارة الاستيراد. معنى X
في import X
يعتمد على السياق: الحزمة X
التي يتم تحميلها تعتمد على الكود الذي تحدث فيه العبارة. وبالتالي، يتم التعامل مع import X
على مرحلتين: أولاً، تحدد ما الحزمة التي تم تعريفها لتكون X
في هذا السياق؛ ثانيًا، تحدد أين يمكن العثور على تلك الحزمة X
المعينة.
تتم الإجابة على هذه الأسئلة من خلال البحث في بيئات المشروع المدرجة في LOAD_PATH
عن ملفات المشروع (Project.toml
أو JuliaProject.toml
)، أو ملفات البيان (Manifest.toml
أو JuliaManifest.toml
، أو نفس الأسماء مع اللاحقة -v{major}.{minor}.toml
للإصدارات المحددة)، أو مجلدات ملفات المصدر.
Federation of packages
معظم الوقت، يمكن التعرف على حزمة بشكل فريد ببساطة من اسمها. ومع ذلك، في بعض الأحيان قد يواجه المشروع حالة يحتاج فيها إلى استخدام حزمتين مختلفتين تشتركان في نفس الاسم. بينما قد تتمكن من إصلاح ذلك عن طريق إعادة تسمية إحدى الحزم، فإن الإضطرار إلى القيام بذلك يمكن أن يكون مدمراً للغاية في قاعدة الشيفرة الكبيرة والمشتركة. بدلاً من ذلك، يسمح آلية تحميل الشيفرة في جوليا أن يشير نفس اسم الحزمة إلى حزم مختلفة في مكونات مختلفة من التطبيق.
تدعم جوليا إدارة الحزم الفيدرالية، مما يعني أن عدة أطراف مستقلة يمكنها الحفاظ على حزم عامة وخاصة وسجلات حزم، وأن المشاريع يمكن أن تعتمد على مزيج من الحزم العامة والخاصة من سجلات مختلفة. يتم تثبيت الحزم من سجلات متنوعة وإدارتها باستخدام مجموعة شائعة من الأدوات وسير العمل. مدير الحزم Pkg
الذي يأتي مع جوليا يتيح لك تثبيت وإدارة تبعيات مشاريعك. يساعد في إنشاء وتعديل ملفات المشاريع (التي تصف ما هي المشاريع الأخرى التي تعتمد عليها مشروعك)، وملفات البيان (التي تلتقط النسخ الدقيقة من الرسم البياني الكامل لتبعيات مشروعك).
نتيجة واحدة للفيدرالية هي أنه لا يمكن أن يكون هناك سلطة مركزية لتسمية الحزم. قد تستخدم كيانات مختلفة نفس الاسم للإشارة إلى حزم غير مرتبطة. هذه الإمكانية لا مفر منها لأن هذه الكيانات لا تنسق وقد لا تعرف حتى عن بعضها البعض. بسبب عدم وجود سلطة مركزية للتسمية، قد ينتهي مشروع واحد بالاعتماد على حزم مختلفة تحمل نفس الاسم. آلية تحميل الحزم في جوليا لا تتطلب أن تكون أسماء الحزم فريدة على مستوى العالم، حتى داخل رسم الاعتماد لمشروع واحد. بدلاً من ذلك، يتم تحديد الحزم بواسطة universally unique identifiers (UUIDs)، والتي يتم تعيينها عند إنشاء كل حزمة. عادةً لن تضطر للعمل مباشرةً مع هذه المعرفات المكونة من 128 بت، حيث سيتولى Pkg
مسؤولية توليدها وتتبعها لك. ومع ذلك، توفر هذه UUIDs الإجابة النهائية على سؤال "أي حزمة تشير إليها X
؟
نظرًا لأن مشكلة التسمية اللامركزية هي مسألة تجريدية إلى حد ما، فقد يساعدك أن تسير عبر سيناريو ملموس لفهم المشكلة. افترض أنك تقوم بتطوير تطبيق يسمى App
، والذي يستخدم حزمتين: Pub
و Priv
. Priv
هي حزمة خاصة أنشأتها، بينما Pub
هي حزمة عامة تستخدمها ولكنك لا تتحكم فيها. عندما أنشأت Priv
، لم تكن هناك حزمة عامة بالاسم Priv
. ومع ذلك، تم نشر حزمة غير مرتبطة أيضًا باسم Priv
وأصبحت شائعة. في الواقع، بدأت حزمة Pub
في استخدامها. لذلك، عندما تقوم بترقية Pub
للحصول على أحدث إصلاحات الأخطاء والميزات، سينتهي الأمر بـ App
بالاعتماد على حزمتين مختلفتين تحملان الاسم Priv
—دون أي إجراء منك بخلاف الترقية. لدى App
اعتماد مباشر على حزمة Priv
الخاصة بك، واعتماد غير مباشر، من خلال Pub
، على حزمة Priv
العامة الجديدة. نظرًا لأن هاتين الحزمتين Priv
مختلفتان ولكن كلاهما مطلوبان لاستمرار عمل App
بشكل صحيح، يجب أن تشير العبارة import Priv
إلى حزمتين Priv
مختلفتين اعتمادًا على ما إذا كانت تحدث في كود App
أو في كود Pub
. للتعامل مع ذلك، تميز آلية تحميل الحزم في جوليا بين حزمتين Priv
من خلال UUID الخاص بهما وتختار الصحيحة بناءً على سياقها (الوحدة التي استدعت import
). كيف تعمل هذه التمييزات يتم تحديده من خلال البيئات، كما هو موضح في الأقسام التالية.
Environments
تحدد البيئة ما تعنيه import X
و using X
في سياقات الشيفرة المختلفة وما هي الملفات التي تتسبب هذه العبارات في تحميلها. تفهم جوليا نوعين من البيئات:
- بيئة المشروع هي دليل يحتوي على ملف مشروع وملف بيان اختياري، وتشكل بيئة صريحة. يحدد ملف المشروع ما هي الأسماء والهويات للاعتمادات المباشرة للمشروع. يوفر ملف البيان، إذا كان موجودًا، رسمًا بيانيًا كاملاً للاعتمادات، بما في ذلك جميع الاعتمادات المباشرة وغير المباشرة، والإصدارات الدقيقة لكل اعتماد، ومعلومات كافية لتحديد موقع وتحميل الإصدار الصحيح.
- دليل الحزمة هو دليل يحتوي على أشجار المصدر لمجموعة من الحزم كدلائل فرعية، ويشكل بيئة ضمنية. إذا كان
X
دليلًا فرعيًا لدليل الحزمة وX/src/X.jl
موجود، فإن الحزمةX
متاحة في بيئة دليل الحزمة وX/src/X.jl
هو ملف المصدر الذي يتم من خلاله تحميلها.
يمكن خلط هذه لإنشاء بيئة مكدسة: مجموعة مرتبة من بيئات المشاريع وأدلة الحزم، مكدسة لتكوين بيئة مركبة واحدة. ثم تتحد قواعد الأولوية والرؤية لتحديد الحزم المتاحة وأين يتم تحميلها. تشكل مسار تحميل جوليا بيئة مكدسة، على سبيل المثال.
تخدم هذه البيئات كل منها غرضًا مختلفًا:
- توفر بيئات المشاريع إمكانية التكرار. من خلال تسجيل بيئة المشروع في نظام التحكم في الإصدارات - مثل مستودع git - جنبًا إلى جنب مع بقية كود المصدر الخاص بالمشروع، يمكنك إعادة إنتاج الحالة الدقيقة للمشروع وجميع تبعياته. يلتقط ملف البيان، على وجه الخصوص، الإصدار الدقيق لكل تبعية، المحدد بواسطة تجزئة تشفيرية لشجرة مصدرها، مما يجعل من الممكن لـ
Pkg
استرداد الإصدارات الصحيحة والتأكد من أنك تقوم بتشغيل الكود الدقيق الذي تم تسجيله لجميع التبعيات. - توفر الراحة أدلة الحزم عندما لا تكون بيئة المشروع المدروسة بالكامل ضرورية. إنها مفيدة عندما تريد وضع مجموعة من الحزم في مكان ما والقدرة على استخدامها مباشرة، دون الحاجة إلى إنشاء بيئة مشروع لها.
- تسمح البيئات المكدسة بـ إضافة الأدوات إلى البيئة الأساسية. يمكنك دفع بيئة من أدوات التطوير إلى نهاية المكدس لجعلها متاحة من REPL والبرامج النصية، ولكن ليس من داخل الحزم.
على مستوى عالٍ، يحدد كل بيئة بشكل مفاهيمي ثلاثة خرائط: الجذور، الرسم البياني والمسارات. عند حل معنى import X
، تُستخدم خرائط الجذور والرسم البياني لتحديد هوية X
، بينما تُستخدم خريطة المسارات لتحديد موقع الشيفرة المصدرية لـ X
. الأدوار المحددة للخرائط الثلاث هي:
الجذور:
name::Symbol
⟶uuid::UUID
تعيين جذور البيئة يخصص أسماء الحزم إلى UUIDs لجميع التبعيات الرئيسية التي تجعلها البيئة متاحة للمشروع الرئيسي (أي تلك التي يمكن تحميلها في
Main
). عندما تواجه جولياimport X
في المشروع الرئيسي، تبحث عن هويةX
كـroots[:X]
.graph:
context::UUID
⟶name::Symbol
⟶uuid::UUID
رسم بياني للبيئة هو خريطة متعددة المستويات تعين، لكل UUID من
context
، خريطة من الأسماء إلى UUIDs، مشابهة لخريطة الجذور ولكنها محددة لذلكcontext
. عندما ترى جولياimport X
في كود الحزمة التي UUID الخاص بها هوcontext
، فإنها تبحث عن هويةX
كـgraph[context][:X]
. بشكل خاص، هذا يعني أنimport X
يمكن أن تشير إلى حزم مختلفة اعتمادًا علىcontext
.المسارات:
uuid::UUID
×name::Symbol
⟶path::String
تعيين خريطة المسارات لكل زوج UUID-name للحزمة، موقع ملف مصدر نقطة الدخول لتلك الحزمة. بعد أن يتم حل هوية
X
فيimport X
إلى UUID عبر الجذور أو الرسم البياني (اعتمادًا على ما إذا كان يتم تحميله من المشروع الرئيسي أو من تبعية)، تحدد جوليا أي ملف يجب تحميله للحصول علىX
من خلال البحث فيpaths[uuid,:X]
في البيئة. يجب أن يتضمن هذا الملف تعريف وحدة باسمX
. بمجرد تحميل هذه الحزمة، سيؤدي أي استيراد لاحق يحل إلى نفسuuid
إلى إنشاء ارتباط جديد لوحدة الحزمة التي تم تحميلها بالفعل.
كل نوع من البيئات يعرف هذه الخرائط الثلاثة بشكل مختلف، كما هو موضح في الأقسام التالية.
لراحة الفهم، تعرض الأمثلة في هذا الفصل هياكل بيانات كاملة للجذور، الرسم البياني والمسارات. ومع ذلك، فإن كود تحميل الحزمة في جوليا لا ينشئ هذه الهياكل بشكل صريح. بدلاً من ذلك، فإنه يحسب بشكل كسول فقط بقدر ما يحتاجه من كل هيكل لتحميل حزمة معينة.
Project environments
يتم تحديد بيئة المشروع من خلال دليل يحتوي على ملف مشروع يسمى Project.toml
، وملف بيان اختياري يسمى Manifest.toml
. قد تُسمى هذه الملفات أيضًا JuliaProject.toml
وJuliaManifest.toml
، وفي هذه الحالة يتم تجاهل Project.toml
وManifest.toml
. وهذا يسمح بالتعايش مع أدوات أخرى قد تعتبر الملفات المسماة Project.toml
وManifest.toml
ذات أهمية. ومع ذلك، بالنسبة لمشاريع جوليا النقية، يُفضل استخدام الأسماء Project.toml
وManifest.toml
. ومع ذلك، اعتبارًا من جوليا v1.10.8 وما بعدها، يتم التعرف على (Julia)Manifest-v{major}.{minor}.toml
كتنسيق لجعل إصدار جوليا معين يستخدم ملف بيان محدد، أي في نفس المجلد، سيتم استخدام Manifest-v1.11.toml
بواسطة v1.11 وManifest.toml
بواسطة أي إصدار جوليا آخر.
تُعرَّف جذور المشروع، والرسم البياني، وخرائط المسارات للبيئة الخاصة بالمشروع كما يلي:
خريطة الجذور للبيئة تحددها محتويات ملف المشروع، وبشكل خاص، إدخالات name
و uuid
في المستوى الأعلى وقسم [deps]
(جميعها اختيارية). اعتبر المثال التالي لملف المشروع للتطبيق الافتراضي، App
، كما تم وصفه سابقًا:
name = "App"
uuid = "8f986787-14fe-4607-ba5d-fbff2944afa9"
[deps]
Priv = "ba13f791-ae1d-465a-978b-69c3ad90f72b"
Pub = "c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"
هذا ملف المشروع يشير إلى خريطة الجذور التالية، إذا تم تمثيلها بواسطة قاموس جوليا:
roots = Dict(
:App => UUID("8f986787-14fe-4607-ba5d-fbff2944afa9"),
:Priv => UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b"),
:Pub => UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"),
)
نظرًا لخريطة الجذور هذه، في كود App
، ستؤدي العبارة import Priv
إلى جعل جوليا تبحث عن roots[:Priv]
، مما ينتج عنه ba13f791-ae1d-465a-978b-69c3ad90f72b
، وهو UUID لحزمة Priv
التي سيتم تحميلها في هذا السياق. يحدد هذا UUID أي حزمة Priv
يجب تحميلها واستخدامها عندما يقوم التطبيق الرئيسي بتقييم import Priv
.
رسم الاعتماديات لبيئة المشروع يتم تحديده بواسطة محتويات ملف البيان، إذا كان موجودًا. إذا لم يكن هناك ملف بيان، فإن الرسم يكون فارغًا. يحتوي ملف البيان على قسم لكل من الاعتماديات المباشرة أو غير المباشرة للمشروع. لكل اعتماد، يسرد الملف UUID للحزمة وهاش شجرة المصدر أو مسارًا صريحًا إلى كود المصدر. اعتبر المثال التالي لملف البيان الخاص بـ App
:
[[Priv]] # the private one
deps = ["Pub", "Zebra"]
uuid = "ba13f791-ae1d-465a-978b-69c3ad90f72b"
path = "deps/Priv"
[[Priv]] # the public one
uuid = "2d15fe94-a1f7-436c-a4d8-07a9a496e01c"
git-tree-sha1 = "1bf63d3be994fe83456a03b874b409cfd59a6373"
version = "0.1.5"
[[Pub]]
uuid = "c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"
git-tree-sha1 = "9ebd50e2b0dd1e110e842df3b433cb5869b0dd38"
version = "2.1.4"
[Pub.deps]
Priv = "2d15fe94-a1f7-436c-a4d8-07a9a496e01c"
Zebra = "f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"
[[Zebra]]
uuid = "f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"
git-tree-sha1 = "e808e36a5d7173974b90a15a353b564f3494092f"
version = "3.4.2"
هذا الملف الوصفي يصف رسم بياني كامل محتمل للاعتماديات لمشروع App
:
هناك حزمتان مختلفتان تحملان الاسم
Priv
التي يستخدمها التطبيق. يستخدم حزمة خاصة، وهي اعتماد جذر، وحزمة عامة، وهي اعتماد غير مباشر من خلالPub
. يتم تمييزها بواسطة UUIDs الخاصة بها، ولديها اعتمادات مختلفة:- تعتمد الحزمة الخاصة
Priv
على حزمتيPub
وZebra
. - العام
Priv
ليس لديه أي تبعيات.
- تعتمد الحزمة الخاصة
تعتمد التطبيق أيضًا على حزمة
Pub
، التي تعتمد بدورها على الحزمة العامةPriv
ونفس حزمةZebra
التي تعتمد عليها الحزمة الخاصةPriv
.
هذه الرسم البياني للاعتماد الممثل كقاموس، يبدو هكذا:
graph = Dict(
# Priv – the private one:
UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b") => Dict(
:Pub => UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"),
:Zebra => UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"),
),
# Priv – the public one:
UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c") => Dict(),
# Pub:
UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1") => Dict(
:Priv => UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c"),
:Zebra => UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"),
),
# Zebra:
UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62") => Dict(),
)
نظرًا لهذه التبعية graph
، عندما ترى جوليا import Priv
في حزمة Pub
—التي لها UUID c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1
—فإنها تبحث في:
graph[UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1")][:Priv]
ويحصل على 2d15fe94-a1f7-436c-a4d8-07a9a496e01c
، مما يشير إلى أنه في سياق حزمة Pub
، فإن import Priv
تشير إلى الحزمة العامة Priv
، بدلاً من الحزمة الخاصة التي تعتمد عليها التطبيق مباشرة. هذه هي الطريقة التي يمكن أن تشير بها الاسم Priv
إلى حزم مختلفة في المشروع الرئيسي مقارنةً بما هو عليه في أحد تبعيات حزمته، مما يسمح بتكرار الأسماء في نظام الحزم.
ماذا يحدث إذا تم تقييم import Zebra
في قاعدة الشيفرة الرئيسية App
؟ نظرًا لأن Zebra
لا تظهر في ملف المشروع، فإن الاستيراد سيفشل على الرغم من أن Zebra
تظهر في ملف البيان. علاوة على ذلك، إذا حدث import Zebra
في حزمة Priv
العامة - تلك التي تحمل UUID 2d15fe94-a1f7-436c-a4d8-07a9a496e01c
- فإن ذلك سيفشل أيضًا لأن تلك الحزمة Priv
ليس لديها أي تبعيات معلنة في ملف البيان وبالتالي لا يمكنها تحميل أي حزم. يمكن تحميل حزمة Zebra
فقط بواسطة الحزم التي تظهر كاعتماد صريح في ملف البيان: حزمة Pub
وأحد حزم Priv
.
خريطة المسارات لبيئة المشروع يتم استخراجها من ملف البيان. يتم تحديد مسار حزمة uuid
المسماة X
وفقًا لهذه القواعد (بالترتيب):
إذا كان ملف المشروع في الدليل يتطابق مع
uuid
واسمX
، فإما:- يحتوي على إدخال
path
في المستوى الأعلى، ثم سيتم ربطuuid
بذلك المسار، المفسر بالنسبة للدليل الذي يحتوي على ملف المشروع. - بخلاف ذلك، يتم تعيين
uuid
إلىsrc/X.jl
بالنسبة إلى الدليل الذي يحتوي على ملف المشروع.
- يحتوي على إدخال
إذا لم يكن الأمر كذلك وكان ملف المشروع يحتوي على ملف بيان مطابق وكان البيان يحتوي على مقطع يتطابق مع
uuid
، فحينئذٍ:- إذا كان هناك إدخال
path
، استخدم هذا المسار (نسبيًا إلى الدليل الذي يحتوي على ملف البيان). - إذا كان يحتوي على إدخال
git-tree-sha1
، احسب دالة تجزئة حتمية لـuuid
وgit-tree-sha1
—سمهاslug
—وابحث عن دليل باسمpackages/X/$slug
في كل دليل في مصفوفةDEPOT_PATH
العالمية في جوليا. استخدم أول دليل موجود.
- إذا كان هناك إدخال
إذا كانت أي من هذه النتائج تؤدي إلى النجاح، فإن مسار نقطة دخول كود المصدر سيكون إما تلك النتيجة، أو المسار النسبي من تلك النتيجة بالإضافة إلى src/X.jl
؛ وإلا، فلا يوجد تعيين مسار لـ uuid
. عند تحميل X
، إذا لم يتم العثور على مسار كود مصدر، ستفشل عملية البحث، وقد يُطلب من المستخدم تثبيت إصدار الحزمة المناسب أو اتخاذ إجراء تصحيحي آخر (مثل إعلان X
كاعتماد).
في ملف البيان المثال أعلاه، للعثور على مسار أول حزمة Priv
—التي تحمل UUID ba13f791-ae1d-465a-978b-69c3ad90f72b
—تبحث جوليا عن قسمها في ملف البيان، وترى أن لديها إدخال path
، وتنظر إلى deps/Priv
بالنسبة إلى دليل مشروع App
—دعنا نفترض أن كود App
يعيش في /home/me/projects/App
—ترى أن /home/me/projects/App/deps/Priv
موجودة وبالتالي تقوم بتحميل Priv
من هناك.
إذا، من ناحية أخرى، كانت جوليا تقوم بتحميل حزمة Priv
الأخرى—التي تحمل UUID 2d15fe94-a1f7-436c-a4d8-07a9a496e01c
—فإنها تجد مقطعها في البيان، وترى أنه لا يحتوي على إدخال path
، ولكنه يحتوي على إدخال git-tree-sha1
. ثم تحسب slug
لهذه الزوجية من UUID/SHA-1، والتي هي HDkrT
(التفاصيل الدقيقة لهذه الحسابات ليست مهمة، لكنها متسقة وحتمية). هذا يعني أن المسار إلى هذه الحزمة Priv
سيكون packages/Priv/HDkrT/src/Priv.jl
في أحد مستودعات الحزم. لنفترض أن محتويات DEPOT_PATH
هي ["/home/me/.julia", "/usr/local/julia"]
، فإن جوليا ستنظر إلى المسارات التالية لترى ما إذا كانت موجودة:
/home/me/.julia/packages/Priv/HDkrT
/usr/local/julia/packages/Priv/HDkrT
تستخدم جوليا أول واحدة من هذه التي توجد لتحميل الحزمة العامة Priv
من الملف packages/Priv/HDKrT/src/Priv.jl
في المستودع حيث تم العثور عليها.
هنا تمثيل لخريطة المسارات المحتملة لبيئة مشروع App
الخاص بنا، كما هو موضح في البيان المقدم أعلاه لرسم الاعتماد، بعد البحث في نظام الملفات المحلي:
paths = Dict(
# Priv – the private one:
(UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b"), :Priv) =>
# relative entry-point inside `App` repo:
"/home/me/projects/App/deps/Priv/src/Priv.jl",
# Priv – the public one:
(UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c"), :Priv) =>
# package installed in the system depot:
"/usr/local/julia/packages/Priv/HDkr/src/Priv.jl",
# Pub:
(UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"), :Pub) =>
# package installed in the user depot:
"/home/me/.julia/packages/Pub/oKpw/src/Pub.jl",
# Zebra:
(UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"), :Zebra) =>
# package installed in the system depot:
"/usr/local/julia/packages/Zebra/me9k/src/Zebra.jl",
)
تتضمن هذه الخريطة مثال ثلاثة أنواع مختلفة من مواقع الحزم (الأولى والثالثة جزء من مسار التحميل الافتراضي):
- الحزمة الخاصة
Priv
هي "vendored" داخل مستودعApp
. - الحزم العامة
Priv
وZebra
موجودة في مستودع النظام، حيث تعيش الحزم المثبتة والمدارة بواسطة مسؤول النظام. هذه متاحة لجميع المستخدمين على النظام. - حزمة
Pub
موجودة في مستودع المستخدم، حيث تعيش الحزم المثبتة بواسطة المستخدم. هذه الحزم متاحة فقط للمستخدم الذي قام بتثبيتها.
Package directories
توفر دلائل الحزم نوعًا أبسط من البيئة دون القدرة على التعامل مع تصادم الأسماء. في دليل الحزمة، مجموعة الحزم على المستوى الأعلى هي مجموعة المجلدات الفرعية التي "تبدو مثل" الحزم. توجد حزمة X
في دليل الحزمة إذا كان الدليل يحتوي على واحدة من "ملفات نقطة الدخول" التالية:
X.jl
X/src/X.jl
X.jl/src/X.jl
يعتمد ما يمكن أن تستورده حزمة في دليل الحزم على ما إذا كانت الحزمة تحتوي على ملف مشروع:
- إذا كان يحتوي على ملف مشروع، فيمكنه استيراد الحزم التي تم تحديدها فقط في قسم
[deps]
من ملف المشروع. - إذا لم يكن لديه ملف مشروع، يمكنه استيراد أي حزمة على المستوى الأعلى - أي نفس الحزم التي يمكن تحميلها في
Main
أو REPL.
خريطة الجذور يتم تحديدها من خلال فحص محتويات دليل الحزمة لإنشاء قائمة بجميع الحزم الموجودة. بالإضافة إلى ذلك، سيتم تعيين UUID لكل إدخال على النحو التالي: بالنسبة لحزمة معينة تم العثور عليها داخل المجلد X
...
- إذا كان
X/Project.toml
موجودًا ويحتوي على إدخالuuid
، فإنuuid
هو تلك القيمة. - إذا كان
X/Project.toml
موجودًا ولكنه لا يحتوي على إدخال UUID على المستوى الأعلى، فإنuuid
هو UUID وهمي تم إنشاؤه عن طريق تجزئة المسار القياسي (الحقيقي) إلىX/Project.toml
. - بخلاف ذلك (إذا لم يكن
Project.toml
موجودًا)، فإنuuid
هو nil UUID جميع الأصفار.
رسم الاعتماديات لدليل المشروع يتم تحديده من خلال وجود ومحتويات ملفات المشروع في الدليل الفرعي لكل حزمة. القواعد هي:
- إذا كان دليل الحزمة الفرعي لا يحتوي على ملف مشروع، فإنه يتم استبعاده من الرسم البياني وتُعتبر عبارات الاستيراد في شفرته على أنها عبارات على المستوى الأعلى، تمامًا مثل المشروع الرئيسي وREPL.
- إذا كان لدى دليل الحزمة ملف مشروع، فإن إدخال الرسم البياني لرقم UUID الخاص به هو خريطة
[deps]
لملف المشروع، والتي تعتبر فارغة إذا كانت القسم غائبة.
كمثال، افترض أن دليل الحزمة يحتوي على الهيكل والمحتوى التالي:
Aardvark/
src/Aardvark.jl:
import Bobcat
import Cobra
Bobcat/
Project.toml:
[deps]
Cobra = "4725e24d-f727-424b-bca0-c4307a3456fa"
Dingo = "7a7925be-828c-4418-bbeb-bac8dfc843bc"
src/Bobcat.jl:
import Cobra
import Dingo
Cobra/
Project.toml:
uuid = "4725e24d-f727-424b-bca0-c4307a3456fa"
[deps]
Dingo = "7a7925be-828c-4418-bbeb-bac8dfc843bc"
src/Cobra.jl:
import Dingo
Dingo/
Project.toml:
uuid = "7a7925be-828c-4418-bbeb-bac8dfc843bc"
src/Dingo.jl:
# no imports
هنا هي بنية الجذور المقابلة، ممثلة كقاموس:
roots = Dict(
:Aardvark => UUID("00000000-0000-0000-0000-000000000000"), # no project file, nil UUID
:Bobcat => UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf"), # dummy UUID based on path
:Cobra => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), # UUID from project file
:Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), # UUID from project file
)
هنا هي بنية الرسم البياني المقابلة، ممثلة كقاموس:
graph = Dict(
# Bobcat:
UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf") => Dict(
:Cobra => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"),
:Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"),
),
# Cobra:
UUID("4725e24d-f727-424b-bca0-c4307a3456fa") => Dict(
:Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"),
),
# Dingo:
UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc") => Dict(),
)
بعض القواعد العامة التي يجب ملاحظتها:
- يمكن أن يعتمد الحزمة التي لا تحتوي على ملف مشروع على أي اعتماد من المستوى الأعلى، ونظرًا لأن كل حزمة في دليل الحزم متاحة على المستوى الأعلى، يمكنها استيراد جميع الحزم في البيئة.
- لا يمكن لحزمة تحتوي على ملف مشروع الاعتماد على حزمة بدون ملف مشروع، حيث يمكن للحزم التي تحتوي على ملفات مشروع تحميل الحزم في
graph
، ولا تظهر الحزم بدون ملفات مشروع فيgraph
. - حزمة تحتوي على ملف مشروع ولكن بدون UUID صريح يمكن الاعتماد عليها فقط من قبل الحزم التي لا تحتوي على ملفات مشروع، حيث أن UUIDs الوهمية المعينة لهذه الحزم هي داخلية بحتة.
يرجى ملاحظة الحالات المحددة التالية من هذه القواعد في مثالنا:
Aardvark
يمكنه الاستيراد على أي منBobcat
أوCobra
أوDingo
؛ إنه يستوردBobcat
وCobra
.- يمكن لـ
Bobcat
استيراد كل منCobra
وDingo
، اللتين تحتويان على ملفات مشروع مع UUIDs وتم الإعلان عنهما كاعتماديات في قسم[deps]
الخاص بـBobcat
. Bobcat
لا يمكن أن يعتمد علىAardvark
لأنAardvark
لا يحتوي على ملف مشروع.Cobra
يمكنه ويقوم باستيرادDingo
، الذي يحتوي على ملف مشروع و UUID، ويُعلن عنه كاعتماد في قسم[deps]
الخاص بـCobra
.Cobra
لا يمكن أن تعتمد علىAardvark
أوBobcat
لأن كلاهما ليس له UUIDs حقيقية.Dingo
لا يمكنه استيراد أي شيء لأنه يحتوي على ملف مشروع بدون قسم[deps]
.
خريطة المسارات في دليل الحزمة بسيطة: إنها تربط أسماء المجلدات الفرعية بمسارات نقاط الدخول المقابلة لها. بعبارة أخرى، إذا كان المسار إلى دليل مشروعنا المثال هو /home/me/animals
فإن خريطة paths
يمكن تمثيلها بهذه القاموس:
paths = Dict(
(UUID("00000000-0000-0000-0000-000000000000"), :Aardvark) =>
"/home/me/AnimalPackages/Aardvark/src/Aardvark.jl",
(UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf"), :Bobcat) =>
"/home/me/AnimalPackages/Bobcat/src/Bobcat.jl",
(UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), :Cobra) =>
"/home/me/AnimalPackages/Cobra/src/Cobra.jl",
(UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), :Dingo) =>
"/home/me/AnimalPackages/Dingo/src/Dingo.jl",
)
نظرًا لأن جميع الحزم في بيئة دليل الحزم هي، حسب التعريف، دلائل فرعية تحتوي على ملفات نقطة الدخول المتوقعة، فإن إدخالات خريطة paths
الخاصة بها دائمًا ما تكون بهذا الشكل.
Environment stacks
النوع الثالث والأخير من البيئات هو الذي يجمع بين بيئات أخرى عن طريق تراكب عدة منها، مما يجعل الحزم في كل منها متاحة في بيئة مركبة واحدة. تُسمى هذه البيئات المركبة أكوام البيئات. يُعرف المتغير العالمي LOAD_PATH
في جوليا كومة بيئات - البيئة التي تعمل فيها عملية جوليا. إذا كنت تريد أن تكون عملية جوليا الخاصة بك قادرة على الوصول فقط إلى الحزم في مشروع أو دليل حزم واحد، اجعلها المدخل الوحيد في LOAD_PATH
. ومع ذلك، من المفيد غالبًا أن يكون لديك وصول إلى بعض أدواتك المفضلة - المكتبات القياسية، أدوات التحليل، أدوات تصحيح الأخطاء، الأدوات الشخصية، إلخ - حتى لو لم تكن تعتمد على المشروع الذي تعمل عليه. من خلال إضافة بيئة تحتوي على هذه الأدوات إلى مسار التحميل، سيكون لديك وصول فوري إليها في الكود على المستوى الأعلى دون الحاجة إلى إضافتها إلى مشروعك.
آلية دمج الجذور، الرسم البياني وهياكل بيانات المسارات لمكونات مكدس البيئة بسيطة: يتم دمجها كقواميس، مع تفضيل الإدخالات السابقة على اللاحقة في حالة تصادم المفاتيح. بعبارة أخرى، إذا كان لدينا stack = [env₁, env₂, …]
فإن لدينا:
roots = reduce(merge, reverse([roots₁, roots₂, …]))
graph = reduce(merge, reverse([graph₁, graph₂, …]))
paths = reduce(merge, reverse([paths₁, paths₂, …]))
تتوافق المتغيرات الفرعية rootsᵢ
و graphᵢ
و pathsᵢ
مع البيئات الفرعية envᵢ
الموجودة في stack
. يتواجد reverse
لأن merge
يفضل الوسيطة الأخيرة بدلاً من الأولى عندما تكون هناك تصادمات بين المفاتيح في قواميس وسائطه. هناك بعض الميزات الجديرة بالملاحظة في هذا التصميم:
- البيئة الرئيسية—أي البيئة الأولى في المكدس—مضمنة بدقة في بيئة مكدسة. يتم ضمان تضمين الرسم البياني الكامل للاعتماديات للبيئة الأولى في المكدس بشكل سليم في البيئة المكدسة بما في ذلك نفس إصدارات جميع الاعتماديات.
- يمكن أن تنتهي الحزم في البيئات غير الأساسية باستخدام إصدارات غير متوافقة من تبعياتها حتى لو كانت بيئاتها الخاصة متوافقة تمامًا. يمكن أن يحدث هذا عندما يتم حجب أحد تبعياتها بواسطة إصدار في بيئة سابقة في المكدس (سواء من خلال الرسم البياني أو المسار، أو كليهما).
نظرًا لأن البيئة الأساسية عادةً ما تكون بيئة المشروع الذي تعمل عليه، بينما تحتوي البيئات اللاحقة في السلسلة على أدوات إضافية، فإن هذه هي المقايضة الصحيحة: من الأفضل كسر أدوات التطوير الخاصة بك ولكن الحفاظ على عمل المشروع. عندما تحدث مثل هذه التوافقات، ستحتاج عادةً إلى ترقية أدوات التطوير الخاصة بك إلى إصدارات متوافقة مع المشروع الرئيسي.
Package Extensions
حزمة "extension" هي وحدة يتم تحميلها تلقائيًا عندما يتم تحميل مجموعة محددة من الحزم الأخرى (المعروفة باسم "المحفزات") في جلسة جوليا الحالية. يتم تعريف الامتدادات تحت قسم [extensions]
في ملف المشروع. المحفزات الخاصة بامتداد هي مجموعة فرعية من تلك الحزم المدرجة تحت قسم [weakdeps]
(وربما، ولكن بشكل غير شائع، قسم [deps]
) في ملف المشروع. يمكن أن تحتوي تلك الحزم على إدخالات توافق مثل الحزم الأخرى.
name = "MyPackage"
[compat]
ExtDep = "1.0"
OtherExtDep = "1.0"
[weakdeps]
ExtDep = "c9a23..." # uuid
OtherExtDep = "862e..." # uuid
[extensions]
BarExt = ["ExtDep", "OtherExtDep"]
FooExt = "ExtDep"
...
المفاتيح تحت extensions
هي أسماء الإضافات. يتم تحميلها عندما يتم تحميل جميع الحزم على الجانب الأيمن (المشغلات) لتلك الإضافة. إذا كانت الإضافة تحتوي على مشغل واحد فقط، يمكن كتابة قائمة المشغلات كسلسلة نصية فقط للاختصار. موقع نقطة الدخول للإضافة يكون إما في ext/FooExt.jl
أو ext/FooExt/FooExt.jl
للإضافة FooExt
. غالبًا ما يتم هيكلة محتوى الإضافة على النحو التالي:
module FooExt
# Load main package and triggers
using MyPackage, ExtDep
# Extend functionality in main package with types from the triggers
MyPackage.func(x::ExtDep.SomeStruct) = ...
end
عند إضافة حزمة مع ملحقات إلى بيئة، يتم تخزين أقسام weakdeps
و extensions
في ملف البيان في القسم الخاص بتلك الحزمة. قواعد البحث عن التبعيات لحزمة ما هي نفسها كما هو الحال بالنسبة لـ "الوالد" الخاص بها باستثناء أن المحفزات المدرجة تُعتبر أيضًا تبعيات.
Package/Environment Preferences
تُعتبر التفضيلات قواميس للبيانات الوصفية التي تؤثر على سلوك الحزمة داخل بيئة معينة. يدعم نظام التفضيلات قراءة التفضيلات في وقت الترجمة، مما يعني أنه في وقت تحميل الكود، يجب علينا التأكد من أن ملفات ما قبل الترجمة التي اختارتها جوليا تم بناؤها بنفس التفضيلات مثل البيئة الحالية قبل تحميلها. تحتوي واجهة برمجة التطبيقات العامة لتعديل التفضيلات على Preferences.jl الحزمة. يتم تخزين التفضيلات كقواميس TOML داخل ملف (Julia)LocalPreferences.toml
بجوار المشروع النشط حاليًا. إذا تم "تصدير" تفضيل، فإنه يتم تخزينه بدلاً من ذلك داخل ملف (Julia)Project.toml
. الهدف هو السماح للمشاريع المشتركة باحتواء تفضيلات مشتركة، مع السماح للمستخدمين بأنفسهم بتجاوز تلك التفضيلات بإعداداتهم الخاصة في ملف LocalPreferences.toml، والذي يجب تجاهله في .git كما يوحي الاسم.
تُعَدُّ التفضيلات التي يتم الوصول إليها أثناء التجميع تلقائيًا تفضيلات وقت التجميع، وأي تغيير يتم تسجيله لهذه التفضيلات سيؤدي إلى إعادة تجميع أي ملف(ملفات) ما قبل التجميع المخزنة (.ji
وملفات .so
أو .dll
أو .dylib
المقابلة) لذلك الوحدة. يتم ذلك عن طريق تسلسل تجزئة جميع التفضيلات الخاصة بوقت التجميع أثناء التجميع، ثم التحقق من تلك التجزئة مقابل البيئة الحالية عند البحث عن الملف(الملفات) المناسبة لتحميلها.
يمكن تعيين التفضيلات باستخدام إعدادات افتراضية على مستوى المستودع؛ إذا تم تثبيت الحزمة Foo ضمن بيئتك العالمية ولديها تفضيلات محددة، ستنطبق هذه التفضيلات طالما أن بيئتك العالمية جزء من LOAD_PATH
. يتم تجاوز التفضيلات في البيئات الأعلى في مكدس البيئة بواسطة الإدخالات الأقرب في مسار التحميل، بدءًا من المشروع النشط حاليًا. يسمح ذلك بوجود تفضيلات افتراضية على مستوى المستودع، مع إمكانية دمج المشاريع النشطة لهذه التفضيلات الموروثة أو حتى الكتابة عليها بالكامل. راجع نص الوثيقة لـ Preferences.set_preferences!()
للحصول على التفاصيل الكاملة حول كيفية تعيين التفضيلات للسماح أو عدم السماح بالدمج.
Conclusion
إدارة الحزم الفيدرالية وإعادة إنتاج البرمجيات بدقة هما هدفان صعبان ولكنهما يستحقان الجهد في نظام الحزم. معًا، تؤدي هذه الأهداف إلى آلية تحميل حزم أكثر تعقيدًا مما تمتلكه معظم اللغات الديناميكية، لكنها أيضًا توفر قابلية التوسع وإعادة الإنتاج التي ترتبط عادةً باللغات الثابتة. عادةً، يجب أن يكون مستخدمو جوليا قادرين على استخدام مدير الحزم المدمج لإدارة مشاريعهم دون الحاجة إلى فهم دقيق لهذه التفاعلات. ستضيف مكالمة Pkg.add("X")
إلى ملفات المشروع والبيان المناسبة، المختارة عبر Pkg.activate("Y")
، بحيث ستقوم مكالمة مستقبلية لـ import X
بتحميل X
دون مزيد من التفكير.