diff --git a/justfile b/justfile
index 83c5402..64c0a96 100644
--- a/justfile
+++ b/justfile
@@ -9,6 +9,9 @@ base128:
bouncycastle:
just msbuild/BouncyCastle/
+generatorequals:
+ just msbuild/Generator.Equals/
+
newtonsoftjson:
just msbuild/Newtonsoft.Json/
diff --git a/msbuild/Generator.Equals/Generator.Equals.Runtime.dll.meta b/msbuild/Generator.Equals/Generator.Equals.Runtime.dll.meta
new file mode 100644
index 0000000..f574116
--- /dev/null
+++ b/msbuild/Generator.Equals/Generator.Equals.Runtime.dll.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 8959dcad84f8f47698720cd3bc07a68a
\ No newline at end of file
diff --git a/msbuild/Generator.Equals/Generator.Equals.dll.meta b/msbuild/Generator.Equals/Generator.Equals.dll.meta
new file mode 100644
index 0000000..c301d1e
--- /dev/null
+++ b/msbuild/Generator.Equals/Generator.Equals.dll.meta
@@ -0,0 +1,59 @@
+fileFormatVersion: 2
+guid: ffbbcd4ba86a548ccb96d73016dcb5b9
+labels:
+- RoslynAnalyzer
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 3
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 1
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ Android:
+ enabled: 0
+ settings:
+ AndroidLibraryDependee: UnityLibrary
+ AndroidSharedLibraryType: Executable
+ CPU: ARMv7
+ Any:
+ enabled: 0
+ settings:
+ Exclude Android: 1
+ Exclude Editor: 1
+ Exclude Linux64: 1
+ Exclude OSXUniversal: 1
+ Exclude Win: 1
+ Exclude Win64: 1
+ Editor:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ DefaultValueInitialized: true
+ OS: AnyOS
+ Linux64:
+ enabled: 0
+ settings:
+ CPU: None
+ OSXUniversal:
+ enabled: 0
+ settings:
+ CPU: None
+ Win:
+ enabled: 0
+ settings:
+ CPU: None
+ Win64:
+ enabled: 0
+ settings:
+ CPU: None
+ WindowsStoreApps:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/msbuild/Generator.Equals/README.md b/msbuild/Generator.Equals/README.md
new file mode 100644
index 0000000..4450dc6
--- /dev/null
+++ b/msbuild/Generator.Equals/README.md
@@ -0,0 +1,37 @@
+# Generator.Equals
+
+The Generator.Equals library from nuget!
+
+## Oddities of this package
+
+The generator has two parts: the roslyn source generator part, and the runtime
+library. The source generator runs only at build time and also has a dependency
+on the runtime library. The runtime library runs both at build time and at
+regular runtime.
+
+The runtime lib uses [`System.HashCode`][System.HashCode] which is included in
+netstandard2.1, but requires the `Microsoft.Bcl.HashCode` package when
+targetting netstandard2.0 (i.e. it is present in netstandard2.0 but
+"package-provided").
+
+Because unity already supports netstandard2.1, it was preferable to build the
+runtime lib against netstandard2.1 instead of vendoring
+`Microsoft.Bcl.HashCode`. However, roslyn source generators [*must* be built
+against netstandard2.0][roslyn-requires-netstandard20]...
+
+To work around this, and avoid needing to vendor `Microsoft.Bcl.HashCode`, we
+build the package twice: Once we build *both* the source generator and the
+runtime against netstandard2.0, and the second time we build *only* the runtime
+against netstandard2.1. To accomplish the second build, we `git apply` a patch
+to enable netstandard2.1 and remove the unneded dependency, and then `dotnet
+build`.
+
+Finally to get the source generator to work in Unity, we follow the [official
+docs][source-generator-unity-docs], in particular we disable all target
+platforms and give it an asset label of `RoslynAnalyzer`, but only for the
+source generator. The runtime dll doesn't get this label and has support for
+all platforms.
+
+[System.HashCode]: https://learn.microsoft.com/en-us/dotnet/api/system.hashcode?view=net-9.0
+[roslyn-requires-netstandard20]: https://github.com/dotnet/roslyn/issues/45162
+[source-generator-unity-docs]: https://docs.unity3d.com/Manual/install-existing-analyzer.html
diff --git a/msbuild/Generator.Equals/build-runtime-for-netstandard21.patch b/msbuild/Generator.Equals/build-runtime-for-netstandard21.patch
new file mode 100644
index 0000000..0273726
--- /dev/null
+++ b/msbuild/Generator.Equals/build-runtime-for-netstandard21.patch
@@ -0,0 +1,22 @@
+diff --git a/Generator.Equals.Runtime/Generator.Equals.Runtime.csproj b/Generator.Equals.Runtime/Generator.Equals.Runtime.csproj
+index b2b1595..d161784 100644
+--- a/Generator.Equals.Runtime/Generator.Equals.Runtime.csproj
++++ b/Generator.Equals.Runtime/Generator.Equals.Runtime.csproj
+@@ -1,14 +1,15 @@
+
+
+
+- netstandard2.0
++ netstandard2.1
+ 9.0
+ enable
+ Generator.Equals
+
+
+
+-
++
++
+
+
+
diff --git a/msbuild/Generator.Equals/justfile b/msbuild/Generator.Equals/justfile
new file mode 100644
index 0000000..2ab1cfa
--- /dev/null
+++ b/msbuild/Generator.Equals/justfile
@@ -0,0 +1,35 @@
+git_repo := "https://github.com/diegofrata/Generator.Equals.git"
+git_ref := "3.2.0"
+
+out_dir := invocation_directory() / "build"
+working_dir := `mktemp -d`
+repo_dir := working_dir / "repo"
+dotnet_artifact_dir := working_dir / "dotnet_artifacts"
+upm_contents_dir := working_dir / "upm_contents"
+
+default: assemble_upm
+
+# Clean build outputs
+clean:
+ rm -rf {{working_dir}}
+
+clone:
+ git clone --single-branch --branch {{git_ref}} {{git_repo}} {{repo_dir}}
+
+build_both: clone
+ cd {{repo_dir}} && dotnet build {{"Generator.Equals" / "Generator.Equals.csproj"}} --configuration Release -o {{dotnet_artifact_dir}} --framework netstandard2.0
+
+patch_runtime_for_netstandard21: clone
+ cd {{repo_dir}} && git apply {{justfile_dir() / "build-runtime-for-netstandard21.patch"}}
+
+build_only_runtime: clone build_both patch_runtime_for_netstandard21
+ cd {{repo_dir}} && dotnet build {{"Generator.Equals.Runtime" / "Generator.Equals.Runtime.csproj"}} --configuration Release -o {{dotnet_artifact_dir}} --framework netstandard2.1
+
+assemble_upm: clone build_both build_only_runtime && clean
+ mkdir -p {{upm_contents_dir}}
+ cp {{dotnet_artifact_dir / "Generator.Equals.dll"}} {{upm_contents_dir}}
+ cp {{dotnet_artifact_dir / "Generator.Equals.Runtime.dll"}} {{upm_contents_dir}}
+ cp package.json {{upm_contents_dir}}
+ cp *.meta {{upm_contents_dir}}
+ mkdir -p {{out_dir}}
+ npm pack --pack-destination {{out_dir}} {{upm_contents_dir}}
diff --git a/msbuild/Generator.Equals/package.json b/msbuild/Generator.Equals/package.json
new file mode 100644
index 0000000..4dfcc6e
--- /dev/null
+++ b/msbuild/Generator.Equals/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "org.basisvr.generator.equals",
+ "version": "3.2.0",
+ "displayName": "Generator.Equals",
+ "description": "A source code generator for automatically implementing IEquatable using only attributes.",
+ "documentationUrl": "https://github.com/diegofrata/Generator.Equals",
+ "publishConfig": { "registry": "https://npm.pkg.github.com/@BasisVR" },
+ "repository":"https://github.com/BasisVR/UpmBuilds"
+}
diff --git a/msbuild/Generator.Equals/package.json.meta b/msbuild/Generator.Equals/package.json.meta
new file mode 100644
index 0000000..2b57dc6
--- /dev/null
+++ b/msbuild/Generator.Equals/package.json.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 4bad799868aa548d8b1fec54e7d7bea5
+PackageManifestImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant: