msbuild adding items to "Source Files" - c++

I want to allow in my project template (c++) to add files with specific extension ".myext" and in msbuild I'll find the suitable header and ".cpp" files and add them accordingly to the and to but I need also to add these files to the "Source Files" filter after adding the items with the ".myext" extension.
this is how I find the files and add them accordingly :
<Target Name="FilterMyFiles">
<ItemGroup>
<Filtered Include="#(None)" Condition="'%(Extension)' == '.myext'" />
<SourceFiles Include="#(Filtered->'%(rootdir)%(directory)%(filename)_source.cpp')" />
<HeaderFiles Include="#(Filtered->'%(rootdir)%(directory)%(filename)_header.h')" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="%(HeaderFiles.Identity)" Condition="Exists('%(HeaderFiles.Identity)')" />
<ClCompile Include="%(SourceFiles.Identity)" Condition="Exists('%(SourceFiles.Identity)')" />
</ItemGroup>
</Target>
I tried to add some changes to the msbuild file (".vcxproj") and to the filter file (".vcxproj.filter") but nothing helped.

Related

Build issues after updating to visual studio 15.8.5

Does anyone have experience with this error?
Severity Code Description Project File Line Suppression State
Error The item "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\Microsoft.Win32.Primitives.dll" in item list "ReferencePath" does not define a value for metadata "CopyLocal". In order to use this metadata, either qualify it by specifying %(ReferencePath.CopyLocal), or ensure that all items in this list define a value for this metadata. Batch_WPF
If I downgrade visual studio the issue goes away.
I've fixed this error by removing the following code in my .csproj file.
<Target Name="FixClickOnceDependencies" BeforeTargets="_DeploymentComputeClickOnceManifestInfo">
<ItemGroup>
<_noCopyRefs Include="#(ReferencePath)" Condition="'%(ReferencePath.Private)' == 'false'" />
<_noCopyRefsByFileName Include="#(_noCopyRefs->'%(FileName)')">
<OriginalItem>%(Identity)</OriginalItem>
</_noCopyRefsByFileName>
<_libByFileName Include="#(ReferenceCopyLocalPaths->'%(FileName)')">
<OriginalItem>%(Identity)</OriginalItem>
</_libByFileName>
<_overlappingRefByFileName Include="#(_noCopyRefsByFileName)" Condition="'#(_noCopyRefsByFileName)' == '#(_libByFileName)' AND '%(Identity)' != ''" />
<_overlappingLibByFileName Include="#(_libByFileName)" Condition="'#(_noCopyRefsByFileName)' == '#(_libByFileName)' AND '%(Identity)' != ''" />
<_overlappingRef Include="#(_overlappingRefByFileName->'%(OriginalItem)')" />
<_overlappingLib Include="#(_overlappingLibByFileName->'%(OriginalItem)')" />
</ItemGroup>
<ItemGroup Condition="'#(_overlappingRef)' != ''">
<ReferencePath Remove="#(_overlappingRef)" />
<ReferencePath Include="#(_overlappingLib)">
<Private>True</Private>
</ReferencePath>
</ItemGroup>
I added this code in order to fix an error with Click Once, as described here: https://github.com/dotnet/standard/issues/529

Default user-specific properties

I'm working on a project that requires a property named $(LibPath) to be set. This value should be set by a plugin, however it is saved in the projectname.vcxproj.user file. This is a problem for when someone has this file removed (through git clean for example) or someone new starting on the project.
In order to have a default value, I've created a Property Sheet which is impoted in the project file at the very top.
<PropertyGroup Label="UserMacros">
<LibPath Condition="'$(LibPath)' == ''">PathToLib</LibPath>
</PropertyGroup>
<ItemGroup>
<BuildMacro Include="LibPath">
<Value>$(LibPath)</Value>
</BuildMacro>
</ItemGroup>
It now works for everyone whose path matches the default. When it doesn't, they can change it from within VS by going to Property Manager -> Properties Sheet file -> User Macros.
Now the remaining issue is that when this path doesn't match and needs to be changed, the value inside the Propety Sheet is changed too.
So my question: Is there a way to define a property that can be edited from within VS, and that is then saved in the vcxproj.user file (or any other user-specific file which overrides the default value)?
Is there a way to define a property that can be edited from within VS that is saved in the vcxproj.user file?
AFAIK, I am afraid the reason why you can not edit the defined property in Visual Studio when that path does not match is that the vcxproj.user file is imported after than the Propety Sheet.
If we create a property sheet, we will get following import file TestSheet.props in the project file:
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="TestSheet.props" />
</ImportGroup>
The vcxproj.user file is imported by following Import:
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
This file is under the TestSheet.props in the project file:
In this case, MSBuild will use the value of $(LibPath) in the vcxproj.user file replace the value in the propety sheet. So it only works for everyone whose path matches the default, if you change the value by going to Property Manager -> Properties Sheet file -> User Macros, the value will be overwrite by the next import file Microsoft.Cpp.targets.
To resolve this issue, you can comment all the <Import Project="TestSheet.props" />lines in the project file, and add this line after import Microsoft.Cpp.targets file:
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="TestSheet.props" />
With this setting, if you change the value in the User Macros, this value will overwrite the value in the vcxproj.user file.

Rename a file before Copy Task in ant build

I am new to ant build files.
Currently I get a list of files for build as:
a.cls
b.cls
c.cls
but in my local I have to run build on files, in the same directory:
a-meta.cls
b-meta.cls
c-meta.cls
Here meta keyword stays consistent. And I am using the following build.xml file. I am not sure how can I rename filename before actually copying them. I tried replace, mapper and other antlib tasks. But not helpful.
<project name="test" default="compile">
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="lib/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<loadfile property="file" srcfile="filesToMove.txt"/> <!-- these are the list of files, i mentioned earlier -->
<target name="compile">
<echo>${file}</echo> <!-- here i have to rename file name to include -meta -->
<copy file="./classes/${file}" tofile="./src/classes/${file}" overwrite="true"/>
</target>
</project>
How to rename the files before moving them.
The solution to it was replacing the .cls to find only the name and then append the -meta.html. As follows (some portion is changed compared to previous version in the question)
<project name="test" default="compile">
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="lib/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<loadfile property="file" srcfile="filesToMove.txt"/> <!-- these are the list of files, i mentioned earlier -->
<target name="compile">
<echo>${file}</echo> <!-- here i have to rename file name to include -meta -->
<copy file="./classes/${file}" tofile="./src/classes/${file}" overwrite="true"/>
<for param="file">
<path>
<fileset dir="./" includes="*.cls"/>
</path>
<sequential>
<basename file="#{file}" property="#{file}" suffix=".md"/>
<echo message=" ${#{file}}"/>
<copy file="${#{file}}-meta.cls" toDir="test"/>
</sequential>
</for>
</target>
</project>

How can one modify an ItemDefinitionGroup from an MSBuild target?

I have an msbuild script I wrote to compile Google Protocol Buffers files:
<ItemGroup>
<ProtocolBuffer Include="Whitelist.proto" />
<ProtocolBuffer Include="Whitelist2.proto" />
</ItemGroup>
<ItemDefinitionGroup>
<ProtocolBuffer>
<ProtoPath>$(ProjectDir)</ProtoPath>
</ProtocolBuffer>
</ItemDefinitionGroup>
<PropertyGroup>
<ProtoC>$([System.IO.Path]::GetFullPath($(ProjectDir)..\ThirdParty\protobuf-2.4.1\protoc.exe))</ProtoC>
<ProtoOutPath>$(IntDir)CompiledProtocolBuffers</ProtoOutPath>
</PropertyGroup>
<Target Name="CompileProtocolBuffers"
BeforeTargets="ClCompile"
Inputs="#(ProtocolBuffer)"
Outputs="#(ProtocolBuffer->'$(ProtoOutPath)\%(FileName).pb.cc');#(ProtocolBuffer->'$(ProtoOutPath)\%(FileName).pb.h')">
<MakeDir Directories="$(ProtoOutPath)" />
<Exec
Command=""$(ProtoC)" --proto_path="$([System.IO.Path]::GetDirectoryName(%(ProtocolBuffer.ProtoPath)))" --cpp_out="$(ProtoOutPath)" "%(ProtocolBuffer.FullPath)" --error_format=msvs"
/>
<ItemGroup>
<ClInclude Include="$(ProtoOutPath)\%(ProtocolBuffer.FileName).pb.h" />
<ClCompile Include="$(ProtoOutPath)\%(ProtocolBuffer.FileName).pb.cc">
<AdditionalIncludeDirectories>$(MSBuildThisDirectory)..\ThirdParty\protobuf-2.4.1\src</AdditionalIncludeDirectories>
<PrecompiledHeader></PrecompiledHeader>
<DisableSpecificWarnings>4244;4276;4018;4355;4800;4251;4996;4146;4305</DisableSpecificWarnings>
<PreprocessorDefinitions>GOOGLE_PROTOBUF_NO_RTTI</PreprocessorDefinitions>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
</ItemGroup>
</Target>
This compiles the protocol buffers files perfectly, and adds them to the compiler's inputs (yay!). However, my other source files that want to include the .pb.h files need to know where these files got generated -- that generation location needs to be put on the include path.
Therefore, if and only if the user has included a <ProtocolBuffer item somewhere in their script, I want to add the generation location (in this case $(ProtoOutPath) to ClCompile's <AdditionalIncludeDirectories>.
Is that possible or do I need to make .cpp files that want to use these generated bits jump through hoops?
Read your question and thought "can't be that hard". Man, was I wrong. First I thought just putting a condition on it, but of course one can't use ItemGroups in toplevel conditions because of evaluation order. Then I figured it's also not possible to put an ItemDefinitionGroup in a target (cause there one can use conditions) and modify it there. Then I bonked my head on the keyboard a couple of times after I realized that's probably why you asked the question :] (btw you know including a nonexisting directory is not really a problem since the compiler will happily ignore it?)
Maybe there's a simpler solution, but lastly I figured: if nothing works, my favourite msbuild toy aka CodeTaskFactory must be able to fix it. It does (I hope, didn't fully test the result), but it's not straightforward at all. Here you go, make sure to invoke the Test target somewhere before the C++ build starts.
<!--Uncomment the below to define some ProtocolBuffers-->
<!--<ItemGroup>
<ProtocolBuffer Include="Whitelist.proto" />
<ProtocolBuffer Include="Whitelist2.proto" />
</ItemGroup>-->
<!--Suppose these are your default include files defined in your C++ project-->
<ItemDefinitionGroup Label="DefaultIncludes">
<ClCompile>
<AdditionalIncludeDirectories>/path/to/x;/path/to/y</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<!--Include at least one item so we can play with it-->
<ItemGroup>
<ClCompile Include="iamaninclude"/>
</ItemGroup>
<!--Use code to append to AdditionalIncludeDirectories-->
<UsingTask TaskName="AppendMetadata" TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<Append ParameterType="System.String" Required="true"/>
<ItemList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true"/>
<OutputItemList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
</ParameterGroup>
<Task>
<Code>
<![CDATA[
const string dirz = "AdditionalIncludeDirectories";
foreach( var item in ItemList )
{
var cur = item.GetMetadata( dirz );
item.SetMetadata( dirz, cur + ";" + Append );
}
OutputItemList = ItemList;
]]>
</Code>
</Task>
</UsingTask>
<!--Main target-->
<Target Name="Test">
<!--stage 1: copy the itemgroup, then clear it:
if an Output TaskParameter is an Itemgroup, apparently the content
gets appended to the group instead of replacing it.
Found no documentation about this whatsoever though???-->
<ItemGroup Condition="#(ProtocolBuffer) != ''">
<ClCompileCopy Include="#(ClCompile)"/>
<ClCompile Remove="#(ClCompile)"/>
</ItemGroup>
<!--stage 2: append 'ProtoBufIncludeDir' to AdditionalIncludeDirectories,
and append the result to the origiginal again-->
<AppendMetadata ItemList="#(ClCompileCopy)" Append="ProtoBufIncludeDir" Condition="#(ProtocolBuffer) != ''">
<Output ItemName="ClCompile" TaskParameter="OutputItemList"/>
</AppendMetadata>
<!--stage 3: use modified itemgroup-->
<Message Text="#(ClCompile->'%(Identity): %(AdditionalIncludeDirectories)')"/>
</Target>
This prints
iamaninclude: /path/to/x;/path/to/y
unless the ProtocolBuffer is not empty in which case it prints
iamaninclude: /path/to/x;/path/to/y;ProtoBufIncludeDir

VS2010 custom build tool for generating .h file

I'm trying to integrate a custom build tool in VS2010 that generates a .h-file from a source file. I've created a .xml, .targets and .props for the step. The XML is mostly copy-pasted from the MASM-file and ends with:
<ItemType Name="FOO" DisplayName="Foo compiler" />
<FileExtension Name="*.foo" ContentType="FOO" />
<ContentType Name="FOO" DisplayName="Foo compiler" ItemType="FOO" />
This maps all my .foo files to the Foo compiler that's defined in the .props:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
<AvailableItemName Include="FOO">
<Targets>FooCompile</Targets>
</AvailableItemName>
</ItemGroup>
<UsingTask TaskName="FOO" TaskFactory="XamlTaskFactory" AssemblyName="Microsoft.Build.Tasks.v4.0">
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
</UsingTask>
<Target Name="FooCompile" BeforeTargets="$(FOOBeforeTargets)" AfterTargets="$(FOOAfterTargets)" Condition="'#(FOO)' != ''" Outputs="%(FOO.Outputs)" Inputs="%(FOO.Identity);%(FOO.AdditionalDependencies);$(MSBuildProjectFile)" DependsOnTargets="_SelectedFiles">
<Message Importance="High" Text="#(FOO)" />
<FOO Condition="'#(FOO)' != '' and '%(FOO.ExcludedFromBuild)' != 'true'"
CommandLineTemplate="%(FOO.CommandLineTemplate)"
OutputFileName="%(FOO.OutputFileName)"
Inputs="%(FOO.Identity)" />
</Target>
</Project>
When I compile my project it successfully identifies and compiles my foo files:
1>FooCompile:
1> apa.foo
1>FooCompile:
1> banan.foo
1>ClCompile:
1> test.cpp
1> main.cpp
My question is why does it print "FooCompile:" once for each file while the ClCompile doesn't? Is there any way to change this?
If I change a cpp file and build, I'll also get this output once for each file, which I want to avoid:
1>FooCompile:
1>Skipping target "FooCompile" because all output files are up-to-date with respect to the input files.
1>FooCompile:
1>Skipping target "FooCompile" because all output files are up-to-date with respect to the input files.
The FooCompile target is using "target batching" which causes the target to iterate once for each item in the array specified for the Outputs attribute, %(Foo). The ClCompile target on the other hand operates using the entire item array #(ClCompile).
You can alter the verbosity of the logger to avoid the messages, specify /v:minimal, but of course you may be filtering out other information as well.