Check if directory is empty using MSBuild - msbuild

Is there a convenient way to check if a directory is empty using MSBuild?

Create an item and see if it contains anything, like this:
<Target Name="CheckDirectoryForEmpty">
<PropertyGroup>
<EmptyCheck>./PathTo/DirectoryToCheck/*.*</EmptyCheck>
</PropertyGroup>
<ItemGroup>
<EmptyCheck Include="$(EmptyCheck)" />
</ItemGroup>
<Message
Condition="'#(EmptyCheck)' == ''"
Text="Directory '$(EmptyCheck)' is empty"
/>
</Target>
To check recursively, use **/*.* instead of *.* in the path.

You could use one of the tasks in the MSBuild Extension Pack for this. Use the FindUnder task, and check whether FoundItems is empty.

Related

wixproj/msbuild program creating automatically unwanted folder in the solution tree

I want to remove folder from my deployment folder.
I'm using removedir task.
<ItemGroup>
<FolderToExcludefromDeploymentFolder Include="$(SourceDir)\Support" />
</ItemGroup>
<Target Name="BeforeBuild" BeforeTargets="Build">
<RemoveDir Condition="Exists('$(sourceDir)\Support')" Directories="#(FolderToExcludefromDeploymentFolder)" />
</Target>
In that case i get a folder in the solution tree.
In case of trying to delete the folder i get this error and the itemgroup element will automatically will be removed.
How can i fix this?
The only way I could fix this issue is to move all problematic properties to BeforeBuild target and set them as DefineConstants.
<Target Name="BeforeBuild">
<PropertyGroup>
<SourceDir>$(MSBuildProjectDirectory)..\..\..\Example</SourceDi>
<FolderToExclude>$(MSBuildProjectDirectory)..\..\..\Example\Support</FolderToExclude>
<DefineConstants>$(DefineConstants);SourceDir=$(SourceDir);FolderToExclude=$(FolderToExclude)</DefineConstants>
</PropertyGroup>
<RemoveDir Condition="Exists('$(sourceDir)\Support')" Directories="#(FolderToExclude)" />
</Target>
This solution worked for me but didn't find the reason behind this.

Msbuild - build with output in diffrent folders

I have right now this in a script and want to have it in a msbuild instead.
msbuild /t:Build;PipelinePreDeployCopyAllFilesToOneFolder XXXXX\XXXX.XXX.xxx\XXXXX.XXXX.XXXXX1.csproj /p:Configuration="Release";_PackageTempDir=....\Deploy\XXXX1
msbuild /t:Build;PipelinePreDeployCopyAllFilesToOneFolder XXXXX\XXXX.XXX.xxx\XXXXX.XXXX.XXXXX2.csproj /p:Configuration="Release";_PackageTempDir=....\Deploy\XXXX2
How would this be written in a msbuild script? I only have managed to do it with one build but to create diffrent folders in the Deploy folder I havent been able to do.
Could someone teach me?
You can specify OutDir (new projects) or OutputPath (some old project types) as Properties when you call msbuild task to build your project. Or whatever property you want, like your "_PackageTempDir"
Something like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="BatchMyProjects" ToolsVersion="4.0">
<ItemGroup>
<BuildMyProjects Include="XXXX-Project-1" />
<BuildMyProjects Include="XXXX-Project-2" />
</ItemGroup>
<Target Name="BatchMyProjects" >
<ItemGroup>
<ProjectsToBuild Condition="Exists('SomeSourcePath\%(BuildMyProjects.Identity)/%(BuildMyProjects.Identity).csproj')">
<ProjectName>SomeSourcePath/%(BuildMyProjects.Identity)/%(BuildMyProjects.Identity).csproj</ProjectName>
<PublishSubFolder>%(BuildMyProjects.Identity)</PublishSubFolder>
</ProjectsToBuild>
</ItemGroup>
<MSBuild Projects="%(ProjectsToBuild.ProjectName)" Targets="Build;PipelinePreDeployCopyAllFilesToOneFolder"
Properties="Configuration=Release;
OutDir=SomePathToDeploy/Deploy/%(ProjectsToBuild.PublishSubFolder)/;
OutputPath= SomePathToDeploy/Deploy/%(ProjectsToBuild.PublishSubFolder)/;
_PackageTempDir=SomePathToDeploy/Deploy/%(ProjectsToBuild.PublishSubFolder)/
" />
</Target>
</Project>
Also I created gist for this same example
This do work but that seems abit strange. I rateher have it to work as you mentioned
<Target Name="testar" >
<MSBuild Projects="..\xxxxxx\xxxxxx1.csproj" Targets="Build;PipelinePreDeployCopyAllFilesToOneFolder" Properties="Configuration=Release;_PackageTempDir=../../Deploy/xxx1/"/>
<MSBuild Projects="..\xxxx\xxxxxx2.csproj" Targets="Build;PipelinePreDeployCopyAllFilesToOneFolder" Properties="Configuration=Release;_PackageTempDir=../../Deploy/xxx2/"/>
</Target>

verify the existence of a folder using the msbuild extension pack?

How can I dependably verify the existence of a folder using an msbuild extension pack task?
How could i do it without throwing an error and stopping the build?
Could you use the Exists condition on a target?
This will execute the OnlyIfExists target only if there is a directory or file called Testing in the same directory as the msbuild file.
<ItemGroup>
<TestPath Include="Testing" />
</ItemGroup>
<Target Name="OnlyIfExists" Condition="Exists(#(TestPath))">
<Message Text="This ran!" Importance="high" />
</Target>
There is no need to use the extension pack, MSBuild can handle this just fine. You need to consider whether this is a folder that might be created or deleted as part of the build. If it is, then you want to be sure to use a dynamic item group declared within a target (in the case of checking more than one folder) or you can use a path if just checking one. This example shows both:
<Target Name="MyTarget">
<!-- single folder with property -->
<PropertyGroup>
<_CheckOne>./Folder1</_CheckOne>
<_CheckOneExistsOrNot
Condition="Exists('$(_CheckOne)')">exists</_CheckOneExistsOrNot>
<_CheckOneExistsOrNot
Condition="!Exists('$(_CheckOne)')">doesn't exist</_CheckOneExistsOrNot>
</PropertyGroup>
<Message
Text="The folder $(_CheckOne) $(_CheckOneExistsOrNot)"
/>
<!-- multiple folders with items -->
<ItemGroup>
<_CheckMultiple Include="./Folder2" />
<_CheckMultiple Include="./Folder3" />
</ItemGroup>
<Message
Condition="Exists('%(_CheckMultiple.Identity)')"
Text="The folder %(_CheckMultiple.Identity) exists"
/>
<Message
Condition="!Exists('%(_CheckMultiple.Identity)')"
Text="The folder %(_CheckMultiple.Identity) does not exist"
/>
</Target>

MSBuild clean directory string

I have a property in MSBuild to represent the directory above the MSBuildProjectDirectory:
<PropertyGroup>
<BuildDir>$(MSBuildProjectDirectory)\..</PRSBuildDir>
</PropertyGroup>
I need to then use this property, but I need the directory string cleaned so that it doesn't include the ... In other words I need the .. evaluated, so that if the current project file is in C:\Test\Tom\MyDir, then I need a property containing the string C:\Test\Tom.
The reason I'm asking is because I'm trying to run a command like this:
msiexec /passive /i "D:\Build\2.3.84.40394\Deployment\..\Vendor\LogParser.msi"
But it's complaining about the path to the msi: This installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package.
There's a ConvertToAbsolutePath task, that any use?
The best method I've got right now is below, but I was wondering if there might be a better way..
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildDir>$(MSBuildProjectDirectory)\..</BuildDir>
</PropertyGroup>
<Target Name="Test">
<ItemGroup>
<CleanBuildDir Include="$(BuildDir)" />
</ItemGroup>
<PropertyGroup>
<BuildDir>%(CleanBuildDir.FullPath)</BuildDir>
</PropertyGroup>
<Message Text="$(BuildDir)" />
</Target>
</Project>
If you want to have wildcard evaluated, you should use Item instead of Property.
<ItemGroup>
<BuildDir Include="$(MSBuildProjectDirectory)\.."/>
</ItemGroup>
<Target Name="ExecMSIExec">
<Exec Command="msiexec /passive /i %(BuildDir.FullPath)\Vendor\LogParser.msi"/>
</Target>
(deleted my answer as I didn't see that Tom had answered in exactly the same way!)
By the way, why don't you set the "WorkingDirectory" attribute of the Exec task where you actually call msiexec to be the location of your MSI - that way you won't run into an path length issues

How do I select all read-only files with msbuild?

I'm trying to write an MsBuild script to zip some files up. I need to select all of the read-only files recursively from a folder into an ItemGroup to add to the zip.
I'm using the community tasks Zip task, but am struggling with selecting files based on their attributes.
Is there anything around to do this out of the box, or do I need to write a custom task?
Thanks for you help.
You can use Property Functions (added to msbuild 4) to figure out if a file is read-only like so:
<ItemGroup>
<MyFiles Include="Testing\*.*" >
<ReadOnly Condition='1 == $([MSBuild]::BitwiseAnd(1, $([System.IO.File]::GetAttributes("%(Identity)"))))'>True</ReadOnly>
</MyFiles>
</ItemGroup>
<Target Name="Run" Outputs="%(MyFiles.Identity)">
<Message Text="%(MyFiles.Identity)" Condition="%(MyFiles.ReadOnly) != True"/>
<Message Text="%(MyFiles.Identity) ReadOnly" Condition="%(MyFiles.ReadOnly) == True" />
</Target>
Have you looked at the community build tasks site?
It has a zip task and an attribute change task - they should get you most of they way there.
This seems to do the job with a bit of dirty command line usage.
<Exec Command="dir .\RelPath\ToFolder\ToSearchIn /S /AR /B > readonlyfiles.temp.txt"/>
<ReadLinesFromFile File="readonlyfiles.temp.txt">
<Output TaskParameter="Lines" ItemName="ReadOnlyFiles"/>
</ReadLinesFromFile>
<Delete Files="readonlyfiles.temp.txt"/>
That gives absolute paths to the files.
To get relative paths, try something like this:
<Exec Command="dir .\RelPath\ToFolder\ToSearchIn /S /AR /B > readonlyfiles.temp.txt"/>
<FileUpdate Files="readonlyfiles.temp.txt"
Multiline="True"
Regex="^.*\\RelPath\\ToFolder\\ToSearchIn"
ReplacementText="RelPath\ToFolder\ToSearchIn"
/>
<ReadLinesFromFile File="readonlyfiles.temp.txt">
<Output TaskParameter="Lines" ItemName="ReadOnlyZipFiles"/>
</ReadLinesFromFile>
<Delete Files="readonlyfiles.temp.txt"/>