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.
Related
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.
I would like to have a foreach task like this, which iterates over all the files/directories in a directory "A" -
<foreach param="dirname" absparam="absname" target="subtask">
<fileset dir="${dir.destination}/${dir.subdir}/">
<type type="file" />
</fileset>
</foreach>
The target "subtask" should check if the counterpart of the file/folder exists in another directory "B" (I am comparing directory A and B basically), and return either of the following if it does not -
a flag.
name of the file.
Following is some code for reference -
<target name="subtask">
<if>
<filesmatch file1="${file1}" file2="${file2}"/>
<then>
Return false. But how?
</then>
<else>
Return true of name of the file. How?
</else>
</if>
</target>
Note - It is okay if this can be done without calling a target. I am not sure if the logic can be fit inside the foreachtask itself. Could not find any such thing in the phing documentation.
Basically, I should be having the list of file names which are not present in the directory B, by the end of the loop.
You may also read this question of mine, if you can give some pointers to solve the issue in some other way.
Update
Rephrasing this question, since I feel that the problem description is not clear. The phing documentation says, a target has no return value -
Targets are collections of project components (but not other targets)
that are assigned a unique name within their project. A target
generally performs a specific task -- or calls other targets that
perform specific tasks -- and therefore a target is a bit like a
function (but a target has no return value).
I don't understand why is it designed so. With this bounty, I would like to know if there is some workaround for me other than having to define my own custom tasks in PHP, and then set properties -
$this->getProject()->setNewProperty('modifiedElements', implode("\n\n",$modifiedElementsArray));
which can be accessed in the build file
I have a target which checks whether my production code base has any differences from the expected git revision -
<target name="compare_prod_with_expected_revision">
<input propertyname="box.git_version">
Enter git version of the production codebase:
</input>
<exec command="git reset --hard ${box.git_version}" dir="${dir.scratchpad}" />
<!-- Scratchpad brought to revision ${box.git_version} -->
<echo>Verifying whether production is at revision ${box.git_version}..</echo>
<exec command="diff -arq --exclude='.git' ${dir.scratchpad}/${dir.subdir} ${dir.destination}/${dir.subdir}" outputProperty="diffList"/><!-- #TODO ignore.swp files in this step. Diff says .swp files present in production code. But doing ls -a there does not show the same. -->
<php function="strlen" returnProperty="productionDeviationFromExpectedBranch"><!-- #TODO - find how to not show this step during build process. Put it in a target and set hidden="true" -->
<param value="${diffList}"/>
</php>
<if>
<equals arg1="${productionDeviationFromExpectedBranch}" arg2="0" />
<then>
<echo>Verified production is at revision ${box.git_version}</echo>
</then>
<else>
<echo>Differences - </echo>
<echo>${diffList}</echo>
</else>
</if>
</target>
Now, I want to phingcall this target and would like to access some property set by it.
I think I understood your purposes, and at the same time I feel like you chosen not the optimal tool of doing this.
As you mentioned in your question, official documentation on phing is clear about tasks (targets):
Targets are collections of project components (but not other targets) that are assigned a unique name within their project. A target generally performs a specific task -- or calls other targets that perform specific tasks -- and therefore a target is a bit like a function (but a target has no return value).
Targets should be components of your application, which execute specific task, atomic task. It could be initialization task, configuration fetching, compilation step, assets preparation and dumping, deployment task, clean-up task, etc. There's no "output", returned by target in the standard sense, but the result of target execution is the success of execution itself: success or failure.
One should not try to put way too much of logic into such project targets, as it is not intended to do complicated calculations, do heavy logical decisions, etc. I mean, Phing can do it, such things are possible, but this setup would be bulky, unreadable, and hard to scale/re-factor.
With Phing you can easily define conditional execution and branching of logical flow, you may define the sequence of execution of tasks (dependencies) - this is what makes it laconic and elegant. Keep targets as simple as possible, split the project into small, finished logical tasks.
Based on the projects I've been working with, the biggest targets, probably, were initialization stage and configs fetching. Here's some example, to understand what it might contain, I took it from real project:
<target name="init_configuration">
<echo msg="Define initial configuration for the deployment..." />
<if>
<not>
<isset property="host" />
</not>
<then>
<property name="host" value="dev" override="true" />
<echo message="The value of hostname has been set to ${host}" />
</then>
<else>
<echo message="The value of hostname is ${host}" />
</else>
</if>
<if>
<not>
<isset property="version" />
</not>
<then>
<property name="version" value="1.0.0" override="true" />
<echo message="The value of version has been set to ${version}" />
</then>
<else>
<echo message="The value of version is ${version}" />
</else>
</if>
<property name="host_credital_file" value="config/hosts/${host}.properties" />
<property file="${host_credital_file}" />
<available file="${host_credital_file}" property="hostfilefound" value="true"/>
<fail unless="hostfilefound" message="Missing Hostfile configuration file (${host_credital_file})!" />
<echo msg="Configuration is done" />
</target>
Other targets were extremely simplistic, they are normally – 1-5 lines long, and do only small purpose, small task. This would be, probably, the best recommendation when working with Phing.
The logic which you are trying to put on shoulders of Phing is possible, but would be extremely bulky.
Consider the point: how much quicker, easier, and more readable the same thing could be done with simple bash script in your example. Or even to write small CLI utility in PHP, which will do the job elegantly and quick. After that in Phing you'll leave parametric target which will execute this "revision diff script" from CLI.
Phing is a great tool for what it is designed for, but it can't be an optimal choice for every purpose. Just do not put way to much responsibility and logic into it.
As a workaround, for more complicated things it's better to combine Phing with with something specialized: bash scripting, PHP CLI, nodeJS (+ Grunt, Gulp, etc)... and just to add calls of a Phing targets later.
This is the way I managed to have targets which behave like functions:
<target name="-console-cmd-return-property" hidden="true">
<exec command="${command}" checkreturn="${checkreturn}" logoutput="${logoutput}" outputProperty="${outputProperty}"/>
</target>
It gets invoked like this:
<phingcall target="--console-return-property">
<property name="command" value="ps auxwww"/>
<property name="checkreturn" value="true"/>
<property name="logoutput" value="false"/>
<property name="outputProperty" value="ps_output"/>
</phingcall>
Of course it works because it relies on existing exec, and it is not generic...
The target "subtask" should check if the counterpart of the file/folder exists in another directory "B" (I am comparing directory A and B basically), and return either of the following if it does not -
a flag.
name of the file.
You could compare two directories without using a foreach task like this:
<project name="Phing Build Test" default="print-missing" basedir=".">
<resolvepath propertyName="dir.a" path="path/to/dir/a"/>
<resolvepath propertyName="dir.b" path="path/to/dir/b"/>
<target name="print-missing">
<apply executable="echo" failonerror="false" returnProperty="files.found" outputProperty="missing">
<srcfile/>
<fileset id="srcfiles" dir="${dir.a}" includes="*">
<present present="srconly" targetdir="${dir.b}"/>
</fileset>
</apply>
<if>
<equals arg1="${files.found}" arg2="0"/>
<then>
<echo msg="${missing}"/>
</then>
</if>
</target>
</project>
I have a JavaHelp project and I have a bat file that basically runs a jhindexer (to create help index).
I would like to make it so that every time I make a build (Run>Target>Other target>Final build - it would run the jhindexer bat on pre-compile. Unfortunately I can't seem to get it working.
Here is my build.xml bit:
<target name="-pre-compile">
<echo message="Creating index"/>
<property name="createIndex" value="${basedir}\" />
<echo>${createIndex}</echo>
<!--<exec command="cmd /C createIndex.bat" />--> //Says its deprecated
<exec dir="${createIndex}" executable="createIndex.bat">
<arg file="cmd createIndex.bat" />
</exec>
</target>
This code gives me:
Creating index
Y:\NetBeansProjects\JavaHelp\
Y:\NetBeansProjects\JavaHelp\build.xml:79: Execute failed:
java.io.IOException: Cannot run program "\createIndex.bat" (in directory "Y:\NetBeansProjects\JavaHelp"): CreateProcess error=2, The system cannot find the file specified
If I change it to:
<exec command="cmd /C createIndex.bat" />
Creating index
Y:\NetBeansProjects\JavaHelp\
The command attribute is deprecated.
Please use the executable attribute and nested arg elements.
I can't seem to figure out a way to run the bat file...
Edit 1:
here is the contents of the bat file:
cd src\helpsetproject
..\..\javahelp\bin\jhindexer topics
It basically goes from basedir too the folder where Images, Topics folders are. Then I run jhindexer (which is in basedir\javahelp\bin) and give it topics (name of folder in the director i am in) as a parameter I guess. It works standalone, but not from Run Target in NetBeans.
Try this:
<target name="-pre-compile">
<echo message="Creating index"/>
<property name="createIndex" value="${basedir}\" />
<echo>${createIndex}</echo>
<exec dir="${createIndex}" executable="cmd">
<arg line="/c createIndex.bat" />
</exec>
</target>
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" />
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