Using MSBuild, how to expand wildcard files as one single line - msbuild

Using MSBuild, after building an exe, I want to run it with a list of files coming from a wildcard expression.
I currently use something like this:
<Target Name="AfterBuild">
<ItemGroup>
<SigFiles Include="$(RootDir)tables\*.xml"/>
</ItemGroup>
<Exec Command="$(OutDir)app.exe --merge %(SigFiles.FullPath) -o $(OutDir)model.xml"/>
</Target>
The problem is that the command is executed once per file, using one file per command. With 3 .xml files, for instance, the following commands are run (paths omitted):
app.exe --merge a.xml -o model.xml
app.exe --merge b.xml -o model.xml
app.exe --merge c.xml -o model.xml
While I would like to run one single command with all files, blank separated:
app.exe --merge a.xml b.xml c.xml -o model.xml
How could I achieve this? No need to mention that I am not an MSBuild expert.
Thanks

When the '%' is used as you have it, a feature of MSBuild known as 'batching' is invoked. As you noted, the task is run multiple times, once for each item of the batch.
To reference the whole collection use '#'. The default delimiter between items is the semi-colon (;).
As an example, the following will display something like
a.xml;b.xml;c.xml
<Message Text="#(SigFiles)" />
A different delimiter can be specified. The following example uses a space.
<Message Text="#(SigFiles, ' ')" />
An item can also be 'transformed'. Your code is using FullPath. The following is not batching. The '%' is part of the transform expression.
<Message Text="#(SigFiles->'%(FullPath)')" />
Putting the transform and the delimiter together:
<Message Text="#(SigFiles->'%(FullPath)', ' ')" />
Taking the syntax we have built up and applying it to your original code:
<Exec Command="$(OutDir)app.exe --merge #(SigFiles->'%(FullPath)', ' ') -o $(OutDir)model.xml"/>

Related

Zipping files in a folder MSBuild

A folder named Test_UL contains files like Test_file_UL.exe, Test_file_UL.iso and Test_file_UL.txt.
I need to zip the Test_file_UL.exe and Test_file_UL.iso files to Test.zip, excluding Test_file_UL.txt.
How can I do this using MSBuild?
You can do this by creating an ItemGroup for the items you want, and then passing that item group to a powershell cmdlet.
<ItemGroup>
<ZipFiles Include="Test_UL\Test_file_UL.exe" />
<ZipFiles Include="Test_UL\Test_file_UL.iso" />
</ItemGroup>
All the usual rules for item groups apply, so you can do whatever filtering you need.
Then invoke powershell to zip the files:
<Target Name="CreateZip">
<Exec Command="PowerShell -NoProfile -Command Compress-Archive #(ZipFiles->'%(Identity)', ',') -DestinationPath $(ZipName)" />
</Target>
Compress-Archive is a powershell cmdlet available in PowerShell
5 onwards.
#(ZipFiles->'%(Identity)', ',') is an MSBuild Transform,
changing the files into a comma separated list.
There are also MSBuild extensions that add a zip/compress target, but I've always found it easier to just quickly call powershell.

How can I hide the command I'm using in an MSBuild Exec task from console output?

I've got a task in my MSBuild file like so:
<Exec command="net use $(DeploymentServerName) /user:username passwd" ContinueOnError="false" />
But in the console output it will dump the command:
...
net use $(DeploymentServerName) /user:username passwd
...
But I'd like to hide the credentials, if possible. I don't care about where the output of the command goes, I just care that the command itself is not echo'd to the console. Any ideas?
Starting with .NET 4.0 Exec MSBuild task got property EchoOFF which allows to achieve exactly that - suppress echoing of the command itself (not the command output). Full documentation is here. Just add EchoOff="true" to the list of Exec properties.
There are a couple of possible approaches, here is one
<Target Name="DoHideCommand">
<Exec Command="MSBuild $(MsBuildThisFile) /t:SpecialCommand /nologo /noconsolelogger"/>
</Target>
<PropertyGroup>
<MyCommand>dir c:</MyCommand>
</PropertyGroup>
<Target Name="SpecialCommand">
<Exec Command="dir"/>
</Target>
This invokes a seperate msbuild process to invoke the actual target, and hides all output resulting in
...
DoHideCommand:
MSBuild test.targets /t:SpecialCommand /nologo /noconsolelogger
...
And here is another one
<Target Name="SpecialCommandViaFile">
<PropertyGroup>
<TmpFile>tmp.bat</TmpFile>
</PropertyGroup>
<WriteLinesToFile File="$(TmpFile)" Lines="$(MyCommand)"/>
<Exec Command="$(TmpFile) > NUL 2>&1" WorkingDirectory="$(MsBuildThisFileDirectory)"/>
<Delete Files="$(TmpFile)"/>
</Target>
This creates a batch file to run the actual command, then redirects all output to NUL so only this is shown:
...
SpecialCommandViaFile:
tmp.bat > NUL 2>&1
Deleting file "tmp.bat".
...
Note though that the one executing your msbuild file can always simply open your file to look at the credentials, even if they are hidden from the output when running it.

USe MSBuild properties in windows batch command line?

defined some msbuild properties, is it possible to 'promote' it to be used in the windows batch file? i.e. in my .bat file write something 'echo %CustomProperty%' where the CustomProperty is defined in the msbuild script
Accessing MSbuild properties within a batch file doesn't seem to be the correct release approach and does not seem necessary. The same facility is easily available within Msbuild itself. In your example case you can use something like the following:
<Message Text="Copying $(ZipFile) to $(PublicFolderToDropZip)" Importance="high" />
to achieve what you were looking.
You can even run batch files in the following manner:
<Target Name="Default">
<Exec Command="CALL mybatch.cmd" />
</Target>

How do I run a jar file with msbuild?

I'm trying to make Visual Studio do my entire build, currently I've got my extra build steps written in nant. But it's not ideal having to run ant separately.
I'm trying to run a jar file named plovr as part of my node application, although at the moment publish keeps failing on the line I've added to my build with exit code 1. This is the code I'm trying at the end of my build file within the <Project></Projet> tags.
<Target Name="Build">
<Exec Command="java -jar $(Plovr) build $(PlovrConfig)" />
</Target>
I've got these properties setup earlier in the file
<Plovr>dependencies\plovr.jar</Plovr>
<PlovrConfig>dependencies\plovr-config.js</PlovrConfig>
How can I get msbuild to run the plovr.jar?
If you put in the full path to java.exe.....the EXEC command should work.
It just does a command line call....at the end of the day.
<Target Name="Build">
<Exec Command=""C:\Program Files (x86)\Java\jre7\bin\java.exe" -jar $(Plovr) build $(PlovrConfig)" />
</Target>
Also note the use of " .. to delimit a quote...
You can also put in some Message's to make sure you have what you think you have:
<Message Text="Plovr: $(Plovr)"/>
<Message Text="PlovrConfig: $(PlovrConfig)"/>

How do I implicitly use a Target to generate an Included file?

I'm a make(1) guy by nature. What I want to do is the equivalent of this make fragment:
FILES = generated.cs
app.exe : $(FILES)
csc -out:$# $(FILES)
generated.cs :
echo "// generated file" > $#
That is, $(FILES) contains a list of files, some of which may be generated by other targets within the Makefile. This Just Works.
I would like to do the ~same thing with MSBuild. Unfortunately, my attempt has failed:
<Target Name="BuildGenerated"
Outputs="Generated.cs"
>
<WriteLinesToFile
File="Generated.cs"
Lines="// generated file"
Overwrite="True"
/>
</Target>
<ItemGroup>
<Compile Include="Generated.cs" />
</ItemGroup>
That is, use <Compile/> to include a generated file and have MSBuild deduce that since Generated.cs doesn't exist, it should find some <Target/> which will generate that file and then execute it.
It looks like the only way to do something like this is to add pre-build steps, but that seems like a hack. Are pre-build steps the only way to do this? Or is there some way to make MSBuild act like it has a brain?
Update 1: For reference, this would be the pre-build incantation needed to make it work, and (again) this is something I'd rather avoid if possible:
<PropertyGroup>
<CompileDependsOn>
BuildGenerated;$(CompileDependsOn)
</CompileDependsOn>
</PropertyGroup>
This would need to occur after the <Import Project="..." /> element(s) defining <CompileDependsOn/>.
The problem is that your file is a generated one and MsBuild parses the item and property group only once (at the beginning of your build). Thus, when MsBuild tries to include the generated.cs file, it is not created yet and MsBuild does include nothing at all.
A correct way of doing it would be to do the include part inside the BuildGenerated target which would cause it to be dynamically evaluated.
<Target Name="BuildGenerated"
Outputs="Generated.cs"
>
<WriteLinesToFile
File="Generated.cs"
Lines="// generated file"
Overwrite="True"
/>
<ItemGroup>
<Compile Include="Generated.cs" />
</ItemGroup>
</Target>
More information in my answer to this question : Bin folder not being copied with MSBuild, Teamcity
EDIT
Sorry, I did not read correctly your question. in your makefile, in the app.exe target (the default one) you explicitely call your generated.cs target. You can do the same with MsBuild.
By default, MsBuild search for the Build target (the 'all' in makefile), if your main target is "app.exe" you have to call BuildGenerated target within with one of the following option :
<Target Name="app.exe" DependsOnTargets="BuildGenerated>
<!-- Stuff here -->
</Target>
or
<Target Name="app.exe">
<CallTarget Targets="BuildGenerated"/>
<!-- Stuff here -->
</Target>
If you don't set a default target, you can do it either via commandline with the /t parameter or in the project declaration :
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="app.exe">