using Init.ps1 to add item in the csproj file - powershell

i am using Nuget to install a package due to some restrictions i have to generate a reference and the path of the assembly in my csproj file.
i am think of doing it with Init.ps1 file which runs when you first install the package.
the element that i want to add is the Reference element some thing like.
and should be like
<Reference Include="Library.MyLib.Imaging, Version=255.255.255.255, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SDK.0.9.2\lib\wp8\X86\Library.mylib.Imaging.winmd</HintPath>
</Reference>
Can some body guide me that how i can add these to the csproj file. i belive that AddItem method can be used from the Project Class
project Class Reference
thanks

Be careful with the false assumption you made for init.ps1: that PowerShell script is run everytime the package is initialized (or every time the solution is loaded) which is likely not what you want in this scenario. You'll have to use install.ps1 instead, which is run once during installation of the package only.
The main issue with making changes to a consuming project file is that you'll have to know the file name somehow. The parameters that get injected into the PowerShell scripts are the following ones:
param($installPath, $toolsPath, $package, $project)
I've had a similar requirement and what I did instead of automating it during package installation: I expose a cmdlet in the Package Manager Console for the consumer to use.
You can find an example of my implementation on GitHub for the NuSpec package: https://github.com/myget/NuGetPackages/blob/master/NuSpec/tools/NuSpec.psm1
Maybe that will help you to get started. If you do have a proper solution to fully automate this during package installation, please feel free to share it here as well (or send me a Pull Request :-)).

Related

How to execute custom script when installing nuget package?

I created this project https://github.com/RemiBou/RemiBou.CosmosDB.Migration, for working it needs the user to do 2 things : create the appropriate folders and edit the csproj so the file inside those folders are embedded.
Before we could do that automaticly when installing with install.ps1 but this feature has been deprecated. Do you know any way how I could do this ?
install.ps1 isn't exactly deprecated, but it's a feature unique to packages.config. PackageReference has no concept of install as anyone can simply edit the csproj and add a PackageReference. When you restore, NuGet has no way of knowing if this is the first time the package is restored for the project after the reference was added, or if it's just the first time the project was restored with a clean repo (after a "msbuild /t:clean" or "git clone", for example).
I don't know an alternative for creating the folders other than having documentation that says the convention is to use that folder name. But, an alternative to modifying the csproj is to take advantage of the fact that MSBuild is a generic build system and NuGet packages can include MSBuild props and targets file.
In your specific case, I would create a props file that defines a property something like <RemiBouCosmosDBMigrationPath>CosmosDB\Migrations\</RemiBouCosmosDBMigrationPath>, which allows your package users to change the property to a different path be overwriting the property value in their csproj, if they prefer.
Then create a targets file which contains a target something like
<Target name="RemiBouCosmosDBMigrationsEmbedMigrations" BeforeTargets="???">
<ItemGroup>
<EmbeddedResource Include="$(RemiBouCosmosDBMigrationPath)**\*.js" />
</ItemGroup>
</Target>
You'll need to figure out what the best target name to put in the BeforeTargets attribute, but I hope you understand the idea. A csproj file is nothing more than a MSBuild file with certain conventions. MSBuild files can import other MSBuild files, and MSBuild and NuGet work together to allow MSBuild to import MSBuild files that come from restored packages. Just compose the MSBuild properties and items in a different way, and the end result can still be the same.

Nesting files in Nuget package without PowerShell

The title says it all. I have files that I want to nest during the installation of a NuGet package but can't use PowerShell scripts since they won't be run any longer (see here).
Are there any other ways to achieve this goal?
UPDATE: By nested I mean like *.resx and *.Designer.cs or *.xaml and code-behind files *.xaml.cs. I know I can achieve that by adding a <DependentUpon> element in the *.csproj file but I don't know how I can add that element without using PowerShell.
UPDATE2: init.ps1 runs the first time a package is installed in a solution. That won't cut it though. I would need the script to run when the package is installed into a project just like install.ps1 was run up to NuGet3.
UPDATE3: What I want to do is to add 3 files into the Properties folder of the target projects (Resources.resx, Resources.tt and Resources.Designer.cs). They are a replacement for the usual resources implementation. These files are installed by the nuget package when it is added to the project.
This is the part of the *.nuspec file that adds them to the Content folder of the package. As only one of them is actually content (the others being an Embedded Resource and Compile respectively) it would be nice to be able to set their build actions accordingly but one step at a time.
<files>
<file src="Properties\Resources.resx" target="content\Properties\Resources.resx" />
<file src="Properties\Resources.tt.pp" target="content\Properties\Resources.tt.pp" />
<file src="Properties\Resources.Designer.cs" target="content\Properties\Resources.Designer.cs" />
</files>
As these files are added to the projects I want the nesting inside the *.csproj file and not happen via a separate *.props file if that is somehow possible.
Packages can add MSBuild items like this to a project by using a .props file in the package. It would contain the same content that you would put into the .csproj file.
The down side of this is that the content cannot be modified by the user. If you need to modify the user's actual project file and copy content to the project folder you would have to include a .targets file in your package and set BeforeTargets="Build" on your target. This would give you a chance to run before build and make changes as needed.
The build folder works for both packages.config and PackageReference (NETCore SDK) projects. You can find more out about it here: https://learn.microsoft.com/en-us/nuget/create-packages/creating-a-package#including-msbuild-props-and-targets-in-a-package

NuGet - only add post processed file to a web project

I have a nuget package that can be applied to any type of C# project.
It has a file that is added to the project as part of the package. The NuSpec looks like this:
<files>
<file src="Content\App_Start\StartUpCode.cs.pp" target="content\App_Start" />
I am using WebActivator to run the code in the file at application start.
I run into a problem when the nuget package is applied to several projects in the same solution. I get several instances of the StartUpCode.cs added, and as a result WebActivator runs the code several times.
How can I stop this code from being added to a project that is not web related? I.e. it's cool to add it to a WebAPI project, or a WebForms project, but not a class library.
I don't think there's anything in the Nuget spec that would allow you to do that easily. Maybe use a Powershell install script and detect the type of project it's being installed into and/or if the assembly has been referenced previously?
Personally, I'd split it into two Nuget packages. One with the business logic, and then another with the WebActivator dependency.

NuGet pack is not packaging all files in the output directory. What am I missing?

I'm trying to create a NuGet package from a .csproj. I have successfully compiled the project and the output folder contains all of the necessary files (my assembly and all of its dependencies). However, NuGet only seems to be placing the assembly created by the .csproj into the package and not any of its dependencies. My command line looks like this:
nuget pack MyProject.csproj -Property Configuration=Release
and my resulting .nupkg file only has my assembly in the lib folder. I have successfully gotten NuGet to work for other projects, but it just so happens that this project is referencing the Enterprise Library logging block, but it was NOT retrieved via NuGet. I'm not sure if that could be related to my problem or not.
Any thoughts on why it's not picking up the dependenices?
If you need to keep your nuspec file up-to-date automatically, its really just an XML file (as I'm sure you know) so there are some very nice tools you can use from MSBuild to automate nuspec creation/updates. Out of the box, MSBuild provides a few tasks that can update or transform XML, and I've used MSBuild Community Tasks to customize the initial nuspec. For example, the default nuspec contains a few lines with broilerplate that I don't need, so I use XmlUpdate tasks to delete them.
Although I have not looked into scanning the csproj file for non-nuget references, I think its likely possible with a little research. Here are some links to blog entries describing my experiences with NuGet automation, they may help you get a head start:
Creating Packages with NuGet the MSBuild Way - This article includes some basic NuSpec updates because the package described is not that different from the type of package NuGet already knows how to automate.
Manage Your MEF Parts With Nuget - This article includes some more complex updates to support distributing MEF parts as runtime-only references.
If you plan on doing this alot, don't want to mess with MSBuild, or just want to get back the behavior you liked from the pre-1360 version of ProjectFactory.cs, NuGet supports third-party extension through MEF. You could go into the source control and grab the earlier code that you liked and create a custom command (for example custompack) that provides that behavior. Then you could use it from the command line like so:
nuget custompack MyProject.csproj -Property Configuration=Release
I think this is a really cool aspect of NuGet but I haven't played with it myself yet. Here is an article that explains how to do it:
Extend NuGet Command Line
So even though David mentioned that NuGet is not designed to support this scenario, if the scenario is correct for you then you can go this route to extend NuGet to meet your needs.

NuGet Restore Fails when dependency adds a .targets import to the .csproj

I ran into an issue recently with NuGet restore. I added a project dependency (in this case PostSharp) and then enabled restore. I checked in the source, but not the /packages directory (as I shouldn't needed to....right!). When TeamCity or another developer grabs the source and runs MsBuild, they receive the following error:
C:\TeamCity\buildAgent\work\e374975c0264c72e\ProjectName\ProjectName.csproj(70, 3): error MSB4019: The imported project "C:\TeamCity\buildAgent\work\e374975c0264c72e\packages\PostSharp.2.1.5.1\tools\PostSharp.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
The problem is, NuGet hasn't run yet to restore/download PostSharp or it's .targets file. This feels like a NuGet bug to me, but wanted to see if others have this same issue.
Anybody have this issue or know the resolution. Yes, I could check-in the /packages directory, but then why use NuGet at all?
Another approach is to modify the <Import> element in question, to make it conditional, e.g.:
<Import Project="$(CodeAssassinTargets)" Condition="Exists($(CodeAssassinTargets))" />
This depends on a new property defined in an earlier <PropertyGroup>. I usually add one at the top of csproj file with other "global" flags, e.g.:
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CodeAssassinTargets>$(SolutionDir)packages\CodeAssassin.ConfigTransform.1.1\tools\CodeAssassin.ConfigTransform.targets</CodeAssassinTargets>
<AutoParameterizationWebConfigConnectionStrings>false</AutoParameterizationWebConfigConnectionStrings>
<UseMsdeployExe>true</UseMsdeployExe>
</PropertyGroup>
Then in an appropriate target, like BeforeBuild, give a helpful error message:
<Target Name="BeforeBuild">
<Error Text="CodeAssassin.ConfigTransforms target is missing. It needs to exist at $(CodeAssassinTargets) in order to build this project!" Condition="!Exists($(CodeAssassinTargets))" />
</Target>
With these modifications, the project will load even if the nuget package restore has never been done. If auto package restore is enabled, the first build attempt should clear up the missing target issue, but if it does not, one manual package restore will.
#porterhouse91, have you checked your csproj file to make sure it has been set up with the appropriate build target?
I haven't yet tried the new built-in Package Restore feature, but I'm assuming it works at least somewhat like the previous workflows out there on the interwebs. If that's the case, enabling Package Restore in your solution only affects the projects in your solution at the time you enable it. If you've added a new project (having NuGet dependencies) to the solution since enabling Package Restore, you're gonna need to enable it again.
Another possibility: the previous workflows involved having a .nuget folder that you needed to check in to VCS, so you might need to check that in if it hasn't been checked in yet (if the built-in Package Restore feature does indeed use this approach).
BTW, if this answer is at all helpful, thank Stephen Ritchie -- he asked me to give it a shot for you.
I had a problem like this as well, but I was able to modify the .targets file in the source package to work around it. Basically, RestorePackages is a build target that runs when the project is built. Unfortunately, the package won't even load properly before the imports are satisfied. The only way I know to fix this is to include the .targets file as content and then change the BuildDependsOn property so it restores the packages before it runs your custom tasks.
<PropertyGroup>
<BuildDependsOn Condition="$(BuildDependsOn.Contains('RestorePackages'))">
RestorePackages;
CustomTarget;
$(BuildDependsOn);
</BuildDependsOn>
<BuildDependsOn Condition="!$(BuildDependsOn.Contains('RestorePackages'))">
CustomTarget;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
To be clear, this doesn't help with pre-built packages, but if you can build the package again yourself, you can fix it.
I ran into this same issue with Visual Studio 2012 and NuGet packages not checked into source control.
The error:
The imported project "\packages\Microsoft.Bcl.Build.1.0.7\tools\Microsoft.Bcl.Build.targets" was not found.
Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
I found a msdn writeup on the situation that gave the following workarounds for grabbing a project from source control without the NuGet packages.
Stop using package restore and check-in all package files
Explicitly run package restore before building the project
Check-in the .targets files
I decided to go with option #2, however, NuGet currently (v2.6) does not include a way to install all packges from the packages.config file from within visual studio. Some searching revealed that you need to use the NuGet Command Line to execute the following command before opening Visual Studio (reference).
c:\path\to\nuget.exe install -o packages project-folder\packages.config