生成Ant构建文件(Generate Ant build file)

2019-08-16 18:04发布

我有以下项目结构:

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

根/ comp / env的/版本/ 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>

根/ 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>

根/ COMP / Build.xml是:

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

每个构建文件导入父生成文件,每个孩子继承重写父任务/属性。

我需要的是让不运行任何生成的构建XML。

例如,如果我对根/ comp / env的/版/运行“蚁族”(或类似的东西),我想获得以下的输出:

<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>

是否有一个Ant插件来做到这一点? 使用Maven? 什么是我的选择,如果不是?

编辑: 我需要这样的东西“MVN帮助:有效-POM”的蚂蚁。

Answer 1:

Based on the description of the import task, it works very much like an entity includes with two additional features:

  • target overriding
  • special properties

For the purposes of viewing the "effective build" I don't think the special properties processing is required (though it could be added by iterating the inserted targets). So the processing to achieve this becomes.

  1. Parse the build.xml to a DOM
    • For each top-level include tag found (only top-level are allowed), find the referenced source file.
    • Parse the referenced build.xml
    • insert any content from the referenced build.xml that don't collide with those in the current file.
    • Repeat step 2 for referenced build.xml file(s) until no more found
    • Output the resultant DOM

You can define a custom Ant task so that this processing can be defined in a task to be run from within your build. See this tutorial for more details.

Here's a basic implementation that recurses through imports and inserts the DOM elements from the referenced files. There's almost certainly a few bugs in it as I threw it together, but it should do largely what you're after:

/**
 * 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;
}


Answer 2:

Eclipse的理解蚂蚁的文件。 你可以看到什么样的任务是在你的内心的build.xml可见。 它不会是相当您所要求的格式,但它可能满足您的需求。



Answer 3:

我一直在写现在Ant构建脚本7-8年,但我真的不明白你正试图在这里实现什么。 也许是我,但我担心,即使你得到你的编译工作(我敢肯定,你也可以),几乎没有任何人会明白/维护它。

为什么不能让事情很简单,有兄弟项目?

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

个人build.xml文件可以导入任务定义别的地方(使用Macrodef本)和您的顶层build.xml将依次调用个别?

一旦你的基本建成运行,可以玩的常春藤或Maven更有趣的东西。

但是,如果你真的想生成构建文件,你可以给Groovy和它的模板引擎一试。



Answer 4:

我建议,让他们使用的依赖关系树是什么蚂蚁是真的好构建您的构建文件。 如果你关注@弗拉基米尔的意见,并组织你的构建文件一样,那么你可以在“根”一个构建文件,并让它递归执行你的构建。 例如:

<!-- 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>


Answer 5:

听起来gradle这个可以帮助你。 摇篮能够导入您的Ant build.xml文件 。 然后,你就可以开始试运行 ,以获得要执行的目标清单。



Answer 6:

你可以在Java中,Groovy中,红宝石写一个脚本/应用程序或任何你知道最好的...脚本将解析构建文件的XML和更换由真正换出适当的DOM节点“过度缠身”的目标与他们的覆盖。 你将最终获得您的复合build.xml文件作为然后可以连载了DOM。

你可以保持手头的剧本在你的源代码控制,使您可以根据需要重新生成。

这听起来可能有点极端,但它听起来像大多数其他解决方案都出来了,你在找什么范围。

注意:您可以在Ant中运行一些脚本LANGS,所以你仍然可以使用,并为您的引发剂。

祝好运。



文章来源: Generate Ant build file
标签: java xml ant build