pubxml custom file copy if not exists - msbuild

I have added a section to my publish profile that copies the contents of an external folder to the bin folder. The contents of the folder are two dll's that are included so that we can use spatial functions.
The issue I am having is that the DLL's are getting locked by IIS.
The publish profile is executed by MS Build.
How can I either restart IIS as part of the publish?
or
Copy the files only if they exist?
Below is the current code in the pubxml file:
<Target Name="CustomCollectFiles2">
<ItemGroup>
<_CustomFiles2 Include="..\Lib\SqlServerTypes\**\*" />
<FilesForPackagingFromProject Include="%(_CustomFiles2.Identity)">
<DestinationRelativePath>bin\SqlServerTypes\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles2;
;
</CopyAllFilesToSingleFolderForPackageDependsOn>
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
CustomCollectFiles2;
;
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
</PropertyGroup>

Related

MSBuild - Copy file to output directory if the file isn't in the output directory

I have a nuget with a .targets file that tells the consuming project to copy all files within a "Dependencies" folder to the output directory.
<ItemGroup>
<Files Include="$(MSBuildThisFileDirectory)/../contentFiles/Dependencies/*.*" />
</ItemGroup>
<Target Name="CopyDependencies" AfterTargets="Build">
<Copy SourceFiles="#(Files)"
DestinationFolder="$(TargetDir)" />
</Target>
This nuget is consumed by two projects: Project A and Project B. For this question, let's say we have a System.Runtime.InteropServices.RuntimeInformation.dll that is one of the dependencies within this nuget. The output directory of Project A does not already have System.Runtime.InteropServices.RuntimeInformation.dll, so it gets copied to the output directory when the project is built. Project B however already contains System.Runtime.InteropServices.RuntimeInformation.dll in the output directory. This causes a runtime issue at startup since the targets file is trying to overwrite the existing DLL of the same name with the System.Runtime.InteropServices.RuntimeInformation.dll file from within the nuget (which is a dependency of other files within the output directory).
So, how can I adjust my .targets file to only copy in files that do not already exist within the output directory based on name, and not size or date modified?
There are several ways but probably the most succinct change to your example code would be the following:
<ItemGroup>
<Files Include="$(MSBuildThisFileDirectory)/../contentFiles/Dependencies/*.*" />
</ItemGroup>
<Target Name="CopyDependencies" AfterTargets="Build">
<Copy SourceFiles="#(Files)" DestinationFolder="$(TargetDir)" Condition="!Exists('$(TargetDir)/%(Filename)%(Extension)')" />
</Target>
The change is adding a Condition on the Copy that is using the metadata of the #(Files) collection to test that the file does not exist in $(TargetDir).
Because of the use of metadata, the Copy is a task batch. Essentially the #(Files) collection is divided into batches by %(Filename)%(Extension) and there is a separate Copy task invoked for each batch.
If there is a large number of files in the Dependencies folder, the following variant may provide better performance.
<ItemGroup>
<Files Include="$(MSBuildThisFileDirectory)/../contentFiles/Dependencies/*.*" />
</ItemGroup>
<Target Name="CopyDependencies" AfterTargets="Build">
<ItemGroup>
<FilesToCopy Include="#(Files)" Condition="!Exists('$(TargetDir)/%(Filename)%(Extension)')" />
</ItemGroup>
<Copy SourceFiles="#(FilesToCopy)" DestinationFolder="$(TargetDir)" />
</Target>
The task batching is moved to the definition of a new ItemGroup collection and the Copy task is invoked once for the set of files. The potential performance improvement is that the implementation of the Copy task tries to parallelize copies, which it can't do when invoked per file.

NuGet Package Packing - Is it possible to copy files to a custom directory?

I'm trying to package a few files into a NuGet package, but the issue is that all of the files are sent to the "content" folder within the NuGet package by default when packaged. Normally this is okay, but for the JSON files I have in "ABCJsons" I'd like them to be sent to "content/NewFolderName".
In my example below, the first block is my AbcToolTester, which has all of its project files files being successfully sent to the content directory in the NuGet package. The second block, is where I attempted to copy all the json files with ABCLibrary (ABCLibrary has subfolders where the actual Jsons are located) to the destination folder "ABCJsons". I thought this would do the trick, but unfortunately the ABCJson files just get sent to the content folder along with all the other files.
<ItemGroup>
<Content Include="..\AbcToolTester\bin\Debug\netcoreapp3.1\**" Exclude="..\AbcToolTester\bin\Debug\netcoreapp3.1\*.pdb">
<IncludeInPackage>true</IncludeInPackage>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<ItemGroup>
<Target Name="CopyABCLibrary" AfterTargets="AfterBuild">
<ItemGroup>
<ABCJsonsInclude="..\..\tests\ABCLibrary\**\*.*"/>
</ItemGroup>
<Copy SourceFiles="#(ABCJsons)" DestinationFolder="$(TargetDir)\ABCJsons" SkipUnchangedFiles="true" />
</Target>
It's hard to tell if the NuGet package you are creating is actually dependent on AbcToolTester project because there are easier ways to package that. That's another question though.
For your actual issue, you can simplify the copying process while also telling it where to pack the files. Replace your CopyABCLibrary target with this:
<ItemGroup>
<Content Include="..\..\tests\ABCLibrary\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>true</Pack>
<PackagePath>ABCJsons\%(RecursiveDir)</PackagePath>
<!-- This line hides the items from showing in the solution explorer -->
<Visible>false</Visible>
</Content>
</ItemGroup>
This will put all those files into the root of the nuget package into the ABCJsons folder and preserve the directory structure. Change the path accordingly to put it somewhere else.

Linked files location on .Net core in Debug vs Publish

I have a shared.{Environment}.json file that is added as linked files in several .Net core 2.1 projects. When project is build or published file gets copied to output directory, in case of release its fine but on debug it doesn't work as when project run it looks up for that file in project directory not in output directory.
Whats the proper way to solve this issue for both debug and publish?
For linked files, it will not exist under the project directory.
For a workaround, you could try to copy the file with task in csproj like below:
<ItemGroup>
<Content Include="..\MVCPro\shared.{Environment}.json">
<Link>shared.{Environment}.json</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="CopyLinkedContentFiles" BeforeTargets="Build">
<Copy SourceFiles="..\MVCPro\shared.{Environment}.json" DestinationFolder=".\" SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" />
</Target>

web deploy copy a file from drop location to project

I have a console project in my solution.
Now I want that exe of that project should be added to my main project post deployment using web deploy.
How can I achieve that?
Regards,
Gautam
There are two high level solutions for this:
Option 1: Copy the file into App_Data
You can copy the exe into the App_Data folder as part of a post build event or as part of the script below. It's your choice.
Now that it's there, we have another problem. The WPP only includes files that are part of the project when it deploys. To get around this, you can create a WebProjectName.wpp.targets file to the root of the web application with the following contents:
<Project>
<PropertyGroup>
<BeforeAddContentPathToSourceManifest>
$(BeforeAddContentPathToSourceManifest);
IncludeExeInDeployment;
</BeforeAddContentPathToSourceManifest>
</PropertyGroup>
<Target Name="IncludeExeInDeployment">
<Copy SourceFiles="$(WebPublishPipelineProjectDirectory)\App_Data\Console\*"
TargetFolder="$(WPPAllFilesInSingleFolder)\App_Data\Console" />
</Target>
</Project>
(You could just as easily skip the interim step and copy the exe from it's original home into the $(WPPAllFilesInSingleFolder) folder)
Option 2: Include the exe as a separate provider
This one requires a bit more understanding of msdeploy, but gives you the option to deploy the exe wherever you want on the target server.
Basically it involves adding an additional dirPath provider in the deployment. Again, add a wpp.targets file in the root:
<Project>
<PropertyGroup>
<AfterAddContentPathToSourceManifest>
$(AfterAddContentPathToSourceManifest);
IncludeConsoleAppInDeployment;
</AfterAddContentPathToSourceManifest>
</PropertyGroup>
<Target Name="IncludeConsoleAppInDeployment">
<ItemGroup>
<MsDeploySourceManifest Include="dirPath">
<Path>full path to console directory</Path>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
</Project>
You'll also need to replace the path in your pubxml to specify where the exe will go on the far end:
<ItemGroup>
<MsDeploySetParameters Include="ConsoleAppPath">
<Kind>ProviderPath</Kind>
<Scope>dirPath</Scope>
<Match>regex that matches console directory</Match>
<Value>Path to console application on remote server</Value>
</MsDeploySetParameters>
</ItemGroup>

MSBuild project file: Copy item to specific location in output directory

In the process of cleaning up the folder/file structure on a project I inherited, I'm running into a problem with organizing the required external libraries. I want to keep them in their own .\dll\ folder, but they're not being copied to the build directory properly. They should be in the root build directory, but they're being moved to a subfolder instead.
My .csproj file contains the following xml:
<Project>
<ItemGroup>
<None Include="dlls\libraryA.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Then, on build, the libraryA.dll file is copied to the bin\Debug\dll\ folder, but I want it in the bin\Debug\ folder.
I tried this and msbuild always wants to copy the files using their directory path, but there is a workaround...
Edit the csproj file and after this line:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Add these lines:
<PropertyGroup>
<PrepareForRunDependsOn>$(PrepareForRunDependsOn);MyCopyFilesToOutputDirectory</PrepareForRunDependsOn>
</PropertyGroup>
<Target Name="MyCopyFilesToOutputDirectory">
<Copy SourceFiles="#(None)" DestinationFolder="$(OutDir)" />
</Target>
The copy of the output files happens in the PrepareForRun target. This adds your own target to the list of targets that are executed as part of PrepareForRun.
This example copies all items in the None item group. You could create your own item group (e.g. MyFiles) and do the copy on that item group if you have other "None" files you don't want copied. When I tried this I had to change the item group name by editing the csproj file directly. Visual Studio did not allow me to set the item group of a file from the UI, but after I edited the csproj and changed it, Visual Studio displayed my custom item group name correctly.
If you only want to change it for one file, it may be easier to use the property:
<None Include="dlls\libraryA.dll">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Including content files in .csproj that are outside the project cone
This approach works
If you need to force copy of a specific file/nuget package into an asp.net core project (2.2), add at the end of your csproj :
<!-- Force copy MathNet because we need it in compilation -->
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="Build">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MathNet.Numerics.4.8.1\lib\netstandard2.0\MathNet.Numerics.dll')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MathNet.Numerics.4.8.1\lib\netstandard2.0\MathNet.Numerics.dll'))" />
</Target>
<ItemGroup>
<ContentWithTargetPath Include="..\packages\MathNet.Numerics.4.8.1\lib\netstandard2.0\MathNet.Numerics.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>MathNet.Numerics.dll</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
In SDK-style csproj you can write something like:
<Target Name="CopyFilesTargetName" AfterTargets="Build">
<Copy SourceFiles="$(OutDir)\dlls\Some.dll;$(OutDir)\dlls\SomeOther.dll" DestinationFolder="$(OutDir)" />
</Target>
You can also use <Move instead of <Copy to move files