Как сделать nuget пакет из dll

Обновлено: 07.07.2024

Разумеется, нет смысла заниматься сразу реализацией реального API, проще проверить всё на маленьком примере. Именно так я и поступил, и хочу пройти путь до работающего решения ещё раз вместе с вами. Краткий список шагов будет приведён в конце статьи.

Первые попытки

Чтобы двинуться дальше, создадим проект нашей тестовой библиотеки. В файле .csproj DryWetMIDI указаны TFM netstandard2.0 и net45, поэтому для тестового проекта я также указал эти целевые платформы для приближения к реальным условиям. Проект назовём DualLibClassLibrary, внутри будет всего один файл Class.cs:

Кроме того, нам, разумеется, нужны сами нативные сборки (test.dll и test.dylib). Я собрал их из простого кода на C (к слову, такого подхода буду придерживаться затем и в реальной библиотеке):

Если интересно, файлы test.dll и test.dylib создавал в рамках тестового пайплайна в Azure DevOps (в действительности двух, для Windows и macOS). В конце концов, мне нужно будет делать всё в рамках CI, так что решил сразу проверить, как всё будет происходить в реальности. Пайплайн простой, состоит из 3 шагов:

1. сгенерировать файл с кодом на C (задача PowerShell):

( return 456; для macOS);

2. собрать библиотеку (задача Command Line):

(test.dylib для macOS);

3. опубликовать артефакт с библиотекой (задача Publish Pipeline Artifacts).

Итак, имеем файлы test.dll и test.dylib, предоставляющие одну и ту же функцию Foo , которая для Windows возвращает 123 , а для macOS – 456 , так что мы всегда сможем проверить корректность вызова и результата. Файлы положим рядом с DualLibClassLibrary.csproj.

Теперь нужно понять, как добавить их в NuGet пакет так, чтобы после установки пакета они копировались в выходную директорию при сборке приложения, обеспечивая таким образом работу установленной библиотеки. Так как библиотека у нас кроссплатформенная и использует новый формат файла .csproj (SDK style), очень хочется там и объявить инструкции для упаковки файлов. Изучив немного вопрос, пришёл к такому содержимому .csproj:

dotnet pack .\DualLibClassLibrary.sln -c Release

Файлы test.dll и test.dylib добавились из пакета

Выглядит обнадёживающе, пишем в файле Program.cs простой код:

Запускаем и грустим:

Программа не нашла файл test.dll

Программа не нашла файл test.dll

Что ж, заглянем в папку bin/Debug:

Файлы test.dll и test.dylib отсутствуют в выходной директории приложения

И правда нет файлов. Как же так, <CopyToOutputDirectory> мы им указали, в структуре проекта файлы видны. Проверив содержимое .csproj нашего приложения, всё становится понятно:

В csproj полный беспорядок с добавленными файлами

В csproj полный беспорядок с добавленными файлами

Во-первых, элемент <CopyToOutputDirectory> отсутствует, а во-вторых, по неведомой причине test.dylib добавился как элемент <None> , а test.dll как элемент <Content> . Остаётся только посмотреть содержимое файла .nupkg. Воспользовавшись программой NuGet Package Explorer, видим следующий манифест:

Как видим, файлы добавились без атрибута copyToOutput , что печально (про атрибут можно почитать в таблице тут: Using the contentFiles element for content files).

Копирование файлов в выходную директорию при сборке приложения

Элемент <PackageCopyToOutput> как раз должен привнести атрибут copyToOutput в манифест пакета. Кроме того, явно указал папки, куда нужно положить файлы, дабы избежать директорий вроде any. Подробнее о том, как всё это работает, можно почитать тут: Including content in a package.

Собираем снова наш пакет и проверяем манифест:

Теперь всё выглядит куда лучше, простая структура файлов и атрибут copyToOutput на месте. Устанавливаем библиотеку в наше консольное приложение и запускаем:

copyToOutput ситуацию не спасает

copyToOutput ситуацию не спасает

Всё так же файлов нет в выходной директории приложения

Всё так же файлов нет в выходной директории приложения

Кроме слегка изменённого текста исключения разницы не видно. Отписался в issue по итогу, на что мне ответили:

Please see our docs on contentFiles . It supports adding different content depending on project's target framework and language, and therefore needs files in a specific structure which your package is not currently using.

Есть статья в документации Microsoft с подозрительно нужным заголовком: Creating native packages. Статья не сильно содержательная, однако кое-что полезное из неё можно почерпнуть. А именно, что можно сделать файл .targets, где мы и укажем <CopyToOutputDirectory> нашим файлам. Сам файл .targets мы включим в пакет вместе с нативными библиотеками. Сказано – сделано. Создаём файл DualLibClassLibrary.targets:

А в файле DualLibClassLibrary.csproj пропишем:

Ошибка уже другая

Ошибка уже другая

Данная ошибка может возникнуть из-за несоответствующей разрядности приложения и нативных сборок. Я собирал их на 64-битных системах, приложение запускаю также в 64-битной ОС. Что ж, продолжаем наше путешествие.

Поддержка 32- и 64-битных процессов

Если зайти в свойства проекта приложения в Visual Studio на вкладку Build, обнаружим такую опцию:

Процесс будет 32-битным

Процесс будет 32-битным

Можно, конечно, выключить эту опцию, и приложение наконец напечатает верный результат:

Result = 123000. Press any key to exit.

Но, разумеется, это не решение по следующим причинам:

не будет возможности использовать библиотеку в 32-битных процессах;

придётся требовать от пользователей лишних действий в виде отключения галки;

Конечно же, так никуда не годится, и проблему нужно победить. На самом деле, вариант тут очевиден: сделать нативные сборки для каждой операционной системы в двух вариантах – 32- и 64-битном. То есть поставка пакета чуть распухнет, вместо 2 платформозависимых библиотек внутри будут 4. Я в этом ничего плохого не вижу, ибо файлы всё равно небольшие, а потому буду продолжать именно с этим подходом (тем более, что иного не придумал).

Немного расскажу о том, как собирал 32-битные версии библиотек. Как я упоминал выше, я произвожу сборку в конвейерах Azure DevOps через gcc. У gcc есть флаг -m32 , который, по идее, должен как раз собрать 32-битную библиотеку. На сборочных агентах с macOS всё здорово, а вот на Windows получил нелицеприятные логи:

C:/ProgramData/Chocolatey/lib/mingw/tools/install/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:/ProgramData/Chocolatey/lib/mingw/tools/install/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/lib\libuser32.a when searching for -luser32

C:/ProgramData/Chocolatey/lib/mingw/tools/install/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/lib/libmsvcrt.a when searching for -lmsvcrt

C:/ProgramData/Chocolatey/lib/mingw/tools/install/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lmsvcrt

collect2.exe: error: ld returned 1 exit status

Задав вопрос и на StackOverflow, и в Microsoft Developer Community, выяснилось, что на агентах Microsoft не предустановлен 32-битный MinGW, что и приводит к падению. Попробовав множество вариантов, я остановился на проекте brechtsanders/winlibs_mingw, придя к простому PowerShell скрипту:

Используя поставляемый в составе архива компилятор i686-w64-mingw32-gcc.exe, удалось наконец-таки собрать 32-битный файл test.dll. Ура!

Теперь осталось придумать, как заставить нашу библиотеку вызывать API либо из 32- либо из 64-битной сборки. Я думаю, варианты тут есть разные, я остановился на таком:

собираем нативные библиотеки test32.dll, test64.dll, test32.dylib и test64.dylib;

делаем абстрактный класс Api с абстрактными методами, соответствующими нашему managed API для внутреннего использования;

делаем два наследника Api32 и Api64 , в которых реализуем абстрактный API из родительского класса, вызывая unmanaged API из test32 и test64 соответственно;

делаем класс ApiProvider , чьё свойство Api будет отдавать нам реализацию, соответствующую разрядности текущего процесса.

Приведу код файлов:

Api.cs

Api32.cs

Api64.cs

ApiProvider.cs

И тогда код нашего класса Class будет таким:

Собрав пакет (разумеется, обновив предварительно содержимое файлов DualLibClassLibrary.targets и DualLibClassLibrary.csproj, добавив новые файлы), убедимся, что метод нашей библиотеки работает корректно при любой разрядности процесса приложения.

Заключение

Я привёл полную хронологию моих мытарств касаемо создания NuGet пакета с платформозависимым API, но будет полезно кратко перечислить основные моменты (я же обещал инструкцию):

создать нативные сборки, причём в двух вариантах: 32- и 64-битном;

положить их рядом с проектом библиотеки (можно и в папку какую-то, главное путь указать к ним потом верный);

добавить файл .targets, в котором для всех нативных сборок добавить элемент <CopyToOutputDirectory> с желаемым значением;

в файле .csproj библиотеки прописать упаковку как нативных сборок, так и файла .targets (должен пойти в папку build пакета);

реализовать механизм выбора нужной версии нативной сборки в зависимости от разрядности процесса.

Это всё. Солюшн нашей тестовой библиотеки можно взять отсюда: DualLibClassLibrary.zip. Решение было проверено в следующих сценариях на Windows и macOS:

Касаемо проверки в 32- и 64-битном процессах – проверял только на Windows, не уверен, как проверить это на macOS.

Это краткое руководство относится только к Visual Studio 2017 для Windows и более поздним версиям. Visual Studio для Mac не поддерживает описанные здесь функции. Используйте вместо этого средства интерфейса командной строки dotnet.

Предварительные требования

Создание проекта библиотеки классов

Щелкните правой кнопкой мыши полученный файл проекта и выберите пункт Сборка, чтобы убедиться, что проект создан правильно. Библиотека DLL находится в папке Debug (или папке Release, если вы используете конфигурацию выпуска для сборки).

Однако в этом пошаговом руководстве вы не будете писать дополнительный код, так как библиотеки классов из шаблона достаточно для создания пакета. Тем не менее, вы можете использовать функциональный код для пакета:

Настройка свойств проекта для пакета

Пакет NuGet содержит манифест (файл .nuspec ) с соответствующими метаданными, такими как идентификатор пакета, номер версии, описание и многое другое. Некоторые из них могут поступать напрямую из свойств проекта, в результате чего исчезает необходимость изменять их как в проекте, так и в манифесте. Этот раздел описывает, где можно задать соответствующие свойства.

Выберите команду меню Проект > Свойства, а затем щелкните вкладку Приложение.

В поле Имя сборки укажите уникальный идентификатор пакета.

Сведения о сборке в проекте .NET Framework в Visual Studio

Необязательно. Чтобы просмотреть или изменить свойства напрямую, откройте файл Properties/AssemblyInfo.cs в проекте.

Задав свойства, укажите для конфигурацию проекта значение Выпуск и перестройте проект для создания обновленной библиотеки DLL.

Создание начального манифеста

Получив библиотеку DLL и задав свойства проекта, вы можете использовать команду nuget spec , чтобы создать начальный файл .nuspec из проекта. Этот шаг включает в себя соответствующие токены замены для получения сведений из файла проекта.

Запускать nuget spec для создания начального манифеста потребуется всего один раз. При обновлении пакета нужно либо изменить значения в проекте, либо отредактировать сам файл манифеста.

Откройте командную строку и перейдите в папку проекта, содержащую файл AppLogger.csproj .

Выполните следующую команду: nuget spec AppLogger.csproj . После указания проекта NuGet создает манифест с тем же именем, что и проект, в данном случае это AppLogger.nuspec . Он также включает в себя токены замены из манифеста.

Откройте файл AppLogger.nuspec в текстовом редакторе для просмотра его содержимого, которое должно иметь следующий вид.

Изменение манифеста

NuGet выдает ошибку при попытке создать пакет со значениями по умолчанию в вашем файле .nuspec , поэтому перед продолжением нужно изменить следующие поля. Их использование описано в разделе о необязательных элементах метаданных в справочнике по файлу NUSPEC.

  • licenseUrl
  • projectUrl
  • iconUrl
  • releaseNotes
  • теги

Кроме того, сейчас можно добавить в манифест любые другие элементы, как описано в разделе Справочник по файлу NUSPEC.

Сохраните файл, прежде чем продолжить.

Выполнение команды pack

Из командной строки перейдите в папку, содержащую файл .nuspec , и выполните команду nuget pack .

NuGet создает в текущей папке файл .nupkg формата идентификатор-версия.nupkg.

Публикация пакета

Получение ключа API

Выберите свое имя пользователя (в правом верхнем углу), а затем щелкните Ключи API.

После создания ключа выберите Копировать для получения ключа доступа, который требуется в интерфейсе командной строки:

Копирование ключа API в буфер обмена

Важно. Сохраните ключ в безопасном расположении, так как позже вы не сможете скопировать его. Если вы вернетесь на страницу ключа API, вам понадобится повторно создать ключ, чтобы скопировать его. Вы также можете удалить ключ API, если больше не хотите отправлять пакеты через интерфейс командной строки.

Определение области позволяет создавать отдельные ключи API для разных целей. Ключи имеют срок действия. Кроме того, их можно привязать к определенным пакетам (или стандартным маскам). Каждый ключ также привязан к конкретным операциям: отправка новых пакетов и обновлений, отправка только обновлений или удаление из списка. Используя определение области, вы можете создавать ключи API разным пользователям, которые управляют пакетами организации. Это позволит предоставлять им только нужные разрешения. См. подробнее о ключах API в определении области.

Публикация с помощью команды nuget push

Откройте командную строку и перейдите к папке с файлом .nupkg .

Выполните следующую команду, указав имя пакета и заменив значение ключа на ключ API:

nuget.exe отображает результаты публикации:

Ознакомьтесь со сведениями о команде nuget push.

Ошибки публикации

Ошибки в результатах выполнения команды push обычно указывают на неполадку. Например, вы забыли обновить номер версии проекта и пытаетесь опубликовать пакет, который уже существует.

Ошибки также возникают при попытке опубликовать пакет с использованием идентификатора, который уже имеется на узле. Например, имя AppLogger уже существует. В этом случае команда push выдает следующую ошибку:

Управление опубликованным пакетом

This package has not been indexed yet. It will appear in search results and will be available for install/restore after indexing is complete (Этот пакет еще не проиндексирован. Он появится в результатах поиска и будет доступен для установки и восстановления после завершения индексирования).

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

Найдите пакет, который требуется исключить из списка, в разделе Published (Опубликованное) и щелкните значок корзины справа:

Значок корзины, отображаемый в списке пакетов на сайте nuget.org

На следующей странице снимите флажок List (package-name) in search results (Вывести (имя пакета) в результатах поиска) и щелкните Save (Сохранить).

I'm using the Nuget Package Explorer to create some nuget packages. I've managed to do so just building a project in Release mode in VS and adding both the dll and pdb files to the package.

So far so good, but when I add the package to another project and try to step into the code while debugging, it will step over it instead.

I understand that I need to build and add the Debug dll and pdb to my package if I want to step into the code while debugging. I'm not sure though how to add these to the package I've already create, which already contains the Release dll and pdb file, which are named the same.

387 1 1 gold badge 5 5 silver badges 14 14 bronze badges

3 Answers 3

My thoughts are, NuGet packaging is a lot about conventions.

There is no problem in packaging same namespaces and same names for different platforms (as in lib/net40/mydll.dll , lib/net35/mydll.dll etc in the same package), as NuGet will filter registered dependencies by platform.

Building several versions for the same platform seems unconventional, this discussion is biased towards making a package per build. That doesn't mean you can't do it, but you should first ask yourself if you should.

That said, if your debug and release builds are very different (conditional compiling etc) this might useful though. But how will end-users choose Release or Debug when installing your package?

An idea could be, one version per build configuration. Both can be installed into the project. To do that, either add a targets file to your package or build a powershell install script (unsupported since Nuget v3) that adds conditional references directly in the target project file, if you want something less basic than whatever MsBuild can do for you.

Example of the first tactic: Create a .target file (in your package, create a build folder and then create build\YourLib.targets with the following contents):

Providing you created debug and release folders (platform folder is optional), the build output will effectively change depending on configuration - provided packet consumers have conventional configuration names, but you could always extend the condition logic a bit with $(Configuration).Contains etc or just put that in the package readme

Я хочу создать пакет NuGet, который добавляет несколько .dll файлы в качестве ссылок на мой проект.

У меня есть папка с 10 .файлы DLL в нем.

когда я устанавливаю это через nuget, я хочу, чтобы эти файлы были добавлены в ссылки проекта.

Я хочу создать пакет nuget, который добавляет несколько .dll как ссылки на мой проект.

Я хотел бы дать вам два решения, чтобы добиться этого:

Во-Первых, Использовать Проводник Пакетов NuGet:

  1. скачать Проводник Пакетов NuGet.
  2. Откройте проводник пакетов NuGet, выберите Создать новый пакет.
  3. добавьте папку lib на вкладке содержимое и добавьте DLL файл
  4. сохраните пакет и установите его в проект, проверьте, добавляет ли он ссылки.

NuGet Package Explorer GUI

во-вторых, как сказал Лекс ли, мы могли бы использовать .nuspec упаковать сборки:

  1. скачать NuGet для.exe.
  2. создать новый проект.
  3. откройте cmd и переключите путь на nuget.exe
  4. командная строка: nuget spec "PathOfProject\TestDemo.csproj"

открыть TestDemo.csproj.nuspec file и измените его и добавьте сборки как файл; ниже мой .файл nuspec:

<?xml version="1.0"?> <package > <metadata> <id>TestDemo</id> <version>1.0.0</version> <authors>Tester</authors> <owners>Tester</owners> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>TestDemo</description> <releaseNotes>Summary of changes made in this release of the package. </releaseNotes> <copyright>Copyright 2017</copyright> <tags>Tag1 Tag2</tags> </metadata> <files> <file src="https://askdev.ru/q/sozdanie-paketa-nuget-iz-dll-172169/MultipleDll/*.*" target="lib\net461" /> </files> </package>

используйте команду pack: nuget pack TestDemo.csproj.nuspec

NuGet Package Explorer - built package output

надеюсь, это поможет вам.

после этой команды у вас будет .файл nuspec, откройте его в редакторе и отредактируйте Id, автора и т. д. Самая важная часть files tag после закрытого тега метаданных.Вы можете определить все DLL здесь как это:

Читайте также: