Performing Simple Calculations with Native Ant Tasks - scripting

Using only native ANT tasks, how can I create a custom ANT task to do the following:
Calculate the the number of days since January 1, 2000 local time and store it in a property.
Calculate the number of seconds since midnight local time, divided by 2 and store it in a property.
The above property values will then be appended to others and written to a file.

ANT is not a general purpose programming language, so you need to write a custom task or alternatively use something like the groovy plugin
The following example demonstrates how a groovy task using the Joda Time library can set the properties as you've specified.
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="build.path"/>
<groovy>
import org.joda.time.*
def now = new DateTime()
def midnight = new DateMidnight()
def year2000 = new DateTime(2000,1,1,0,0,0,0)
properties["year2000.days"] = Days.daysBetween(year2000, now).days
properties["midnight.seconds"] = Seconds.secondsBetween(midnight, now).seconds
properties["midnight.seconds.halved"] = Seconds.secondsBetween(midnight, now).dividedBy(2).seconds
</groovy>
I can't recommend Joda Time highly enough, standard Date and Time manipulation in Java just sucks!
Additional notes
The groovy task above will require the following jars on your classpath:
groovy-all-1.7.4.jar
joda-time-1.6.1.jar
I'd recommend using the ivy plugin to manage these by adding a "resolve" target that downloads the jars and sets the classpath automatically:
<target name="resolve">
<ivy:resolve/>
<ivy:cachepath pathid="build.path"/>
</target>
The following is the ivy.xml that lists the dependencies to be downloaded:
<ivy-module version="2.0">
<info organisation="org.myspotontheweb" module="demo"/>
<dependencies>
<dependency org="org.codehaus.groovy" name="groovy-all" rev="1.7.4" conf="default"/>
<dependency org="joda-time" name="joda-time" rev="1.6.1" conf="default"/>
</dependencies>
</ivy-module>

Related

Referencing a sibling changeset in a rollback section

I'm having an issue trying to rollback a changeSet by referring sibling-changeset.
master-changelog.xml
includes v.1.changes.xml (here is the table created)
includes v.2.changes xml (here the table dropped and I would like to refer a changeset from v.1.changes.xml as a rollback)
However no matter how do I reference the changeset in v.1.changes.xml it's not visible to v.2.changes.xml and I'm getting liquibase.exception.SetupException: liquibase.parser.core.ParsedNodeException: Change set not found.
master-changelog.xml
<include file="v1/v1.changes.xml" relativeToChangelogFile="true"/>
<include file="v2/v2.changes.xml" relativeToChangelogFile="true"/>
v1.changes.xml
<changeSet id="1" author="dima">
<createTable tableName="test-table">
<column name="test" type="number"></column>
</createTable>
</changeSet>
v2.changes.xml
<changeSet id="1" author="dima">
<dropTable tableName="test"/>
<rollback changeSetAuthor="dima" changeSetId="1" changeSetPath="src/main/resources/std/v1/v1.changes.xml"/>
</changeSet>
It appears that you're using Maven, so this answer will be Maven specific, as I have not been able to reproduce the solution on the command line.
First of all, it's possible that Liquibase is confused because you're using the same changeSet id in both files, and I'm not sure if it correctly scopes those ids to the changeSet file, or if the ids need to be global. You might first try changing the id on the second changeSet and see if it clears it up for you.
If that's not the issue, then the trick to getting this to work is to make sure your relative references are all in the context of the Java classpath. As I interpret your example, the classpath resources of your files would be:
std/master-changelog.xml
std/v1/v1.changes.xml
std/v2/v2.changes.xml
When running your migration, your changeLogFile setting should reference the classpath resource, not the disk file; i.e. std/master-changelog.xml instead of src/main/resources/std/master-changelog.xml. This puts the origin changelog in a classpath context rather than a file context.
In your v2.changes.xml, you then refer to the first change using the classpath resource name: v1/v1.changes.xml. This should allow Liquibase to find it correctly.
If you have more than one level of changeLog file inclusion, you might be running into this issue which prevents Liquibase from finding sibling changeLogs below the first level of inclusion. Until the pull request is merged and released, you'll be limited to a single level of file inclusion.
This solution is assuming you're using the Maven plugin, liquibase will still find the changelog, since Maven puts your resource files on the classpath by default. I also attach the plugin to the process-resources step (or later) so that the source resources will be in the target/classes directory when the migration is run.

Latest stable revision with ivy excluding alpha & beta releases

For our project we like to have most dependecies automaticaly up to date so we want to use the lastest strategies in IVY. However we dont want to run the bleeding edge of the dependencies ie. alpha and beta versions.
When using:
<dependency org="org.apache.httpcomponents" name="httpclient" rev="latest.revision" />
or
<dependency org="org.apache.httpcomponents" name="httpclient" rev="latest.release" />
We get revision 4.4-alpha1
This is understandable as we use the ibiblio resolver which contains the following xml in maven-metadata.xml
<metadata>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<versioning>
<latest>4.4-alpha1</latest>
<release>4.4-alpha1</release>
<versions>
<version>4.0-alpha1</version>
<!-- snip --->
<version>4.3-alpha1</version>
<version>4.3-beta1</version>
<version>4.3-beta2</version>
<version>4.3</version>
<version>4.3.1</version>
<version>4.3.2</version>
<version>4.3.3</version>
<version>4.3.4</version>
<version>4.3.5</version>
<version>4.4-alpha1</version>
</versions>
<lastUpdated>20140801101402</lastUpdated>
</versioning>
</metadata>
The meta data indicates the alpha version as both release and latest. (not sure if this is related actualy)
In this case we there is a version we would like to get in the metadata list being 4.3.5
Now ivy has a construct with and but the documentation is quite sparse, and i cant figure out how to make this strategy 'ignore' the alpha release.
I tried variation of the following to no avail (using rev="latest.test") :
`
Edit:
From the source code of org.apache.ivy.plugins.latest.LatestRevisionStrategy it appears specialmeanings wont be able to solve this since the version is first split in parts and then compared on a part by part basis.
If there is a way to forbid revisions that contain a specific string my problem would also be solved.
`
The source code of org.apache.ivy.plugins.latest.LatestRevisionStrategy indicated it's impossible to fix this with special-meaning strings in a latestStrategy element. (thanks to: this post)
we ended up using a version matcher to enforse ivy to not use -beta- or -alpha releases.
Its not an optimal solution and the regexp probably needs to be updated a few times still.
in ivysettings.xml:
<version-matchers usedefaults="true">
<pattern-vm name="lastest.nobeta">
<match revision="latest.nobeta" pattern="\.*\d+\.\d+\.?\d*(FINAL|RELEASE|STABLE)?" matcher="regexp" />
</pattern-vm>
</version-matchers>
and in ivy.xml:
<dependency org="org.apache.poi" name="poi" rev="latest.nobeta"/>
Not entirely sure if this takes the lastest version but so far that seems to be the case.

How to check if a MSBuild-Task fails if using ContinueOnError=true

I am running the MSBuild task with ContinueOnError=true:
<MSBuild Projects="#(ComponentToDeploy)"
Targets="$(DeploymentTargets)"
Properties="$(CommonProperties);%(AdditionalProperties)"
ContinueOnError="true"
Condition="%(Condition)"/>
So my build always succeeds.
Is there a way to find out if any error occurs?
I could not find any Output of the MSBuild task containing this information.
The only way I know is to parse the log file for errors but it looks like a workaround for me.
(I am using MSBuild 4.0)
This is an answer to the last feedback of #Ilya.
I'm using feedback/answer because of the length and formatting restrictions of the comments.
Log is scoped to individual targets or to be more specific tasks...
This was indeed the first question arose when I was reading your comment with the suggestion to use Log.HasLoggedErrors: "Was is the scope of the Log?".
Unfortunately I was not be able to finde a proper documentation. MSND does not help much...
Why did you know it is scoped to the task?
I'm not in doubt about your statement at all! I'm just wondering if there is a proper documentation somewhere..
(I haven't been using MSBuild for years ;-)
In any case, what are you building as project?
My test projects are very simple.
MyTest.project
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="ElenasTarget" ToolsVersion="4.0">
<UsingTask AssemblyFile="$(MSBuildProjectDirectory)\MyCompany.Tools.MSBuild.Tasks.dll" TaskName="MSBuildWithHasLoggedErrors" />
<ItemGroup>
<MyProjects Include="CopyNotExistingFile.proj" />
</ItemGroup>
<Target Name="ElenasTarget">
<MSBuildWithHasLoggedErrors Projects="#(MyProjects)" ContinueOnError="true" >
<Output TaskParameter="HasLoggedErrors" PropertyName="BuildFailed" />
</MSBuildWithHasLoggedErrors>
<Message Text="BuildFailed=$(BuildFailed)" />
</Target>
</Project>
The CopyNotExistingFile.proj just tries to copy a file that does not exist:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Target1" ToolsVersion="4.0">
<Target Name="Target1">
<Copy SourceFiles="C:\lalala.bum" DestinationFiles="C:\tralala.bam" />
</Target>
</Project>
And this is my custom task MSBuildWithHasLoggedErrors
namespace MyCompany.Tools.MSBuild.Tasks
{
public class MSBuildWithHasLoggedErrors : Microsoft.Build.Tasks.MSBuild
{
[Output]
public bool HasLoggedErrors { get; private set; }
public override bool Execute()
{
try
{
base.Execute();
HasLoggedErrors = Log.HasLoggedErrors;
}
catch (Exception e)
{
Log.LogErrorFromException(e, true);
return false;
}
return true;
}
}
}
If I build my MyTest.proj the HasLoggedErrorswill be set to false although an error (MSB3021) was logged(?) to the console logger:
Project "C:\Users\elena\mytest.proj" on node 1 (default targets).
Project "C:\Users\elena\mytest.proj" (1) is building "C:\Users\elena\CopyNotExistingFile.proj" (2) on node 1 (default targets).
Target1:
Copying file from "C:\lalala.bum" to "C:\tralala.bam".
C:\Users\elena\CopyNotExistingFile.proj(5,4): error MSB3021: Unable to copy file "C:\lalala.bum" to "C:\tralala.bam". Could not find file 'C:\lalala.bum'.
Done Building Project "C:\Users\elena\CopyNotExistingFile.proj" (default targets) -- FAILED.
ElenasTarget:
BuildFailed=False
Done Building Project "C:\Users\elena\mytest.proj" (default targets).
Build succeeded.
My expectation was HasLoggedErrors would be set to true.
one way is to build self but with different target, for example your DefaultTargets one launches your custom MSBuildWrapper task pointing to itself (ie $(MSBuildProjectFile)) but with a different target that does other builds, copies
I've already tried it (that were my investigations I meant in my post). Unfortunately it doesn't work either :-(
(I am aware you said in theory).
My new single project looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="ElenasTarget" ToolsVersion="4.0">
<UsingTask AssemblyFile="$(MSBuildProjectDirectory)\MyCompany.Tools.MSBuild.Tasks.dll" TaskName="MSBuildWithHasLoggedErrors" />
<Target Name="ElenasTarget">
<MSBuildWithHasLoggedErrors Projects="$(MSBuildProjectFile)" Targets="CopyNotExistingFile" ContinueOnError="true" >
<Output TaskParameter="HasLoggedErrors" PropertyName="BuildFailed" />
</MSBuildWithHasLoggedErrors>
<Message Text="BuildFailed=$(BuildFailed)" />
</Target>
<Target Name="CopyNotExistingFile" >
<Copy SourceFiles="C:\lalala.bum" DestinationFiles="C:\tralala.bam" />
</Target>
</Project>
If I build this project HasLoggedErrors will still be set to false.
(Furthermore, my "real" build I'm currently maintaining is much complexer containing several project files with targets... so I can't pack them all in a single project file ).
or writing custom logger and passing it through command line
That was my last hope!
My "real" build has a custom logger passed through the command line (I didn't use it for my test project for the sake of simplicity). That is actually producing the log (a XML file) I'm going to parse to find out if any errors have been logged.
BTW, I thought the console logger is a kind of "global" logger. Am I wrong?
Anyway, the custom logger does not help neither, the Log.HasLoggedErrors is still set to false.
Is there some way I am not aware of to reference a particular logger (e.g. my custom logger) to ask if it has logged any errors?
It really looks like Log is scoped to individual targets.
Hmm... if the reflection on the buildengine instance is the last resort I would still prefer parsing the log.
(Don't blame me! :-) )
My decision
After some investigations I've decided to stick with my initial solution: parse the log to find out if the build failed.
Check my comments to see why I prefer that to the suggestions have been provided so far.
If someone has some other ideas do not hesitate to share :-)
(Otherwise this question can be closed, I suppose...)
The MSBuildLastTaskResult reserved property will be set to True if the last task succeeded and False if the last task failed:
<MSBuild Projects="#(ComponentToDeploy)"
Targets="$(DeploymentTargets)"
Properties="$(CommonProperties);%(AdditionalProperties)"
ContinueOnError="true"
Condition="%(Condition)" />
<Message Text="MSBuild failed!" Condition="'$(MSBuildLastTaskResult)' == 'False'" />
I believe this was introduced with MSBuild v4.0.
I know this thread is a bit old, but another possible solution, as I presume you needed to know that build failed in order to execute some "final task", is to use:
<OnError ExecuteTargets="FinalReportTarget;CleanupTarget" />
That would fail the build in case of error, but execute the "FinalReportTarget" and "CleanupTarget".
ContinueOnError="true" is not needed in this case.
You could capture TargetOutputs and check them for error conditions afterwards, but that's still quite hackish.
If you only want to check if MSBuild task failed, use Exec task. Set IgnoreExitCode to true and check ExitCode output value. If not zero, something is wrong.
If you need the list of build errors, use /fileloggerparameters command line switch to log errors only to some specific file:
/flp1:logfile=errors.txt;errorsonly
But if another task inside some target (e.g. Copytask) raised an error the Log.HasLoggedErrors returns false.
Didn't know comments have length limits...
Log is scoped to individual targets or to be more specific tasks, and (as far as I'm aware) there is no way to get a "global" one, may be through reflection on the buildengine instance, or writing custom logger and passing it through command line. In any case, what are you building as project? HasLoggedErrors works as expected (and has been working unchanged for years), it shows if project being built logged any errors. It doesn't, and shouldn't, have any control over logging of other tasks (that might use other types of loggers). If you want a global one, one way is to build self but with different target, for example your DefaultTargets one launches your custom MSBuildWrapper task pointing to itself (ie $(MSBuildProjectFile)) but with a different target that does other builds, copies, etc, in theory it should simulate a global HasLoggedErrors...

CruiseControl.net dynamicValue for sourcecontrol branch

maybe i'm just not seeing it, but i'd like a way to "inject" the value for branch (externally somehow) for a set of predefined build projects.
we have builds configured for Project1, Project2 and Project3. But at any time, the projects may take from a different branch, based on merge schedules. i'd like to store the Project=>branch mapping in either an external file or database, then dynamically inject it into the config file when we do a ForceBuild.
The following block is used in all 3 project config files, which are referenced at the bottom (end) of the cnet.config file.
<cb:define name="cvs-block">
<sourcecontrol type="cvs">
<cvsroot>:sspi;username=johnDoe;password=passTheSalt;hostname=127.0.0.1;port=1776:/$(repository)</cvsroot>
<module>"$(module)"</module>
<executable>c:\Program Files (x86)\cvsnt\cvs.exe</executable>
<workingDirectory>D:\CruiseBuild\$(workingDir)</workingDirectory>
<branch>[SOME EXTERNALLY DYNAMIC VALUE]</branch>
<autoGetSource>true</autoGetSource>
<timeout units="minutes">20</timeout>
</sourcecontrol>
</cb:define>
<cb:include href="D:\CruiseBuild\ACME-project1.xml" xmlns:cb="urn:ccnet.config.builder" />
<cb:include href="D:\CruiseBuild\ACME-project2.xml" xmlns:cb="urn:ccnet.config.builder" />
<cb:include href="D:\CruiseBuild\ACME-project3.xml" xmlns:cb="urn:ccnet.config.builder" />
Just generate a file (injectpath.config) with a defined value:
<cb:define branchpath="yourpath"/>
...and then include it into your config file shown above at the top.
Change your [SOME DYNAMIC VALUE] to $(branchpath).

Simulating the Maven2 filter mechanism using Ant

I have a properties file, let say my-file.properties.
In addition to that, I have several configuration files for my application where some information must be filled regarding the content of my-file.properties file.
my-file.properties:
application.version=1.0
application.build=42
user.name=foo
user.password=bar
Thus, in my configuration files, I will find some ${application.version}, ${user.name} that will be replaced by their value taken in the properties file...
When I build my application using Maven2, I only need to specify the properties file and say that my resources files are filtered (as in this answer to another problem). However, I need to achieve the same thing by using only Ant.
I've seen that Ant offers a filter task. However, it forces me to use the pattern #property.key# (i.e. #user.name# instead of #{user.name}) in my configuration files, which is not acceptable in my case.
How can I solve my problem?
I think expandproperties is what you are looking for. This acts just like Maven2's resource filters.
INPUT
For instance, if you have src directory (one of many files):
<link href="${css.files.remote}/css1.css"/>
src/test.txt
PROCESS
And in my ANT build file we have this:
<project default="default">
<!-- The remote location of any CSS files -->
<property name="css.files.remote" value="/css/theCSSFiles" />
...
<target name="ExpandPropertiesTest">
<mkdir dir="./filtered"/>
<copy todir="./filtered">
<filterchain>
<expandproperties/>
</filterchain>
<fileset dir="./src" />
</copy>
</target>
</project>
build.xml
OUTPUT
*When you run the ExpandPropertiesTest target you will have the following in your filtered directory: *
<link href="/css/theCSSFiles/css1.css"/>
filtered/test.txt
You can define a custom FilterReader. So you have a couple of choices:
Extend/copy the org.apache.tools.ant.filters.ReplaceTokens class and define a Map property that references another properties file containing all the replacements. This is still a bit of a chore as you have to define all the replacements.
Extend/copy the org.apache.tools.ant.filters.ReplaceTokens class with additional processing that just substitutes the matched token with a version with the correct garnish. Of course you'd have to be really careful where you use this type as it will match anything with the begin and end token.
So in the read() method of ReplaceTokens, replace:
final String replaceWith = (String) hash.get(key.toString());
with a call to a getReplacement() method:
...
final String replaceWith = getReplacement(key.toString);
...
private String getReplacement(String key) {
//first check if we have a replacement defined
if(has.containsKey(key)) {
return (String)hash.get(key);
}
//now use our built in rule, use a StringBuilder if you want to be tidy
return "$" + key + "}";
}
To use this, you'd ensure your class is packaged and on Ant's path and modify your filter:
<filterreader classname="my.custom.filters.ReplaceTokens">
<!-- Define the begin and end tokens -->
<param type="tokenchar" name="begintoken" value="$"/>
<param type="tokenchar" name="endtoken" value="}"/>
<!--Can still define explicit tokens, any not
defined explicitly will be replaced by the generic rule -->
</filterreader>
One hooooooorible way to make this work, inspired by the solution of Mnementh, is with the following code:
<!-- Read the property file -->
<property file="my-file.properties"/>
<copy todir="${dist-files}" overwrite="true">
<fileset dir="${src-files}">
<include name="*.properties"/>
</fileset>
<filterchain>
<filterreader classname="org.apache.tools.ant.filters.ReplaceTokens">
<!-- Define the begin and end tokens -->
<param type="tokenchar" name="begintoken" value="$"/>
<param type="tokenchar" name="endtoken" value="}"/>
<!-- Define one token per entry in the my-file.properties. Arggh -->
<param type="token" name="{application.version" value="${application.version}"/>
<param type="token" name="{user.name" value="${user.name}"/>
...
</filterreader>
</filterchain>
</copy>
Explanations:
I am using the ReplaceTokens reader to look for all $...} pattern. I cannot search for ${...} patterns, as the begintoken is a char and not a String. Then, I set the list of tokens starting with a { (i.e. I see {user.name instead of user.name). Hopefully, I have "only" about 20 lines in my-file.properties, so I need to define "only" 20 tokens in my Ant file...
Is there any simple and stupid solution to solve this simple and stupid problem??
Ant knows a concept named Filterchains, that is useful here. Use the ReplaceTokens-filter and specify the begintoken and endtoken as empty (normally that's '#'). That should do the trick.