اللغات: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
← واجهة NeverC الثنائية للإضافات
تنشر PluginSource.h جدولين. NevercIOAPI هي نظام الملفات: مزوّدو الملفات
الافتراضية، والقراءة، واجتياز الأدلة، ومصارف الإخراج، وتسجيل التبعيات. أما
NevercSourceLocationAPI فتعيد مواضع المترجم الداخلية إلى ملفات وأسطر ونص
مكتوب. وبهما معًا تستطيع الإضافة أن تقدّم ترويسة لا وجود لها إلا في الذاكرة، أو
أن تحلّ توسيع ماكرو حتى موضع كتابته، أو أن تكتب خرجًا جانبيًا يشارك في حساب
ديمومة البناء.
#include "neverc/Plugin/PluginSource.h"| الواجهة | الجدول | وحدات ماكرو الإصدار |
|---|---|---|
NEVERC_INTERFACE_IO_{HIGH,LOW} |
NevercIOAPI |
NEVERC_IO_API_MAJOR / _MINOR |
NEVERC_INTERFACE_SOURCE_LOCATION_{HIGH,LOW} |
NevercSourceLocationAPI |
NEVERC_SOURCE_LOCATION_API_MAJOR / _MINOR |
NEVERC_SOURCE_API_MAJOR و_MINOR اسمان مرادفان لزوج source-location.
| المرحلة | السياسة | المعنى |
|---|---|---|
neverc.source.resolve_input |
OBSERVABLE، INTERCEPTABLE | تحويل دخل المشغّل إلى دخل مصدري |
neverc.source.open |
إضافةً إلى REPLACEABLE | إنتاج وحدة المصدر لدخل ما |
neverc.source.after_open |
OBSERVABLE | إشعار بأن وحدةً صارت متاحة |
ولأن neverc.source.open قابلة للاستبدال، يستطيع المزوّد أن يعيد وحدةً ركّب
بايتاتها بنفسه، وهذه هي الطريقة المدعومة لحقن شيفرة مولَّدة دون لمس القرص.
يستحوذ مزوّد VFS على بادئة مسار، ويجيب عن الأسئلة الأربعة التي يطرحها المترجم عن الملف.
typedef struct NevercVFSProviderDescriptor {
NevercABITableHeader Header;
NevercStringView ProviderID;
NevercStringView RoutePrefix;
NevercBool Deterministic;
NevercBool Cacheable;
uint64_t Reserved;
NevercVFSPathPredicateFn MatchesPath;
NevercVFSProviderStatusFn Status;
NevercVFSProviderOpenReadFn OpenRead;
NevercVFSProviderReadDirectoryFn ReadDirectory;
NevercVFSProviderCanonicalizeFn Canonicalize;
void *UserData;
NevercDestroyUserDataFn DestroyUserData;
} NevercVFSProviderDescriptor;يملأ كل ردّ نداء نتيجةً يبيّن حقل Disposition فيها ما إذا كان المزوّد قد عالج
الطلب:
static NevercStatus NEVERC_CALL
open_read(NevercTaskHandle Task, NevercStringView Path, void *UserData,
NevercVFSOpenReadResult *OutResult) {
static const char Header[] = "#define GENERATED 1\n";
if (!path_matches(Path)) {
OutResult->Disposition = NEVERC_VFS_RESULT_NOT_HANDLED;
return neverc_status_ok();
}
OutResult->Disposition = NEVERC_VFS_RESULT_HANDLED;
OutResult->Status.Type = NEVERC_VFS_FILE_REGULAR;
OutResult->Status.Size = sizeof(Header) - 1;
OutResult->Content.Data = (const uint8_t *)Header;
OutResult->Content.Length = sizeof(Header) - 1;
OutResult->Content.NullTerminated = NEVERC_TRUE;
return neverc_status_ok();
}إعادة NEVERC_VFS_RESULT_NOT_HANDLED تمرّر الطلب إلى المزوّد التالي، ثم إلى
نظام الملفات الحقيقي في النهاية. وأنواع الملفات هي
NEVERC_VFS_FILE_UNKNOWN وREGULAR وDIRECTORY وSYMLINK وOTHER.
يتم التسجيل أثناء Register:
IO->RegisterVFSProvider(IO->Context, RegistrarContext, &Descriptor);أما إن أردت ملفًا واحدًا في الذاكرة يكفي أن يوجد طوال جلسة واحدة، فتجاوز المزوّد كليًا:
IO->AddMemoryFile(IO->Context, Session, SV("/virtual/config.h"),
Content, ModificationTime);pluginsdk/examples/VirtualHeaderPlugin.c مزوّد كامل وعامل.
NevercVFSStatus Status;
IO->Stat(IO->Context, Task, Path, &Status);
NevercFileHandle File;
IO->OpenFileForRead(IO->Context, Task, Path, &File);
NevercBufferHandle Buffer;
IO->ReadFile(IO->Context, Task, File, /*Offset=*/0, /*Length=*/Status.Size,
&Buffer);
NevercBufferView View;
IO->GetBufferView(IO->Context, Task, Buffer, &View);
/* View.Data / View.Length / View.NullTerminated */
IO->ReleaseBuffer(IO->Context, Task, Buffer);
IO->CloseFile(IO->Context, Task, File);تحوّل CopyBuffer بايتاتٍ تملكها أنت إلى مخزن مؤقت لدى المضيف، وتحلّ
Canonicalize المسار، وتتولى GetWorkingDirectory / SetWorkingDirectory
الدليل الحالي للمهمة. أما الأدلة فتُجتاز بـOpenDirectory وReadDirectory
(التي تضبط OutHasEntry إلى NEVERC_FALSE عند النهاية) وCloseDirectory.
تُبلَّغ رموز أخطاء الإدخال/الإخراج في NevercStatus.Detail:
NEVERC_IO_ERROR_NOT_FOUND وPERMISSION_DENIED وNOT_DIRECTORY
وIS_DIRECTORY وINVALID_PATH وIO.
المخرجات معامَلاتية. تفتح مصرفًا، ثم تكتب، ثم تُنهي لتحصل على ختم — حجمٍ وبصمةٍ من 32 بايت يستطيع نظام البناء التحقق منها.
NevercOutputSinkHandle Sink;
IO->BeginFileOutput(IO->Context, Task, SV("out.json"), /*SizeBudget=*/0, &Sink);
IO->OutputWrite(IO->Context, Task, Sink, Bytes);
IO->OutputMetadataSet(IO->Context, Task, Sink, SV("content-type"),
SV("application/json"));
NevercOutputSeal Seal = {0};
Seal.Header = (NevercABITableHeader){sizeof(Seal), NEVERC_IO_API_MAJOR,
NEVERC_IO_API_MINOR, 0};
IO->OutputFinish(IO->Context, Task, Sink, &Seal);| الدالة | الغرض |
|---|---|
BeginMemoryOutput |
مصرف مسنود بالذاكرة ومسمّى منطقيًا |
BeginFileOutput |
مصرف يحطّ ذريًّا عند مسار نهائي |
BeginStreamOutput |
مصرف على NEVERC_OUTPUT_STREAM_STDOUT أو _STDERR |
OutputWrite، OutputWriteAt |
الإلحاق، أو الكتابة عند إزاحة |
OutputTell، OutputTruncate |
التحكم بالموضع والحجم |
OutputMetadataSet |
إرفاق زوج مفتاح/قيمة بالخرج |
OutputFinish |
ختم الخرج وإنتاج NevercOutputSeal |
OutputAbort |
إسقاط كل ما كُتب |
OutputGetSummary |
فحص الحالة والرايات والحجم والبصمة في أي وقت |
تنتقل NevercOutputSummary.State عبر NEVERC_OUTPUT_OPEN وFINISHED
وCOMMITTED وABORTED وFAILED_PARTIAL، بينما تسجّل Flags كلًّا من
PUBLISHED وDURABLE وMAY_BE_PARTIAL وRECOVERY_REQUIRED
وDURABILITY_UNCONFIRMED. وهذه الرايات هي نفس المعلومات التي يظهرها المشغّل في
NevercStatus.Flags، فيمكن تمييز انهيارٍ في منتصف الكتابة عن إخفاق نظيف.
قيمة SizeBudget صفرًا تعني بلا حدّ؛ أما الميزانية غير الصفرية فتجعل التجاوز
يفشل بـNEVERC_STATUS_RESOURCE_EXHAUSTED بدلًا من ملء القرص.
إن قرأت الإضافة شيئًا ينبغي لنظام البناء تتبّعه، فصرّح بذلك. وإلا فلن تعيد البناءات التزايدية البناء عندما يتغير ذلك الدخل.
NevercDependencyDescriptor Dependency = {0};
Dependency.Header = (NevercABITableHeader){sizeof(Dependency),
NEVERC_IO_API_MAJOR,
NEVERC_IO_API_MINOR, 0};
Dependency.CanonicalPath = SV("/etc/mytool/rules.txt");
Dependency.ContentDigest = Digest;
Dependency.Kind = NEVERC_INPUT_DEPENDENCY_RESOURCE;
Dependency.System = NEVERC_FALSE;
Dependency.ProviderID = SV("com.example.myplugin");
NevercDependencyHandle Handle;
IO->RecordDependency(IO->Context, Task, &Dependency, &Handle);والأنواع هي NEVERC_INPUT_DEPENDENCY_SOURCE وINCLUDE وMODULE وRESOURCE
وTOOL وPLUGIN.
NevercSourceLocation معتِمة. وجدول المواضع يحوّلها إلى شيء يمكنك طباعته أو
مقارنته.
NevercSourceLocationInfo Info = {0};
Info.Header = (NevercABITableHeader){sizeof(Info),
NEVERC_SOURCE_LOCATION_API_MAJOR,
NEVERC_SOURCE_LOCATION_API_MINOR, 0};
Source->GetLocationInfo(Source->Context, Task, Location, &Info);
/* Info.Kind هي NEVERC_SOURCE_LOCATION_FILE أو _MACRO؛
ثم تأتي Info.FileOffset وInfo.Line وInfo.Column. */هناك أربعة تحويلات تنقّلك بين رؤى الموضع الواحد، وكلها تتشارك التوقيع
NevercTransformSourceLocationFn:
| الدالة | ما تُعيده |
|---|---|
GetSpellingLocation |
حيث كُتبت محارف الرمز فعليًا |
GetExpansionLocation |
حيث يظهر توسيع الماكرو في المصدر |
GetFileLocation |
أقرب موضع ملف |
GetIncludeLocation |
تعليمة #include التي جلبت الملف |
GetTokenEnd |
الموضع التالي لآخر محرف في الرمز |
تطبّق GetPresumedLocation توجيهات #line وتعطي اسم ملف وسطرًا وعمودًا وموضع
تضمين. ويمنحك GetLocationFile مع GetFileInfo المسار المعياري والحجم ووقت
التعديل والمعرّف الفريد، وما إذا كان الملف للمستخدم أو للنظام أو لنظام extern-C:
typedef struct NevercFileInfo {
NevercABITableHeader Header;
NevercStringView Path;
NevercStringView CanonicalPath;
uint64_t Size;
int64_t ModificationTime;
NevercFileUniqueID UniqueID; /* {Device, File} */
NevercFileCharacteristic Characteristic;
NevercBool NamedPipe;
} NevercFileInfo;وتُقرأ المدَيات بـGetRangeInfo (التي تبلّغ عن Begin وEnd وعمّا إذا كان
المدى NEVERC_SOURCE_RANGE_CHARACTER أم _TOKEN)، أما البايتات نفسها
فبـGetSourceText أو GetCharacterData.
وحين تحتاج مواضع كثيرة دفعةً واحدة — كتمريرة تشخيص على دالة كاملة مثلًا — استعمل الصيغة الدُفعية بدل نداء لكل موضع:
Source->GetLocationInfoBatch(Source->Context, Task, Locations, LocationCount,
OutInfos, OutInfoCapacity);رؤية الدخل وبايتاته على مستوى المراحل:
NevercSourceInputInfo Input = {0};
Source->GetSourceInput(Source->Context, Frame, Frame->Input, &Input);
/* Input.Path و.Kind (FILE أو BUFFER) و.Language و.System و.Preprocessed */ويجيب مزوّد neverc.source.open بوحدة مسنودة بالذاكرة:
NevercMemorySourceUnitDescriptor Unit = {0};
Unit.Header = (NevercABITableHeader){sizeof(Unit),
NEVERC_SOURCE_LOCATION_API_MAJOR,
NEVERC_SOURCE_LOCATION_API_MINOR, 0};
Unit.LogicalPath = SV("/virtual/generated.c");
Unit.CanonicalIdentity = SV("com.example:generated:v1");
Unit.Content = Bytes;
Unit.ProviderID = SV("com.example.myplugin");
Unit.Deterministic = NEVERC_TRUE;
Unit.Cacheable = NEVERC_TRUE;
NevercArtifactHandle Output;
Source->CreateMemorySourceUnit(Source->Context, Frame, Frame->Input, &Unit,
&Output);CanonicalIdentity هي ما تفهرس عليه الذاكرة المخبّأة، فوجب أن تتغير كلما تغيّر
المحتوى. وتقرأ GetSourceUnit الوحدة مجددًا، وتبلّغ زيادةً عن MemoryBacked.
- المخازن المؤقتة الآتية من
ReadFileوCopyBufferوPathToBufferمملوكة للمضيف؛ حرّر كلًّا منها بـReleaseBuffer. - كل
OpenFileForReadيحتاجCloseFile، وكلOpenDirectoryيحتاجCloseDirectory، وكل مصرف إخراج يحتاجOutputFinishأوOutputAbort. - الرؤى داخل
NevercFileInfoوNevercVFSStatusونتائج المواضع مستعارة طوال ردّ النداء فقط. - يعمل ردّ نداء مزوّد VFS على خيط المهمة، ويجب ألا يعاود استدعاء المترجم؛ أجب من بيانات تملكها سلفًا.
- صرّح بـ
DeterministicوCacheableبصدق. فالمزوّد الذي يقرأ الساعة أو البيئة ثم يدّعي الحتمية سينتج ذاكرة بناء مخبّأة مسمومة. - نطاق
AddMemoryFileهو الجلسة؛ وحين يعتمد المحتوى على المهمة يكون المزوّد هو الأداة الصحيحة.
انظر PluginSource.h للتصريحات المعيارية، وSchema/PhaseSchema.json
لمراحل المصدر الثلاث وسياساتها، و
pluginsdk/examples/VirtualHeaderPlugin.c لمزوّد كامل.