fsprojects / Paket

A dependency manager for .NET with support for NuGet packages and Git repositories.
https://fsprojects.github.io/Paket/
MIT License
1.99k stars 520 forks source link

Missing option to create/fill attribute `GeneratePathProperty` for element `PackageReference` #4194

Open apm1grb opened 1 year ago

apm1grb commented 1 year ago

Description

To access artifacts (e.g. additional content like shared .proto) from a package, there is an MSBuild property Pkg{normalized package identifier} available, which points to the package cache folder.

To fill that property, it needs to set the attribute GeneratePathProperty for the PackageReference element.

Repro steps

Please provide the steps required to reproduce the problem

  1. Consume a package XYZ in new project via Paket

  2. Install/Restore the packages

  3. Open the file `.../obj/.csproj.paket.props

Expected behavior

It needs such an enhanced entry with GeneratePathProperty="true":

<PackageReference Include="XYZ.Foundation.Messages.Protobuf" GeneratePathProperty="true">
  <Version>0.0.1-alpha.58</Version>
</PackageReference>

Actual behavior

Currently, only this simple form is created:

<PackageReference Include="XYZ.Foundation.Messages.Protobuf">
  <Version>0.0.1-alpha.58</Version>
</PackageReference>
apm1grb commented 1 year ago

This need is based on sharing .proto files via NuGet packages accordingly to the described approach in SO: Sharing one gRPC proto file for all solutions

apm1grb commented 1 year ago

To workaround this missing capability, these (similar) approaches are helpful.

Using a package .targets file

Using a <package ID>.targets as part of the package, located in a build folder on the package root level.

This file should have content like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target
      Name="CopyProtobufs"
      BeforeTargets="BeforeBuild">
        <CreateItem
          Include="$(MSBuildThisFileDirectory)..\content\Protos\**\*.*">
            <Output TaskParameter="Include" ItemName="ProtoFiles" /> 
        </CreateItem>

        <Copy
          SourceFiles="@(ProtoFiles)"
          DestinationFolder="$(ProjectDir)\include\%(RecursiveDir)"
          SkipUnchangedFiles="true"
          OverwriteReadOnlyFiles="true"
        />

        <WriteLinesToFile
          File="$(ProjectDir)\include\.gitignore"
          Lines=".gitignore;foundation/"
          Overwrite="true"
          Encoding="UTF-8"
        />
    </Target>
</Project>

Such a package-related .targets file is automatically weaved into the build process of a package consumer.

This approach copies the (common) .proto files to the project folder of the consuming project. In case this project is Git versioned, it makes sense to suppress the tracking of such imported files. That's the aim of the 2nd step - create dynamically a .gitignore file in the root folder of the imported stuff which ignores the .gitignore file itself and the folder with .proto files.

So the consuming project can import the (common) .proto files that way:

<ItemGroup>
    <Protobuf Include="Protos/**/*.proto" ProtoRoot="Protos" AdditionalImportDirs="include/" />
</ItemGroup>

Using a package .props file

Using a <package ID>.props as part of the package, located in a build folder on the package root level.

This file should have content like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <PkgMessages_Protobuf>$(MSBuildThisFileDirectory)/../content/Protos/</PkgMessages_Protobuf>
  </PropertyGroup>
</Project>

Again, such a package-related .props file is automatically weaved into the build process of a package consumer.

It creates an MSBuild property named PkgMessages_Protobuf which points to the folder of the .props files. Later this property can get evaluated in the build process of the package consumer.

So the consuming project can import the (common) .proto files that way:

<ItemGroup>
    <Protobuf Include="Protos/**/*.proto" ProtoRoot="Protos" AdditionalImportDirs="$(PkgMessages_Protobuf)" />
</ItemGroup>

Remarks

In both cases, these files must be part of the package. We keep them next to the .csproj file and the packaging is ensured via:

  <PropertyGroup Label="Protobuf asset root folders">
    <Assets_ProtoInclude>Protos/foundation/</Assets_ProtoInclude>
  </PropertyGroup>
  <ItemGroup Label="NuGet package Protobuf assets">
    <_ProtoAssetName Include="messages" />
    <_Asset PackagePath="content/Protos/foundation/" Include="@(_ProtoAssetName->'$(Assets_ProtoInclude)%(Identity).proto')" />
    <None Include="@(_Asset)" Pack="true" Visible="false" />
  </ItemGroup>

  <ItemGroup Label="Targets package assets">
    <None Include="XYZ.Messages.Protobuf.props" PackagePath="build/XYZ.Messages.Protobuf.props" Pack="true" Visible="false" />
  </ItemGroup>

To add additional folders (e.g. with .proto files), add them comma-separated in the variable _ProtoAssetName (e.g. Include="messages, events, commands, queries".