I found a similar question like this here.
But this doesn't solve my problem. I have a ItemGroup like this
<ItemGroup>
<DocumentationSource Include="TestLibrary\TestLibrary.csproj;TestLibrary2\TestLibrary2.csproj;TestLibrary2\TestLibrary3.csproj" />
</ItemGroup>
I need to change this into propertygroup in this format
<PropertyGroup>
<DocumentationSources>
<DocumentationSource sourceFile="TestLibrary\TestLibrary.csproj" />
<DocumentationSource sourceFile="TestLibrary2\TestLibrary2.csproj" />
<DocumentationSource sourceFile="TestLibrary2\TestLibrary3.csproj" />
</DocumentationSources>
</PropertyGroup>
I am using sandcastle document builder for generating documents. That needs the document source as I shown in the format of PropertyGroup. But in my build script already I have a ItemGroup which has all the project as mentioned in the above format.
How to use that ItemGroup here as document source of SandCastle or how to convert the ItemGroup to PropertyGroup in the above format?
Actually I can change the ItemGroup to the PropertyGroup format but that has been formed dynamically with some logic like this
<_ProjectFilesPlatform Include="%(ProjectDefinitionsPlatform.Identity)">
<_ProjectPath>$([System.String]::Copy(%(ProjectDefinitionsPlatform.Identity)).Replace(".","\"))</_ProjectPath>
</_ProjectFilesPlatform>
[This is a rough outline I gave here. This manipulation is not the actual one used]
I am new to this MSBUILD scripts. Can anyone throw some light on this ?
Thanks.
You can use the #() syntax to transform the items to a string, separated by newlines. Here is an example project file (tested with MSBuild 15 on .net core):
<Project>
<ItemGroup>
<DocumentationSource Include="TestLibrary\TestLibrary.csproj;TestLibrary2\TestLibrary2.csproj;TestLibrary2\TestLibrary3.csproj" />
</ItemGroup>
<PropertyGroup>
<DocumentationSources>
#(DocumentationSource->'<DocumentationSource sourceFile="%(Identity)" />', '
')
</DocumentationSources>
</PropertyGroup>
<Target Name="Build">
<Message Importance="high" Text="Value of DocumentationSources: $(DocumentationSources)" />
</Target>
</Project>
Which produces the following output:
$ dotnet msbuild
Microsoft (R) Build Engine version 15.3.378.6360 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Value of DocumentationSources:
<DocumentationSource sourceFile="TestLibrary/TestLibrary.csproj" />
<DocumentationSource sourceFile="TestLibrary2/TestLibrary2.csproj" />
<DocumentationSource sourceFile="TestLibrary2/TestLibrary3.csproj" />
This even allows you to use a wildcard for your item element:
<DocumentationSource Include="**\*.csproj" />
Related
I use the following to get a list of project files that need to be compiled. Each project is stored in a subdirectory of the projects directory.
<ItemGroup>
<dprs Include="c:\projects\**\*.dpr" />
</ItemGroup>
Is there a task that I can use to extract to extract the directory that each project file is in? I know I can write my own task to do this but I was hoping that one already exists and that I simply have not found it yet.
If I understand the question correctly, you shouldn't need a task - you can do this with well-known meta data. Does this do the trick?
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<ItemGroup>
<dprs Include="c:\projects\**\*.dpr" />
</ItemGroup>
<Target Name="Default">
<CreateItem Include="%(dprs.RelativeDir)">
<Output ItemName="_ProjectFileLocations" TaskParameter="Include" />
</CreateItem>
<Message Text="#(_ProjectFileLocations->'%(FullPath)', '%0D%0A')" />
</Target>
</Project>
From the tests I ran, it shouldn't list a directory twice in the new item group.
I'm deploying a MsBuild task using nuget, that generates a .dll before each build.
I fail to get the generated dll referenced in the References node in the Visual Studio project of consumers.
I'm also using MSBuild to build the .nupkg file. Generating and compiling works just fine, I deploy the following target in the build/directory
<Project>
<!-- this will automatically run before the 'Build' target -->
<Target Name="GenerateAndBuild" BeforeTargets="Build">
<!--the deployed task that generates the code-->
<Utils.CreateUtilResourceTask/>
<ItemGroup>
<CompileGeneratedSources Include="GeneratedClass.cs" />
</ItemGroup>
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
<Csc Sources="#(CompileGeneratedSources )" OutputAssembly="$(OutputPath)Util.dll" TargetType="library" EmitDebugInformation="true" />
<Delete Files="#(CompileGeneratedSources )" />
</Target>
</Project>
That generates the util.dll in the project output folder but I fail to get it referenced in the consuming project.
I thought that this would work in the .csproj file:
<PropertyGroup>
<TargetFrameworks>netstandard1.6;net46</TargetFrameworks>
<BuildOutputTargetFolder>tasks</BuildOutputTargetFolder>
<VersionPrefix>0.1.1</VersionPrefix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>$(MSBuildThisFileDirectory)</PackageOutputPath>
<PackageId>BuildUtil</PackageId>
<!-- that does not add a refenrece in consuming projects -->
<references>
<reference file="$(OutputPath)Util.dll"/>
</references>
<files>
<file src="$(OutputPath)Util.dll"/>
</files>
</PropertyGroup>
Perhaps someone has a hint on that?
You need to declare the assembly reference in an ItemGroup, not a PropertyGroup. For example:
<ItemGroup>
<Reference Include="$(OutputPath)Util.dll" />
</ItemGroup>
I am trying to include files from a second project as EmbeddedResources using the following msbuild target:
<CreateItem Include="..\MyProject.Templates\**\*.css">
<Output ItemName="EmbeddedResource" TaskParameter="Include" />
</CreateItem>
but the included file loose their path e.g. ~\Views\_Layout.cshtml is included as _Layout.cshtml (not Views._Layout.cshtml as is desired). Is there some way to achieve the desired effect?
MSBuild has New Methods for Manipulating Items and Properties. Using these methods, you can map your resources using an ItemGroup (instead of CreateItem), then create another ItemGroup applying MSBuild Transforms with MSBuild Well-known Item Metadata. There are many item metadata options you could use to achieve the desired effect. There's a clear example of the syntax on this answer.
I wrote a little script as an example. It creates an ItemGroup with *.exe files and transforms them. Tested with MSBuild 3.5.
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<Project DefaultTargets="CreateItems" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CreateItems">
<ItemGroup>
<Exe Include="..\**\*.exe" />
</ItemGroup>
<ItemGroup>
<TransformedExe Include="#(Exe->'%(Relativedir)')"/>
</ItemGroup>
<Message Text="1 - #(Exe)" />
<Message Text="2 - #(TransformedExe)" />
</Target>
</Project>
I have a Silverlight project with multiple configuration files, and am using the transformation approach shown here:
App.Config Transformation for projects which are not Web Projects in Visual Studio 2010?
This approach doesn't work as-is for Silverlight projects though. I've re-written the MSBuild project to look like this:
<ItemGroup>
<None Include="App.config" />
<None Include="App.QABuild.config">
<DependentUpon>App.config</DependentUpon>
</None>
</ItemGroup>
....
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="BeforeCompile" Condition="Exists('App.$(Configuration).config')">
<!-- Generate transformed app config in the output directory -->
<Message Importance="high" Text="Transforming 'App.$(Configuration).config' to output config file..." />
<TransformXml Source="App.config" Destination="$(OutputPath)App.config" Transform="App.$(Configuration).config" />
<ItemGroup>
<Content Include="$(OutputPath)App.config" />
</ItemGroup>
</Target>
<Target Name="BeforeCompile" Condition="!Exists('App.$(Configuration).config')">
<Message Importance="high" Text="Using default 'App.config' as output config file..." />
<Copy SourceFiles="App.config" DestinationFiles="$(OutputPath)App.config" />
<ItemGroup>
<Content Include="$(OutputPath)App.config" />
</ItemGroup>
</Target>
This code generates the correct output file for the correct configuration, however it is never included in the XAP file, even though I am putting the output config into the Content item group. All I need to happen is for the output config to get included in the output XAP but I can't get this to happen.
Can anyone tell me what I'm doing wrong? I'm not an MSBuild expert by any means!
Found the solution by digging into the Silverlight 4 targets. Turns out the XAP packager target actually takes an item called XapFilesInputCollection, which is where the input files come from. The Content item looks likes it is copied to this item before my target runs, so modifying the Content item afterwards is the wrong approach.
All I did was add the transformed files directly to the XapFilesInputCollection item and it worked as I expected.
How to specify additional assembly reference paths for the MSBuild tasks?
I have following script so far, but can't figure out how to specify additional search paths.
<ItemGroup>
<ProjectsToBuild Include="..\Main\Main.sln" />
</ItemGroup>
<!-- The follwing paths should be added to reference search paths for the build tasks -->
<ItemGroup>
<MyAddRefPath Include="$(MSBuildProjectDirectory)\..\..\Build\Lib1" />
<MyAddRefPath Include="$(MSBuildProjectDirectory)\..\..\Build\Lib2" />
</ItemGroup>
<MSBuild
Projects="#(ProjectsToBuild)"
Properties="Configuration=Debug;OutputPath=$(BuildOutputPath)">
</MSBuild>
UPDATE:
Please show one complete working script which invokes original project, such as an SLN with multiple additional reference paths.
No suggestions on how to improve the project structure please.
I know how to build a good structure, but now it's the task of building an existing piece of crap.
I have finaly figured out how to do it:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectsToBuild Include="ConsoleApplication1\ConsoleApplication1.csproj" />
</ItemGroup>
<ItemGroup>
<AdditionalReferencePaths Include="..\Build\ClassLibrary1" />
<AdditionalReferencePaths Include="..\Build\ClassLibrary2" />
</ItemGroup>
<PropertyGroup>
<BuildOutputPath>..\Build\ConsoleApplication1</BuildOutputPath>
</PropertyGroup>
<Target Name="MainBuild">
<PropertyGroup>
<AdditionalReferencePathsProp>#(AdditionalReferencePaths)</AdditionalReferencePathsProp>
</PropertyGroup>
<MSBuild
Projects="ConsoleApplication1\ConsoleApplication1.csproj"
Properties="ReferencePath=$(AdditionalReferencePathsProp);OutputPath=$(BuildOutputPath)"
>
</MSBuild>
</Target>
The property you want to modify is AssemblySearchPaths. See the ResolveAssemblyReference task more information.
<Target Name="AddToSearchPaths">
<CreateProperty Value="x:\path\to\assemblies;$(AssemblySearchPaths)">
<Output PropertyName="AssemblySearchPaths" TaskParameter="Value" />
</CreateProperty>
</Target>
Making use of item groups, as in your example, it would look like:
<Target Name="AddToSearchPaths">
<CreateProperty Value="#(MyAddRefPath);$(AssemblySearchPaths)">
<Output PropertyName="AssemblySearchPaths" TaskParameter="Value" />
</CreateProperty>
</Target>
Looking in %WINDIR%\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets, you can see that the ResolveAssemblyReference Task is executed as part of the ResolveAssemblyReferences target. Thus, you want the newly added target to modify the AssemblySearchPaths property before ResolveAssemblyReferences is executed.
You've stated that you want to be able to modify the assembly search paths without modifying the project files directly. In order to accomplish that requirement you need to set an environment variable that will override the AssemblySearchPaths. With this technique you will need to provide every assembly reference path used by all the projects in the solutions. (Modifying the projects or copies of the projects would be easier. See final comments.)
One technique is to create a batch file that runs your script at sets the environment variable:
set AssemblySearchPaths="C:\Tacos;C:\Burritos;C:\Chalupas"
msbuild whatever.msbuild
Another way is to define a PropertyGroup in your custom msbuild file (otherwise known as the "hook" needed to make this work):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectsToBuild Include="..\Main\Main.sln" />
</ItemGroup>
<PropertyGroup>
<AssemblySearchPaths>$(MSBuildProjectDirectory)\..\..\Build\Lib1;$(MSBuildProjectDirectory)\..\..\Build\Lib2</AssemblySearchPaths>
</PropertyGroup>
<Target Name="Build">
<MSBuild Projects="#(ProjectsToBuild)" Properties="AssemblySearchPaths=$(AssemblySearchPaths);Configuration=Debug;OutputPath=$(OutputPath)" />
</Target>
</Project>
Now if it were me, and for whatever unexplained reason I couldn't modify the project files to include the updated references that I am going to build with, I would make copies of the project files, load them into the IDE, and correct the references in my copies. Synching the projects becomes a simple diff/merge operation which is automatic with modern tools like mercurial (heck I'm sure clearcase could manage it too).
...and remember that you don't need to use a target for this, you can use project-scoped properties or items, as...
<ItemGroup>
<MyAddRefPath Include="$(MSBuildProjectDirectory)\..\..\Build\Lib1" />
<MyAddRefPath Include="$(MSBuildProjectDirectory)\..\..\Build\Lib2" />
</ItemGroup>
<PropertyGroup>
<MyAddRefPath>$(MSBuildProjectDirectory)\..\..\Build\Lib3</MyAddRefPath>
<!-- add in the property path -->
<AssemblySearchPaths>$(MyAddRefPath);$(AssemblySearchPaths)</AssemblySearchPaths>
<!-- add in the item paths -->
<AssemblySearchPaths>#(MyAddRefPath);$(AssemblySearchPaths)</AssemblySearchPaths>
</PropertyGroup>
...and if you do need to do this in a target to pick up paths from a dynamically populated item group, use inline properties, not the CreateProperty task (if you are not stuck in v2.0)
<Target Name="AddToSearchPaths">
<PropertyGroup>
<!-- add in the item paths -->
<AssemblySearchPaths>#(MyDynamicAddRefPath);$(AssemblySearchPaths)</AssemblySearchPaths>
</PropertyGroup>
</Target>