Best practice for code modification during ant build
Admitted, this doesn't sound like a best practice altogether, but let me explain. During the build, we need to paste the build number and the system version into a class whose sole purpose is to contain these values and make them accessible.
Our first idea was to use system properties, but due to the volatility of the deployment environment (an other way of saying "the sysadmins are doing weird unholy creepy things") we would like to have them hard-coded.
Essentially I see 4 possibilities to achieve it in ant :
use
<replace>
on a token in the classThe problem with this approach is that the file is changed, so you have to replace the token back after compilation with a
<replaceregexp>
...sooo ugly, I don't want to touch source code with regex. Plus temporal dependencies.copy the file, make replace on the copy, compile copy, delete copy
One one has to mind the sequence - the origi开发者_StackOverflownal class has to be compiled first in order to be overwritten by the copy. Temporal dependencies are ugly too.
copy the file, replace the token on the original, compile, replace the stained original with the copy
Same temporal dependency issue unless embedded in the compile target. Which is ugly too, because all our build files use the same imported compile target.
create the file from scratch in the build script / store the file outside the source path
Is an improvement over the first three as there are no temporal dependencies, but the compiler/IDE is very unhappy as it is oblivious of the class. The red markers are disturbingly ugly.
What are your thoughts on the alternatives?
Are there any best practices for this?
I sure hope I have missed a perfectly sane approach.
Thank you
EDIT
We ended up using the manifest to store the build number and system version in the Implementation-Version
attribute, unsing MyClass.class.getPackage().getImplementationVersion()
. I have found this solution was one of the answers to this thread, which was posted in the comment by andersoj
I think a simpler approach would be to have your Version.java
class read from a simple .properties file included in the JAR, and just generate this .properties file at build-time in the Ant build. For example just generate:
build.number = 142
build.timestamp = 5/12/2011 12:31
The built-in <buildnumber>
task in Ant does half of this already (see the second example).
#2 is generally the way I've seen it done, except that your not-ready-to-compile sources should be in a separate place from you ready-to-compile sources. This avoids the temporal issues you talk about as it should only be compiled once.
This is a common pattern that shows up all the time in software build processes.
The pattern being: Generate source from some resource and then compile it.
This applies to many things from filtering sources before compilation to generating interface stubs for RMI, CORBA, Web Services, etc...
Copy the source to a designated 'generated sources' location and do the token replacement on the copies files to generate sources, then compile the generated sources to your compiled classes destination.
The order of compilation will depend on whether or not your other sources depend on the generated sources.
My solution would be to:
use on a token in the class:
<replace dir="${source.dir}" includes="**/BuildInfo.*" summary="yes"> <replacefilter token="{{BUILD}}" value="${build}" /> <replacefilter token="{{BUILDDATE}}" value="${builddate}" /> </replace>
This replacement should only take place in the build steps performed by your build system, never within a compile/debug session inside an IDE.
The build system setup should not submit changed source code back to the source repository anyway, so the problem of changed code does not exist with this approach.
In my experience it does not help when you place the build information in a property file, as administrators tend to keep property files while upgrading - replacing the property file that came out of the install. (Build information in a property file is informational to us. It gives an opportunity to check during startup if the property file is in synch with the code version.)
I remember we used the 4th approach in a little different way. You can pass release number to the ant script while creating a release.Ant script should include that in the release(config/properties file) and your class should read it from there may be using properties file or config file.
I always recommend to create some sort of directory and put all built code there. Don't touch the directories you checked out. I usually create a target
directory and place all files modified and built there.
If there aren't too many *.java files (or *.cpp files), copy them to target/source' and compile there. You can use the
task with a
` to modify this file one file with the build number as you copy it.
<javac srcdir="${target.dir}/source"
destdir="${target.dir}/classes"
[yadda, yadda, yadda]
</java>
This way, you're making no modification in the checked out source directory, so no one will accidentally check in the changes. Plus, you can do a clean by simply deleting the target
directory.
If there are thousands, if not millions of *.java
files, then you can copy the templates to target/source
and then compile the source in both {$basedir}/source
and target/source
. That way, you're still not mucking up the checked out code and leaving a chance that someone will accidentally check in a modified version. And, you can still do a clean
by simply removing target
.
I was looking for a solution to the same problem, reading this link: http://ant.apache.org/manual/Tasks/propertyfile.html I was able to findout the solution.
I work with netbeans, so I just need to add this piece of code to my build.xml
<target name="-post-init">
<property name="header" value="##Generated file - do not modify!"/>
<propertyfile file="${src.dir}/version.prop" comment="${header}">
<entry key="product.build.major" type="int" value="1" />
<entry key="product.build.minor" type="int" default="0" operation="+" />
<entry key="product.build.date" type="date" value="now" />
</propertyfile>
</target>
This will increment the minor version each time yo compile the project with clean and build. So you are save to run the project any time that the minor version will stay still.
And I just need to read the file in Runtime. I hope this help.
精彩评论