I'm trying to set the OutputPath value to an absolute path:
<OutputPath>c:\Projects\xxx\Deployment</OutputPath>
But I get this error:
Error 17 The expression "[System.IO.Path]::GetFullPath(D:\Projects\xxx\trunk\xxx.Web.Deployment\c:\Projects\xxx\Deployment\)" cannot be evaluated. The given path's format is not supported. 1 1 xxx.Web.Deployment
Is there a way to use an absolute path with the OutputPath property? I've tried experimenting with the BaseOutputPath property:
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Deployment|AnyCPU'">
<BaseOutputPath>C:\Projects\xxx\</BaseOutputPath>
<OutputPath>.\Deployment</OutputPath>
<EnableUpdateable>true</EnableUpdateable>
<UseMerge>true</UseMerge>
<SingleAssemblyName>xxx.Web.Deployment</SingleAssemblyName>
But it seems to get ignored. What are BaseOutputPath and BaseIntermediateOutputPath used for?
I'm not sure whether you can do what you're talking about, but you can add something similar to the following:
<PropertyGroup>
<CentralisedBinariesFolderLocation>c:\wherever</CentralisedBinariesFolderLocation>
</PropertyGroup>
<Target Name="AfterBuild">
<Exec Command="xcopy /Y /S /F /R "$(TargetPath)" "$(CentralisedBinariesFolderLocation)"" />
</Target>
Which will copy it to the relevant location after the build.
Try using OutDir instead of OutputPath :
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Deployment|AnyCPU'">
<OutDir>C:\Projects\xxx\$(Configuration)</OutDir>
<EnableUpdateable>true</EnableUpdateable>
<UseMerge>true</UseMerge>
<SingleAssemblyName>xxx.Web.Deployment</SingleAssemblyName>
</PropertyGroup>
Copy the .target and .dll files from the installer directory
Modify the lines at the top that look like <UsingTask TaskName="GetProjectProperties" AssemblyFile="../../ ..lallal/VisualStudio/v10.0/Microsoft.Web.Publishing.Tasks.dll"/> and c*opy those .target and .dll files to your vendors folder next to the copied Microsoft.WebDeployment.targets-file your are editing*. Set the attr, AssemblyFile="Microsoft.Web.Publishing.Tasks.dll"
Add the line <EnablePackageProcessLoggingAndAssert Condition="'$(EnablePackageProcessLoggingAndAssert)' == ''">True</EnablePackageProcessLoggingAndAssert> to the initial PropertyGroup.
Set the OutputPath as you wish in the actual file/other tagets/other build-proj file.
Edit line ~290 to <WebPublishPipelineProjectDirectory Condition="'$(WebPublishPipelineProjectDirectory)'==''">$(OutputPath)</WebPublishPipelineProjectDirectory>
Instead of all the steps in the October answer, is it not possible just to define WebPublishPipelineProjectDirectory with the same path as OutputPath?
I tried it in my CI solution (using CruiseControl) and it seemed to work.
Does anyone know of any side effects that are not apparent to me from doing this?
Related
I have a set of files I need to copy that are underneath a folder that contains the version of the package that deposited those files:
<ItemGroup>
<MyFiles Include="$(MyPackages)\foo.x64*\binaries\*.*"/>
</ItemGroup>
foo.x64* can resolve to more than one folder like foo.x64.17.5.50 and foo.x64.17.6.2 where the suffix represents the version of package. I want to filter my items based on that version but I can't seem to find a way to extract any part of the item file path as metadata on my item to then use it in batching or conditionals.
There are a few features of msbuild that can be used here. those are:
Defining custom item metadata. let's call themFooArch and FooVersion
MSBuild allows calling some .net APIs including regular expressions. So we can use RegEx.Match() here to extract the information you need to know.
When doing numeric comparison on strings, MSBuild parses them into Versions. This will allow you to do range conditions like '%(FooItems.FooVersion)' < '17.6.2'.
What's not possible out of the box is determining the highest version of a list of items. To achieve this, you'll need to write a custom msbuild task.
A more concrete example: To add the required metadata on the items and copy the right version and architecture, you can do the following:
<PropertyGroup>
<FooRegEx>[\\/]foo\.(?<arch>x(\d*))\.(?<version>((\d+)(\.\d+)+))</FooRegEx>
<FooVersionToUse>17.5.50</FooVersionToUse>
</PropertyGroup>
<ItemGroup>
<FooFiles Include="mypkgs\foo*\binaries\**\*">
<FooArch>$([System.Text.RegularExpressions.Regex]::Match(%(Identity), $(FooRegEx)).get_Groups().get_Item("arch"))</FooArch>
<FooVersion>$([System.Text.RegularExpressions.Regex]::Match(%(Identity), $(FooRegEx)).get_Groups().get_Item("version"))</FooVersion>
</FooFiles>
</ItemGroup>
<Target Name="IncludeFoo" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<FooArchToUse Condition="'$(FooArchToUse)' == '' and '$(Platform)' != 'AnyCPU'">$(Platform)</FooArchToUse>
<FooArchToUse Condition="'$(FooArchToUse)' == ''">x64</FooArchToUse>
</PropertyGroup>
<ItemGroup>
<Content Include="#(FooFiles)"
Link="foo\%(Filename)%(Extension)"
CopyToOutputDirectory="PreserveNewest"
Condition="'%(FooFiles.FooVersion)' == '$(FooVersionToUse)' and '%(FooFiles.FooArch)' == '$(FooArchToUse)'"/>
</ItemGroup>
</Target>
This will copy the items of the selected version and platform built for (defaulting to x64) into a foo subfolder of the output directory. (Tested on MSBuild 15.1). I've uploaded the example project to GitHub here.
You can you use the ItemGroup's Condition Attribute to filter out elements based on some boolean expression. You can use regex for instance as per this answer.
I have been attempting to use the zip task of msbuild in a project I am working on at the moment.
My project file looks something like this:
<PropertyGroup> <MSBuildCommunityTasksPath>$(SolutionDir)\.build</MSBuildCommunityTasksPath> </PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets" />
<ItemGroup>
<FileToZip include="C:\FilePath"></FilesToZip>
<FileToZip include="C:\FilePath"></FilesToZip>
</ItemGroup>
<Target Name="BeforeBuild">
<PropertyGroup>
<ReleasePath>\releasepath</ReleasePath>
<Zip Files="#(FilesToZip)" WorkingDirectory="$(ReleasePath)" ZipFileName="HTMLeditor.html" ZipLevel="9" />
</Target>
However, the zip file updates but does not contain the files specified in the item group FilesToZip. I cannot figure out why they aren't being recognised! I have double checked file paths and they are correct. Any ideas?
I think you want to do something like this:
<ItemGroup>
<FileToZip include="C:\FilePath;C:\FilePath"/>
</ItemGroup>
As I mentioned in my comment, simply creating a variable (FileToZip) and repeating it twice with different values does not give you an array that contains both of the values. You end up with only the last value (and not an array at all). Your include attribute is a selector which is used to build the array and it can contain multiple values, wildcards and other patterns which are used to build out that array for you.
Here's a link to MSDN that gives you more information on how to use the Include attribute: http://msdn.microsoft.com/en-us/library/ms171454.aspx
I ditched the ItemGroup in the end, and went with another way of doing it.
<Target Name="Zip">
<CreateItem Include="FilesToInclude" >
<Output ItemName="ZipFiles" TaskParameter="Include"/>
<Zip ZipFileName="ZipFile.zip" WorkingDirectory="FolderToWriteZipTo" Files="#(ZipFiles)" />
</Target>
This method seemed to be easier and wasn't adding files to the root of the file.
Thanks for the help though guys.
Below is a portion of a MSBuild file that I'm working on:
<ItemGroup>
<Tests Include="$(SolutionDir)\**\bin\$(TestPlatform)\$(Configuration)\*.Tests.dll" />
</ItemGroup>
<PropertyGroup>
<TestProperties>/testcontainer:%(Tests.FullPath)</TestProperties>
</PropertyGroup>
I want to have a property that holds a command line switch. However, when I try to use $(TestProperties) in an Exec Command string, %(Tests.FullPath) is never resolved to the absolute path of the Tests item. Instead, it's always processed literally, as "%(Tests.FullPath)".
Am I doing something wrong or is this a standard MSBuild behavior? If the latter, is there a way for me to workaround this?
Thanks
P.S. - I realize I probably don't need to access the FullPath property since my Include value is an absolute path. However, I'd still like to understand the issue, along with how to handle it.
You have a syntax error. Item lists are referenced via the # character and item meta data is referenced via %. Reference the MSBuild Special Character Reference for details. To access the well known item metadata, you need to apply a transform inside the Property itself.
<ItemGroup>
<Tests Include="MyFile.txt" />
</ItemGroup>
<PropertyGroup>
<TestProperties>/testcontainer:#(Tests->'%(FullPath)')</TestProperties>
</PropertyGroup>
You can find more help here
I have a test file in MSBuild to create a ZIP. I need exclude certain folders. I have the following working.
<PropertyGroup>
<TestZipPath>C:\path\to\my\folder\</TestZipPath>
<ExcludeList>$(TestZipPath)\**\_svn\**;$(TestZipPath)\**\.svn\**;$(TestZipPath)\**\obj\**;$(TestZipPath)\**\*.config</ExcludeList>
</PropertyGroup>
<ItemGroup>
<ZipFiles Include="$(TestZipPath)\**\*.*" Exclude="$(ExcludeList)" />
</ItemGroup>
<Message Text="%(ZipFiles.FullPath)"/>
That seems hideously verbose to me. Ideally I would want the ExcludeList to be formatted like this:
<ExcludeList>**\_svn\**;**\.svn\**;**\obj\**;**\*.config</ExcludeList>
But it doesn't seem to work. Why do I need to include $(TestZipPath) before every exclude pattern? Is ** not intended to be used at the beginning of a path? Is there a better way to do this?
I figured out the problem. The issue is that I am trying to include files that are not relative to the msbuild file that I'm executing. MSBuild assumes that file paths are relative to this location and gives you no way to change that. Because of this, all of my paths have to be absolute and can't be relative.
Try to add '.\' before every include pattern. Like this:
'.\**\obj\**'
I would like to run a task if any file in an item list is missing. How do I do that?
My current script has a list of "source" files #(MyComFiles) that I translate another list of "destination" files #(MyInteropLibs), using the following task:
<CombinePath BasePath="$(MyPath)\interop"
Paths="#(MyComFiles->'%(filename).%(extension)')">
<Output TaskParameter="CombinedPaths"
ItemName="MyInteropLibs" />
</CombinePath>
I want to check if any of the files in #(MyInteropLibs) is missing and run a task that will create them.
If you only need to create the missing files, and not get a list of the files that were missing you can you the touch task, which will create if the files don't exist.
<Touch Files="#(MyInteropLibs)" AlwaysCreate="True" />
If you only want to create the missing files, and avoid changing timestamps of the existing files, then batching can help
<Touch Files="%(MyInteropLibs.FullPath)" AlwaysCreate="True"
Condition=" ! Exists(%(MyInteropLibs.FullPath)) "/>
If you want a list of the files created then
<Touch Files="%(MyInteropLibs.FullPath)" AlwaysCreate="True"
Condition=" ! Exists(%(MyInteropLibs.FullPath)) ">
<Output TaskParameter="TouchedFiles" ItemName="CreatedFiles"/>
</Touch>
<Message Text="Created files = #(CreatedFiles)"/>
I am not very experienced with MSBuild so there may be better solutions than this but you could write a FilesExist task that takes the file list and passes each file to File.Exists returning true if they do exist and false otherwise and thenn react based on the result
Sorry I can't provide code to help out, my knowlege of MSBuild sytax is not strong
You can find out pretty easily using Exec.
To test if ALL of a set of files exists: The DOS FOR /D command accepts a semicolon-separated list of files - i.e. a flattened item array.
<!-- All exist -->
<Exec
Command="for /D %%i in (#(MyFiles)) do if not exist %%i exit 1"
IgnoreExitCode="true">
<Output TaskParameter="ExitCode" PropertyName="ExistExitCode"/>
</Exec>
To test if ANY of a set of files exists: The DOS DIR command accepts a semicolon-separated list of files. It sets the %ERRORLEVEL% to 0 if it finds any files in the list, nonzero if it finds none. (This is the simpler case, but it does not address the original question...)
<!-- Any exists -->
<Exec Command="dir /B #(MyFiles)" IgnoreExitCode="true">
<Output TaskParameter="ExitCode" PropertyName="DirExitCode"/>
</Exec>
Then most likely you will want to define a boolean property based on the output.
EDIT: BTW this is a code smell. Usually when you find yourself wanting to do this, it's an indication that you should set the Outputs property of the target so it will loop over the items.