Home > Mobile >  How to get a list of nuget packages installed via MSBuild macro?
How to get a list of nuget packages installed via MSBuild macro?

Time:07-23

From following question How do I list all installed NuGet packages?

I need to get a list of nuget packages from MSBuild itself.

What I try to do:

<Target Name="test" AfterTargets="ResolveReferences">
        <ItemGroup>
  <BuildOutputInPackage Include="@(ReferenceCopyLocalPaths)"/>
</ItemGroup>

    <Message Text="Files @(BuildOutputInPackage -> '%(identity)', ', ')" Importance="high"/>

</Target>

Above code return all references whether its nuget or project references dlls. How exactly to return a list of nuget package that currently project *.csproj used?

Format i.e: packageid:Portable.BouncyCastle version = "1.9.0"

I use old classic non-sdk csproj template and .NET 4.8 and <PackageReference> not packages.config files.

Its very bad to read *.csproj and do regex to get a list of <packageReferences. Maybe there's a recommended MSBuild macro to do this like code shown above <Target....

CodePudding user response:

ReferenceCopyLocalPaths is only "Paths to files that should be copied to the local directory." It is not all resolved references.

Yes, don't regex the project file. There is no need. PackageReference is an ItemGroup.

<Target Name="DisplayPackageReference">
    <Message Text="@(PackageReference ->'packageid:%(identity) version = &quot;%(version)&quot;', '
')" />
</Target>

But note that PackageReference is what the project is asking for and the version can be floating, e.g Version="3.6.*". PackageReference won't give you the package version that the package reference was resolved to.

If you need to know the specific version used, then you may need to install dotnet, use an exec task to run dotnet list "$(MSBuildProjectFile)" package, and parse the command's output.

CodePudding user response:

Very thankful to Jonathan Dodds for his answer above. I combined some approach with his @(PackageReference) idea to get a version of currently referenced nuget package in old classic csproj template. Because a referenced version will not work with @(PackageReference) only. Also dotnet list not worked well in old classic csproj template (non-sdk).

A code below do following:

(Note this approach is not 100% organic, MSBuild should provide a simple target for that):

  1. Get all references that maybe be nuget or non-nuget (DLLs, etc).
  2. Get only references that included inside @(PackageReference). Jonathan Doods approach above.
  3. Get .nuspec file for that package to know exactly what version current *.csproj referenced.
  4. Create a new nuspec file for current project. with a list of dependencies.
  5. You can then call nuget.exe pack ProjectPath.csproj a ProjectName.nupkg nuget will generated.

Use this code inside Directory.Build.targets or within your .csproj file.

<Target Name="GenerateNuspecFile" AfterTargets="AfterBuild">
    <ItemGroup>
        <!-- Get all references -->
        <MyReference Include="@(ReferenceCopyLocalPaths)" Condition="%(extension) == '.dll'">
            <FirstDir>$([System.IO.Directory]::GetParent(%(RelativeDir)))</FirstDir>
            <LibDir>$([System.IO.Path]::GetFileNameWithoutExtension(%(MyReference.FirstDir)))</LibDir>
            <SecondDir>$(FirstDir)</SecondDir>
            <SecondDir Condition="$(LibDir) != 'lib'">$([System.IO.Directory]::GetParent(%(MyReference.FirstDir)))</SecondDir>
            <VersionDir>$([System.IO.Directory]::GetParent(%(MyReference.SecondDir)))</VersionDir>
            <PackageDir>$([System.IO.Directory]::GetParent(%(MyReference.VersionDir)))</PackageDir>
            <PackageID>$([System.IO.Path]::GetFileName(%(MyReference.PackageDir)))</PackageID>
        </MyReference>

        <!-- Get Non nuget references -->
        <ExcludedReference Condition="@(MyReference) != ''" Include="@(MyReference -> '%(PackageID)')" Exclude="@(PackageReference -> '%(identity)')"/>

        <!-- Exclude Non nuget references. The remain will be nuget references -->
        <MyPackage Condition="@(MyReference) != ''" Include="@(MyReference -> '%(PackageID)')" Exclude="@(ExcludedReference -> '%(identity)')"/>

        <!-- Get Nuspec File -->
        <NuspecFile Condition="@(MyPackage) != ''" Include="%(MyPackage.VersionDir)\**\*.nuspec"/>

        <!-- Read Nuspec File -->
        <NuspecContent Condition="@(NuspecFile) != ''" Include="@(NuspecFile)">
            <Content>$([System.IO.File]::ReadAllText(%(fullpath)))</Content>
            <PackageIDRegex>&lt;id&gt;(.*?)&lt;/id&gt;</PackageIDRegex>
            <PackageID>$([System.Text.RegularExpressions.Regex]::Match('%(NuspecContent.Content)', '%(NuspecContent.PackageIDRegex)' ))</PackageID>
            <PackageID>$([System.String]::Copy('%(NuspecContent.PackageID)').Replace('&lt;id&gt;','').Replace('&lt;/id&gt;', '').Trim())</PackageID>

            <VersionRegex>&lt;version&gt;(.*?)&lt;/version&gt;</VersionRegex>
            <Version>$([System.Text.RegularExpressions.Regex]::Match('%(NuspecContent.Content)', '%(NuspecContent.VersionRegex)' ))</Version>
            <Version>$([System.String]::Copy('%(NuspecContent.Version)').Replace('&lt;version&gt;','').Replace('&lt;/version&gt;', '').Trim())</Version>
        </NuspecContent>
    </ItemGroup>

    <!-- Generate Nuspec File with dependencies-->
    <PropertyGroup>
        <NewLine>
</NewLine>
        <Space2>  </Space2>
        <Space4>$(Space2)$(Space2)</Space4>
        <Space6>$(Space4)$(Space2)</Space6>
        <HasDependencies>false</HasDependencies>
        <HasDependencies Condition="@(MyPackage) != ''">true</HasDependencies>

        <Nuspec>$(Nuspec)&lt;?xml version="1.0" encoding="utf-8"?&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)&lt;package &gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space2)&lt;metadata&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;id&gt;$id$&lt;/id&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;version&gt;$version$&lt;/version&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;title&gt;$title$&lt;/title&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;authors&gt;$author$&lt;/authors&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;owners&gt;$author$&lt;/owners&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;requireLicenseAcceptance&gt;false&lt;/requireLicenseAcceptance&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;licenseUrl&gt;https://company.com&lt;/licenseUrl&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;icon&gt;PackageIcon.png&lt;/icon&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;projectUrl&gt;http://company.com&lt;/projectUrl&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;description&gt;$description$&lt;/description&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;releaseNotes&gt;$description$&lt;/releaseNotes&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;copyright&gt;$copyright$&lt;/copyright&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;tags&gt;$title$ company company.com&lt;/tags&gt;$(NewLine)</Nuspec>

        <Nuspec Condition="$(HasDependencies)">$(Nuspec)$(Space4)&lt;dependencies&gt;$(NewLine)</Nuspec>
        <Nuspec Condition="$(HasDependencies)">$(Nuspec)@(NuspecContent -> '$(Space6)&lt;dependency id=&quot;%(PackageID)&quot; version=&quot;%(Version)&quot;/&gt;', '$(NewLine)')$(NewLine)</Nuspec>
        <Nuspec Condition="$(HasDependencies)">$(Nuspec)$(Space4)&lt;/dependencies&gt;$(NewLine)</Nuspec>

        <Nuspec>$(Nuspec)$(Space2)&lt;/metadata&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space2)&lt;files&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space4)&lt;file src="..\**\PackageIcon.png"/&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)$(Space2)&lt;/files&gt;$(NewLine)</Nuspec>
        <Nuspec>$(Nuspec)&lt;/package&gt;$(NewLine)</Nuspec>
    </PropertyGroup>

    <WriteLinesToFile Lines="$(Nuspec)" File="$(MSBuildProjectDirectory)\$(MSBuildProjectName).nuspec" Overwrite="true"/>
</Target>

The generated .nuspec file will be:

<?xml version="1.0" encoding="utf-8"?>
<package >
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <title>$title$</title>
    <authors>$author$</authors>
    <owners>$author$</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <licenseUrl>https://company.com</licenseUrl>
    <icon>PackageIcon.png</icon>
    <projectUrl>http://company.com</projectUrl>
    <description>$description$</description>
    <releaseNotes>$description$</releaseNotes>
    <copyright>$copyright$</copyright>
    <tags>$title$ company company.com</tags>
    <dependencies>
      <dependency id="Company.Globals" version="2022.7.19"/>
      <dependency id="Portable.BouncyCastle" version="1.9.0"/>
    </dependencies>
  </metadata>
  <files>
    <file src="..\**\PackageIcon.png"/>
  </files>
</package>

Its really to hard to achieve anything with MSBuild. Maybe you can create a console app for that and just call console app directly with <Exec Command =""/>. But current approach is friendly with MSBuild. Its not 100% organic approach. MSBuild should provide us a target to get a referenced nuget packages with its referenced version.

  • Related