生成Ant构建文件

时间:2009-09-24 04:33:25

标签: java xml ant build

我有以下项目结构:

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

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>

每个构建文件都会导入父构建文件,每个子项继承覆盖父级任务/属性。

我需要的是获取生成的构建XML而不运行任何东西

例如,如果我在root / comp / env / version /上运行“ant”(或类似的东西),我想获得以下输出:

<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?如果没有,我有什么选择?

修改 对于Ant,我需要类似“mvn help:effective-pom”的内容。

6 个答案:

答案 0 :(得分:5)

根据import task的描述,它非常像实体包含两个附加功能:

  • 目标覆盖
  • 特殊属性

为了查看“有效构建”,我不认为需要特殊属性处理(尽管可以通过迭代插入的目标来添加它)。所以实现这一目标的过程就变成了。

  1. 将build.xml解析为DOM
    • 对于找到的每个顶级包含标记(仅允许顶级),找到引用的源文件。
    • 解析引用的build.xml
    • 插入引用的build.xml中不会与当前文件中的内容发生冲突的任何内容。
    • 对引用的build.xml文件重复步骤2,直到找不到
    • 输出结果DOM
  2. 您可以定义自定义Ant任务,以便可以在要在构建中运行的任务中定义此处理。有关详细信息,请参阅此tutorial

    这是一个基本实现,它通过导入进行递归并从引用的文件中插入DOM元素。当我把它扔在一起时几乎肯定会有一些错误,但它应该在很大程度上完成你所追求的:

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

答案 1 :(得分:0)

Eclipse了解Ant文件。您可以看到内部build.xml中可见的任务。它不是您要求的格式,但它可能满足您的需求。

答案 2 :(得分:0)

我已经编写了7到8年的Ant构建脚本,但我真的不明白你想要在这里实现什么。也许是我,但我担心,即使你让你的构建工作(我相信你可以),几乎没有其他人会理解/维护它。

为什么不让事情变得非常简单并且有兄弟项目?

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

单个build.xml个文件可以在其他地方导入任务定义(使用Macrodef),你的顶级build.xml会按顺序调用各个文件吗?

一旦你的基本版本运行,你可以玩Ivy或Maven来获得更多有趣的东西。

但是如果确实想要生成构建文件,那么可以尝试使用Groovy及其模板引擎。

答案 3 :(得分:0)

我建议您构建构建文件,以便它们使用依赖树,这是蚂蚁真正擅长的。如果您遵循@ Vladimir的建议并构建类似的构建文件,那么您可以在“root”中拥有一个构建文件,并以递归方式执行构建。例如:

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

答案 4 :(得分:0)

听起来像gradle可以帮到你。 Gradle能够import your ant build.xml file。 然后,您可以启动dry run以获取执行的目标列表。

答案 5 :(得分:0)

你可以用Java,Groovy,Ruby或者你最了解的任何东西编写一个脚本/应用程序......脚本会解析构建文件的xml,并通过实际交换来替换“over-ridden”目标及其覆盖适当的DOM节点。你最终得到的是你的复合build.xml作为一个DOM,然后可以序列化。

您可以将脚本保存在源代码管理中,以便您可以根据需要进行重新生成。

这可能听起来有点极端,但听起来大多数其他解决方案都超出了您所寻找的范围。

注意:您可以从Ant运行一些脚本lang,因此您仍然可以使用And作为启动器。

祝你好运。