Simple way to enumerate the files in a fileset in phing - phing

I am new to phing and trying to verify if my build.xml works as expected. I am looking for a convenient way to enumerate the files in a phing fileset.
The only thing that I've been able to get working is foreach (like in how to iterate (loop) through directories in phing?). However, it feels way too complex: I have to create a subtask, and phing gets called once for every file, making the outupt list hard to parse visually.
Any better alternatives? Thanks!

With Phing 2.4.8, the <echo> task supports filesets:
http://www.phing.info/trac/ticket/792

There is currently no better way. You could grep the output, though :)
<task name="dummy">
<foreach param="filename" absparam="absfilename" target="echoFilesetFile">
<fileset refid="co"/>
</foreach>
</task>
<target name="echoFilesetFile">
<echo>file: rel:${filename}|abs:${absfilename}</echo>
</target>
then $ phing dummy | grep 'file:'

Related

Conditional <exec> based on results of <copy>

In our nant build script for our web-based application, we <copy> a set of files to a target directory and then run aspnet_compiler over them via <exec>.
<copy> only copies files that have changed, however here is no way to pass this information to <exec>, and I want to avoid running aspnet_compiler when nothing has actually changed.
Options I've tried to find are: <copy> setting a property when any file is copied that can then be checked with <if>; or being able to create a file before the copy and doing something like <if test="any-file-newer-than(targetdir, timestampfile)">. Even better would be if <copy> could return a list of copied files that I can then iterate over to avoid having to process the entire tree, but I think that might be asking a bit too much.
So far, I've drawn a blank: is what I'm looking for possible without writing a custom extension?
Why don't you just simply replace copy task with robocopy? (you're on Windows, right?)
Robocopy returns different exit codes on different successful copy situations:
https://ss64.com/nt/robocopy-exit.html
For example:
0 - ok, nothing copied
1 - ok, something copied
You could do something like this:
<exec program="robocopy.exe" commandline="${SourceDir} ${DestDir}" failonerror="false" resultproperty="ExitCode" />
<fail unless="${ExitCode < 8}" message="Failed to copy"/> <!-- Anything between 0..7 is OK for robocopy -->
<exec unless="${ExitCode == 0}" ...

How to diff two files in NAnt

How can I diff 2 text files using NAnt to produce a file containing the differences?
I suggest you to use <exec> task and call cmd with fc command that compares files. It will look something like that
<exec program="cmd.exe" commandline="/C fc file1 file2" />
You can see fc manpage here

Running perl script on multiple files using Ant

I have a set of files that I want to run a perl script on. The problem is they are not in the same directory. In fact they are all part of an ordered but complex directory hierarchy.
I wrote a Bash script that does what I want:
if [ ${HOME:+1} ]; then
for A in ${HOME}/a/b/*; do
for B in ${A}/c/*; do
for C in ${B}/*; do
if [ -f $C/somefile ]; then
some_perl_script $C/somefile;
fi
done;
done;
done;
fi
So that works, and it's not too complicated. However, I need that functionality to be available using Ant. Of course I could just call that script from Ant, but I want to know if there is anyway of having Ant do it in the first place, which seems like a more straightforward way of doings things. Essentially, I want something similar to the following build.xml:
<project name="application" basedir=".">
<target name="apply_to_all">
<exec executable="perl" dir="${basedir}">
<arg value="some_perl_script.pl"/>
<arg value="a/b/**/c/**/**/somefile"/>
</exec>
</target>
</project>
I found this SO post. However, I'd like to do it without ant-contrib.
Use the apply task to invoke your perlscript on all files that are included in the nested fileset(s) :
<apply executable="perl">
<arg value="-your args.."/>
<fileset dir="/some/dir">
<patternset>
<include name="**/*.pl"/>
</patternset>
</fileset>
<fileset refid="other.files"/>
</apply>
Optionally use the attribute parallel = true, means run the command only once, appending all files as arguments. If false, command will be executed once for every file, default =false => see Ant manual on apply
You may use include / exclude patterns and one or more filesets. F.e. you may use **/*.pl for
all your perl scripts.

MSDeploy runCommand using relative path

I am trying to run a command as a part of my packaging/deployment process via MSDeploy. In particular, I am trying to create a custom event log by running installutil against one of my DLLs, but I am having trouble with specifying a relative path to the DLL from the deployment directory. To get started, I added the below config to my csproj in order to generate the runCommand provider inside of my Manifest file. Please note the absolute path to the DLL.
<PropertyGroup>
<!-- Extends the AfterAddIisSettingAndFileContentsToSourceManifest action to create Custom Event Log -->
<IncludeEventLogCreation>TRUE</IncludeEventLogCreation>
<AfterAddIisSettingAndFileContentsToSourceManifest Condition="'$(AfterAddIisSettingAndFileContentsToSourceManifest)'==''">
$(AfterAddIisSettingAndFileContentsToSourceManifest);
CreateEventLog;
</AfterAddIisSettingAndFileContentsToSourceManifest>
</PropertyGroup>
<Target Name="CreateEventLog" Condition="'$(IncludeEventLogCreation)'=='TRUE'">
<Message Text="Creating Event Log" />
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>installutil C:\inetpub\wwwroot\MyTestApp\bin\BusinessLayer.dll</path>
</MsDeploySourceManifest>
</ItemGroup>
</Target>
<ItemGroup>
After calling msbuild, this generated my manifest correctly inside of my package.zip. When I ran MyTestApp.deploy.cmd /Y it correctly called msdeploy and deployed my files to inetpub\wwwroot\MyTestApp and ran my command from the manifest below:
<runCommand path="installutil C:\inetpub\wwwroot\MyTestApp\bin\BusinessLayer.dll ... etc
The problem I am having is I do not want to hardcode this DLL path to c:\inetpub\etc. How can I make the above call using the relative path from my deployment directory under Default Web Site? Ideally, I would like MSDeploy to take this path and pass it as a variable to the runCommand statement in order to find the DLL. Then I could write something like: <path>installutil $DeploymentDir\NewTestApp\bin\BusinessLayer.dll</path> without having to worry about hard-coding an absolute path in.
Is there any way to do this without using the absolute path to my DLL every time?
You can add definition of DeploymentDir to the .csproj with the action you wrote above:
<PropertyGroup>
<DeploymentDir Condition="'$(Configuration)'=='Release' AND '$(DeploymentDir)'==''">Release Deployment Dir</DeploymentDir>
<DeploymentDir Condition="'$(Configuration)'=='Debug' AND '$(DeploymentDir)'==''">Debug Deployment Dir</DeploymentDir>
<DeploymentDir Condition="'$(DeploymentDir)'==''">C:\inetpub\wwwroot</DeploymentDir>
<AplicationName Condition="'$(Configuration)'=='Release' AND '$(AplicationName)'==''">NewTestApp</AplicationName>
<AplicationName Condition="'$(Configuration)'=='Debug' AND '$(AplicationName)'==''">MyTestApp</AplicationName>
<ApplicationDeploymentDir Condition="'$(ApplicationDeploymentDir)'==''">$(DeploymentDir)\$(ApplicationName)\bin</ApplicationDeploymentDir>
</PropertyGroup>
Theese conditions will allow to change everything from command line to take full control over the build process in your build system or script.
MSBuild.exe yourproj.proj /p:Configuration=Release /p:DeploymentDir=D:\package /p:ApplivationName=BestAppForever
And inside of your task you can use it
<ItemGroup>
<MsDeploySourceManifest Include="runCommand">
<path>installutil $(ApplicationDeploymentDir)\BusinessLayer.dll</path>
</MsDeploySourceManifest>
</ItemGroup>
I realize this isn't the answer you probably wanted to hear but this is how I got around it.
We created a powershell script on the destination server. So instead of running your command:
installutil C:\inetpub\wwwroot\MyTestApp\bin\BusinessLayer.dll ... etc
We would run:
c:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe d:\powershell\installSites.ps1 siteName <NUL
The "sitename" is being passed in as a param into the powershell script. Inside the script it knows on that destination server which files to install, any commands that need to run, app pools to recycle, etc.
Again, not as easy as finding a relative path, but it does the job.

Nant cmd.exe redirection creating file called 'program' on c:\ drive

I have NAnt script which as part of its project calls a batch file using the following task:
<target name="makeplane">
<exec program="C:\WINDOWS\system32\CMD.EXE"
commandline="/C ${make.file} > ${make.log}"
verbose="false"
workingdir="${make.dir}"
basedir="${make.dir}">
</exec>
<delete>
<fileset basedir="c:\">
<include name="program" />
</fileset>
</delete>
</target>
Unfortunately i dont have control over the contents on the batch file and it spews out a lot of garbage onto the screen which is of no use in the log. So to get around this im redirecting the output from the bat file to a text file using the
> ${make.log}
part which equates to "> log.txt".
This redirection seems to create a file called "program" on the C drive and messes up all sorts of services and windows generally doesnt like it. To get around this Im manually deleting this file after the bat file has executed.
The problem is i now need to run a similar task for another project entirely and if they run at the same time then the first will lock the file called "program" and the second will fail. Not exactly a great situation for Continuous integration.
I searched on the net but because the file is called program i get all sorts of rubbish results. Anyone got any ideas on a work around. I tried the output parameter on the exec task but the issue remains the same.
If the file path to the log contains spaces, one generally would want to surround the path in quotes. In order to do this in nant one can use the " entity.
It sounds like this is what's happening in your particular situation. Therefore, if you change your example to the following I think things should work as expected.
<target name="makeplane">
<exec program="C:\WINDOWS\system32\CMD.EXE"
commandline="/C ${make.file} > "${make.log}""
verbose="false"
workingdir="${make.dir}"
basedir="${make.dir}">
</exec>
</target>
Usually this happens because the script is trying to create a file with a long file name with space in it (c:\program files in your case), but it is not using quotes around the long file name.
Here is what I did. I think it is a bit cleaner for complex commands.
<property name="cmd.label" value="\${ss.previous.label}#$Project.SSPath" />
<echo message="Getting $Project.Name source code with label \${cmd.label}" />
<property name="cmd" value=""\${tfs.root}\tf.exe" get $Project.SSPath "/version:L\${cmd.label}" /force /recursive /noprompt"/>
<exec program="cmd.exe"
workingdir="\${shadow.dir}"
failonerror="true"
verbose="true">
<arg value="/c" />
<arg value=""\${cmd}"" />
<arg value="> nul" />
</exec>