Pack, Push and Import Clojure to Unity
2022-04-22 | Blog Article

Prerequisites
Your Clojure library is assumed to be already compiled to dotnet.
To know how to do this, refer to the article: Port your Clojure lib to the CLR with MAGIC
Goal
In this article, I will show you:
- how to package your lib to NuGet
- push it in to your host repo
- import in Unity in this article
Build the dlls with Nostrand
Just use the command nos dotnet/build
at the root of the Clojure project.
The dlls are by default generated in a /build
folder.
Dependency management
A .csproj
file (XML) must be added at the root of the Clojure project.
You can find an example here: clr.test.check.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<NuspecFile>clr.test.check.nuspec</NuspecFile>
<RestoreAdditionalProjectSources>
https://api.nuget.org/v3/index.json
</RestoreAdditionalProjectSources>
</PropertyGroup>
</Project>
There is no need to add References as they were already built by Nostrand in the /build
folder.
Note the NuspecFile
that is required to use the nuspec.
Package Manager
A .nuspec
file (XML) must be added at the root of the Clojure project.
The references
are the references to the dlls in /build
.
You can find an example here: clr.test.check.nuspec
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>clr.test.check</id>
<version>1.1.1</version>
<title>clr.test.check</title>
<authors>skydread1</authors>
<description>Contains the core references for the Clojure lib test.check.</description>
<repository type="git" url="https://github.com/skydread1/clr.test.check" />
<dependencies>
<group targetFramework="netstandard2.0"></group>
</dependencies>
</metadata>
<files>
<file src="build\*.clj.dll" target="lib\netstandard2.0" />
</files>
</package>
The dependency
tag is required to indicate the targeted framework.
The file
(using a wild card to avoid adding the files one by one) is required to add the dlls files that will be available for the consumer. So the target must be lib\TFM
.
In our case, Unity recommends to use netstandard2.0
so our target is lib\netstandard2.0
.
GitHub/GitLab local config
To push the package to a git host, one of the most convenient way is to have a nuget.config
(XML) locally at the root of the project.
The nuget.config for GitHub
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="github" value="https://nuget.pkg.github.com/skydread1/index.json" />
</packageSources>
<packageSourceCredentials>
<github>
<add key="Username" value="skydread1" />
<add key="ClearTextPassword" value="PAT" />
</github>
</packageSourceCredentials>
</configuration>
In order to push a Package to a Package Registry
to your GitHub project repo, you will need to create a PAT (Personal Access Token) with the write:packages
,:read:packages
and delete:packages
permissions.
Replace Username value by your Github username
Replace Token value by your newly created access token
Replace the repo URL by the path to your GitHub account page (not the repo).
Note: Do not push your config in GitHub as it contains sensitive info (your PAT), it is just for local use.
The nuget.config for GitLab
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="gitlab" value="https://sub.domain.sg/api/v4/projects/777/packages/nuget/index.json" />
</packageSources>
<packageSourceCredentials>
<gitlab>
<add key="Username" value="deploy-token-name" />
<add key="ClearTextPassword" value="deploy-token-value" />
</gitlab>
</packageSourceCredentials>
</configuration>
In order to push a Package to a Package Registry
to your GitLab project repo, you will need to create a deploy token (not access token) with the read_package_registry
and write_package_registry
permissions.
Replace Username value by your token username
Replace Token value by your newly created deploy token
Replace the domain (for private server) and project number in the GitLab URL. (don’t forget the index.json at the end)
Note: Do not push your config in GitLab as it contains sensitive info (your deploy token), it is just for local use.
Pack and Push nuget packages with Nostrand
At the root of the project, the dotnet.clj
contains the convenient function to be used with nasser/nostrand.
You can find an example here: dotnet.clj
We added to our Clojure library a convenient function to avoid having to manually use the dotnet commands, you can just run at the root at the Clojure directory:
nos dotnet/nuget-push
This will create the nuget code package .nupkg
file in the folder bin/Release
. the name is the package name and the version such as clr.test.check.1.1.1.nupkg
.
It will then push it to either Gitlab or Github depending on the host using the credentials in nuget.config
.
It is equivalent to the 2 dotnet commands:
dotnet pack --configuration Release
dotnet nuget push "bin/Release/clr.test.check.1.1.1.nupkg" --source "github"
Note: for a Clojure project, you can let the default option for the packing. There is no need to build in theory as we already have our dlls ready in our /build
folder. The dotnet build
will just create a unique dll with the name of your library that you can just ignore.
Download nuget Packages
Using package references is the new way of doing this but it does not work with Unity.
Import nuget packages to a regular C# project
The new way of importing the nuget packages is to use the PackageReference
tag directly in the .csproj
file such as:
<PackageReference Include="Sitecore.Kernel" Version="12.0.*" />
But this method only works if you are using the .csproj
file which we don’t use in Unity as we use the manifest.json
.
Import nuget packages to a Unity project
Unity uses a json file in Packages/manifest.json
to download deps. However it does not work for nuget packages.
There is no .csproj
at the root so we cannot use the method above, and all the other underlying csproj
are generated by Unity so we cannot change them.
The only choice we have is to use the old way of importing the nuget packages which is to use a packages.config
and then use the command nuget restore
to fetch the packages last versions.
So we need to add 2 config files in our root of our Unity project:
nuget.config
: github/gitlab credentialspackages.config
: packages name and their version/target
nuget.config
In order to fetch all the packages at once using nuget restore
, we need to add locally the nuget.config
with the different sources and credentials.
So to restore our GitHub and GitLab packages from our example, we use the following nuget.restore
:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="repositoryPath" value="Assets/ClojureLibs" />
</config>
<packageSources>
<clear />
<add key="gitlab" value="https://sub.domain.sg/api/v4/projects/777/packages/nuget/index.json" />
<add key="github" value="https://nuget.pkg.github.com/skydread1/index.json" />
</packageSources>
<packageSourceCredentials>
<gitlab>
<add key="Username" value="deploy-token-name" />
<add key="ClearTextPassword" value="deploy-token-value" />
</gitlab>
<github>
<add key="Username" value="skydread1" />
<add key="ClearTextPassword" value="PAT" />
</github>
</packageSourceCredentials>
</configuration>
The repositoryPath
allows us to get our packages in a specific directory. In our case, we put it in Assets/ClojureLibs
(it needs to be in the Asset
dir anywhere)
packages.config
To tell Unity which packages to import while running nuget restore
, we need to provide the packages.config
. Here is the config in our example:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Magic.Unity" version="1.0.0" targetFramework="netstandard2.0" />
<package id="my-private-proj" version="1.0.0" targetFramework="netstandard2.0" />
<package id="clr.test.check" version="1.1.1" targetFramework="netstandard2.0" />
</packages>
Magic.Unity
To run clojure in Unity, you need Magic.Unity. It is a the runtime for Clojure compiles with Magic in Unity.
Note the Magic.Unity
in the packages.config
above. Magic.Unity has its own nuget package deployed the same way as you would deploy a Clojure library, so you import it along side your nuget packages with your compiles clojure libs.
nuget restore
Once you have the github/gitlab credentials ready in nuget.config
and the packages and their version/target listed in packages.config
, you can run the command nuget restore
at the root of the unity project.
If running nuget restore
do not fetch the last version, it is because it is using the local cache. In this case you need to force restore using those commands.
Most of the time, ignoring the cache is fixing this issue:
nuget restore -NoCache
Here is the packages tree of our project for instance:
~/workspaces/unity-projects/my-proj:
.
├── clr.test.check-legacy.1.1.1
│ ├── clr.test.check-legacy.1.1.1.nupkg
│ └── lib
│ └── netstandard2.0
│ ├── clojure.test.check.clj.dll
│ ├── clojure.test.check.clojure_test.assertions.clj.dll
│ ├── clojure.test.check.clojure_test.clj.dll
│ ├── clojure.test.check.generators.clj.dll
│ ├── clojure.test.check.impl.clj.dll
│ ├── clojure.test.check.random.clj.dll
│ ├── clojure.test.check.results.clj.dll
│ └── clojure.test.check.rose_tree.clj.dll
├── my-private-lib.1.0.0
│ ├── my-private-lib.1.0.0.nupkg
│ └── lib
│ └── netstandard2.0
│ ├── domain.my_prate_lib.core.clj.dll
│ └── domain.my_prate_lib.core.utils.clj.dll
Finally, You can add Magic.Unity (runtime for magic inside Unity) in the manifest.json like so:
{
"dependencies": {
...,
"sr.nas.magic.unity": "https://github.com/nasser/Magic.Unity.git"
}
}
Conclusion
Once you have the proper required config files ready, you can use Nostrand
to Build your dlls:
nos dotnet/build
Pack your dlls in a nuget package and push to a remote host:
nos dotnet/nuget-push
Import your packages in Unity:
nuget restore
Magic.Unity
is the Magic runtime for Unity and is already nuget packaged on its public repo
Contribute
Found any typo, errors or parts that need clarification? Feel free to raise a PR on the GitHub repo and become a contributor.