Ориентация главы
Глава 130 компилирует, глава 131 обрезает; эта глава превращает результат в поставляемое. tools/package.py упаковывает из манифеста два вида артефактов: static (libxrt.a/xrt.lib — архивирование по замыканию) и shared (xrt.dll+импорт-библиотека/libxrt.so/libxrt.dylib — трёхзначное именование по платформе); --verify делает проверку релизного класса — реальная линковка и запуск минимального потребителя релизной библиотеки (не «сслинковалось», а «потребитель пробежал»). Одиночный заголовок (single/xrt.h + декларационный xrt_decl.h): tools/amalgamate.py синтезирует его в топологическом порядке модулей — локальные include инлайнятся, макросы выбора переупорядочиваются, --check утверждает нулевое отличие повторной генерации от закоммиченного файла (машинная гарантия того, что одиночный заголовок не дрейфует). Проверка зрелости релиза (check_release_maturity.py) и самостоятельная упаковка расширений (libxruntime и др. из их собственных манифестов) завершают главу.
132.1引入
«Упаковка» звучит как простое архивирование — настоящие ямы в глубине проверки. Мелкая проверка: артефакт сгенерирован, размер разумен. Глубокая: взгляд потребителя — реально ли статическую библиотеку слинкует и запустит сторонний проект (отсутствующие символы/дубликаты/несовпадение ABI взрываются на линковке или в рантайме)? Согласована ли импорт-библиотека динамической с её экспортом? Действительно ли одиночный заголовок поведенчески идентичен модульным исходникам (ведомый руками одиночный заголовок неизбежно дрейфует — источник изменили, регенерацию забыли)? Ответы XRT сплошь механические: --verify компилирует и запускает минимального потребителя (end-to-end релизной семантики); amalgamate --check регенерирует и сравнивает (нулевая терпимость к дрейфу одиночного заголовка — та же дисциплина, что SPEC применяет к «ручное редактирование wwwroot запрещено», перенесённая на код); проверка зрелости сводит все красные линии.
Ценность одиночного заголовка стоит повторить: один файл + один include — полная интеграция — без стыковки со сборочной системой, без конфигурации линковки, без путей поиска заголовков — первый выбор для скриптов, быстрых прототипов и обучения (строки компиляции всех term с главы 3 этой книги и есть однозаголовочная форма). Цена — время компиляции (вся библиотека входит в одну TU) и невозможность выборочной компиляции (макросы обрезки всё ещё работают — XRT_MODULE_* внутри одиночного заголовка действует так же; семантика обрезки главы 131 переносится без потерь).
132.2概念
Матрица артефактов и именование
| kind | Windows/MSVC | Windows/GNU | macOS | Linux |
|---|---|---|---|---|
| static | xrt.lib | libxrt.a | libxrt.a | libxrt.a |
| shared | xrt.dll+xrt.lib (импорт) | xrt.dll+libxrt.dll.a | libxrt.dylib | libxrt.so |
Замыкание обрезается в артефакт: --suite решает, какие модули пакуются (замыкание разворачивается) — «динамическая библиотека только с сетью» и «полная статическая» — каждая одной командой. Имена артефактов несут префикс продукта (расширения пакуются как libxruntime.a и т.д. — поле product их собственных манифестов).
verify: потребитель end-to-end
Глубина --verify — в запуске: успешная линковка доказывает лишь наличие символов; успешный запуск доказывает корректность инициализации/ABI/поведения в рантайме. Это та же философия, что у шлюза учебника «примеры компилируются и запускаются» (глава 130) — считаются только исполнимые факты.
amalgamate: синтез одиночного заголовка
Смысл декларационной версии (xrt_decl.h): чисто декларационный одиночный заголовок — форма «нужны только прототипы» для многоту-проектов (декларации при линковке библиотечного артефакта, полная версия для единственной реализационной TU). Топологический порядок — корень корректности синтеза: зависимости между модулями задают порядок склейки — от кого зависят, тот раньше (amalgamate переиспользует топологическую сортировку манифеста — ещё одно потребление того же источника фактов).
Полная цепочка релизного шлюза
Последовательность из README xruntime и есть шаблон: build.py (регрессия) → amalgamate --check (согласованность одиночного заголовка) → check_release_maturity.py --release (сводка красных линий зрелости) → package.py --kind static --verify / --kind shared --verify (два артефакта + проверка потребителем). Каждая команда проверяет одно ортогональное измерение — сообщение о провале указывает прямо на измерение.
132.3示例
Первая полная программа: минимальная интеграция как потребитель
Программа ниже из examples/core/version_limits — потребляемая в формах одиночного заголовка, статической и динамической библиотек:
*/ #include <stdio.h> #include <xrt.h> int main(void) { xrtresourcelimits Limits; printf("version=%s\n", xrtVersion()); xrtResourceLimitsInit(&Limits); printf("limits: depth=%u entries=%llu input=%llu\n", (unsigned)Limits.iMaxDepth, (unsigned long long)Limits.iMaxEntries, (unsigned long long)Limits.iMaxInputBytes); return 0; }
Что только что произошло. (1) Эта строка компиляции — форма потребителя одиночного заголовка: -I single -include xrt.h + impl.c (реализационная TU с XRT_IMPLEMENTATION — фиксированная пара учебника); минимальный потребитель, порождаемый package --verify, изоморфен этому. (2) Смена на форму библиотеки: после сборки libxrt.a компиляция потребителя становится -I include + main.c -lxrt (декларации из публичных заголовков, реализация из библиотеки) — один main в трёх формах потребления, поведение обязано совпадать (неявное утверждение verify). (3) Без выбора = Core (глава 131) здесь окупается: этот же потребитель линкуется и запускается против минимального артефакта (замыкание core).
Вторая полная программа: потребитель с настройкой распределителя
Вторая программа из examples/core/allocator_tour — захват памяти на стороне потребителя:
*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <xrt.h> /* 计数分配器:仍路由系统堆,便于验证替换生效。 */ typedef struct examplecount { size_t iAllocs; size_t iFrees; } examplecount; static ptr exampleAlloc(ptr pContext, size_t iSize) { ++((examplecount*)pContext)->iAllocs; return (ptr)malloc(iSize); } static ptr exampleRealloc(ptr pContext, ptr pMemory, size_t iSize) { (void)pContext; return (ptr)realloc(pMemory, iSize); } static void exampleFree(ptr pContext, ptr pMemory) { ++((examplecount*)pContext)->iFrees; free(pMemory); } int main(void) { xallocator Default; xallocator Custom; examplecount Count; uint8* pBlock; uint8* pDup; int iResult = 1; /* ---- 必须在任何 XRT 分配前完成替换。 ---- */ xrtGetAllocator(&Default); memset(&Count, 0, sizeof(Count)); Custom.Context = (ptr)&Count; Custom.Alloc = exampleAlloc; Custom.Realloc = exampleRealloc; Custom.Free = exampleFree; if ( !xrtSetAllocator(&Custom) ) { goto Cleanup; } /* ---- At 族五接口:位置参数不影响堆行为。 ---- */ pBlock = (uint8*)xrtMallocAt(4u, __FILE__, __LINE__); if ( (pBlock == NULL) || ((pDup = (uint8*)xrtMemDupAt("hi", 2u, __FILE__, __LINE__)) == NULL) || (memcmp(pDup, "hi", 2u) != 0) || ((pBlock = (uint8*)xrtReallocAt(pBlock, 16u, __FILE__, __LINE__)) == NULL) || (xrtCallocAt(2u, 4u, __FILE__, __LINE__) == NULL) ) { goto Cleanup; } /* Calloc 清零验证单独做——保留指针用于释放。 */ { uint8* pZero = (uint8*)xrtCallocAt(2u, 4u, __FILE__, __LINE__); if ( (pZero == NULL) || (pZero[0] != 0u) || (pZero[7] != 0u) ) { goto Cleanup; } xrtFreeAt(pZero, __FILE__, __LINE__); } xrtFreeAt(pDup, __FILE__, __LINE__); xrtFreeAt(pBlock, __FILE__, __LINE__); printf("allocator: swap -> at-family alloc/calloc/dup/realloc ok\n"); * 且 GetAllocator 仍报告已安装的计数分配器。 ---- */ * 分配才经过自定义 Alloc;计数只需 >= 1 即证明替换生效。 */ if ( xrtSetAllocator(&Default) || (Count.iAllocs < 1u) ) { goto Cleanup; } { xallocator Current; xrtGetAllocator(&Current); if ( (Current.Alloc != Custom.Alloc) || (Current.Free != Custom.Free) ) { goto Cleanup; } } printf("allocator: locked after first alloc -> swap rejected ok\n"); iResult = 0; Cleanup: return iResult; }
Что только что произошло. (1) Семантика «поменял в первой строке и заблокировалось» действует и для библиотечных артефактов: все распределения внутри статической/динамической библиотеки идут через распределитель хозяина (интеграционный контракт релизной библиотеки «память принадлежит хозяину»). (2) Роль этого образца в шлюзе — именно «разнообразие потребителей»: кроме минимального потребителя verify, весь набор examples — настоящие потребители (глава 130: examples есть шлюз); подключение распределителя — глубочайший из путей настройки. (3) Обрезка глазами упаковки: артефакт замыкания core уже покрывает все символы этого образца — минимальная релизная грань достаточна.
Выбор между тремя видами потребителей
Три формы артефакта соответствуют трём видам потребителей — выбор на одной странице. Одиночный заголовок (single/xrt.h): скрипты, быстрые прототипы, обучение, мелкие утилиты — нулевая стоимость стыковки со сборочной системой, один include — всё; цена — время компиляции всего заголовка в одной TU. Статическая библиотека (libxrt.a): умолчание настоящих проектов — линковка есть заморозка (без рантайм-зависимостей), обрезка замыканием точна до модуля, естественная интеграция со сборочной системой хозяина; цена — артефакт на каждую платформу и конфигурацию. Динамическая библиотека (so/dll/dylib): множество программ делят одну реализацию, независимое обновление (при стабильном ABI) — для C-библиотек реже, но для хозяев плагинов обычно; цена — платформенная матрица импорт-библиотек/экспорта символов и развёртывание. Учебник этой книги всюду одиночный заголовок (строки компиляции term это доказывают) — читатель копирует и запускает; реальные проекты выбирают по форме развёртывания — три формы ведут себя одинаково (неявное утверждение verify), переключение бесплатно.
132.4契约
- Матрица артефактов: static/shared × правила именования на четырёх платформах; shared на Windows несёт импорт-библиотеку; имя продукта следует манифесту.
- Обрезка замыканием: --suite решает содержимое артефакта; разворот замыкания как в build.
- Глубина verify: минимальный потребитель → реальная линковка → запуск → утверждение — исполнимый факт.
- Синтез одиночного заголовка: склейка в топологическом порядке; локальные include инлайнятся, системные сохраняются; макросы выбора переупорядочиваются по зависимостям.
- Две версии одиночного заголовка: реализационная xrt.h / декларационная xrt_decl.h — многоту берёт декларации.
- Проверка согласованности: --check регенерирует и сравнивает с нулевой разницей — нулевая терпимость к дрейфу одиночного заголовка.
- Сводка зрелости: check_release_maturity сводит красные линии — релиз есть вывод, а не догадка.
- Упаковка расширений: каждая библиотека-расширение пакует свой артефакт из своего манифеста (libxruntime и др.) — замыкание включает нужную обрезку ядра.
- Взаимоисключающая линковка: нельзя одновременно линковать две библиотеки с одинаковым замыканием ядра (в README xruntime прямо).
Типичные ошибки
Симптом: модульные тесты зелёные, пользователи одиночного заголовка не компилируются или поведение отличается — single/xrt.h устарел.
Причина: одиночный заголовок — генерируемый артефакт — ручная правка или забытый amalgamate оставляет закоммиченный файл несогласованным с исходниками. --check перехватывает в CI; локальная привычка — регенерировать сразу после правки.
/* поправить опечатку руками в single/xrt.h - следующая генерация затрёт, а CI --check провалится *//* изменить исходник модуля -> python tools/amalgamate.py -> закоммитить исходник и сгенерированный заголовок одним PR */Симптом: ошибки повторного определения символов, или хуже — два экземпляра состояния ядра (по распределителю у каждого) со сбоями в рантайме.
Причина: артефакт расширения (скажем libxruntime) уже содержит нужное замыкание ядра — линковать поверх ещё полную libxrt значит добавить вторую реализацию. Контракт прямо: «другую библиотеку, содержащую то же замыкание XRT, одновременно линковать нельзя».
gcc app.c -lxruntime -lxrt /* две копии core: дублирующиеся символы / двойное состояние */
или интегрировать расширение в исходной форме (его одиночный заголовок содержит нужное ядро) */Симптом: «новая версия на 5% больше — откатывать» — без сверки базовой линии (компилятор/оптимизация/strip другие).
Причина: сравнение размеров требует одной базовой линии среды (семь элементов главы 131); красные линии качества релиза — verify + проверка зрелости (функциональность и согласованность), размер — лишь ориентир тренда.
if ( size(new) > size(old) * 1.05 ) { rollback(); } /* между средами бессмысленно */
/* мониторинг трендов в одной базовой линии + структурное утверждение forbid_symbols + функциональная красная линия verify */Практические задания
package.py --suite core --kind static --verify и --kind shared --verify — проверить имена артефактов и вывод проверки потребителем. Критерии приёмки: оба артефакта сгенерированы, verify проходит для каждого; импорт-библиотека (Windows) присутствует.
Статическая библиотека с --suite core,queue,queue_spsc; пример SPSC главы 21 как потребитель — линковка и запуск. Критерии приёмки: потребитель проходит; в артефакте нет символов невыбранных модулей (сравнение nm).
Изменить комментарий одного исходника модуля → amalgamate --check (должен провалиться — заголовок устарел) → регенерировать → --check проходит → регрессия на обоих треках. Критерии приёмки: цикл замыкается; вы понимаете семантику сравнения --check (регенерация против закоммиченного файла).
Итоги
| Тема | Кратко |
|---|---|
| Матрица артефактов | static (libxrt.a/xrt.lib) / shared (dll+импорт/so/dylib) |
| Замыкание в артефакт | --suite решает содержимое — минимальная релизная грань одной командой |
| Глубина verify | потребитель реально линкуется + запускается — исполнимый факт |
| Синтез одиночного заголовка | склейка в топологическом порядке, локальные include инлайнятся, макросы переупорядочены |
| Две версии | xrt.h реализация / xrt_decl.h декларации — многоту берёт декларации |
| Согласованность | --check нулевая разница — нулевая терпимость к дрейфу одиночного заголовка |
| Зрелость | check_release_maturity сводит красные линии |
| Упаковка расширений | свой манифест, свой артефакт — замыкание включает ядро |
| Взаимоисключение | не линковать две библиотеки с одинаковым замыканием |
| Ценность одиночного заголовка | один файл один include полная интеграция — скрипты/прототипы/обучение |
📖 Справочники API в этой главе:core (25 записей)error (34 записей)
Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки