Introduce features-builder 10/11910/3
authorRobert Varga <rovarga@cisco.com>
Sun, 12 Oct 2014 08:32:04 +0000 (10:32 +0200)
committerRobert Varga <rovarga@cisco.com>
Mon, 13 Oct 2014 21:42:45 +0000 (23:42 +0200)
The planned rework of version handling requires the features artifacts
to stop relying on maven properties for versions. This patch introduces
a base pom (e.g. suitable for being a parent), which provides the
versioning functionality based of dependency tree.

The user will typically use features-builder as their parent pom,
activate the plugins mentioned in pluginManagement and provide a
template features file, where the required version is marked by a
{{VERSION}} placeholder. The location of this template is hard-coded to
src/features, but the file name is controlled by features.file property
(features.xml) by default.

The build process' plugin wiring will take care of processing the
template and looking through the artifact's dependency tree. If a
required version is not found, the build is forced to fail.

Change-Id: If3252ffe8ca2b099ffd10c1cc37b47b3a1ac7698
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
common/features-builder/pom.xml [new file with mode: 0644]
common/pom.xml

diff --git a/common/features-builder/pom.xml b/common/features-builder/pom.xml
new file mode 100644 (file)
index 0000000..0576893
--- /dev/null
@@ -0,0 +1,250 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>odlparent</artifactId>
+        <version>1.5.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>features-builder</artifactId>
+    <packaging>pom</packaging>
+    <version>0.7.0-SNAPSHOT</version>
+
+    <properties>
+        <features.file>features.xml</features.file>
+    </properties>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+
+        <pluginManagement>
+            <plugins>
+                <!-- generate dependencies versions -->
+                <plugin>
+                    <artifactId>maven-dependency-plugin</artifactId>
+                      <executions>
+                          <execution>
+                              <phase>generate-resources</phase>
+                              <goals><goal>resolve</goal></goals>
+                              <configuration>
+                                  <outputFile>${project.build.directory}/dependencies.txt</outputFile>
+                              </configuration>
+                          </execution>
+                      </executions>
+                </plugin>
+                <plugin>
+                    <groupId>com.alexecollins.maven.plugin</groupId>
+                    <artifactId>script-maven-plugin</artifactId>
+                    <version>1.0.0</version>
+                    <executions>
+                        <execution>
+                        <id>add-version-to-features</id>
+                        <phase>generate-resources</phase>
+                            <goals>
+                                 <goal>execute</goal>
+                            </goals>
+                            <configuration>
+                                <language>groovy</language>
+                                <script>
+                                    /**
+                                     * Placeholder, which is used in src/feature/features.xml
+                                     * to mark version which should be inserted from dependencies.
+                                     * Currently works only for bundle and configfile tags
+                                     * with mvn: url schema, and needs to be used 
+                                     * as third component of schema.
+                                     * eg. mvn:group/artefact/{{VERSION}}
+                                     */
+                                    def versionPlaceholder = "{{VERSION}}"
+                                    /**
+                                     * Path to features.xml which uses versionPlaceholder.
+                                     * This will be processed by this script.
+                                     *
+                                     */
+                                    def featureFilePath = "src/main/feature/features.xml"
+                                    // Contains mapping of groupID:artefactID to versoin
+                                    def versionMap = new HashMap();
+                                    /* Loads transitive dependency list generated from
+                                     * maven-dependency-plugin resolve goal
+                                     * and populates map
+                                     */
+                                    def dependencies = new File(project.build.directory,"dependencies.txt")
+                                    dependencies.eachLine {
+                                        def cmps = it.trim().split(":")
+                                        // 0 - groupId
+                                        // 1 - artifactId
+                                        // 2 - Type
+                                        // 3 - Version
+                                        if(cmps.length >= 4) {
+                                            def id = cmps[0] + ":" + cmps[1]
+                                            versionMap[id] = cmps[3]
+                                        }
+                                    }
+
+                                    /*
+                                     * Takes splitted mvn: URL, looks for placeholder
+                                     * and returns new mvn: URL with version learned
+                                     * from dependency plugin.
+                                     *
+                                     * If referenced bundle is not dependency (direct or transitive)
+                                     * throws an exception and fails build.
+                                     *
+                                     */
+                                    def updatedURLFromProject = { args ->
+                                        // 0 - groupID, 1 - artifactID
+                                        // 2 - version, 3 - type, 4 - Classifier
+
+                                        def groupId = args[0];
+                                        def artifactId = args[1];
+                                        def id = groupId + ":" + artifactId
+                                        def dependencyVersion = versionMap[id]
+                                        if(dependencyVersion != null) {
+                                            // Overriding version
+                                            args[2] = dependencyVersion
+                                            return "mvn:" + args.join("/")
+                                        }
+                                        throw new IllegalArgumentException("Feature dependency $groupId:$artifactId is not dependecy of project.")
+                                    }
+
+
+                                    def updateMavenDependency  = { dep ->
+                                       def mvnUrl = dep.text()
+                                       if(mvnUrl.startsWith("mvn:")) {
+                                         def components =  mvnUrl.substring(4).split("/")
+                                         if(components[2] == versionPlaceholder) {
+                                         dep.value = updatedURLFromProject(components)
+                                         }
+                                       }
+                                    }
+
+                                    def featureFile = new File(project.basedir,featureFilePath)
+                                    def root = new XmlParser().parse(featureFile)
+
+                                    root.feature.each { feature ->
+                                        println "[INFO] Processing feature: ${feature.@name}"
+                                        feature.bundle.each updateMavenDependency
+                                        feature.configfile.each updateMavenDependency
+                                    }
+
+                                    def outDir = new File(project.build.directory,"generated-resources/script")
+                                    outDir.mkdirs();
+                                    def outFile = new File(outDir,"features.xml")
+                                    def outWriter = outFile.newPrintWriter("ASCII");
+                                    xmlPrinter = new XmlNodePrinter(outWriter);
+                                    xmlPrinter.preserveWhitespace = true
+                                    xmlPrinter.print(root)
+                                    outWriter.close();
+                                </script>
+                            </configuration>
+                        </execution>
+                    </executions>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.codehaus.groovy</groupId>
+                            <artifactId>groovy</artifactId>
+                            <version>1.8.6</version>
+                        </dependency>
+                    </dependencies>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.karaf.tooling</groupId>
+                    <artifactId>karaf-maven-plugin</artifactId>
+                    <version>${karaf.version}</version>
+                    <extensions>true</extensions>
+                    <executions>
+                        <execution>
+                            <id>features-create-kar</id>
+                            <goals>
+                                <goal>features-create-kar</goal>
+                            </goals>
+                            <configuration>
+                                <featuresFile>${project.build.directory}/classes/${features.file}</featuresFile>
+                            </configuration>
+                        </execution>
+                    </executions>
+                    <!-- There is no useful configuration for the kar mojo. The features-generate-descriptor mojo configuration may be useful -->
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>build-helper-maven-plugin</artifactId>
+                    <executions>
+                        <execution>
+                        <phase>generate-resources</phase>
+                        <goals><goal>add-resource</goal></goals>
+                        <configuration>
+                            <resources>
+                              <resource>
+                                <directory>${project.build.directory}/generated-resources/script</directory>
+                                <filtering>true</filtering>
+                              </resource>
+                            </resources>
+                        </configuration>
+                        </execution>
+                        <execution>
+                            <id>attach-artifacts</id>
+                            <phase>package</phase>
+                            <goals>
+                                <goal>attach-artifact</goal>
+                            </goals>
+                            <configuration>
+                                <artifacts>
+                                    <artifact>
+                                        <file>${project.build.directory}/classes/${features.file}</file>
+                                        <type>xml</type>
+                                        <classifier>features</classifier>
+                                    </artifact>
+                                </artifacts>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-resources-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>filter</id>
+                            <phase>generate-resources</phase>
+                            <goals>
+                                <goal>resources</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <configuration>
+                        <dependenciesToScan>
+                            <dependency>org.opendaylight.yangtools:features-test</dependency>
+                        </dependenciesToScan>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <dependencies>
+        <!-- test the features.xml -->
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>features-test</artifactId>
+            <version>0.7.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
index f9a2d2423fd0a0107cdbcb6cb10785683a63ceca..a2e57da7e957b2a1a168135704cc41f449bdffad 100644 (file)
@@ -26,6 +26,7 @@
         <module>checkstyle-logging</module>
         <module>concepts</module>
         <module>features</module>
+        <module>features-builder</module>
         <module>features-test</module>
         <module>mockito-configuration</module>
         <module>object-cache-api</module>