NuGet पैकेज से मूल आउटपुट निर्देशिका में मूल फ़ाइलें जोड़ें




nuget-package nuget-spec (8)

मैं एक .NET असेंबली के लिए NuGet पैकेज बनाने की कोशिश कर रहा हूं जो देशी Win32 dll को पिनवोक करता है। मुझे प्रोजेक्ट संदर्भों में शामिल असेंबली के साथ असेंबली और देशी डीएल दोनों को पैक करने की आवश्यकता है (इस भाग में कोई समस्या नहीं है) और देशी डीएल को परियोजना आउटपुट निर्देशिका या किसी अन्य रिश्तेदार निर्देशिका में कॉपी किया जाना चाहिए।

मेरे प्रश्न हैं:

  1. मैं संदर्भ स्टूडियो में इसे जोड़ने के लिए दृश्य स्टूडियो के बिना मूल डीएल कैसे पैक करूं?
  2. क्या मुझे मूल डीएलएल की प्रतिलिपि बनाने के लिए install.ps1 लिखना है? यदि ऐसा है तो मैं इसे कॉपी करने के लिए पैकेज सामग्री तक कैसे पहुंच सकता हूं?

एक शुद्ध सी # समाधान है जो मुझे उपयोग करने में आसान लगता है और मुझे NuGet सीमाओं से परेशान नहीं होना है। इन कदमों का अनुसरण करें:

अपनी प्रोजेक्ट में मूल लाइब्रेरी शामिल करें और Embedded Resource अपनी बिल्ड एक्शन प्रॉपर्टी सेट करें।

निम्न कोड को कक्षा में पेस्ट करें जहां आप इस लाइब्रेरी को PInvoke करते हैं।

private static void UnpackNativeLibrary(string libraryName)
{
    var assembly = Assembly.GetExecutingAssembly();
    string resourceName = $"{assembly.GetName().Name}.{libraryName}.dll";

    using (var stream = assembly.GetManifestResourceStream(resourceName))
    using (var memoryStream = new MemoryStream(stream.CanSeek ? (int)stream.Length : 0))
    {
        stream.CopyTo(memoryStream);
        File.WriteAllBytes($"{libraryName}.dll", memoryStream.ToArray());
    }
}

इस विधि को स्थैतिक कन्स्ट्रक्टर से कॉल करें जैसे कि UnpackNativeLibrary("win32"); और इससे पहले कि आपको इसकी आवश्यकता हो, पुस्तकालय को डिस्क पर अनपैक कर देगा। बेशक, आपको यह सुनिश्चित करने की ज़रूरत है कि डिस्क के उस हिस्से में आपके पास लेखन अनुमतियां हों।


मैंने हाल ही में एक ही समस्या की थी जब मैंने इम्गुसीवी न्यूजेट पैकेज बनाने की कोशिश की जिसमें दोनों प्रबंधित असेंबली और गैर-प्रबंधित साझा लिरीरी (जिसे एक x86 उपनिर्देशिका में भी रखा जाना था) शामिल था, जिसे प्रत्येक के बाद बिल्ड आउटपुट निर्देशिका में स्वचालित रूप से कॉपी किया जाना था निर्माण।

यहां एक समाधान है जिसके साथ मैं आया था, जो केवल नुगेट और एमएसबिल्ड पर निर्भर करता है:

  1. प्रबंधित उपनिवेशों को पैकेज (स्पष्ट भाग) और गैर-प्रबंधित साझा पुस्तकालयों और संबंधित फ़ाइलों (जैसे .pdb संकुल) में /build उपनिर्देशिका (जैसा कि NuGet दस्तावेज़ों में वर्णित है) में /lib निर्देशिका में रखें।

  2. किसी गैर-प्रबंधित *.dll फ़ाइल के अंतराल को कुछ अलग करने के लिए नाम बदलें, उदाहरण के लिए *.dl_ को NuGet को किसी गलत स्थान पर कथित असेंबली के बारे में *.dl_ से रोकने के लिए ( "समस्या: lib फ़ोल्डर के बाहर असेंबली" ) को रोकने के लिए।

  3. निम्न सामग्री जैसे कुछ के साथ उपनिर्देशिका में एक कस्टम <PackageName>.targets फ़ाइल जोड़ें (विवरण के लिए नीचे देखें):

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <ItemGroup>
        <AvailableItemName Include="NativeBinary" />
      </ItemGroup>
      <ItemGroup>
        <NativeBinary Include="$(MSBuildThisFileDirectory)x86\*">
          <TargetPath>x86</TargetPath>
        </NativeBinary>
      </ItemGroup>
      <PropertyGroup>
        <PrepareForRunDependsOn>
          $(PrepareForRunDependsOn);
          CopyNativeBinaries
        </PrepareForRunDependsOn>
      </PropertyGroup>
      <Target Name="CopyNativeBinaries" DependsOnTargets="CopyFilesToOutputDirectory">
        <Copy SourceFiles="@(NativeBinary)"
              DestinationFiles="@(NativeBinary->'$(OutDir)\%(TargetPath)\%(Filename).dll')"
              Condition="'%(Extension)'=='.dl_'">
          <Output TaskParameter="DestinationFiles" ItemName="FileWrites" />
        </Copy>
        <Copy SourceFiles="@(NativeBinary)"
              DestinationFiles="@(NativeBinary->'$(OutDir)\%(TargetPath)\%(Filename).%(Extension)')"
              Condition="'%(Extension)'!='.dl_'">
          <Output TaskParameter="DestinationFiles" ItemName="FileWrites" />
        </Copy>
      </Target>
    </Project>
    

उपरोक्त। लक्ष्य लक्ष्य फ़ाइल को लक्ष्य प्रोजेक्ट फ़ाइल में NuGet पैकेज की स्थापना पर इंजेक्शन दिया जाएगा और देशी पुस्तकालयों को आउटपुट निर्देशिका में कॉपी करने के लिए ज़िम्मेदार है।

  • <AvailableItemName Include="NativeBinary" /> प्रोजेक्ट के लिए एक नया आइटम "बिल्ड एक्शन" जोड़ता है (जो विजुअल स्टूडियो के अंदर "बिल्ड एक्शन" ड्रॉपडाउन में भी उपलब्ध हो जाता है)।

  • <NativeBinary Include="... वर्तमान प्रोजेक्ट में /build/x86 में रखी गई मूल पुस्तकालयों को जोड़ता है और उन्हें कस्टम लक्ष्य के लिए सुलभ बनाता है जो उन फ़ाइलों को आउटपुट निर्देशिका में कॉपी करता है।

  • <TargetPath>x86</TargetPath> फ़ाइलों में कस्टम मेटाडेटा जोड़ता है और मूल फ़ाइलों को वास्तविक आउटपुट निर्देशिका की x86 उपनिर्देशिका में कॉपी करने के लिए कस्टम लक्ष्य बताता है।

  • <PrepareForRunDependsOn ... ब्लॉक लक्ष्य पर निर्भर करता है लक्ष्य के सूची में कस्टम लक्ष्य जोड़ता है, विवरण के लिए Microsoft.Common.targets फ़ाइल देखें।

  • कस्टम लक्ष्य, CopyNativeBinaries , में दो प्रति कार्य शामिल हैं। पहला *.dl_ किसी भी *.dl_ फ़ाइलों को आउटपुट निर्देशिका में कॉपी करने के लिए ज़िम्मेदार है, जबकि उनका एक्सटेंशन मूल *.dll बदल रहा है। दूसरा एक ही स्थान पर बाकी की प्रतिलिपि बनाता है (उदाहरण के लिए किसी भी *.pdb फ़ाइलों)। इसे एक प्रतिलिपि कार्य और एक install.ps1 स्क्रिप्ट द्वारा प्रतिस्थापित किया जा सकता है जिसे पैकेज स्थापना के दौरान सभी *.dl_ फ़ाइलों को *.dll पुनर्नामित करना था।

हालांकि, यह समाधान मूलभूत बाइनरी को किसी अन्य प्रोजेक्ट की आउटपुट निर्देशिका में प्रतिलिपि नहीं करेगा जिसमें प्रारंभ में NuGet पैकेज शामिल है। आपको अभी भी अपने "अंतिम" प्रोजेक्ट में NuGet पैकेज का संदर्भ देना होगा।


अगर कोई और इस पर ठोकर खाता है।

.targets फ़ाइल नाम NuGet पैकेज आईडी के बराबर होना चाहिए

कुछ और काम नहीं करेगा।

क्रेडिट यहां जाते हैं: https://sushihangover.github.io/nuget-and-msbuild-targets/

वास्तव में यहां उल्लेख किया गया है कि मुझे और अधिक अच्छी तरह से पढ़ना चाहिए था। मुझे उम्र ले ली ..

कस्टम <PackageName>.targets


यहां एक विकल्प है जो प्रोजेक्ट में देशी गुणों के साथ निम्नलिखित गुणों के साथ इंजेक्ट करने के लिए .targets का उपयोग करता है।

  • Build action = None
  • Copy to Output Directory Copy if newer = Copy if newer

इस तकनीक का मुख्य लाभ यह है कि मूल डीएलएल को निर्भर परियोजनाओं के bin/ फ़ोल्डर में प्रतिलिपि बनाई जाती है।

.nuspec फ़ाइल का लेआउट देखें:

.targets फ़ाइल यहां दी गई है:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <None Include="$(MSBuildThisFileDirectory)\..\MyNativeLib.dll">
            <Link>MyNativeLib.dll</Link>
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
    </ItemGroup>
</Project>

यह MyNativeLib.dll को सम्मिलित करता है जैसे कि यह मूल प्रोजेक्ट का हिस्सा था (लेकिन उत्सुकता से फ़ाइल विजुअल स्टूडियो में दिखाई नहीं दे रही है)।

<Link> तत्व पर ध्यान दें जो bin/ फ़ोल्डर में गंतव्य फ़ाइल नाम सेट करता है।


मैं आपकी सटीक समस्या का समाधान नहीं कर सकता, लेकिन मैं आपको एक सुझाव दे सकता हूं।

आपकी मुख्य आवश्यकता यह है: "और क्या यह संदर्भ स्वत: पंजीकृत नहीं है" .....

तो आपको "समाधान आइटम" से परिचित होना होगा

यहां संदर्भ देखें:

NuGet पैकेज में समाधान-स्तर आइटम जोड़ना

आपको अपने मूल डीएल की प्रतिलिपि प्राप्त करने के लिए कुछ पावरहेल वूडू लिखना होगा (फिर से, क्योंकि आप ऑटो-एड-रेफरेंस वूडू को आग नहीं चाहते हैं)

यहां एक पीएस 1 फ़ाइल है जिसे मैंने लिखा है ..... फ़ाइलों को किसी तृतीय पक्ष संदर्भ फ़ोल्डर में रखने के लिए।

स्क्रैच से शुरू किए बिना अपने मूल डीएल को कुछ "घर" में कॉपी करने के तरीके के बारे में जानने के लिए पर्याप्त है।

फिर, यह प्रत्यक्ष हिट नहीं है, लेकिन यह कुछ भी नहीं है।

param($installPath, $toolsPath, $package, $project)
if ($project -eq $null) {
$project = Get-Project
}

Write-Host "Start Init.ps1" 

<#
The unique identifier for the package. This is the package name that is shown when packages are listed using the Package Manager Console. These are also used when installing a package using the Install-Package command within the Package Manager Console. Package IDs may not contain any spaces or characters that are invalid in an URL.
#>
$separator = " "
$packageNameNoVersion = $package -split $separator | select -First 1

Write-Host "installPath:" "${installPath}"
Write-Host "toolsPath:" "${toolsPath}"
Write-Host "package:" "${package}"
<# Write-Host "project:" "${project}" #>
Write-Host "packageNameNoVersion:" "${packageNameNoVersion}"
Write-Host " "

<# Recursively look for a .sln file starting with the installPath #>
$parentFolder = (get-item $installPath)
do {
        $parentFolderFullName = $parentFolder.FullName

        $latest = Get-ChildItem -Path $parentFolderFullName -File -Filter *.sln | Select-Object -First 1
        if ($latest -ne $null) {
            $latestName = $latest.name
            Write-Host "${latestName}"
        }

        if ($latest -eq $null) {
            $parentFolder = $parentFolder.parent    
        }
}
while ($parentFolder -ne $null -and $latest -eq $null)
<# End recursive search for .sln file #>


if ( $parentFolder -ne $null -and $latest -ne $null )
{
    <# Create a base directory to store Solution-Level items #>
    $thirdPartyReferencesDirectory = $parentFolder.FullName + "\ThirdPartyReferences"

    if ((Test-Path -path $thirdPartyReferencesDirectory))
    {
        Write-Host "--This path already exists: $thirdPartyReferencesDirectory-------------------"
    }
    else
    {
        Write-Host "--Creating: $thirdPartyReferencesDirectory-------------------"
        New-Item -ItemType directory -Path $thirdPartyReferencesDirectory
    }

    <# Create a sub directory for only this package.  This allows a clean remove and recopy. #>
    $thirdPartyReferencesPackageDirectory = $thirdPartyReferencesDirectory + "\${packageNameNoVersion}"

    if ((Test-Path -path $thirdPartyReferencesPackageDirectory))
    {
        Write-Host "--Removing: $thirdPartyReferencesPackageDirectory-------------------"
        Remove-Item $thirdPartyReferencesPackageDirectory -Force -Recurse
    }

    if ((Test-Path -path $thirdPartyReferencesPackageDirectory))
    {
    }
    else
    {
        Write-Host "--Creating: $thirdPartyReferencesPackageDirectory-------------------"
        New-Item -ItemType directory -Path $thirdPartyReferencesPackageDirectory
    }

    Write-Host "--Copying all files for package : $packageNameNoVersion-------------------"
    Copy-Item $installPath\*.* $thirdPartyReferencesPackageDirectory -recurse
}
else
{
        Write-Host "A current or parent folder with a .sln file could not be located."
}


Write-Host "End Init.ps1" 

यह थोड़ी देर हो चुकी है लेकिन मैंने इसके लिए एक nuget पैकेज exaclty बनाया है।

विचार आपके nuget पैकेज में एक अतिरिक्त विशेष फ़ोल्डर है। मुझे यकीन है कि आप पहले से ही लिब और सामग्री जानते हैं। मैंने बनाया गया नुजेट पैकेज आउटपुट नामक फ़ोल्डर की तलाश करता है और प्रोजेक्ट आउटपुट फ़ोल्डर में मौजूद सभी चीजों की प्रतिलिपि बनायेगा।

केवल एक चीज जो आपको करना है वह पैकेज में एक न्यूज निर्भरता जोड़ना है http://www.nuget.org/packages/Baseclass.Contrib.Nuget.Output/

मैंने इसके बारे में एक ब्लॉग पोस्ट लिखा है: http://www.baseclass.ch/blog/Lists/Beitraege/Post.aspx?ID=6&mobile=0


इसे सामग्री फ़ोल्डर रखो

कमांड nuget pack [projfile].csproj स्वचालित रूप से आपके लिए यह करेगा यदि आप फ़ाइलों को सामग्री के रूप में चिह्नित करेंगे।

फिर यहां प्रोजेक्ट फ़ाइल को संपादित करें जैसा कि यहां बताया गया है आइटम समूह और नेटिवलिब्स और कोई भी तत्व नहीं

<ItemGroup>
    <NativeLibs Include="$(MSBuildThisFileDirectory)**\*.dll" />
    <None Include="@(NativeLibs)">
      <Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
</ItemGroup>

मेरे लिए काम किया


यह एक पुराना सवाल है, लेकिन मुझे अब भी एक ही समस्या है, और मुझे एक टर्नअराउंड मिला जो थोड़ा मुश्किल लेकिन बहुत सरल और प्रभावी है: प्रत्येक कॉन्फ़िगरेशन के लिए एक सबफ़ोल्डर के साथ निम्न संरचना को Nuget मानक सामग्री फ़ोल्डर में बनाएं:

/Content
 /bin
   /Debug
      native libraries
   /Release
      native libraries

जब आप nuspec फ़ाइल पैक करते हैं, तो आपको डीबग और रिलीज़ फ़ोल्डर्स में प्रत्येक मूल लाइब्रेरी के लिए निम्न संदेश प्राप्त होगा:

समस्या: lib फ़ोल्डर के बाहर असेंबली। विवरण: असेंबली 'सामग्री \ Bin \ Debug \ ??????। Dll' 'lib' फ़ोल्डर के अंदर नहीं है और इसलिए जब किसी प्रोजेक्ट में पैकेज स्थापित किया जाता है तो इसे संदर्भ के रूप में नहीं जोड़ा जाएगा। समाधान: इसे 'lib' फ़ोल्डर में ले जाएं यदि इसे संदर्भित किया जाना चाहिए।

हमें ऐसे "समाधान" की आवश्यकता नहीं है क्योंकि यह सिर्फ हमारा लक्ष्य है: मूल पुस्तकालयों को नेट असेंबली संदर्भों के रूप में नहीं जोड़ा जाता है।

फायदे हैं:

  1. अजीब प्रभाव वाले किसी भी बोझिल स्क्रिप्ट के साथ सरल समाधान जो पैकेज अनइंस्टॉल करने के लिए मुश्किल है।
  2. नूजेट स्थापित करने और अनइंस्टॉल करने पर मूल पुस्तकालयों को किसी भी अन्य सामग्री के रूप में प्रबंधित करता है।

नुकसान हैं:

  1. आपको प्रत्येक कॉन्फ़िगरेशन के लिए एक फ़ोल्डर की आवश्यकता है (लेकिन आमतौर पर केवल दो होते हैं: डीबग और रिलीज, और यदि आपके पास अन्य कॉन्फ़िगरेशन फ़ोल्डर में अन्य सामग्री स्थापित की जानी चाहिए, तो यह या तो जाने का तरीका है)
  2. प्रत्येक पुस्तकालय में मूल पुस्तकालयों को डुप्लिकेट किया जाना चाहिए (लेकिन यदि आपके पास प्रत्येक कॉन्फ़िगरेशन के लिए मूल पुस्तकालयों के विभिन्न संस्करण हैं, तो यह या तो जाने का तरीका है)
  3. प्रत्येक फ़ोल्डर में प्रत्येक देशी डीएल के लिए चेतावनियां (लेकिन जैसा कि मैंने कहा था, वे पैकेज निर्माता को पैक समय पर चेतावनी जारी की जाती हैं, वीएस इंस्टॉल समय पर पैकेज उपयोगकर्ता को नहीं)