اللغات: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
← واجهة NeverC الثنائية للإضافات
يحوّل المشغّل (driver) سطر الأوامر إلى مجموعة من المهام المنفَّذة. تكشف
PluginDriver.h هذا المسار في صورة ست مراحل وجدول قدرات واحد هو
NevercDriverAPI، فتستطيع الإضافة إعادة كتابة الوسائط، واختيار سلسلة الأدوات،
وإعادة هيكلة مخطط الإجراءات، وإضافة المهام أو استبدالها، بل وحتى تنفيذ مهمة
داخل العملية الحالية بدل إطلاق عملية جديدة.
#include "neverc/Plugin/PluginDriver.h"
Bootstrap->QueryInterface(
Bootstrap->Context,
(NevercInterfaceID){NEVERC_INTERFACE_DRIVER_HIGH,
NEVERC_INTERFACE_DRIVER_LOW},
NEVERC_DRIVER_API_MAJOR, NEVERC_DRIVER_API_MINOR,
&Table, &Minor, &TableSize);NevercDriverAPI جدول مسطّح واحد يضم 67 فتحة دالة موزَّعة على خمس مناطق:
الوسائط الخام، والخيارات المُحلَّلة، واختيار سلسلة الأدوات، ومخطط الإجراءات،
ومخطط المهام. تحقَّق من TableSize مقابل إزاحة آخر فتحة تستعملها؛ الذيل الحالي
هو GetJobResult.
| المرحلة | السياسة | الدخل ← الخرج |
|---|---|---|
neverc.driver.raw_arguments |
OBSERVABLE، INTERCEPTABLE | argv ← argv |
neverc.driver.parsed_arguments |
OBSERVABLE، INTERCEPTABLE | قائمة خيارات محلَّلة ← قائمة خيارات محلَّلة |
neverc.driver.select_toolchain |
إضافةً إلى REPLACEABLE | طلب سلسلة أدوات ← اختيار سلسلة أدوات |
neverc.driver.build_actions |
إضافةً إلى REPLACEABLE | طلب ← مخطط إجراءات |
neverc.driver.build_jobs |
إضافةً إلى REPLACEABLE | مخطط إجراءات ← مخطط مهام |
neverc.driver.execute_job |
إضافةً إلى REPLACEABLE | طلب تنفيذ مهمة ← نتيجة مهمة |
تتبع وحدات الماكرو الخاصة بها النمط المعتاد:
NEVERC_PHASE_DRIVER_RAW_ARGUMENTS_{NAME,HIGH,LOW,POLICY,…}.
تُعلَن الخيارات مرة واحدة أثناء Register، وبعدها يقبلها المشغّل على سطر
الأوامر تمامًا كما لو كانت مدمجة.
typedef struct NevercOptionDescriptor {
NevercABITableHeader Header;
NevercStringView Spelling;
NevercStringList Aliases;
NevercOptionForm Form; /* FLAG, JOINED, SEPARATE, MULTI_ARG */
NevercOptionValueType ValueType; /* BOOL, INT, UINT, STRING, ENUM, PATH */
NevercOptionMultiplicity Multiplicity; /* SINGLE, LAST_WINS, APPEND */
uint32_t ArgumentCount;
NevercBool Required;
NevercBool Hidden;
NevercStringView Help;
NevercStringView Metavar;
NevercStructArrayView EnumValues; /* NevercOptionEnumValue[] */
NevercStringList Conflicts;
NevercStringList Requires;
NevercStringView TargetPredicate;
NevercOptionValidatorFn Validator;
void *UserData;
NevercDestroyUserDataFn DestroyUserData;
} NevercOptionDescriptor;من pluginsdk/examples/DriverTracePlugin.c:
NevercOptionDescriptor Option = {0};
Option.Header = (NevercABITableHeader){sizeof(Option), NEVERC_DRIVER_API_MAJOR,
NEVERC_DRIVER_API_MINOR, 0};
Option.Spelling = SV("--driver-trace");
Option.Form = NEVERC_OPTION_FLAG;
Option.ValueType = NEVERC_OPTION_BOOL;
Option.Multiplicity = NEVERC_OPTION_SINGLE;
Option.Help = SV("enable the driver trace example plugin");
Status = Registrar->RegisterOption(RegistrarContext, &Option);تُستدعى Validator عند كل ظهور مع NevercOptionValidationContext يحمل معرِّف
الإضافة، والهجاء، وثلاثي الهدف، ورقم الظهور، فيمكن رفض قيمة بتشخيص حقيقي بدل
الفشل لاحقًا. ويقصر TargetPredicate الخيار على الثلاثيات المطابقة. تُقرأ القيم
مرة أخرى عبر NevercCoreAPI.GetPluginOptionValueCount وGetPluginOptionValue.
عند neverc.driver.raw_arguments يكون الأثر هو متجه argv. القراءة قائمة على
الفهرس، وكل مدخل يُبلِّغ عن مصدره:
Driver->GetArgumentCount(Driver->Context, Frame, Frame->Input, &Count);
NevercStringView Value, Source;
NevercArgumentOrigin Origin; /* COMMAND_LINE, CONFIGURATION, PLUGIN */
uint64_t Position;
Driver->GetArgument(Driver->Context, Frame, Frame->Input, Index,
&Value, &Origin, &Source, &Position);التحرير معاملاتي ولا يجوز إلا من داخل مُعترِض، لأن التعديل مرتبط بالمتابعة (continuation):
NevercArgumentMutationHandle Mutation;
Driver->BeginArgumentMutation(Driver->Context, Frame, Continuation,
Frame->Input, &Mutation);
Driver->InsertArgument(Driver->Context, Mutation, Index, SV("-O2"));
Driver->ReplaceArgument(Driver->Context, Mutation, Index, SV("-O3"));
Driver->EraseArgument(Driver->Context, Mutation, Index);
Driver->CommitArgumentMutation(Driver->Context, Mutation); /* أو Abort */تعمل neverc.driver.parsed_arguments على ظهورات الخيارات لا على السلاسل، وهو
ما تحتاجه عند إضافة راية يجب ألّا يُعاد تحليلها معجميًّا:
typedef struct NevercOptionOccurrence {
NevercABITableHeader Header;
uint64_t Occurrence;
NevercStringView Spelling;
NevercStringList Values;
NevercArgumentOrigin Origin;
uint32_t Reserved;
} NevercOptionOccurrence;تقرأ GetOptionOccurrenceCount وGetOptionOccurrence؛ ثم تُحرِّر
BeginParsedArgumentMutation وAddOptionOccurrence و
RemoveOptionOccurrence وReplaceOptionOccurrence و
CommitParsedArgumentMutation / AbortParsedArgumentMutation.
يصف الطلب ما طُلب وما حسبه المشغّل:
typedef struct NevercToolChainRequest {
NevercABITableHeader Header;
NevercStringView RequestedTriple;
NevercStringView ComputedTriple;
NevercStringView SysRoot;
NevercStringView ResourceDir;
NevercStringView CPU;
NevercStringList Features;
NevercExecutionLevel ExecutionLevel; /* UNSPECIFIED, USER, KERNEL */
NevercBool DynamicCodeProfile;
uint32_t Reserved;
} NevercToolChainRequest;يستطيع المُعترِض تعديل الطلب عبر BeginToolChainMutation و
SetToolChainTriple وSetToolChainCPU وSetToolChainFeatures و
CommitToolChainMutation. أما المزوِّد فيجيب عن المرحلة كاملة عبر
CreateToolChainSelection، مسمِّيًا أحد معرِّفات سلاسل الأدوات المدمجة أو
معرِّفه الخاص:
NEVERC_TOOLCHAIN_ID_DARWIN /* "neverc.builtin.darwin" */
NEVERC_TOOLCHAIN_ID_LINUX /* "neverc.builtin.linux" */
NEVERC_TOOLCHAIN_ID_MSVC /* "neverc.builtin.msvc" */
NEVERC_TOOLCHAIN_ID_GENERIC_ELF /* "neverc.builtin.generic-elf" */
NEVERC_TOOLCHAIN_ID_MACHO /* "neverc.builtin.macho" */
NEVERC_TOOLCHAIN_ID_GENERIC_GCC /* "neverc.builtin.generic-gcc" */تقرأ GetToolChainSelection النتيجة وتُبلِّغ عن BuiltinProviderUsed، فيعرف
المراقب ما إذا كانت إضافة قد فازت بالمرحلة.
عقدة الإجراء خطوة ترجمة ذات نوع. وتشير العقد إلى مدخلات المشغّل وإلى عقد أخرى:
typedef struct NevercActionNode {
NevercABITableHeader Header;
NevercActionNodeID Node;
NevercActionKind Kind;
NevercDriverType OutputType;
uint64_t InputCount;
NevercDriverInputID DriverInput;
NevercStringView BindArch;
uint64_t Reserved;
} NevercActionNode;NevercActionKind |
NevercDriverType |
||
|---|---|---|---|
INPUT |
BIND_ARCH |
PP_C، C، C_HEADER |
PP_ASM، ASM |
PREPROCESS |
COMPILE |
LLVM_IR، LLVM_BC |
LTO_IR، LTO_BC |
BACKEND |
ASSEMBLE |
OBJECT، IMAGE |
DSYM |
LINK، LIPO |
DSYMUTIL |
DEPENDENCIES |
NOTHING |
STATIC_LIB |
DYNCODE |
القراءة عبر GetDriverInputCount / GetDriverInput، وGetActionNodeCount /
GetActionNode / GetActionNodeInput، وGetActionRootCount /
GetActionRoot.
يمر بناء مخطط بديل عبر باني، ثم نشر واحد:
NevercActionGraphBuilderHandle Builder;
Driver->CreateActionGraphBuilder(Driver->Context, Frame, Request, &Builder);
NevercActionNodeDescriptor Node = {0};
Node.Header = (NevercABITableHeader){sizeof(Node), NEVERC_DRIVER_API_MAJOR,
NEVERC_DRIVER_API_MINOR, 0};
Node.Kind = NEVERC_ACTION_COMPILE;
Node.OutputType = NEVERC_DRIVER_TYPE_OBJECT;
Node.Inputs = /* NevercActionNodeIDList */;
NevercActionNodeID Created;
Driver->AddActionNode(Driver->Context, Builder, &Node, &Created);
Driver->SetActionRoots(Driver->Context, Builder, Roots);
Driver->PublishActionGraph(Driver->Context, Frame, Builder, &OutGraph);وتُحرِّر RemoveActionNode وReplaceActionNodeInputs و
SetActionNodeOutputType وSetActionNodeBindArch بانيًا قيد الإنشاء. ولتعديل
مخطط المضيف القائم بدل إعادة بنائه، استعمل BeginActionGraphMutation و
CommitActionGraphMutation؛ وتتخلص AbortActionGraphEdit من أي من الشكلين.
المهمة أمر يُنفَّذ. ويصف NevercJobDescriptor واحدة منها:
typedef struct NevercJobDescriptor {
NevercABITableHeader Header;
NevercJobKind Kind; /* COMMAND, FRONTEND, LINKER,
ARCHIVE, PLUGIN, DYNCODE */
NevercResponseFileKind ResponseFileKind; /* NONE, FULL, LIST */
NevercResponseFileEncoding ResponseFileEncoding;/* UTF8, CURRENT_CODE_PAGE,
UTF16 */
NevercBool InProcess;
NevercActionNodeID SourceAction;
NevercLinkerFlavor LinkerFlavor; /* NONE, GNU, WIN_LINK, DARWIN */
uint32_t Reserved;
NevercStringView Executable;
NevercStringList Arguments;
NevercStringList Environment;
NevercJobFileList Inputs;
NevercJobFileList Outputs;
NevercJobIDList Dependencies;
NevercStringView CallbackID;
NevercPluginJobCallbackFn Callback;
void *UserData;
} NevercJobDescriptor;اضبط Kind على NEVERC_JOB_PLUGIN مع Callback، فيشغّل المشغّل دالتك في
الموضع الذي كان سيطلق فيه عملية:
static NevercStatus NEVERC_CALL run_job(const NevercPluginJobContext *Context,
int32_t *OutExitCode, void *UserData) {
/* Context->Arguments و->Environment و->Inputs و->Outputs مُستعارة. */
*OutExitCode = 0;
return neverc_status_ok();
}قراءة المخطط تماثل مخطط الإجراءات: GetJobCount / GetJob، و
GetJobDependency، وGetJobArgument / GetJobEnvironment، وGetJobInput /
GetJobOutput. لاحظ أن NevercJob يُبلِّغ عن الأعداد فقط؛ اجلب كل سلسلة أو ملف
بالفهرس بدل توقّع مصفوفة مضمَّنة.
يستعمل التحرير CreateJobGraphBuilder أو BeginJobGraphMutation، ثم AddJob
وRemoveJob وMoveJobBefore وReplaceJob وSetJobArgument و
SetJobEnvironment وSetJobInput وSetJobOutput و
ReplaceJobDependencies. والنشر بـPublishJobGraph أو
CommitJobGraphMutation؛ والتخلص بـAbortJobGraphEdit.
عند neverc.driver.execute_job يكون أثر الدخل هو
NevercJobExecutionRequest: المهمة مع قوائمها المُجسَّدة بالكامل من الوسائط
والبيئة والمدخلات والمخرجات والاعتماديات. ينفِّذ المزوِّد المهمة ويُبلِّغ عن
النتيجة:
typedef struct NevercJobResultDescriptor {
NevercABITableHeader Header;
int32_t ExitCode;
NevercBool ExecutionFailed;
NevercBool HasProcessStatistics;
uint32_t Reserved;
NevercStringView ErrorMessage;
NevercOutputSealList OutputSeals;
uint64_t TotalTimeMicroseconds;
uint64_t UserTimeMicroseconds;
uint64_t PeakMemoryKiB;
} NevercJobResultDescriptor;يحمل OutputSeals مقابض NevercOutputSealHandle المنتَجة عبر واجهة الإدخال
والإخراج (انظر المصدر والإدخال/الإخراج)، وبها
يتأكد المضيف من أن الملفات التي ادّعت مهمةٌ كتابتها موجودة فعلًا بالبصمات
المُبلَّغ عنها. وتقرأ GetJobResult نتيجة مُثبَّتة، وتُبلِّغ مثل اختيار سلسلة
الأدوات عن BuiltinProviderUsed.
مُختصَر من pluginsdk/examples/DriverTracePlugin.c. لا تحتفظ الإضافة بأي
متغيرات عامة: حالة العملية تحمل الجداول المُتفاوَض عليها، أما عدّادات الجلسة
والمَهمة فتُجلب من المضيف داخل كل نداء راجع.
static NevercStatus NEVERC_CALL
observe_arguments(const NevercPhaseFrame *Frame, NevercObserverPoint Point,
void *UserData) {
DriverTraceProcessState *Process = (DriverTraceProcessState *)UserData;
DriverTraceSessionState *Session = NULL;
uint64_t ArgumentCount = 0;
NevercStatus Status;
if (Frame == NULL || Process == NULL)
return status_code(NEVERC_STATUS_INVALID_ARGUMENT);
Status = Process->Core->GetSessionState(Process->Core->Context,
Frame->Session, plugin_id(),
(void **)&Session);
if (Status.Code != NEVERC_STATUS_OK)
return Status;
Status = Process->Driver->GetArgumentCount(Process->Driver->Context, Frame,
Frame->Input, &ArgumentCount);
if (Status.Code != NEVERC_STATUS_OK)
return Status;
++Session->ArgumentCallbacks;
if (Point == NEVERC_OBSERVER_BEFORE && !Session->Announced) {
Session->Announced = NEVERC_TRUE;
return emit_trace_remark(Process, Frame, "driver argument phase observed",
30, 1001);
}
return neverc_status_ok();
}
static NevercStatus NEVERC_CALL
intercept_job(const NevercPhaseFrame *Frame,
NevercPhaseContinuation *Continuation,
NevercPhaseResult *OutResult, void *UserData) {
DriverTraceProcessState *Process = (DriverTraceProcessState *)UserData;
NevercJobExecutionRequest Request = {0};
NevercPhaseResult Downstream = {0};
NevercStatus Status;
if (Frame == NULL || Continuation == NULL || OutResult == NULL || !Process)
return status_code(NEVERC_STATUS_INVALID_ARGUMENT);
Request.Header = (NevercABITableHeader){
sizeof(Request), NEVERC_DRIVER_API_MAJOR, NEVERC_DRIVER_API_MINOR, 0};
Status = Process->Driver->GetJobExecutionRequest(
Process->Driver->Context, Frame, Frame->Input, &Request);
if (Status.Code != NEVERC_STATUS_OK)
return Status;
Downstream.Header = (NevercABITableHeader){
sizeof(Downstream), NEVERC_PLUGIN_ABI_MAJOR, NEVERC_PLUGIN_ABI_MINOR, 0};
Status = Continuation->InvokeNext(Continuation, Frame, &Downstream);
if (Status.Code != NEVERC_STATUS_OK)
return Status;
*OutResult = (NevercPhaseResult){0};
OutResult->Header = (NevercABITableHeader){
sizeof(*OutResult), NEVERC_PLUGIN_ABI_MAJOR, NEVERC_PLUGIN_ABI_MINOR, 0};
OutResult->Action = NEVERC_PHASE_CONTINUE;
return neverc_status_ok();
}ويربط التسجيل كلًّا منهما بمرحلته:
Observer.Phase = phase_id(NEVERC_PHASE_DRIVER_RAW_ARGUMENTS_HIGH,
NEVERC_PHASE_DRIVER_RAW_ARGUMENTS_LOW);
Observer.Points = NEVERC_OBSERVER_BEFORE | NEVERC_OBSERVER_AFTER;
Observer.Callback = observe_arguments;
Observer.UserData = Process;
Registrar->RegisterObserver(RegistrarContext, &Observer);
Interceptor.Phase = phase_id(NEVERC_PHASE_DRIVER_EXECUTE_JOB_HIGH,
NEVERC_PHASE_DRIVER_EXECUTE_JOB_LOW);
Interceptor.Callback = intercept_job;
Interceptor.UserData = Process;
Registrar->RegisterInterceptor(RegistrarContext, &Interceptor);البناء والتشغيل:
cmake --build build-neverc --target neverc-plugin-example-driver-trace
build-neverc/bin/neverc \
-fplugin=build-neverc/neverc/pluginsdk/examples/host/DriverTracePlugin.so \
--driver-trace -c input.c -o input.o- تتطلب تعديلات الوسائط والوسائط المُحلَّلة وسلسلة الأدوات ومخطط الإجراءات
ومخطط المهام جميعُها
NevercPhaseContinuationالخاصة بالمُعترِض؛ وخارجه تُرفَض بـNEVERC_STATUS_WRONG_SCOPE. - استدعِ
InvokeNextمرة واحدة على الأكثر، وعلى خيط النداء الراجع فقط. - يجب أن يصل كل مقبض تعديل إلى
Commit*أوAbort*واحد بالضبط. - العروض التي يعيدها نداء
Get*مُستعارة طوال مدة النداء الراجع. انسخ ما تريد الاحتفاظ به. - لا يجوز لنداء
NEVERC_JOB_PLUGINالراجع أن يُطلق العملية التي كان المضيف سيطلقها ثم يُبلِّغ أيضًا بنجاح المسار المدمج؛ أعلنREPLACEوتحمَّل النتيجة. - أبلِغ عن فشل مهمة عبر
NevercJobResultDescriptor.ExecutionFailedوErrorMessageبدل إعادة حالة غير OK لمهمة نُفِّذت وفشلت فشلًا مشروعًا.
راجع PluginDriver.h للتصريحات المعيارية، وPhaseSchema.json لسياسات مراحل
المشغّل، وcoverage.json لأدلة الاختبار.