Setting file properties with NuGet using .targets file
I am not seeing the FrameworkTests.feature file get copied to the project when the package is installed. What do I need to change?
This is the default behavior of the nuget folder conventions. If you set the file in the content
folder, nuget will copy those contents to the project root, then you will see them in the solution explorer. If you set files in the build
folder, nuget will automatically inserted them into the project file or project.lock.json
, like following script in the project file:
<Import Project="..\packages\MyProject.1.0.0\build\MyProject.targets" Condition="Exists('..\packages\MyProject.1.0.0\build\MyProject.targets')" />
So, this is the reason why you could not see the file FrameworkTests.feature
in the solution explorer unless you change the folder to content
.
You can refer to the document Creating the .nuspec file for more details:
Besides, if you change the folder to content, the .targets
would not work. Because when you change the folder to content, in the .targets
file, you need to change the path from:
<None Include="$(MSBuildThisFileDirectory)FrameworkTests\FrameworkTests.feature">
To:
<None Include="$(MSBuildThisFileDirectory)..\content\FrameworkTests\FrameworkTests.feature">
But MSBuild could not parse the path ..\content
. To resolve this issue, we need to change the .targets file to:
<None Include="$(ProjectDir)FrameworkTests.feature">
Alternatively, you can change property of files with Install.ps1
file, set it into the nuget package.
See this thread for more details.
Update:
I have also applied the changes you've described, but still cannot get the CopyToOutputDirectory file property to be updated.
Found it. There are two points you need update and one point should to know.
First point:
I found following script in your .nuspec
:
<files>
<file src="build\MyProject.targets" target="build\MyProject.targets" />
<file src="build\FrameworkTests\FrameworkTests.feature" target="build\Framework\FrameworkTests.feature" />
</files>
Note: You set your target folder for build\Framework
but in your .targets
file, you including it with FrameworkTests
;
<None Include="$(MSBuildThisFileDirectory)FrameworkTests\FrameworkTests.feature">
So, when change it to content folder, your .nuspec
file should be:
<files>
<file src="build\MyProject.targets" target="build\MyProject.targets" />
<file src="content\FrameworkTests\FrameworkTests.feature" target="content\FrameworkTests\FrameworkTests.feature" />
</files>
And the .targets
file should be:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(ProjectDir)FrameworkTests\FrameworkTests.feature">
<Link>FrameworkTests.feature</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CustomToolNamespace></CustomToolNamespace>
</None>
</ItemGroup>
</Project>
Second point need update:
The value of CopyToOutputDirectory
in the .targets
file should be PreserveNewest Not Copy if newer.
The point you need to know:
When you use this method to change the property of the FrameworkTests.feature
, the property of this file would not changed in the solution explorer, however, MSBuild will apply this change when you build the project. That because <Import Project="....\MyProject.targets" Condition="Exists('...')" />
will be parsed when you compile the project.
So, you can check the output for CopyToOutputDirectory
file property after build the project(After build your project, the file FrameworkTests.feature
would be copied to the output folder.)
Update2:
A lot of comments circulating that PowerShell scripts can't be run on build servers because the install command doesn't run them; Only VisualStudio will
Not sure of all the reasons why PowerShell scripts can't be run on build servers, but the vast majority are because PowerShell's default security level.
Try typing this in the PowerShell:
set-executionpolicy remotesigned
In the Visual Studio 2015, you need to install the extension PowerShell Tools for Visual Studio 2015 and the Windows Management Framework 4.0. After install them, the install.ps1 would works fine. Following is my .nuspec
file and install.ps
1 script.
.nuspec file:
<files>
<file src="content\FrameworkTests\FrameworkTests.feature" target="content\FrameworkTests\FrameworkTests.feature" />
<file src="scripts\Install.ps1" target="tools\Install.ps1" />
</files>
Note: Do not forget remove the MyProject.targets
in the .nuspec
file.
install.ps`1 script:
param($installPath, $toolsPath, $package, $project)
function MarkDirectoryAsCopyToOutputRecursive($item)
{
$item.ProjectItems | ForEach-Object { MarkFileASCopyToOutputDirectory($_) }
}
function MarkFileASCopyToOutputDirectory($item)
{
Try
{
Write-Host Try set $item.Name
$item.Properties.Item("CopyToOutputDirectory").Value = 2
}
Catch
{
Write-Host RecurseOn $item.Name
MarkDirectoryAsCopyToOutputRecursive($item)
}
}
#Now mark everything in the a directory as "Copy to newer"
MarkDirectoryAsCopyToOutputRecursive($project.ProjectItems.Item("FrameworkTests"))
Then, after install the nuget package, the property of the file FrameworkTests.feature would be changed to copy if newer
:
Hope this helps.
Matt W
I write code for fun and profit. Sometimes I blog about it. Not enough time for either. At work I break websites. At home I break physics based mobile games. "No code more than 8 lines ever worked as intended first time." - My Dad.
Updated on June 26, 2022Comments
-
Matt W almost 2 years
I am building a project to be installed as a NuGet package and I want to set the properties of a SpecFlow feature file (because it is latest SpecFlow and should not produce code-behind files for the features.)
To achieve the effect of selecting a file, opening it's Properties pane and setting a couple of values, I have set my project structure like this:
\MyProject \build MyProject.targets \Framework <the folder containing the file I want to affect> FrameworkTests.feature <the file I want to affect> \Framework FrameworkTests.feature <the original location of the file I want to affect>
My .nuspec like this:
<?xml version="1.0"?> <package > <metadata minClientVersion="2.5"> <id>$id$</id> <version>$version$</version> ... </metadata> <files> <file src="build\MyProject.targets" target="build\MyProject.targets" /> <file src="build\FrameworkTests\FrameworkTests.feature" target="build\Framework\FrameworkTests.feature" /> </files> </package>
My .targets file like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <None Include="$(MSBuildThisFileDirectory)FrameworkTests\FrameworkTests.feature"> <Link>FrameworkTests.feature</Link> <CopyToOutputDirectory>Copy if newer</CopyToOutputDirectory> <CustomToolNamespace></CustomToolNamespace> </None> </ItemGroup> </Project>
I am not seeing the FrameworkTests.feature file get copied to the project when the package is installed. What do I need to change?
-
Matt W about 6 yearsI have already tried adding a
content
folder and placing files into it but they did not get copied into the target project when the nuget package was installed. I am packing by providing the name of the .csproj - does that imply a difference? I have also applied the changes you've described, but still cannot get theCopyToOutputDirectory
file property to be updated. -
Leo Liu about 6 years@MattW, there should be have something I did not explain clearly, I will update my answer later, and will let you know it.
-
Leo Liu about 6 years@MattW, after create a sample with your
.nuspec
and.targets
files, I found the reason why it not work for you, there are two points you need update and one point should to know. Please check my Update answer for details. And I have test it, it works fine on my side, if it still not work for you, please let me know. -
Matt W about 6 yearsThanks - at a quick glance I see
PreserveNewest
is a change I've already made. Taking a further look... -
Matt W about 6 yearsOk, so I've applied everything you've said. Thank you for the help. One problem remains... You'll notice the files I'm trying to apply the property changes to are
.feature
files - these are used with SpecFlow/xUnit and when changing those properties in Visual Studio, the SpecFlow plugin is removing the code-behind.cs
files which back the.feature
files. As the NuGet is currently, this is not happening and when I build the project (after installing the NuGet package) and try to run the SpecFlow/xUnit tests the build complains because it thinks the auto-generated.cs
files are there. -
Matt W about 6 yearsAnother gotcha I'm seeing is that the
.feature
files are being copied into the target project'sbin\Debug\FrameworkTests` folder, which means the Test Explorer shows 3 copies of each
.feature` file, rather than the one in the project folder. How can I prevent thebin\Debug
item being created when the package installs? -
Leo Liu about 6 years@MattW, Since you are limited by the SpecFlow/xUnit, you could use the another way which I mentioned in the answer. Using Install.ps1 file, in this case, when you complete the installation nuget package, nuget would change the property.stackoverflow.com/questions/8474253/…
-
Matt W about 6 yearsA lot of comments circulating that PowerShell scripts can't be run on build servers because the install command doesn't run them; Only VisualStudio will. :(
-
Leo Liu about 6 years@MattW, Not sure of all the reasons why PowerShell scripts can't be run on build servers, but the vast majority are because PowerShell's default security level. You could check my update2 answer for details, it works fine when I install that nuget package in VS2015.
-
Matt W about 6 yearsThank you! You are amazing! No words! Truly exceptional - thank you so much!
-
bruestle2 about 3 yearsHey, I just want to say this helped me a ton. Thank you!