I'd like to search for a string within my source files with Ant. (I'd like my build to fail in case certain string is found within my source files).
So, I should be able to recursively search for a certain string within a file set.
I already found that I can use loadfile task to check whether a string pattern is found within one file. But that seems to be working & sensible only with a single file.
On the other hand, replace task would provide recursively search-and-replace. I guess I could do that before build and replace my string with something that would break the build but I wonder if there is some cleaner solution?
br, Touko
You might consider using fileset selectors to do this. Selectors allow you to choose files based on content, size, editability and so on. You can combine selectors with name-based includes and excludes, or patternsets.
Below is an example. The second fileset is derived from the first, with a selector that simply matches on file content. For more sophisticated matching there is the containsregexp
selector. The result is a fileset containing only files matching the string. A fail task with a resourcecount
condition is then used to fail the build, unless that fileset is empty.
<property name="src.dir" value="src" />
<property name="search.string" value="BAD" />
<fileset id="existing" dir="${src.dir}">
<patternset id="files">
<!-- includes/excludes for your source here -->
</patternset>
</fileset>
<fileset id="matches" dir="${src.dir}">
<patternset refid="files" />
<contains text="${search.string}" />
</fileset>
<fail message="Found '${search.string}' in one or more files in '${src.dir}'">
<condition>
<resourcecount when="greater" count="0" refid="matches" />
</condition>
</fail>
(Old answer): If adjusting or reusing filesets might be problematic, here's an illustration of a relatively simple alternative.
The idea is to make a copy of the files,
then replace the string you wish to search for
with some flag value in the copied files.
This will update the last modified time on any matching file.
The uptodate
task can then be used to look for affected files.
Finally, unless no files matched, you can fail
the build.
<property name="src.dir" value="src" />
<property name="work.dir" value="work" />
<property name="search.string" value="BAD" />
<delete dir="${work.dir}" />
<mkdir dir="${work.dir}" />
<fileset dir="${src.dir}" id="src.files">
<include name="*.txt" />
</fileset>
<copy todir="${work.dir}" preservelastmodified="true">
<fileset refid="src.files" />
</copy>
<fileset dir="${work.dir}" id="work.files">
<include name="*.txt" />
</fileset>
<replaceregexp match="${search.string}"
replace="FOUND_${search.string}">
<fileset refid="work.files" />
</replaceregexp>
<uptodate property="files.clean">
<srcfiles refid="work.files" />
<regexpmapper from="(.*)" to="${basedir}/${src.dir}/\1" />
</uptodate>
<fail message="Found '${search.string}' in one or more files in dir '${src.dir}'"
unless="files.clean" />
This was very helpful as a start, but I have a list of strings which should be checked in a fileset.
My current code sofar is:
<property name="search4" value="XYZ"/>
<fileset id="existing" dir="../src">
<patternset id="files">
<include name="content/**/*.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}'"/>
That works well, but how to expand that so the ${search4} is read from a list. Actually the list can be read from a file containing each search item is on a separate line.
Slightly more concise variation on the first part of @martinclayton's answer:
<property name="log.dir" value="logs" />
<property name="fail.string" value=" FAILED " />
<fileset id="build.failures" dir="${log.dir}" includes="*.log">
<contains text="${fail.string}"/>
</fileset>
<fail status="1" message="One or more failures detected">
<condition>
<resourcecount when="greater" count="0" refid="build.failures" />
</condition>
</fail>