BNuget is a tool for Unity 2021+ that installs Nuget packages into Assembly Definitions. Only Nuget packages that support NETStandard are supported. This documentation assumes the reader has a basic understanding of Unity, Unity Assembly Definitions, and Nuget. To learn more about these subjects, check their documentation.
BNuget works with Unity Assembly Definitions. An Assembly Definition is a file that logically groups a set of C# script files into one compile unit. Every C# script in a Unity project is grouped into an Assembly Definition. By default, there are a set of predefined assemblies that capture most C# scripts. However, if an Assembly Definition file is created, then all C# script files in the Assembly Definition's folder and sub folders are part of the Assembly Definition. When Unity compiles the C# code in a project, each Assembly Definition's C# script files are built into individual .DLL
files and shipped with the Unity game.
Assembly Definitions may reference existing .DLL
files that have pre-built libraries. BNuget takes advantage of this capability, and extends the Assembly Definition .DLL
referencing to include references to Nuget packaged .DLL
files. BNuget allows you to create a new file alongside an Assembly Definition, called a .bnuget
file, that defines a list of Nuget package dependencies for the given Assembly Definition. BNuget will download all of the Nuget packages, extract the required .DLL
files, and automatically add them as references to the Assembly Definition. The process of downloading and applying the references is called a Restore Operation.
Each Assembly Definition may only have one .bnuget
file, and the .bnuget
file must be located in the same folder as the Assembly Definition, and share the same filename as the Assembly Definition.
To demonstrate how to use BNuget, the following steps will guide you through the installation of the ICanHazDadJoke.NET Nuget package, which provides a free SDK to access the DadJokes API. Check the sample, /BNuget/Samples/DadJokes
, for a complete code based example.
First, you should create a new Assembly Definition.
- Create a folder called
DadJokeSample
, - Inside the folder, right click the Project Window and select
Create/Assembly Definition
. Name the Assembly Definition as,"DadJokeSample"
.
Now that you have an Assembly Definition, Unity will take any C# script files that exist in the DadJokeSample
folder and compile them as an isolated .DLL
. The next step is to add the Nuget package reference.
- Right click on the
DadJokeSample
Assembly Definition, and clickNuget References
on the context menu dropdown. - Select the resulting,
DadJokeSample.bnuget
file.
NOTE: DO NOT rename the DadJokeSample.bnuget
file. If you do, you should also rename the Assembly Definition. However, Assembly Definition file names are not required to match the compiled .DLL
name. If you rename either the Assembly Definition file, or its internal, "name"
property, make sure to rename the DadJokeSample.bnuget
file to match.
When you clicked on the .bnuget
file, the Inspector for the asset should show a list of Packages from nuget.org. At the top of the window, there is a search box. Select the search box and type, "DadJoke", and then press enter. The list of packages will update to reflect the search query. Find the ICanHazDadJoke.NET
package, make sure the version dropdown is set to 1.0.13 (or greater), and click "Install". It may take a moment, but Unity will recompile. BNuget is running a Restore Operation, which is a term that comes from the Nuget community. The ICanHazDadJoke.NET
package is being downloaded, its own internal dependencies downloaded, and all subsequent .DLL
files associated with the DadJokeSample
Assembly Definition.
After Unity has recompiled, the Inspector should show the ICanHazDadJoke.NET
under the Installed Packages section. Additionally, there is an Implicit Packages section that should contain the Newtonsoft.JSON
package. The ICanHazDadJoke.NET
packages has an internal dependency on Newtonsoft.JSON
, so it has been included implicitly. To confirm that the Restore operation completed successfully, check the following,
- Select the
DadJokeSample
Assembly Definition, and check that theOverride References
checkbox is set to true. Additionally, there should be two entries under theAssembly References
list; one for theICanHazDadJoke.NET
package, and a second for theNewtonsoft.JSON
package. - Observe that there is a new folder,
Assets/BNugetDlls
in your project. The folder should contain 4 files. There will be 2.DLL
files, and 2.nuspec
files.
The ICanHazDadJoke.NET
package is now available to be used inside the DadJokeSample
assembly. Create a new script file, and paste the code below.
using ICanHazDadJoke.NET;
using UnityEditor;
using UnityEngine;
public class Example
{
[MenuItem("BNuget Example/Get Dad Joke")]
public static async void PrintDadJoke()
{
Debug.Log("Getting joke");
var libraryName = "ICanHazDadJoke.NET Readme";
var contactUri = "https://github.com/mattleibow/ICanHazDadJoke.NET";
var client = new DadJokeClient(libraryName, contactUri);
var joke = await client.GetRandomJokeAsync();
Debug.Log(joke.Joke);
}
}
Now, back in Unity, you can use the BNuget Example/Get Dad Joke
menu item at the top of the screen to test the code.
BNuget has installed the Nuget dependency, and it is available to use in C# scripts.
In addition to the above video, watch the video about configuration contexts as well.
BNuget uses a hierarchical file based configuration system. A configuration asset will define the behavior of BNuget operations for all .bnuget
files in the sub directories of the configuration. Each .bnuget
file will be configured by exactly one configuration asset. If there are multiple configuration assets in a .bnuget
file's parent path, then the closest configuration by file distance will be used. If there is no configuration file anywhere in the parent path of a .bnuget
file, then that .bnuget
file will use the default hardcoded configuration.
Consider the following example folder structure.
/Assets
file1.bnuget
/Example
ConfigA.asset
file2.bnuget
/Folder
file3.bnuget
/SubFolder
ConfigB.asset
file4.bnuget
/Elsewhere
file5.bnuget
The table below shows the mapping from the .bnuget
files to their associated configuration.
.bnuget file |
configuration file |
---|---|
file1.bnuget | default |
file2.bnuget | ConfigA.asset |
file3.bnuget | ConfigA.asset |
file4.bnuget | ConfigB.asset |
file5.bnuget | default |
Anytime a Nuget Restore operation happens, it applies to the entire configuration context. In the example above, when a Restore operation happens for file3.bnuget
, the associated configuration is loaded, ConfigA.asset
, and then the Restore operation includes all .bnuget
files associated with ConfigA.asset
, which are file3.bnuget
and file2.bnuget
.
Nuget packages contain .DLL
files, which are the compiled code outputs of the package. A Restore operation will download the required Nuget packages defined in the various .bnuget
files, and extract the inner .DLL
files into a folder within the Unity project. If the same Nuget package is referenced from multiple .bnuget
files within the configuration context, then the package's .DLL
file will only be imported into Unity once.
A Restore operation for ConfigA.asset
will not affect the .DLL
files associated with any other configuration context. Configuration contexts represent isolated dependency groups. If the same Nuget package is referenced within multiple configuration contexts, then the package's .DLL
file will be imported into once for each Configuration context that references it.
In order to create a new configuration file, follow the steps below.
- Right click in the Unity Project Window, and select
Create/BNuget Config
. - Give a unique and descriptive name to the newly created configuration asset.
- Click on the asset, and fill out the configuration properties.
Each configuration file defines a set of properties that control how the Restore operation is executed.
When BNuget imports .DLL
files from Nuget packages into a Unity project, it creates a unique name for that .DLL
. The name follows the following format,
$"{config.DllPrefix}{package.id}_{version}_{framework}_{PackageSourceKey}.dll"
The config.DllPrefix
is the only part of the .DLL
file name that is specific to the configuration context that is responsible for the .DLL
file.
By default, the prefix is "z_nuget_"
. The prefix starts with a z
so that all .DLL
files imported to Unity via BNuget are at the end of alphabetized lists. Unity's .DLL
reference dropdown for Assembly Definitions show .DLL
files alphabetized, so the z
prefix helps keep developer .DLL
s at the top of the list, and BNuget .DLL
s at the bottom.
A Critical axiom of BNuget is that different configuration contexts produce unique .DLL
names. the config.DllPrefix
MUST be unique per configuration file. If the sample prefix is used between multiple configurations, and the same Nuget package is referenced between those configurations, then a .DLL
will be imported multiple times into Unity with the same name. Multiple .DLL
files with the same name is a bad practice in Unity and it should be avoided.
Each configuration context uses a single folder to import the entire group of .DLL
files required by the associated .bnuget
definitions within the configuration. By default, BNuget stores imported .DLL
files in Assets/BNugetDlls
. After every Restore operation, all extraneous files in the folder will be removed.
If there are multiple configuration files, then they MUST have unique .DLL
import folders. When multiple configuration files share a .DLL
import folder, then .DLL
uniquely imported from one configuration will be removed anytime another configuration context performs a Restore operation.
The Restore DLL Path field on the configuration asset is actually stored as a path relative to the Unity project's root folder. The actual data stored inside the configuration file is the path relative to the project folder. However, the path displayed in the Unity Inspector for the configuration is relative to the configuration's current file location.
When BNuget performs a Restore operation, Nuget packages may need to be downloaded from the Internet. However, these Nuget packages are cached locally so that future Restore operations happen faster. The cache location is relative to the Unity project's root. By default, the cache path is Library/BNuget/Cache
. The Library
folder is usually ignored from version control systems, which means that multiple developers won't share a package cache.
If multiple configuration files share the same cache path, then the configuration contexts may share preexisting work. The best practice is to have configuration files share a single cache path.
BNuget will always show Nuget packages available from the default Package Source, nuget.org. However, additional Package Sources can be added to a configuration file. The inclusion of a Package Source will allow every associated .bnuget
file to browse and install packages from the new Package Source. The Package Sources section has more detail.
Nuget packages can come from a variety of locations. Many packages are located on nuget.org, which is a public repository of (mostly) free packages for the Dotnet environment. However, BNuget can be configured to use packages from additional locations. The Nuget API defines the schema for a Nuget Package Source, and BNuget supports the v3 Nuget API specification.
BNuget has only been tested and verified with Github's Nuget Package Source. However, BNuget should be able to support many other custom Package Sources, such as Artifactory, Nexus, and ProGet.
Each Package Source is defined by a BNuget Source
asset file. The file defines a unique name for the Package Source, where the Nuget packages can be found on the internet, and optionally how to authenticate with the Package Source. The Bnuget Source
asset file must be associated to a BNuget Config
file so that the Package Source can be used. It is impossible to add a custom Package Source to the global configuration context.
In order to add a custom Package Source, follow the steps below.
- Right click in the Unity Project Window, and select
Create/BNuget Source
. - Give a unique and descriptive name to the newly created source asset.
- Click on the asset, and fill out the properties.
- Select an existing Configuration Asset, and add the newly created
Bnuget Source
asset reference to the configuration'sPackage Sources
field. - Select any
.bnuget
file within the configuration context, and click the "Restore" button to cause the window to reload and show the custom Package Source.
Each Package Source has a set of configuration properties that must be configured.
Every Package Source must have a unique Key
value. The Key
field is used as the primary key in various BNuget lookup tables. The Key
should be a short descriptive moniker for the Package Source, and once it has been set, it shouldn't be changed often.
When BNuget imports .DLL
files into the Unity project, the .DLL
filenames have the following format,
$"{config.DllPrefix}{package.id}_{version}_{framework}_{PackageSourceKey}.dll"
The PackageSourceKey
value is the Package Source's Key
field. If the Key
field changes, then all subsequent Restore operations will import new .DLL
files.
BNuget only supports Package Sources that use the Nuget v3 API specification. The Index URL
is the entry point to the Nuget API. Follow the documentation provided by the custom Package Source's author.
Package Sources often require authentication. BNuget supports two forms of common web authentication, Basic, and Bearer.
In either case, once the authentication has been configured, the "Test Connection" button will trigger a request to the custom Package Source's query API. If the Index URL
or the authentication hasn't been configured correctly, an error message will be logged in the console and shown in the Inspector.
PLEASE NOTE that BNuget does not save your credentials. Every time you start Unity, you must re-enter the credentials for every Package Source before they can be used. BNuget puts the credentials into the application memory space of Unity, via SessionState
, but BNuget does not write the credentials to disk. This may be inconvenient, but it will help keep secure credentials safe.
Basic Auth is a common form of web authentication that require a Username and Password. When applied to a custom Package Source, the Username and Password are combined according to the Basic Auth protocol, and sent as an Authentication
header with every API request directed to the custom Nuget Package Source.
Bearer Tokens are a second common form of web authentication, and they only require a single token string. The token is sent inside the Authentication
header to every API request directed to the custom Nuget Package Source.
BNuget is not a complete Nuget client. A Nuget package contains a lot of individual files, but BNuget only
tries to extract the built managed .dll
file for the correct target platform for Unity. That means BNuget
does nothing with many of the files in a Nuget package. For most cases, this works fine, because the managed
.dll
file contains all of the built code.
However, there are edge cases where a Nuget package contains additional information that is required for the package to work correctly. In this case, it is possible to completely override how the files are extracted from a Nuget package.
All overrides are handled per configuration file, so you must create a custom configuration file. However, the
code that controls the download is a virtual method on the configuration itself, so you need to create a sub class
of the configuration. Make a subclass of the BNugetConfigFile
class, and then create a Scriptable Object instance of that class. Now, override the HandleDownload
method.
The HandleDownload
method is called every time a package is being downloaded. When you override this method,
you inherit complete responsibility for how EVERY package is downloaded into the project.
I advise you to use a switch statement to check for the edge case package ids, but leave the default case untouched, like in the example below.
public override Task<NupkgDownloadHandlerData> HandleDownload(NupkgPackageDownloadArgs args)
{
switch (args.package)
{
case "llamasharp.backend.cpu":
// only in the llamasharp.backend.cpu case, we have a custom download function
return HandleLlamaBackend(args);
default:
// in all other cases, use the basic managed dll extraction default case.
return base.HandleDownload(args);
}
}
There are a few known limitations regarding BNuget.
Net Standard Only
Nuget packages can contain code built for a variety of target frameworks, including the now outdated Dotnet Framework, the more modern .NET 8, or Net Standard. BNuget only supports the installation of packages that support Net Standard. Unity itself does not support .NET 8. Even though Unity does support Dotnet Framework, BNuget does not.
Unfortunately, BNuget does not make it obvious which Nuget packages are able to be installed. This is due to the structure and nature of the Nuget API itself. If a non Net Standard package is installed, an error will be printed to the console.
No Support for predefined Assembly Definitions
BNuget does not support the ability to add global nuget packages to predefined Assembly Definitions. You MUST create an Assembly Definition to add Nuget dependencies.
No Support for Local File Package Sources
Most Nuget clients allow developers to setup Nuget Package sources pointed to a local folder containing built .nupkg
packages. Sadly, BNuget does not support this (yet?).
There is a public Discord Server available to discuss questions and comments regarding BNuget.