URLEncode within NAnt - nant

Is there a way to URLEncode something in NAnt generally - specifically in an echo to a file? One of my build processes enumerates all the PDF output files in a folder and makes an index.html, but some of the PDF files have [] characters in them and they need to be URLEncoded to %5D/%5B in the hrefs
<echo append="false" file="${reportlayout.dir}\index.html"><html><head><title>Product ${modulename} Report PDFs</title><head><body></echo>
<foreach item="File" property="filename">
<in>
<items>
<include name="${reportlayout.dir}\${modulename}/*.pdf" />
</items>
</in>
<do>
<echo append="true" file="${reportlayout.dir}\index.html"><a href="${modulename}/${path::get-file-name(filename)}">${path::get-file-name(filename)}</a><br/></echo>
</do>
</foreach>
<echo append="true" file="${reportlayout.dir}\index.html"></body></html></echo>
This is where href="${modulename}/${path::get-file-name(filename)}" contains characters coming from the filename that need to be URLEncoded, but I can't find a function to do that in the NAnt function list and I don't know if there is a way to get it to call through to .NET HttpUtility.URLEncode or similar.

Related

Iterating file structure in Phing and compressing with YUICompressor

I have a directory structure like so:
-css
---subdir1
------common.css
---subdir2
------common.css
------custom.css
---subdir3
------common.css
------styles.css
I'm trying to loop each directory in Phing, and subsequently minify each file into a single hashed filename within each directory using the YUI compressor. The result would look something like this:
-css
---subdir1
------1973a613f7c87b03dbe589e6935a09bd.min.css
---subdir2
------1973a613f7c87b03dbe589e6935a09bd.min.css
---subdir3
------1973a613f7c87b03dbe589e6935a09bd.min.css
I therefore need to know each directory that I'm within so I can output my minified scripts to it.
These are my two targets:
<target name="minify">
<echo msg="Minifying CSS and JS files with YUI at ${yuicompressor}" />
<foreach param="filename" absparam="absfilename" target="runyui">
<fileset dir="${publicdir}/css">
<include name="*.css" />
<include name="**/*.css" />
</fileset>
</foreach>
</target>
<target name="runyui">
<filehash file="${abspathtopwd}" hashtype="MD5" propertyname="filehash" />
<echo msg="java -jar ${yuicompressor} -v --line-break 5000 --type css ${absfilename} >> ${abspathtopwd}/${filehash}.min.css" />
<exec command="java -jar ${yuicompressor} -v --line-break 5000 --type css ${absfilename} >> ${abspathtopwd}/${filehash}.min.css" />
</target>
Where:
yuicompressor is the path to the yui compressor jar
publicdir is just an absolute path to my applications public directory
abspathtopwd is the property I wish to use for the "current iteration's directory"
How can I get the current working directory (or pwd if you prefer) in the current foreach iteration with Phing? All I can see I have access to is the relative and absolute paths to the files themselves.
Note: I'm aware that this current solution would create a new file for each input file, but that's what I'm aiming to fix with abspathtopwd.
Thanks!
For anyone interested in this problem, check out this post, which led me to:
<foreach param="dir" absparam="absdir" target="minify.directory">
<fileset dir="${publicdir}/css">
<type type="dir" />
<depth max="0" min="0" />
</fileset>
</foreach>
This allows me to specify a directory constraint when iterating, thus passing through the relative and absolute directory name as opposed to the filename.

How to replace string in a file using NANT?

I am trying to replace the occurance of a string in a wxs file using Nant.
I have only found the following example, which uses <replaceString>, but it seems like it can only be used within the copied files. Are there any other way of replacing a string, without actually copying the files over?
<property name="NOW" value="${datetime::now()}" />
<copy todir="out">
<fileset basedir="in">
<include name="**/*" />
</fileset>
<filterchain>
<replacetokens>
<token key="NOW" value="${TODAY}" />
</replacetokens>
<tabstospaces />
</filterchain>
</copy>
Here's the code:
<loadfile file="token.txt" property="token-file">
<filterchain>
<replacetokens>
<token key="NOW" value="${datetime::now()}" />
</replacetokens>
</filterchain>
</loadfile>
The official NAnt docs for <loadfile> element contain the exact sample you need. See the bottom of the page.
Here's how I did it.
<loadfile file="${file}" property="file.content">
<filterchain>
<replacestring from="StringToMatch" to="StringToReplace" ignorecase="true" />
</filterchain>
</loadfile>
<echo file="${file}">${file.content}</echo>
So you are trying to modify a .wxs file which is XML, right?
In this particular case you might use <xmlpoke> if you are able to determine the position of the strings to replace via XPath.
I found a solution for you here: http://frank.overseakids.com/?p=182
<loadfile file=”${dir.template}\template.db_name.sql” property=”restore.db.sql.db_name”>
<filterchain>
<replacetokens>
<!– this looks for tokens like #blah.blah# in the file being loaded and replaces them–>
<token key=”restore.db.prefix” value=”${restore.db.prefix}” />
<token key=”backup.file.path” value=”${backup.file.path}” />
</replacetokens>
</filterchain>
</loadfile>
<property name=”current.db” value=”db_name” />
<property name=”current.log” value=”${dir.log}\${restore.db.logfile.prefix}_db_name.log” />
<property name=”current.file” value=”${dir.template}\restore.db_name.tmp.sql” />
<delete if=”${file::exists(current.file)}” file=”${current.file}” />
<echo file=”${current.file}”>${restore.db.sql.db_name}</echo>
You can wrap this in a <foreach /> element.
I never managed to get the filterchain and replacetokens to work properly. I ended up using this and it works great.
<replacetext filename="${filename}" src="stringToBeReplaced" replacement="replacementString" />
All these answers did not work for me, maybe because I needed to replace a string with spaces in it. Loading a file content with filterchain/replacetokens did nothing to the contents of the associated property. Maybe I'm using it wrong.
The tasks "replacestring" and "replacetext" suggested by #Ally and #John Sterne do not exist.
It's included in a Jenkins build process, thus the ENVIRONMENT variable must be set to the working dir.
<loadfile file="./my/batch.bat" property="file.content" />
<property name="file.content"
value="${string::replace(file.content, 'D:\path to\the working\space', environment::get-variable('WORKSPACE'))}" />
<property name="file.content"
value="${string::replace(file.content, 'Cd C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin', 'CD /D C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin')}" />
<echo file="./my/batch.bat">${file.content}</echo>
I had that problem today. To solve it I used the move command instead of loadfile or copy. This worked for me because since my file was pretty small. The other caution about this is that replacetokens needs a start identifier and end identifier of the token; begintoken and endtoke respectively. If those are not set the default values are the # symbol. So if you want to replace a value such as MY_SERVER_PLACE_HOLDER that means the value in your file must be #MY_SERVER_PLACE_HOLDER#. If you want your token to start with something different than you should specify the begintoken and endtoken values. That should give you an idea of the problems the begin token and endtoken will bring you.
So here is what I did in a nutshell
Moved the file to a temporary location. In that move I used
filterchain with removetokens to change the values in the file.
In step 2, I moved the file back to it's original location.
I then used the delete command to delete the temp folder I created.
Here is a what I did. (May not be syntactically correct since I am not in front of the code at the moment)
<move todir="temp">
<fileset basedir="in">
<include name="myfile.dat" />
</fileset>
<filterchain>
<replacetokens>
<token key="MY_SERVER_PLACE_HOLDER" value="http://www.someserver.com" />
</replacetokens>
<tabstospaces />
</filterchain>
</move>
<move todir="in">
<fileset basedir="temp">
<include name="myfile.dat" />
</fileset>
</move>
<delete dir="temp" />

Nant: Find file by pattern

What I am trying to do, is to find a file with NAnt. This file could by anywhere in a directory structure of a given folder.
I tried to this with the NAnt-foreach task (this works) but I am not quite convinced of that:
<target name="find-file">
<fail message="Property param.dir must be set" unless="${property::exists('param.dir')}" />
<fail message="Property param.pattern must be set" unless="${property::exists('param.pattern')}" />
<property name="return.file" value="" />
<foreach item="File" property="iterator.file">
<in>
<items>
<include name="${param.dir}\**\${param.pattern}" />
</items>
</in>
<do>
<property name="return.file" value="${iterator.file}" if="${string::get-length(return.file) == 0}" />
</do>
</foreach>
</target>
Is there anybody aware of a better approach? If not how can I accomplish to exit the foreach-loop after the first element is found?
This nantcontrib function will put the matching filenames into a delimited string..
If you know that only one matching file will exist then it may get you what you want. If there are several then you could use the nant substring function to just get the first match by taking the substring up to the first delimiter.
The following nant script:
<?xml version="1.0" encoding="utf-8"?>
<project default="find-file2">
<property name="NantContrib.dir" value="C:\Program Files\nantcontrib-0.85\" readonly="true" />
<target name="LoadNantContrib">
<loadtasks assembly="${NantContrib.dir}bin\NAnt.Contrib.Tasks.dll" />
</target>
<target name="find-file2" depends="LoadNantContrib">
<fileset id="find.set">
<include name="${param.dir}\**\${param.pattern}" />
</fileset>
<property name="return.file" value="${fileset::to-string('find.set', ' | ')}" />
<echo message="return.file=${return.file}"/>
<echo message="Found ${fileset::get-file-count('find.set')} files"/>
</target>
</project>
...and the following folder structure:
\---folderroot
+---folder1
| dontfindme.txt
| findme.txt
|
+---folder2
| dontfindme.txt
|
\---folderempty
...works as expected. Searching for findme.txt finds one file. Searching for dontfindme.txt finds two files. Searching for *.txt finds three files.
Example call:
nant -D:param.dir=folderroot -D:param.pattern=findme.txt
Example output:
find-file2:
[echo] return.file=C:\Documents and Settings\rbaker\My Documents\nantfindfile\folderroot\folder1\findme.txt
[echo] Found 1 files
BUILD SUCCEEDED

How do I optionally require a command line arguement for Ant?

I'm new to ant, and I want to require a file name if something other than the default target is used, so the calling syntax would be something like this:
ant specifictarget -Dfile=myfile
I'm using the ant contrib package to give me additional functionality, so I have this:
<if>
<equals arg1="${file}" arg2="" />
<then>
<!-- fail here -->
</then>
</if>
My thinking is that if file was not specified it might be equal to the empty string. Obviously, this didn't work, and I'm not finding any examples on google or the right syntax in the manual.
So what syntax should I use?
You don't really need the contrib package. This is more conveniently done using built-in ant capabilities like if/unless and depends. See below:
<target name="check" unless="file" description="check that the file property is set" >
<fail message="set file property in the command line (e.g. -Dfile=someval)"/>
</target>
<target name="specifictarget" if="file" depends="check" description=" " >
<echo message="do something ${file}"/>
</target>
You've got the right idea. The
ant specifictarget -Dfile=myfile
sets Ant Properties from the command line. All you really need is
<property name="file" value=""/>
for your default value. That way if file is not specified, it will be equal to the empty string.
Since properties are not mutable in Ant, you can add this:
<property name="file" value="" />
This will set the property file to an empty string if it hasn't already been set on the command line. Then your equality test will work as you intended.
Alternately, you can use escape the value since ant just spits out the actual text when it can't do a property substitution.
<if>
<equals arg1="${file}" arg2="$${file}" />
<then>
<echo>BARF!</echo>
</then>
</if>

nant: expanding properties in a string

Summary:
How do I expand a property with value "download\${bulidmode}\project\setup.msi" to "download\Debug\project\setup.msi" if the property buildmode contained debug so I can use it as the file="" part of < copy >
Detail:
I have a bit of a requirement to be able to expand properties within a string in nant.
For example I have a target that is copying file A to B. A and B both come from a simple two field CSV file which I'm iterating through using
<foreach item="Line" in="filelist.csv" delim="," property="source.file,target.file">
<property name="sourcefile" value="${path::combine(source.dir,source)}" />
<property name="targetfile" value="${path::combine(download.dir,destination)}" />
<echo message="Copy ${sourcefile} to ${targetfile}" />
<copy file="${sourcefile" tofile="${destination}" />
</foreach>
and the filelist.csv will be
build\manifest.xml
solutiondirectory\setup-proj-directory\Release\setupproj.msi,ProductA\ProductA.msi
solutiondirectory\another-proj-dir\Release\setupproj.msi,ProductB\ProductB.msi
(The reason we split these out is that we write multi-tiered applications and deploy by MSI to each tier - so one product has multiple msi's all built with the same version numbers)
Anyway - I want to change this to that I no longer have "Release" in the filelist.csv file but something like ${build.mode}. I would wrap the above code with a
<foreach item="String" in="Release,Debug" delim="," property="build.mode">
....as above
</foreach>
and the property embedded within the string in the file gets expanded.
I've been beating my head against a brick wall for a few hours, but just can't figure it out.
Thanks
It is possible with a custom function :
<?xml version="1.0"?>
<project>
<script language="C#" prefix="vbfox" >
<code>
<![CDATA[
[Function("expand")]
public string ExpandString(string str)
{
return Project.Properties.ExpandProperties(str, Location.UnknownLocation);
}
]]>
</code>
</script>
<property name="hello" value="{path::combine('_hello_', '_world_')}" />
<property name="hello" value="${'$' + hello}" />
<echo message="${hello}" />
<echo message="${vbfox::expand(hello)}" />
</project>