Compile .bond files of projects using my NuGet package - c#

I have a NuGet package with a .bond file. Users of my package can derive their Bond structs from the structs in my package's .bond file.
I want the user's Bond files to be compiled when they include my NuGet package. Today they must include my NuGet and the Bond.CSharp NuGet. But, my NuGet already has a reference to Bond.CSharp.
How can I author my package so that the consumers do not need to have their own <PackageReference Include="Bond.CSharp" ... />?

Bond codegen is run from the Bond.CSharp's build targets.
By default, the build targets of packages you consume do not flow to your consumers. The default value of a PackageReference's PrivateAssets is "contentfiles;analyzers;build".
You can override this behavior in your csproj's PackageReference:
<ItemGroup>
<PackageReference Include="Bond.CSharp" Version="[9.0.3]">
<!-- removing "build" from default so that consumers also run codegen -->
<PrivateAssets>contentfiles;analyzers</PrivateAssets>
</PackageReference>
</ItemGroup>
I assume you are compiling the base struct into an assembly in your package. Bond codegen assumes that the generated code and the runtime library exactly match, so I've used an exact match version bound in the PackageReference: [9.0.3]
You said that you want your consumers to be able to derive from your Bond structs, so you'll probably also want to configure their BondImportPath to include the .bond file inside your package. To do this, you need to
make sure the .bond files are included in the package and
add a package .props file to set the BondImportPath to the package directory with said .bond files.
The make sure the .bond files are included in the package, add something like this to your package's .csproj file:
<ItemGroup>
<None Include="bond/**">
<Pack>true</Pack>
<PackagePath>build/bond/</PackagePath>
</None>
</ItemGroup>
This assumes that your .bond files live in a bond\ subdirectory.
To automatically add something to BondImportPath, you need to add a package .props file that will be automatically imported by consumers. Create a file named ExactNameOfPackage.props with the following content:
<Project>
<ItemGroup>
<BondImportDirectory Include="$(MSBuildThisFileDirectory)bond" />
</ItemGroup>
</Project>
This .props file also needs to be packed. Add this to your project's .csproj file:
<ItemGroup>
<None Include="ExactNameOfPackage.props">
<Pack>true</Pack>
<PackagePath>build/</PackagePath>
</None>
</ItemGroup>
Now, the consumer can just use a PackageReference. Any .bond files in their project will be compiled automatically, and they can use import "you-file.bond" to refer to a .bond file in your package.
Build assets do not flow transitively. The NuGet 5+ buildTransitive feature looks like it solves this, but I haven't experimented with it.
Here are the complete project files I used. The complete code is in my GitHub repository, export-bond-file-nuget.
lib-with-bond.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<PackageId>LibWithBond</PackageId>
<Version>1.2.0</Version>
</PropertyGroup>
<ItemGroup>
<None Include="LibWithBond.props">
<Pack>true</Pack>
<PackagePath>build/</PackagePath>
</None>
<None Include="bond/**">
<Pack>true</Pack>
<PackagePath>build/bond/</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Bond.CSharp" Version="[9.0.3]">
<PrivateAssets>contentfiles;analyzers</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
LibWithBond.props
<Project>
<ItemGroup>
<BondImportDirectory Include="$(MSBuildThisFileDirectory)bond" />
</ItemGroup>
</Project>
consume-lib.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- I had a local NuGet source pointing to the output of running
`dotnet pack` on lib-with-bond.csproj -->
<PackageReference Include="LibWithBond" Version="1.2.0" />
</ItemGroup>
</Project>
I was able to make this work for a PackageReference. My initial experiments making it work for ProjectReference were not successful, and I ran out of time to work more on this answer.

Related

How to Exclude Content directory in Nuget

I am trying to build nuget with my csproj and MSBuild.
Nuget package gets created successfully. But I see content and contentFiles folder where it has my 20 .proto files.
And when someone downloads my nuget, they also get my proto files. But I don't want to give those files. How Can I avoid having .proto files.
I don't have .nuspec file.
My csproj looks like this :
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProduceReferenceAssemblyInOutDir>true</ProduceReferenceAssemblyInOutDir>
<PackageId>BlahBlah</PackageId>
<Title>BlahBlah</Title>
<Version>1.0.0</Version>
<Authors>Test</Authors>
<Company>Test</Company>
<PackageTags>Test nuget</PackageTags>
<DebugType>portable</DebugType>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<PackageOutputPath>Blah</PackageOutputPath>
</PropertyGroup>
<Target Name="Test1" AfterTargets="Pack" Condition="$(PackOnBuild) == 'true'">
<ItemGroup>
<PackageFile Include="$(TestVersion).nupkg" />
</ItemGroup>
<Copy SourceFiles="#(src)" DestinationFolder="$(dst)" />
</Target>
According to the Microsoft docs, you can set <Pack>false</Pack> to exclude files from the NuGet package.
Since I don't know whether proto files are treated as Content or None, it should be either
<PropertyGroup>
<Content Include="*.proto">
<Pack>false</Pack>
</Content>
</PropertyGroup>
or
<PropertyGroup>
<None Include="*.proto">
<Pack>false</Pack>
</Content>
</PropertyGroup>
within the csproj.

Extract git branch name while building C# project

While building the C# project locally/CI server, I wanted to control the NuGet package reference in the .csproj file. If the developer is building a C# project on github master branch (locally/CI server) I would like to add RC build NuGet package reference otherwise PRE releases NuGet package reference. How to do this? Can someone please assist me?
Some thoughts like -
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<GitBranch>$(GitBranch.Trim())</GitBranch>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GitInfo" Version="2.2.0" />
</ItemGroup>
<Choose>
<When Condition="$(GitBranch) == 'master'">
<ItemGroup>
<PackageReference Include="Data.Account.Domain.Messaging" Version="1.0.0-rc*" IncludePrerelease="true" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<PackageReference Include="Data.Account.Domain.Messaging" Version="1.0.0-pre*" IncludePrerelease="true" />
</ItemGroup>
</Otherwise>
</Choose>
</Project>
You could read the git branch from the .git/HEAD file. Naive implementation like
<PropertyGroup>
<GitBranch>$([System.IO.File]::ReadAlltext('$(MsBuildThisFileDirectory)\.git\HEAD').Replace('ref: refs/heads/', '').Trim())</GitBranch>
</PropertyGroup>
You might want to adjust how you get the path to that file, and perhaps use something more robust (e.g. if you just checkout a random commit in git, the file won't contain a branch name but a commit SHA)
You could also use variable $(GitRoot) from GitInfo itself to build the path, just a small adaption to the existing code but helped for my case where I had to deal with nested folders.
<PropertyGroup>
<GitBranch>$([System.IO.File]::ReadAlltext('$(GitRoot)\.git\HEAD').Replace('ref: refs/heads/', '').Trim())</GitBranch>
</PropertyGroup>

PackageReference to project in the same Solution/git repository

At the moment I'm trying to setup a solution with a implementations class library and an abstractions project. I want to have both packages on nuget.org.
Normally when you're just using ProjectReferences, you'd only have to point to the csproj:
Random.Abstractions:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Description>Abstractions for the Randomizer</Description>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<Company>MyCompany</Company>
<Authors>Pieterjan De Clippel</Authors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
</ItemGroup>
</Project>
Random
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Random.Abstractions\Random.Abstractions.csproj" />
</ItemGroup>
</Project>
But off course when you build a nuget package, you don't want the Random.Abstractions.dll to be inside the Random nuget package, but the Random package to depend on the Random.Abstractions package. But since this package doesn't exist yet, you can't build it yet.
Microsoft is able to solve this problem like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
...
</PropertyGroup>
<ItemGroup>
...
</ItemGroup>
<ItemGroup>
...
<Reference Include="Microsoft.AspNetCore.DataProtection.Abstractions" />
</ItemGroup>
</Project>
So this is neither a ProjectReference nor a PackageReference, which is why they are able to:
build and test the solution locally (so there they'd be acting as ProjectReferences)
push the code to GitHub and let the GitHub Actions create and publish the new version of the packages, while they depend on each other (so there they'd be acting as PackageReferences)
I've tried doing the same in my solution, but the project containing the concrete implementations is unable to find the abstractions project:
MyCompany.Random.Abstractions.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Description>Abstractions for the Randomizer</Description>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<Company>MyCompany</Company>
<Authors>John Wick</Authors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
</ItemGroup>
</Project>
MyCompany.Random.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="MyCompany.Random.Abstractions" />
</ItemGroup>
</Project>
So no ProjectReference (we don't want the dll to be in this nuget package), no PackageReference (the package is not yet published), but just Reference.
But somehow I'm still getting an error that the Abstractions assembly cannot be found:
I've checked the NuGet.config but there's nothing really special in there.
How can I use the Reference tag in a dotnet-based project, in order to reference to code while building/testing locally and reference the new package while building/pushing in a CI pipeline?
Reference to Microsoft.NET.Sdk documentation
Update:
#pinkfloydx33, you're right. I can see only one assembly/dll inside the nupkg:
Update 2:
I added the Microsoft.SourceLink.GitHub package to my project, and after downloading/unzipping the new version of my package I can see the following nuspec:
Where 77b181a24a83e46b4c80376f65bb305bedba1a64 is the commit ID.
Thanks for the tip.

Create NuGet package of Function App project

I need to create a NuGet package of a .NET Core 2.1 Azure Functions project. The problem is that dotnet pack just creates a folder structure like presented below, but not the .nupkg file itself. What may be wrong?
Publish directory tree:
-- bin
-- Function
---- function.json
-- FunctionApp.deps.json
-- host.json
FunctionApp.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AzureFunctionsVersion>v2</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.24" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
.nupkg is the manifest that described your NuGet package. To generate the .nupkg file you have to add required properties in the .csproj project file.
<PropertyGroup>
<PackageId>AppLogger</PackageId>
<Version>1.0.0</Version>
<Authors>name</Authors>
<Company>company</Company>
</PropertyGroup>
You can generate the Nuget package using the following command.
dotnet pack tempproject.csproj -o D:\Temp --no-build /p:Configuration=debug /p:PackageVersion=1.0.12.0 --verbosity Detailed

Antlr not working with VS2017

I am trying to set up a simple project with Antlr in .net core 1.0 project using VS2017.
Following https://github.com/sharwell/antlr4cs, added .g4 file to the project. The project file looks like this,
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework> </PropertyGroup>
<ItemGroup>
<None Remove="Calculator.g4" /> </ItemGroup>
<ItemGroup>
<PackageReference Include="Antlr4" Version="4.5.4-beta001" /> </ItemGroup>
<ItemGroup>
<AdditionalFiles Include="Calculator.g4" />
</ItemGroup>
</Project>
But the document says,
Locate an existing XML element according to the MSBuild Property
column in the table above, or add one if it does not already exist.
For example, to generate both the parse tree listener and visitor
interfaces and base classes for your parser, update the project item
to resemble the following.
<Antlr4 Include="CustomLanguage.g4">
<Generator>MSBuild:Compile</Generator>
<CustomToolNamespace>MyProject.Folder</CustomToolNamespace>
<Listener>True</Listener> <Visitor>True</Visitor> </Antlr4>
There is no any Antlr4 tag in this proj file. Is Antlr not supported in VS2017?
is tehre a good example I can follow to use ANtlr with .net core?
This is simply all I need in my .NET Standard 1.3 class library project to hold the grammar file.
<ItemGroup>
<Antlr4 Include="Something.g4">
<Generator>MSBuild:Compile</Generator>
<Listener>False</Listener>
<Visitor>False</Visitor>
</Antlr4>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Antlr4" Version="4.6.1-beta001" />
</ItemGroup>
Note that you might use a newer Antlr4 package version as 4.6.1 was the only version available when this answer was created.
I was just looking for the same thing, for .NET Core 2.0.
The current ANTLR4 package version is 4.6.5 Beta 1. In this version the C# generator was ported to C# so the dependency to Java was removed. This is still experimental and has to be enabled manually with :
<Antlr4UseCSharpGenerator>True</Antlr4UseCSharpGenerator>
Files aren't generated when a .g4 file is modified. They will be generated when dotnet build is called.
File globbing works as expected BUT changing settings like <Visitor>false</Visitor> requires a call dotnet clean before dotnet build.
The default Antlr task options can be found in the source :
<Antlr4>
<Generator>MSBuild:Compile</Generator>
<CustomToolNamespace Condition="'$(Antlr4IsSdkProject)' != 'True'">$(RootNamespace)</CustomToolNamespace>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<Encoding>UTF-8</Encoding>
<TargetLanguage>CSharp</TargetLanguage>
<Listener>true</Listener>
<Visitor>true</Visitor>
<Abstract>false</Abstract>
<ForceAtn>false</ForceAtn>
</Antlr4>
Globbing works, so if I want to build all g4 files and disable visitors, all I have to write is :
<ItemGroup>
<Antlr4 Include="**/*.g4" >
<Visitor>false</Visitor>
</Antlr4>
</ItemGroup>
My entire csproj file looks like this :
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Antlr4UseCSharpGenerator>True</Antlr4UseCSharpGenerator>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Antlr4">
<Version>4.6.5-beta001</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Antlr4 Include="**/*.g4" >
<Visitor>false</Visitor>
</Antlr4>
</ItemGroup>
</Project>

Categories