Create Ant Build File

I have the following project structure:

root/ comp/ env/ version/ build.xml build.xml build.xml 

Where root / comp / env / version / build.xml :

 <project name="comp-env-version" basedir="."> <import file="../build.xml" optional="true" /> <echo>Comp Env Version tasks</echo> <target name="run"> <echo>Comp Env Version run task</echo> </target> </project> 

root / comp / env / build.xml :

 <project name="comp-env" basedir="."> <import file="../build.xml" optional="true" /> <echo>Comp Env tasks</echo> <target name="run"> <echo>Comp Env run task</echo> </target> </project> 

root / comp / build.xml :

 <project name="comp" basedir="."> <echo>Comp tasks</echo> </project> 

Each assembly file imports the parent assembly file, and each child inherits and overrides the parent tasks / properties.

I need to get the generated XML assembly file without running .

For example, if I run "ant" (or something like that) on root / comp / env / version /, I would like to get the following output:

 <project name="comp-env-version" basedir="."> <echo>Comp tasks</echo> <echo>Comp Env tasks</echo> <echo>Comp Env Version tasks</echo> <target name="run"> <echo>Comp Env Version run task</echo> </target> </project> 

Is there an Ant plugin for this? With Maven? What are my options if not?

EDIT: I need something like "mvn help: effective-pom" for Ant.

+4
source share
6 answers

Based on the description of the import task, it is very similar to an object that includes two additional functions:

  • target redirection
  • special properties

To view an “efficient build”, I don’t think that special handling of the properties is required (although it can be added by iterating the inserted targets). Thus, the processing to achieve this becomes.

  • Parse the build.xml file in the DOM
    • For each top level tag found (only the top level is available), find the reference source file.
    • Parse the build.xml reference file
    • insert any content from the build.xml reference file that does not interfere with the files in the current file.
    • Repeat step 2 for the build.xml referenced files until you find more
    • Print Resulting DOM

You can define a custom Ant task so that this processing can be defined in a task running from your assembly. See the tutorial for more details.

Here is a basic implementation that recursively imports and inserts DOM elements from referenced files. There are almost certainly some errors there when I reset them, but it should do basically what you need:

 /** * Reads the build.xml and outputs the resolved build to stdout */ public static void main(String[] args) { try { Element root = new EffectiveBuild().parse(new File(args[0])); XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); outputter.output(root, System.out); } catch (Exception e) { // TODO handle errors e.printStackTrace(); } } /** * Get the DOM for the passed file and iterate all imports, replacing with * non-duplicate referenced content */ private Element parse(File buildFile) throws JDOMException, IOException { Element root = getRootElement(buildFile); List<Element> imports = root.getChildren("import"); for (int i = 0; i < imports.size(); i++) { Element element = imports.get(i); List<Content> importContent = parseImport(element, root, buildFile); int replaceIndex = root.indexOf(element); root.addContent(replaceIndex, importContent); root.removeContent(element); } root.removeChildren("import"); return root; } /** * Get the imported file and merge it into the parent. */ private List<Content> parseImport(Element element, Element currentRoot, File buildFile) throws JDOMException, IOException { String importFileName = element.getAttributeValue("file"); File importFile = new File(buildFile.getParentFile(), importFileName) .getAbsoluteFile(); if (importFileName != null) { Element importRoot = getRootElement(importFile); return getImportContent(element, currentRoot, importRoot, importFile); } return Collections.emptyList(); } /** * Replace the passed element with the content of the importRoot * (not the project tag) */ private List<Content> getImportContent(Element element, Element currentRoot, Element importRoot, File buildFile) throws JDOMException, IOException { if (currentRoot != null) { // copy all the reference import elements to the parent if needed List<Content> childNodes = importRoot.cloneContent(); List<Content> importContent = new ArrayList<Content>(); for (Content content : childNodes) { if (content instanceof Element && ((Element) content).getName().equals("import")) { importContent.addAll(parseImport((Element) content, currentRoot, buildFile)); } if (!existsInParent(currentRoot, content)) { importContent.add(content); } else { // TODO note the element was skipped } } return importContent; } return Collections.emptyList(); } /** * Return true if the content already defined in the parent */ private boolean existsInParent(Element parent, Content content) { if (content instanceof Text) { if (((Text) content).getText().trim().length() == 0) { // let the pretty printer deal with the whitespace return false; } return true; } if (content instanceof Element) { String id = ((Element) content).getAttributeValue("name"); String name = ((Element) content).getName(); List<Content> parentContent = parent.getChildren(); if (id != null) { for (Content content2 : parentContent) { if (content2 instanceof Element && ((Element) content2).getName().equals(name)) { String parentId = ((Element) content2) .getAttributeValue("name"); if (parentId != null && parentId.equals(id)) { return true; } } } } } return false; } /** * Parse the passed file. */ private Element getRootElement(File buildFile) throws JDOMException, IOException { SAXBuilder builder = new SAXBuilder(); builder.setValidation(false); builder.setIgnoringElementContentWhitespace(true); Document doc = builder.build(buildFile); Element root = doc.getRootElement(); return root; } 
+5
source

Eclipse understands Ant files. You can see what tasks are visible in your internal build.xml file. This will not be exactly the format you requested, but it can satisfy your needs.

0
source

I have been writing Ant build scripts for 7-8 years, but I really don’t understand what you are trying to achieve here. Maybe it's me, but I'm afraid that even if you earn your work (I'm sure you can), hardly anyone will understand / support it.

Why not keep things very simple and have projects for sisters?

 root build.xml comp build.xml env build.xml version build.xml 

can individual build.xml files import tasks that define elsewhere (using Macrodef for this), and your top level build.xml will invoke individual ones sequentially?

Once you start the basic build, you can play with Ivy or Maven for more interesting things.

But if you really want to generate assembly files, you can try Groovy and its template engine.

0
source

I would recommend structuring your assembly files so that they use a dependency tree, which really means ant. If you follow @Vladimir’s recommendations and structure your build files like this, then you can have one build file in root and recursively build your build. For instance:

 <!-- iterate finds all build files, excluding this one and invokes the named target --> <macrodef name="iterate"> <attribute name="target"/> <sequential> <subant target="@{target}"> <fileset dir="." includes="**/build.xml" excludes="build.xml"/> </subant> </sequential> </macrodef> <target name="build" description="Build all sub projects"> <iterate target="build"/> </target> <target name="clean" description="Clean all sub projects"> <iterate target="clean"/> </target> 
0
source

Sounds like gradle can help you. Gradle can import your ant build.xml file . You can then run dry run to get a list of goals to be completed.

0
source

You can write a script / application in Java, Groovy, Ruby or something that you know better ... the script will parse the xml of the assembly files and replace the "redirected" targets; it will override the actual replacement of the corresponding DOM nodes. What you get is your composite build.xml as a DOM, which can then be serialized.

You can save the script in your original control so that you can regenerate as needed.

This may seem a bit extreme, but it seems like most other solutions go beyond what you are looking for.

NOTE. You can run some scripting languages ​​from Ant, so you can still use And as your initiator.

Good luck.

0
source

All Articles