Поделиться через


Включение собственных библиотек в пакеты .NET

В этом документе объясняется, как создавать пакеты, которые работают для проектов, предназначенных для .NET (5+), где управляемая сборка .NET использует платформу Invoke (P/Invoke) из управляемого языкового проекта .NET, с средой выполнения .NET с использованием путей проверки для загрузки и поиска собственной библиотеки.

Если вы хотите поддерживать платформа .NET Framework с пакетом, ознакомьтесь с разделом о проектах, предназначенных для платформа .NET Framework. Как правило, лучше всего упаковать собственные props и целевые файлы MSBuild, а также самостоятельно обрабатывать целевые объекты сборки и публикации. Не используя NuGet и встроенную функцию среды выполнения .NET, необходимо иметь достаточно знаний в MSBuild и системах сборки для соответствующих типов проектов.

Хотя NuGet имеет contentFiles/ функцию, не рекомендуется использовать эту функцию, так как она не поддерживает выбор на уровне RID и поэтому не участвует в функциях проверки путей среды выполнения .NET.

Общие сведения

Сначала мы рассмотрим пакеты ссылок на проект. Когда приложение публикуется для определенного идентификатора среды выполнения (RID), ресурсы пакетов NuGet для этого RID будут скопированы в выходной каталог. Когда приложение публикуется без определенного RID, все содержимое RID будет опубликовано в подкаталогах, и {project}.deps.json создается файл, который загружает среда выполнения .NET, чтобы определить, какой каталог следует использовать в качестве пути проверки.

Кроме того, помните, что .NET 8 вводит критические изменения в отношении того, как он определяет совместимые идентификаторы RID из .deps.json файла и какие идентификаторы RID распознаются пакетом SDK.

Общие сведения о выборе ресурса пакета NuGet

Существует три типа ресурсов в файле NuGet, наиболее релевантных для этого сценария: компиляция, среда выполнения и собственные ресурсы. Полный список типов активов см. в документации по управлению ресурсами зависимостей в PackageReference проектах.

Вид актива Краткое описание
Компиляции Управляемые сборки, передаваемые компилятору. ref/{tfm}/ Значение , если оно существует, в противном случае lib/{tfm}/.
среда выполнения Управляемые сборки, скопированные в выходной каталог. runtimes/{rid}/lib/{tfm}/ Значение , если оно существует, в противном случае lib/{tfm}/.
native Собственные библиотеки, скопированные в выходной каталог. runtimes/{rid}/native/.

Во всех случаях NuGet выбирает наиболее совместимый каталог для заданной платформы RID и Target Framework, а только файлы в этом каталоге копируются. Поэтому вы не можете поместить общие ресурсы в унаследованный RID (например), так как anyесли какой-либо другой RID является лучшим совпадением, any содержимое RID не будет считаться.

Сведения о реализации выбора ресурсов NuGet см. в разделе NuGet.Client" ManagedCodeConventions.cs Файл".

Компиляция ресурсов

Ресурсы компиляции передаются компилятору и поэтому должны содержать только сборки .NET. NuGet будет выбирать ресурсы компиляции из ref/{tfm}/и, если этот каталог не существует, он будет возвращаться к lib/{tfm}{tfm} целевой платформе, которая является ближайшей платформой, которая соответствует проекту, ссылающемся на пакет. Однако в этом сценарии создания пакета .NET с собственными библиотеками рекомендуется использовать ref/ , так как если lib/ используется, NuGet будет думать, что пакет совместим с проектами с использованием packages.config, но эти проекты завершаются ошибкой во время выполнения, так как собственные файлы не будут скопированы.

Компилятор .NET создает предупреждения, когда сборка предназначена для другой архитектуры, отличной от целевой для текущей компиляции, поэтому потребители пакетов будут иметь лучший интерфейс, если целевые ref/ сборки или lib/ сборки AnyCPU.

Если сборка содержит много управляемого кода, а не только определения методов P/Invoke, вы можете сэкономить место, сделав ref/ сборку эталонной сборкой, но обязательно включите ее ref/ в пакет, а не lib/.

Таким образом, нет возможности предоставлять разные сборки во время компиляции для разных идентификаторов RID, вам потребуется предоставить одну область API для всех идентификаторов RID и выполнять проверка среды выполнения (например, создание).PlatformNotSupportedException

Ресурсы среды выполнения

Ресурсы среды выполнения — это управляемые сборки .NET, скопированные в выходной каталог при сборке и публикации, а пакет SDK для .NET создает deps.json все сведения о ресурсах, зависящих от RID. NuGet будет выбирать ресурсы среды выполнения из runtimes/{rid}/lib/{tfm}/, и если это не существует, он будет возвращаться к lib/{tfm}/. Сначала выбирается лучший {rid} каталог, а затем {tfm}. Однако в этом сценарии создания пакета .NET с собственными библиотеками рекомендуется использовать runtimes/ , так как если lib/ используется, NuGet будет думать, что пакет совместим с проектами с использованием packages.config, но эти проекты завершаются ошибкой во время выполнения, так как собственные ресурсы не будут скопированы.

Собственные ресурсы

Собственные ресурсы также копируются в выходной каталог при сборке и публикации и присутствуют в deps.json файле. NuGet выберет собственные runtimes/{rid}/native/ ресурсы из каталога.

Примечание.

Пакет SDK для .NET неструктурирует любую структуру runtimes/{rid}/native/ каталогов при копировании в выходной каталог.

Подведем итог

Как создать все собственные библиотеки двоичных файлов из область этого документа. Предполагается, что все собственные двоичные файлы и сборки .NET были завершены и скопированы на компьютер для упаковки.

При упаковке с целевым объектом пакета MSBuild NuGet можно включить произвольные файлы в произвольные пути пакета с помощью Pack="true" PackagePath="{path}" метаданных в элементах MSBuild. Например, <None Include="../cross-compile/linux-x64/libcontoso.so" Pack="true" PackagePath="runtimes/linux-x64/native/" />.

Рассмотрим пакет Contoso.Native , предоставляющий API contoso.dll .NET для Windows, libcontoso.dylib OSX и libcontoso.so Linux с помощью [DllImport("contoso")]. Содержимое пакета должно быть (за исключением файлов, необходимых для открытых соглашений об упаковке)

Пример 1

Если Contoso.Native он построен как AnyCPU:

Contoso.Native.1.0.0.nuspec
ref/net8.0/Contoso.Native.dll
runtimes/any/lib/net8.0/Contoso.Native.dll
runtimes/linux-arm64/native/libcontoso.so
runtimes/linux-x64/native/libcontoso.so
runtimes/osx-arm64/native/libcontoso.dylib
runtimes/osx-x64/native/libcontoso.dylib
runtimes/win-arm64/native/contoso.dll
runtimes/win-x64/native/contoso.dll

ref/ Хотя и runtimes/any/lib копии Contoso.Native.dll могут быть дедупликированы в один lib/net8.0/Contoso.Native.dll файл, это не рекомендуется, так как NuGet считает, что этот пакет совместим с проектами с использованиемpackages.config, и эти проекты завершаются ошибкой во время выполнения, когда собственный contoso.dll код не найден.

Пример 2

Если Contoso.Native имеется какие-либо различия между архитектурами. Например, но не ограничивается, используя код разности для разных идентификаторов, или использование #if<PlatformTarget> (или что-либо другое, которое приводит -platform: к передаче компилятору) со значением, отличным от AnyCPUзначения.

Contoso.Native.1.0.0.nuspec
ref/net8.0/Contoso.Native.dll
runtimes/linux-arm64/lib/net8.0/Contoso.Native.dll
runtimes/linux-arm64/native/libcontoso.so
runtimes/linux-x64/lib/net8.0/Contoso.Native.dll
runtimes/linux-x64/native/libcontoso.so
runtimes/osx-arm64/lib/net8.0/Contoso.Native.dll
runtimes/osx-arm64/native/libcontoso.dylib
runtimes/osx-x64/lib/net8.0/Contoso.Native.dll
runtimes/osx-x64/native/libcontoso.dylib
runtimes/win-arm64/lib/net8.0/Contoso.Native.dll
runtimes/win-arm64/native/contoso.dll
runtimes/win-x64/lib/net8.0/Contoso.Native.dll
runtimes/win-x64/native/contoso.dll

Пример 3

Если Contoso.Native имеется условная компиляция для каждой операционной системы, но не на архитектуру ЦП.

Contoso.Native.1.0.0.nuspec
ref/net8.0/Contoso.Native.dll
runtimes/linux/lib/net8.0/Contoso.Native.dll
runtimes/linux-arm64/native/libcontoso.so
runtimes/linux-x64/native/libcontoso.so
runtimes/osx/lib/net8.0/Contoso.Native.dll
runtimes/osx-arm64/native/libcontoso.dylib
runtimes/osx-x64/native/libcontoso.dylib
runtimes/win/lib/net8.0/Contoso.Native.dll
runtimes/win-arm64/native/contoso.dll
runtimes/win-x64/native/contoso.dll

Проекты, предназначенные для платформа .NET Framework

.NET развивалась с течением времени, вызывая несовместимость проекта и пакета при использовании определенных функций. Как описано выше, предполагается, что проект, использующий пакет, предназначен для .NET 5 или более поздней версии, или .NET Core (netcoreapp), который использует максимальные функции для упрощения содержимого пакета максимально возможно. Проекты, предназначенные для .NET Standard, не особенно относятся к этому документу, так как эти проекты могут быть только библиотеками классов, которые нельзя запускать напрямую. Однако проекты, предназначенные для платформа .NET Framework, могут использовать либо проекты стилей SDK, либо проекты стилей, отличные от пакета SDK, либо использовать PackageReferencepackages.config пакеты NuGet. Все эти сочетания имеют разные уровни поддержки и накладывают ограничения на авторов пакетов, которые хотят поддерживать проекты платформа .NET Framework с помощью пакета.

Мы рекомендуем разделить пакет на 3 пакета. Первым является метапакет (пакет без содержимого или ресурсов, только зависимостей от других пакетов), который имеет зависимости от одного из двух других пакетов в зависимости от целевой платформы. Второй пакет содержит .NET 5+ ref/* и runtimes/* ресурсы, как описано выше. Последний пакет будет содержать платформа .NET Framework ресурсы и не будет использовать ref/* соглашения или runtime/* соглашения. Это связано с поддержкой системы сборки (частичной) для этих соглашений NuGet, но среда выполнения работает достаточно по-разному, чтобы избежать ее, чтобы максимально повысить совместимость проектов.

Пакет с платформа .NET Framework собственными библиотеками должен иметь пользовательские целевые объекты MSBuild и файлы props в разделе buildTransitive/<tfm>/<package ID>.[props|targets]. Например, buildTransitive/net472/Contoso.Native.targets. Для поддержки packages.config проектов также есть файл build/<tfm>/<package ID>.[props|targets] , который импортирует buildTransitive файл. Этот скрипт MSBuild должен скопировать собственную библиотеку в подкаталоги. Кроме того, собственные библиотеки должны иметь разные имена файлов для каждой архитектуры ЦП, поэтому их можно скопировать в один каталог.

Наконец, у управляемой библиотеки, которая использует P/Invoke для вызова в собственную библиотеку, она должна иметь проверка среды выполнения, чтобы определить, в какой платформе работает текущий исполняемый файл (x86, x64 или ARM64), а затем P/Invoke в собственную библиотеку с правильным именем файла или правильным подкаталогом.

Проекты стилей SDK, предназначенные для платформа .NET Framework

Когда пакет SDK для .NET создает проект, предназначенный для платформа .NET Framework, если один из RuntimeIdentifier них или PlatformTarget задан, пакет SDK для .NET установит другое свойство соответствующим значением, а содержимое пакета runtimes/<rid>соответствии с соглашениями NuGet) будет скопировано в выходной каталог. Если проект не задан или не заданRuntimeIdentifier, но любой пакет содержит содержимое RID, пакет SDK для .NET будет иметь значение x86PlatformTarget .PlatformTarget Поэтому проекты стилей ПАКЕТА SDK, предназначенные для платформа .NET Framework, будут использоваться AnyCPU только по умолчанию, если ни один из пакетов не содержит определенное содержимое RID.

dotnet build -r <rid>или publish эквивалент (например, dotnet publish -r win-arm64) можно использовать для явной сборки или публикации для определенной платформы.

Для поддержки AnyCPU проектов авторы пакетов не должны использовать соглашения в своем пакете и должны использовать runtimes/ файлы целевых объектов ИЛИ props MSBuild, как описано выше.

Проекты, не относящиеся к пакету SDK, предназначенные для платформа .NET Framework сPackageReference

При тестировании проекта, отличного от пакета PackageReference sdk, с помощью runtimes/* соглашения происходит следующее поведение. Если конфигурация проекта предназначена для платформы "Любой ЦП", сборка не будет копировать какие-либо определенные ресурсы RID из пакета, и приложение завершится сбоем во время выполнения. С помощью Configuration Manager Visual Studio проекты .NET можно изменить на целевые объекты x64, x86 или ARM64, а не "Любой ЦП", в этом случае приложение будет работать правильно во время выполнения.

Для поддержки AnyCPU проектов авторы пакетов должны использовать файлы целевых объектов и props MSBuild, как показано выше.

packages.config

Исходный стиль восстановления NuGet, packages.configне поддерживает ref/* или runtimes/* соглашения. Поэтому авторы пакетов должны создавать собственные реквизиты MSBuild и целевые файлы для поддержки проектов, которые по-прежнему используются packages.config.