I have the below MSBuild to generate .cs files from my proto files. The build works fine until I do a rebuild where it complains of Source file 'generated-proto-output/Trade.cs# specified multiple times.
How do I delete my .cs files before building/rebuilding everytime?
Error
Severity Code Description Project File Line Suppression State
Warning CS2002 Source file 'generated-proto-output\ErrorTrade.cs' specified multiple times MyComp.Trade.Model C:\dev\workspaces\trade-model-workspace\model\csharp\MyComp.Trade.Model
build snippet in csproj file
<ItemGroup>
<Protobuf Remove="%(RelativePath)generated-proto-output/**/*.cs" />
<Protobuf Include="../../proto/**/*.proto" ProtoRoot="../../proto/" OutputDir="%(RelativePath)generated-proto-output/" GrpcServices="None" />
<Protobuf Update="../../proto/**/*Service.proto" GrpcServices="Both" />
</ItemGroup>
UPDATE - Complete CSProj file (as requested by Lance)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>TradeStore.Model</PackageId>
<ProtoIncludes>.;../../proto</ProtoIncludes>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<Protobuf_NoWarnMissingExpected>true</Protobuf_NoWarnMissingExpected>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="Grpc" Version="1.19.0" />
<PackageReference Include="Grpc.Tools" Version="1.19.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<FilesToDelete Include="%(RelativePath)generated-proto-output/*.cs" />
</ItemGroup>
<Target Name="DeleteSpecificFiles" BeforeTargets="Build">
<Message Text="Specific Files: #(FilesToDelete)"/>
<Message Text ="Beginning to delete specific files before build or rebuild..."/>
<Delete Files="#(FilesToDelete)"/>
</Target>
<ItemGroup>
<Protobuf Include="../../proto/**/*.proto" ProtoRoot="../../proto/" OutputDir="%(RelativePath)generated-proto-output/" GrpcServices="None" />
<Protobuf Update="../../proto/**/*Service.proto" GrpcServices="Both" />
</ItemGroup>
</Project>
Try adding CompileOutputs="false" to the directive. This will suppress the warning and won't require you to delete files before building csharp protobuf build integration
How do I delete my .cs files before building/rebuilding everytime?
Try the following script with BeforeTargets below:
<Project...>
...
<ItemGroup>
<FilesToDelete Include="MyPath/*.cs" />
</ItemGroup>
<Target Name="DeleteSpecificFiles" BeforeTargets="build">
<Message Text="Specific Files: #(FilesToDelete)"/>
<Message Text ="Beginning to delete specific files before build or rebuild..."/>
<Delete Files="#(FilesToDelete)"/>
</Target>
</Project>
In addition:
Not seeing the whole content of your .csproj, so I can't figure out why the build snippet you use can't work. But a message task may help output some message whether the engine finds the files by your given path.
In visual studio, if you go Tools=>Options=>Project and Solutions=>Build and Run to change the build out verbosity to Detailed, you will see detailed output message after every build and rebuild.Ctrl+Fand type the Target name you will find the details about delete process:
Hope it makes some help for your trouble-shooting.
Related
I'm trying to include a Project Reference based on a value in the ItemGroup Condition
This is what I have tried. With BeforeTargets Compile it does recognize the project but it won't let me build the project.
<Target Name="ErpSystemToUse" BeforeTargets="Compile">
<ReadLinesFromFile File="$(MSBuildProjectDirectory)\..\..\Presentation\Nop.Web\App_Data\erpSystemToUse.txt">
<Output TaskParameter="Lines" ItemName="ValueTextFile" />
</ReadLinesFromFile>
<Message Text="#(ValueTextFile)" Importance="high" />
<ItemGroup Condition="'#(ValueTextFile)' == 'Twinfield'">
<ProjectReference Include="..\..\Dimerce.Twinfield\Dimerce.Plugin.Misc.Twinfield\Dimerce.Plugin.Misc.Twinfield.csproj" />
</ItemGroup>
</Target>
Is what I am trying to achieve possible?
I have a project where I had to inspect output of System.Reflection.Emit. Decided to include package reference only when appropriate constant is defined
Main difference is that target executes before CollectPackageReferences. Try to change BeforeTargets.
Also use Inputs and Outputs to reduce build time
<Target Name="IncludeIlPackReference"
Inputs="$(DefineConstants)" Outputs="#(PackageReference)"
BeforeTargets="CollectPackageReferences"
Condition="$(DefineConstants.Contains('TRACE_GENERATED_IL'))">
<ItemGroup>
<PackageReference Include="Lokad.ILPack" Version="0.1.6" />
</ItemGroup>
</Target>
I'm using ConfuserEx to obfuscate my app, but it requires whole .dll binary file.
So, is there a way to obfuscate it using cli and then pack it to single file, or
access binary before compressing it to single file, so i can obfuscate it
I tried to do exclude main binary by ExcludeFromSingleFile, but it didn't work
My .crproj file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>exe</OutputType>
<AssemblyName>jay-$(RuntimeIdentifier)</AssemblyName>
<PublishSingleFile>true</PublishSingleFile>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Content Update="$(AssemblyName).dll">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CloudFlareUtilities" Version="1.3.0" />
<PackageReference Include="Colorful.Console" Version="1.2.15" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="YamlDotNet" Version="9.1.4" />
</ItemGroup>
</Project>
You need to add it as a post-build event, for example:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>exe</OutputType>
<AssemblyName>jay-$(RuntimeIdentifier)</AssemblyName>
<PublishSingleFile>true</PublishSingleFile>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<!-- runs: TheAppToPassItTo.exe "<path to dll>" -->
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="TheAppToPassItTo.exe "$(TargetPath)"" />
</Target>
<ItemGroup>
<PackageReference Include="CloudFlareUtilities" Version="1.3.0" />
<PackageReference Include="Colorful.Console" Version="1.2.15" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="YamlDotNet" Version="9.1.4" />
</ItemGroup>
</Project>
Edited To Add
So, it turns out that the new "single file" executable option doesn't build it's executable from the bin directory, but from the obj directory. This has to be a simple oversight from the .NET team. There are many times where you'd want to modify your executable code before packing it up. What you are asking for is not unreasonable.
We can accomplish this by implementing a kludge until this gets rectified. We will still use the "post-build" job, but we are going to do some string replacement to build the correct path to the executable you want to modify.
This is the new script:
#ECHO off
SET tp=$(TargetPath)
SET tp=%tp:\bin\=\obj\%
ECHO Target file to modify: %tp%
YourObfuscatorEngine.exe "%tp%"
This will get the target path, in my case it is:
D:\Repositories\Source\ConsoleApp2\ConsoleApp2\bin\Release\netcoreapp3.1\win-x64\ConsoleApp2.dll
Then we do a string replace. We replace \bin\ with \obj\. The path will then be:
D:\Repositories\Source\ConsoleApp2\ConsoleApp2\obj\Release\netcoreapp3.1\win-x64\ConsoleApp2.dll
Now when you call your obfuscator engine, it will modify the correct file.
Please keep in mind that if you turn on the PublishReadyToRun option, your path will change to:
D:\Repositories\Source\ConsoleApp2\ConsoleApp2\obj\Release\netcoreapp3.1\win-x64\R2R\ConsoleApp2.dll
Which will make this a tad more complicated. So just keep that in mind if you decide you want to do this.
At the end of the day, your post-build script will look like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>exe</OutputType>
<AssemblyName>jay-$(RuntimeIdentifier)</AssemblyName>
<PublishSingleFile>true</PublishSingleFile>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="#ECHO off
SET tp=$(TargetPath)
SET tp=%25tp:\bin\=\obj\%25
ECHO Target file to modify: %25tp%25
YourObfuscatorEngine.exe "%25tp%25"" />
</Target>
<ItemGroup>
<PackageReference Include="CloudFlareUtilities" Version="1.3.0" />
<PackageReference Include="Colorful.Console" Version="1.2.15" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="YamlDotNet" Version="9.1.4" />
</ItemGroup>
</Project>
if you only need to modify the file on publish, I suggest hooking into the publish pipeline:
<Target Name="ObfuscateAssembly" BeforeTargets="PrepareForPublish">
<Exec Command="some.exe %(IntermediateAssembly.FullPath)" />
</Target>
In case a larger process is needed, e.g. for this sample all dependencies need to be present for the obfuscator to work on all assemblies, an extended method of hooking into the build process would be after ComputeResolvedFilesToPublishList where the SDK figures out what files are needed to publish.
Here's the full example where the obfuscator works on all the assemblies in a directory:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<PublishReadyToRun>True</PublishReadyToRun>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>False</SelfContained>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<Target Name="CalculateObfuscationInputs" DependsOnTargets="_ComputeAssembliesToPostprocessOnPublish">
<PropertyGroup>
<ObfuscationDir>$(IntermediateOutputPath)obfuscation\</ObfuscationDir>
</PropertyGroup>
<ItemGroup>
<AssembliesToObfuscate Include="#(ResolvedFileToPublish->WithMetadataValue('PostprocessAssembly', 'true'))" />
<AssembliesToObfuscateTemporaryLocation Include="#(AssembliesToObfuscate->'$(ObfuscationDir)%(Filename)%(Extension)')" />
<_PdbsToObfuscateInput Include="#(AssembliesToObfuscate->'%(RelativeDir)%(Filename).pdb')" />
<PdbsToObfuscate Include="#(_PdbsToObfuscateInput)" RelativePath="%(_PdbsToObfuscateInput.Identity)" Condition="Exists(%(_PdbsToObfuscateInput.Identity))" />
<PdbsToObfuscateTemporaryLocation Include="#(PdbsToObfuscate->'$(ObfuscationDir)%(Filename)%(Extension)')" />
</ItemGroup>
<MakeDir Directories="$(ObfuscationDir)" />
</Target>
<Target Name="PrepareForObfuscation" Inputs="#(AssembliesToObfuscate);#(PdbsToObfuscate)" Outputs="#(AssembliesToObfuscateTemporaryLocation);#(PdbsToObfuscateTemporaryLocation)">
<Copy SourceFiles="#(AssembliesToObfuscate);#(PdbsToObfuscate)" DestinationFiles="#(AssembliesToObfuscateTemporaryLocation);#(PdbsToObfuscateTemporaryLocation)" SkipUnchangedFiles="True" />
</Target>
<Target Name="ObfuscateAssembly" AfterTargets="ComputeResolvedFilesToPublishList" DependsOnTargets="CalculateObfuscationInputs;PrepareForObfuscation">
<Exec Command="some-obfuscator.exe $(ObfuscationDir)" />
<ItemGroup>
<ResolvedFileToPublish Remove="#(AssembliesToObfuscate);#(PdbsToObfuscate)" />
<ResolvedFileToPublish Include="#(AssembliesToObfuscateTemporaryLocation);#(PdbsToObfuscateTemporaryLocation)" />
</ItemGroup>
</Target>
</Project>
I have a project that integrates TextTransform with MSBuild, but I am now at the point that the files are too many and it is slowing down development time.
The process that I have is, I have a POCO.cs; I use Roslyn to Parse the .cs file to gather some metadata; hand the metadata to a .tt file that will then generate a [Implementation].cs
Here is part of the .csproj file that pertains to my question:
<PropertyGroup>
<_CommonProgramFiles>$([System.Environment]::GetEnvironmentVariable('CommonProgramFiles(x86)'))</_CommonProgramFiles>
<_CommonProgramFiles Condition=" '$(_CommonProgramFiles)' == '' ">$(CommonProgramFiles)</_CommonProgramFiles>
<TextTransformPath Condition="'$(TextTransformPath)' == ''">$(_CommonProgramFiles)\Microsoft Shared\TextTemplating\$(VisualStudioVersion)\TextTransform.exe</TextTransformPath>
<!-- Initial default value -->
<_TransformExe>$(TextTransformPath)</_TransformExe>
<_RoslynDllPath>$(ProjectDir)Lib\RoslynWrapper.dll</_RoslynDllPath>
<!-- Cascading probing if file not found -->
<_TransformExe Condition="!Exists('$(_TransformExe)')">$(_CommonProgramFiles)\Microsoft Shared\TextTemplating\10.0\TextTransform.exe"</_TransformExe>
<_TransformExe Condition="!Exists('$(_TransformExe)')">$(_CommonProgramFiles)\Microsoft Shared\TextTemplating\11.0\TextTransform.exe"</_TransformExe>
<_TransformExe Condition="!Exists('$(_TransformExe)')">$(_CommonProgramFiles)\Microsoft Shared\TextTemplating\12.0\TextTransform.exe"</_TransformExe>
<!-- Future proof 'til VS2013+2 -->
<_TransformExe Condition="!Exists('$(_TransformExe)')">$(_CommonProgramFiles)\Microsoft Shared\TextTemplating\13.0\TextTransform.exe"</_TransformExe>
<_TransformExe Condition="!Exists('$(_TransformExe)')">$(_CommonProgramFiles)\Microsoft Shared\TextTemplating\14.0\TextTransform.exe"</_TransformExe>
</PropertyGroup>
<ItemGroup>
<Compile Include="SomePoco.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SomePocoMetadata.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>SomePocoMetadata.tt</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="SomePocoMetadata.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>SomePocoMetadata.cs</LastGenOutput>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="TransformOnBuild" AfterTargets="BeforeBuild">
<Error Text="Failed to find TextTransform.exe tool at '$(_TransformExe)." Condition="!Exists('$(_TransformExe)')" />
<ItemGroup>
<_TextTransform Include="#(None)" Condition="'%(None.Generator)' == 'TextTemplatingFileGenerator'" />
</ItemGroup>
<!-- Perform task batching for each file -->
<Exec Command=""$(_TransformExe)" "%(_TextTransform.FullPath)" -r "$(_RoslynDllPath)"" Condition="'%(_TextTransform.Identity)' != ''" />
</Target>
With the above, the .tt files are always transforming and it takes a couple of minutes to compile the DLL. Is there a way to do this in MSBuild to only transform when the SomePoco.cs file is modified only?
Is there maybe another approach that I should look into to get what i want accomplished?
Make sure you specify Inputs and Outputs attributes for your target.
In your specific case you seem to obtain name of one of the inputs inside the target, so you cannot use it in the enclosing Target's Input attribute. A workaround for you is to move the definition of _TextTransform item group outside of the target.
what i ended up doing was using with ITaskItem[] as one of the input parameters and in there i used C# to figure out the last modified of the files and determined if i needed to generate that way.
I have a DLL that I want to copy to "\Folder1\DestinationDir" and "\Folder2\DestinationDir". I tried using a wild carded destination path:
"\Folder*\DestinationDir",
but I got an error:
No Destination specified for Copy.
Here's my XML:
<ItemGroup>
<ItemToCopy Include="$(OutDir)Mydll.dll" />
</ItemGroup>
<ItemGroup>
<DeployPath Include="$(MSBuildProjectDirectory)\Folder*\DestinationDir" />
</ItemGroup>
<MakeDir Directories="$(DeployPath)" />
<Copy SourceFiles="#(ItemToCopy)" DestinationFolder="%(DeployPath.FullPath)" />
Any help would be much appreciated.
See also
Creating a list of Folders in an ItemGroup using MSBuild
You build file does not work because ItemToCopy does not expand directory paths, it expands files.
So, if you want to enumerate directories, you should target the existing files in those directoris, then get directory list from the file list.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ItemToCopy Include="$(MSBuildProjectDirectory)\toCopy.txt" />
</ItemGroup>
<ItemGroup>
<DeployPath Include="$(MSBuildProjectDirectory)\Folder*\*.*" />
<DeployFolders Include="#(DeployPath->'%(RootDir)%(Directory)'->Distinct())" />
</ItemGroup>
<Target Name="Test">
<Copy SourceFiles="#(ItemToCopy)" DestinationFolder="%(DeployFolders.FullPath)" />
<Message Text="Destination folder = #(DeployFolders)" />
</Target>
</Project>
Note that this would NOT work for empty directories.
Another thread discusses this problem:
Creating a list of Folders in an ItemGroup using MSBuild
I would recommend to specify a set of folders explicitly.
This can be done with item metadata, for example, and not rely on existing folder structure:
<ItemGroup>
<DeploySpecificFolders Include="$(MSBuildProjectDirectory)\toCopy.txt">
<FolderToCopyTo>Folder1</FolderToCopyTo>
</DeploySpecificFolders>
</ItemGroup>
...
<Message Text="Specific folders = %(DeploySpecificFolders.FullPath) will be copies to %(DeploySpecificFolders.FolderToCopyTo)" />
<Copy SourceFiles="#(DeploySpecificFolders)" DestinationFolder="$(MSBuildProjectDirectory)\%(DeploySpecificFolders.FolderToCopyTo)" />
I have a situation where i want the versioning to be dynamic at build time.
Version Pattern: <year>.<month>.<day>.<hhmm>
But i have read where the String value used in the Attribute is reparsed at compile time.
Any advise on how to get this dynamic versioning completed?
Ideal situation:
<Assembly: AssemblyVersion("4.0.0.0")>
<Assembly: AssemblyFileVersion(Year(Now) & "." & Month(Now()) & "." & Day(Now()) & "." & String.format("hhmm", now()))>
I know it wont work but should get the point acrossed.
You can use the MsbuildCommunityTasks to generate the build number and to customize the assembly file version on pre-build time.
Download the zip at MsbuildCommunityTasks
Unzip to the folder [SolutionFolder]\MsBuildExtensions\MSBuildCommunityTasks
Add the sample below on your project (csproj), just after the Microsoft.CSharp.Targets import.
<PropertyGroup>
<MSBuildCommunityTasksPath>$(MSBuildThisFileDirectory)..\MsBuildExtensions\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
<My-PropertiesDir>Properties</My-PropertiesDir>
</PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets"/>
<Target Name="BeforeBuild">
<Time Format="yyyy.MM.dd.HHmm">
<Output TaskParameter="FormattedTime" PropertyName="My-VersionNumber" />
</Time>
<Message Text="Building $(My-VersionNumber) ...">
</Message>
<ItemGroup>
<My-AssemblyInfo Include="$(My-PropertiesDir)\AssemblyVersionInfo.cs" />
<Compile Include="#(My-AssemblyInfo)" />
</ItemGroup>
<MakeDir Directories="$(My-PropertiesDir)" />
<AssemblyInfo OutputFile="#(My-AssemblyInfo)"
CodeLanguage="CS"
AssemblyFileVersion="$(My-VersionNumber)"
AssemblyInformationalVersion="$(My-VersionNumber)"
Condition="$(My-VersionNumber) != '' "
/>
</Target>
<Target Name="AfterBuild">
<Delete Files="#(My-AssemblyInfo)" />
</Target>
Wipe the AssemblyFileVersion attribute from your AssemblyInfo.cs. It will be generated at build time.
You'll see the version number being printed on the console when you build. The generated file is deleted on the AfterBuild target, to keep your source control clean.
BeforeBuild:
Building 2013.01.14.1016 ...
Created AssemblyInfo file "Properties\AssemblyVersionInfo.cs".
(...)
AfterBuild:
Deleting file "Properties\AssemblyVersionInfo.cs".
If you want do this to many projects with less msbuild code, it will be necessary to create a customized build script to wrap up your solution.