Ориентация главы

Глава 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概念

Матрица артефактов и именование

kindWindows/MSVCWindows/GNUmacOSLinux
staticxrt.liblibxrt.alibxrt.alibxrt.a
sharedxrt.dll+xrt.lib (импорт)xrt.dll+libxrt.dll.alibxrt.dyliblibxrt.so

Замыкание обрезается в артефакт: --suite решает, какие модули пакуются (замыкание разворачивается) — «динамическая библиотека только с сетью» и «полная статическая» — каждая одной командой. Имена артефактов несут префикс продукта (расширения пакуются как libxruntime.a и т.д. — поле product их собственных манифестов).

verify: потребитель end-to-end

1Упаковкаисточники замыкания -> компиляция объектов -> архив (static) / линковка (shared)
2Проверкагенерируется минимальная потребительская программа (include публичных заголовков + вызов базовых API)
3Семантикарелиз «жив» с точки зрения потребителя - не файл существует, а можно пользоваться

Глубина --verify — в запуске: успешная линковка доказывает лишь наличие символов; успешный запуск доказывает корректность инициализации/ABI/поведения в рантайме. Это та же философия, что у шлюза учебника «примеры компилируются и запускаются» (глава 130) — считаются только исполнимые факты.

amalgamate: синтез одиночного заголовка

1Входманифест (топологический порядок модулей) + исходники модулей
2Синтезлокальные include инлайнятся (внутрирепозиторные #include разворачиваются в один файл)
3Согласованностьamalgamate --check регенерирует и сравнивает байт в байт с закоммиченным файлом

Смысл декларационной версии (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 — потребляемая в формах одиночного заголовка, статической и динамической библиотек:

examples/core/version_limits/main.c源码 ↗
















 */

#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;
}
$ gcc -O1 -I single -include xrt.h impl.c examples/core/version_limits/main.c -lws2_32 -liphlpapiversion=2.0.0-devlimits: depth=128 entries=100000 input=268435456

Что только что произошло. (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 — захват памяти на стороне потребителя:

examples/core/allocator_tour/main.c源码 ↗



















 */

#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;
}
$ gcc -O1 -I single -include xrt.h impl.c examples/core/allocator_tour/main.c -lws2_32 -liphlpapiallocator: swap -> at-family alloc/calloc/dup/realloc okallocator: locked after first alloc -> swap rejected ok

Что только что произошло. (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 прямо).

Типичные ошибки

Ловушка 1: изменил исходники и забыл регенерировать одиночный заголовок:Ловушка 1: изменил исходники и забыл регенерировать одиночный заголовок

Симптом: модульные тесты зелёные, пользователи одиночного заголовка не компилируются или поведение отличается — single/xrt.h устарел.

Причина: одиночный заголовок — генерируемый артефакт — ручная правка или забытый amalgamate оставляет закоммиченный файл несогласованным с исходниками. --check перехватывает в CI; локальная привычка — регенерировать сразу после правки.

错误写法
/* поправить опечатку руками в single/xrt.h - следующая генерация затрёт, а CI --check провалится */
正确写法
/* изменить исходник модуля -> python tools/amalgamate.py -> закоммитить исходник и сгенерированный заголовок одним PR */
Ловушка 2: одновременная линковка библиотеки и расширения с дублирующим замыканием:Ловушка 2: одновременная линковка библиотеки и расширения с дублирующим замыканием

Симптом: ошибки повторного определения символов, или хуже — два экземпляра состояния ядра (по распределителю у каждого) со сбоями в рантайме.

Причина: артефакт расширения (скажем libxruntime) уже содержит нужное замыкание ядра — линковать поверх ещё полную libxrt значит добавить вторую реализацию. Контракт прямо: «другую библиотеку, содержащую то же замыкание XRT, одновременно линковать нельзя».

错误写法
gcc app.c -lxruntime -lxrt   /* две копии core: дублирующиеся символы / двойное состояние */
正确写法
   или интегрировать расширение в исходной форме (его одиночный заголовок содержит нужное ядро) */
Ловушка 3: размер артефакта взят за метрику качества релиза:Ловушка 3: размер артефакта взят за метрику качества релиза

Симптом: «новая версия на 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 записей)

Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки