How can I echo a filename twice using 'FileSet' and 'PathConvert'? - ant

I have this simple Ant task that lists all '.png' files in a folder:
<target name="listimages">
<!-- Assume files a A and B -->
<fileset id="dist.contents" dir="${basedir}">
<include name="**/*.png"/>
</fileset>
<pathconvert pathsep="${line.separator}"
property="prop.dist.contents"
refid="dist.contents">
<mapper type="flatten" />
<map from="${basedir}" to=""/>
</pathconvert>
<echo>${prop.dist.contents}</echo>
</target>
This prints
[echo] A.png
[echo] B.png
But, what I want is for the filenames to appear twice on each line.
[echo] A.png,A.png
[echo] B.png,B.png
How can I do that?
(This question is a follow up to How can I print a fileset to a file, one file name per line?)

You could use a regexp mapper (instead of the flatten) that implements the flattening and duplication. This is pretty simplistic, but might do:
<mapper type="regexp" from=".*/(.*)" to="\1,\1" />
Would need adjusting for your local path separator.
Better though, use a chainedmapper in place of the flatten:
<chainedmapper>
<mapper type="flatten" />
<mapper type="regexp" from="(.*)" to="\1,\1" />
</chainedmapper>

Related

Find the number of occurrences of a particular character like comma in a string using ant script?

<project>
<target name="test">
<property name="src.dir" value="src" />
<property name="search4" value=","/>
<fileset id="existing" dir="${src.dir}/src">
<patternset id="files">
<include name="*.txt"/>
</patternset>
</fileset>
<resourcecount property="count">
<fileset id="matches" dir="../src">
<patternset refid="files" />
<contains text="${search4}" />
</fileset>
</resourcecount>
<echo message="Found '${search4}' in files : '${count}'"/>
</target>
</project>
I used this, but this only prints the first occurrence. I would like to print the total count.
For eg - abc,xyz,pg--> The number of occurrences of commas(,) is 2.
Here's one way. Copies the file to another file, with a filter to remove all non-commas, then gets the size of the output, which is the number of commas in the input file.
<delete file="out.txt" />
<copy file="in.txt" tofile="out.txt">
<filterchain>
<striplinebreaks />
<replaceregex pattern="[^,]" replace="" flags="gm" />
</filterchain>
</copy>
<length file="out.txt" property="out.size" />
<echo message="Commas found: ${out.size}" />
On your follow up question: how to restrict this to just the first line of the file: add this before the "striplinebreaks" line:
<headfilter lines="1" />
That will count commas in just the first line of the file.

ant : apply executable when output file doesn't exist

I try to create a ant target which processes some GLSL shaders from an input directory, output them in another folder. And I'd like to avoid the processing if the output file already exists.
The executable I use can either take an output directory argument, or directly the output file path.
Currently, I have :
<target name="optimize_programs">
<apply executable="TOOLS/glsl_processor" dir="." verbose="true" >
<srcfile/>
<arg value="-output_directory=OUTPUT/PROGRAMS/" />
<fileset dir="INPUT/PROGRAMS/OPENGLES2" includes="**/*.glfx" />
<flattenmapper />
</apply>
</target>
The shaders are correctly processed, but the problem is they are processed each time, even when the output file already exists.
I suspect this is because the flattenmapper is not aware of the glsl_processor output.
I've tried to use to tell the glsl_processor where to output the file :
<target name="optimize_programs">
<apply executable="TOOLS/glsl_processor" dir="." verbose="true" >
<srcfile/>
<targetfile/>
<fileset dir="INPUT/PROGRAMS/OPENGLES2" includes="**/*.glfx" />
<flattenmapper />
</apply>
</target>
But I don't know how to make targetfile point to the output folder.
Any idea?
Thanks!
Well the answer is actually pretty simple:
<target name="optimize_programs">
<apply executable="TOOLS/glsl_processor" dir="INPUT/PROGRAMS/OPENGLES2/" dest="OUTPUT/PROGRAMS" verbose="true" >
<targetfile/>
<srcfile/>
<fileset dir="INPUT/PROGRAMS/OPENGLES2" includes="**/*.glfx" />
<mapper type="glob" from="*.glfx" to="*.glfx"/>
</apply>
</target>
I was just missing to fill the dest attribute of the apply task.

Replaceregexp with property value depending on the matched group

I'd like to replace some replace marks in css files with values out of a property file. What i did so far is:
<target depends="prepare" name="build_css">
<replaceregexp>
<fileset refid="temp_css_files"/>
<regexp pattern="\{(.*)\}"/>
<substitution expression="${testprop}"/>
</replaceregexp>
</target>
which will successfully replace the matched string with the value of the testprop property.But what i like to do is, to replace the matched string by a property whose name is the matched string.
So a replacement mark {myprop} will be replaced by the value of the property myprop.
I tried:
<target depends="prepare" name="build_css">
<replaceregexp>
<fileset refid="temp_css_files"/>
<regexp pattern="\{(.*)\}"/>
<substitution expression="${\1}"/>
</replaceregexp>
</target>
with no success since the matched string is then replaced by the String ${myprop}.
Is it possible to to this? Or is there an easier way with another task that i'm missing?
If you can use the typical Ant ${...} syntax to represent properties in your CSS files, then Ant's <expandproperties> FilterReader may be useful:
<project name="ant-replace-tokens-with-copy-task" default="run">
<target name="run">
<property name="src-root" location="src"/>
<fileset id="temp_css_files" dir="${src-root}">
<include name="**/*.css"/>
</fileset>
<!-- The <copy> task cannot "self-copy" files. So, for each -->
<!-- matched file we'll have <copy> read the file, replace the -->
<!-- tokens, and write the result to a temporary file. Then, we'll -->
<!-- use the <move> task to replace the original files with the -->
<!-- modified files. -->
<property name="filtered-file.extension" value="*.filtered-file"/>
<copy todir="${src-root}">
<fileset refid="temp_css_files"/>
<globmapper from="*" to="${filtered-file.extension}"/>
<filterchain>
<expandproperties/>
</filterchain>
</copy>
<move todir="${src-root}">
<fileset dir="${src-root}" includes="**"/>
<globmapper from="${filtered-file.extension}" to="*"/>
</move>
</target>
</project>
If you need to stick with the {...} syntax, the ReplaceTokens FilterReader replaces tokens with properties defined in a properties file:
<project name="ant-replace-tokens-with-copy-task" default="run">
<target name="run">
<property name="src-root" location="src"/>
<fileset id="temp_css_files" dir="${src-root}">
<include name="**/*.css"/>
</fileset>
<!-- The <copy> task cannot "self-copy" files. So, for each -->
<!-- matched file we'll have <copy> read the file, replace the -->
<!-- tokens, and write the result to a temporary file. Then, we'll -->
<!-- use the <move> task to replace the original files with the -->
<!-- modified files. -->
<property name="filtered-file.extension" value="*.filtered-file"/>
<copy todir="${src-root}">
<fileset refid="temp_css_files"/>
<globmapper from="*" to="${filtered-file.extension}"/>
<filterchain>
<filterreader classname="org.apache.tools.ant.filters.ReplaceTokens">
<param type="tokenchar" name="begintoken" value="{"/>
<param type="tokenchar" name="endtoken" value="}"/>
<param type="propertiesfile" value="dev.properties"/>
</filterreader>
</filterchain>
</copy>
<move todir="${src-root}">
<fileset dir="${src-root}" includes="**"/>
<globmapper from="${filtered-file.extension}" to="*"/>
</move>
</target>
</project>

How to preserve file order in Ant concat?

How to preserve file order in Ant concat?
Simple concat with fileset & includesfile produces rather "random" order, as order is not guaranteed:
<concat destfile="C:/targetdir/concatenated.file">
<fileset dir="C:/sourcedir/">
<includesfile name="C:/targetdir/includes.file" />
</fileset>
</concat>
What I need is concatenation in specific order that the files are listed in the includes file.
So far I've found resourcelist, which should preserve order, but I can't seem to be able to produce any concatenated file with it. :/
<concat destfile="C:/targetdir/concatenated.file">
<resourcelist>
<file file="C:/targetdir/includes.file"/>
<filterchain>
<striplinecomments>
<comment value="#"/>
</striplinecomments>
<prefixlines prefix="C:/sourcedir/"/>
</filterchain>
</resourcelist>
</concat>
Plus, the resourcelist can't seem to handle rows like
LibraryX/A/Stuff/Morestuff/*
Instead the row just produces a ".../Morestuff/* does not exist." -error
Includes file has list of relative paths:
LibraryX/A/Stuff/FileA.txt
LibraryX/A/Stuff/FileB.txt
LibraryX/A/Stuff/FileC.txt
LibraryX/A/Stuff/FileY.txt
I was able to get a filelist working pretty easily:
<concat destfile="C:/targetdir/concatenated.file">
<filelist dir="C:/sourcedir/">
<file name="i.txt" />
<file name="n.txt" />
<file name="o.txt" />
<file name="r.txt" />
<file name="d.txt" />
<file name="e.txt" />
<file name="r.txt" />
</filelist>
</concat>
Hope that helps!
If you are using Ant 1.7+, you can use the sort command
<concat destfile="C:/targetdir/concatenated.file">
<sort>
<fileset dir="C:/sourcedir/">
<include name="C:/targetdir/*.file" />
</fileset>
</sort>
</concat>
You can find the documentation of sort here
[On Ant 1.8.2+] You can also pass the fileset via a sort, and sort on filename, like below:
<concat destfile="./${dir.publish}/${dir.js}/b.main-${build.number}.debug.js">
<sort xmlns:rcmp="antlib:org.apache.tools.ant.types.resources.comparators">
<fileset dir="./${dir.publish}/">
<include name="**/${dir.js.main}/**/*.js"/>
<exclude name="**/${dir.js.main}/**/*.min.js"/>
</fileset>
<rcmp:name />
</sort>
</concat>
Couple of things to watch out for:
Directories are sorted before files
Capitals come before lowercase
UPDATE: Another alternative if you need to manually specify order:
<!-- create a ordered list of all the build files so that CIAPI & CIAPI.widget are built first
(can't find a smarter way to do this, since ant filesets are unordered) -->
<fileset id="a" dir="."><include name="CIAPI/build.project.xml"/></fileset>
<fileset id="b" dir="."><include name="CIAPI.widget/build.project.xml"/></fileset>
<fileset id="c" dir=".">
<include name="**/build.project.xml"/>
<exclude name="CIAPI/build.project.xml" />
<exclude name="CIAPI.widget/build.project.xml" />
</fileset>
<union id="all_build_files">
<fileset refid="a"/>
<fileset refid="b"/>
<fileset refid="c"/>
</union>
Ugly, but, erm, this is ant?
try this, put in alphabetical order
<project name="concatPath" default="full">
<target name="full">
<fileset id="fs" dir="./files" />
<pathconvert refid="fs" property="concatList" pathsep=";" targetos="unix"/>
<echo>${concatList}</echo>
</target>
</project>
this can be used with hierarchical structure of directories, and the order will be the exposed by David.
Remember that XML is not order-dependent, by definition.
To concatenate files in a sorted order, consider using <replace> instead.
Create an order file that defines the order. Then, in your build file:
Copy the order file to the destination file with <copy>
Concatenate your files together into a temporary file with <concat>
Load the files into properties with <loadfile>
Insert the text from those files into the destination file with <replace>
Example order file order_file.txt:
FILE_A_HERE
CONCAT_FILES_HERE
Example ant build file build.xml:
<copy file="order_file.txt" tofile="destination.txt" overwrite="yes">
<concat destfile="tempfile.txt">
<fileset dir="includes/">
<include name="*.txt">
<exclude name="fileA.txt">
</fileset>
</concat>
<loadfile property="fileA" srcFile="includes/fileA.txt" />
<loadfile property="concatFile" srcFile="tempfile.txt" />
<replace file="destination.txt" token="FILE_A_HERE" value="fileA" />
<replace file="destination.txt" token="CONCAT_FILES_HERE" value="concatFile" />

How can I print a fileset to a file, one file name per line?

I have a populated fileset and I need to print the matching filenames into a text file.
I tried this:
<fileset id="myfileset" dir="../sounds">
<include name="*.wav" />
<include name="*.ogg" />
</fileset>
<property name="sounds" refid="myfileset" />
<echo file="sounds.txt">${sounds}</echo>
which prints all the files on a single line, separated by semicolons. I need to have one file per line. How can I do this without resorting to calling OS commands or writing Java code?
UPDATE:
Ah, should have been more specific - the list must not contain directories. I'm marking ChssPly76's as the accepted answer anyway, since the pathconvert command was exactly what I was missing. To strip the directories and list only the filenames, I used the "flatten" mapper.
Here is the script that I ended up with:
<fileset id="sounds_fileset" dir="../sound">
<include name="*.wav" />
<include name="*.ogg" />
</fileset>
<pathconvert pathsep="
" property="sounds" refid="sounds_fileset">
<mapper type="flatten" />
</pathconvert>
<echo file="sounds.txt">${sounds}</echo>
Use the PathConvert task:
<fileset id="myfileset" dir="../sounds">
<include name="*.wav" />
<include name="*.ogg" />
</fileset>
<pathconvert pathsep="${line.separator}" property="sounds" refid="myfileset">
<!-- Add this if you want the path stripped -->
<mapper>
<flattenmapper />
</mapper>
</pathconvert>
<echo file="sounds.txt">${sounds}</echo>
Since Ant 1.6 you can use toString:
<echo file="sounds.txt">${toString:myfileset}</echo>

Resources