Showing posts with label Maven. Show all posts
Showing posts with label Maven. Show all posts

Sunday, March 24, 2013

Whateva! I do what I want!

My projects are now all deploying with Maven, except when scp stalls out. I'm still troubleshooting that. Each project's wiki page displays an infobox including links to the latest Maven build. I've been slowly writing other wiki content. And I cast my first line toward getting Weapon M into classrooms. I emailed three professors at the University of Arizona and a blogger who writes about computer science education.

I could write a whole post about the subjective differences between commercial and open-source development. But the biggest difference is that as long as nobody is giving me feedback on my open source projects, my only guide when it comes to what to work on is what I feel like working on. I'm the only person with input on project goals, but nobody holds me accountable if I don't stick to my own plans.

And lately, despite my commitment to moving ahead with JPlex, I'm obsessed with the idea of writing a better lexer and parser generator. So maybe I will. Maybe I will...

Saturday, March 16, 2013

Bossy Maven

The nagging issue I have with Maven right now is that if you want the Maven version and the project version to be in sync, and you want to follow the DRY principle with the source of the version information, and the project needs to be aware of its own version, e.g., to display it, then the project becomes dependent on Maven to supply the version. Maven can filter a project resource and inject the version string from the POM, or a project can access the metadata that Maven adds to its jars. But the reverse is not true; the Maven project version property must be hard-coded in the POM. It's among the few properties that are excluded from being defined by other properties.

I don't like the idea of projects being dependent on a particular build tool, but there's not much I can do about it right now. I have a couple of ideas that I may pursue later. Maybe I could have a parent POM filter the project POM and then use the filtered version to build the project. That seems pretty hackish. Or maybe I could set up another execution of maven-assembly-plugin to produce an Eclipse project archive with the version resource already filtered.

For now, I think I'll let DRY slide and duplicate the version string in the POM and source. I'll just add comments to remind me to change it in both places.

Thursday, March 14, 2013

Whipping Maven, part deux

Success! I've got it attaching the zip and uploading everything automatically. This snippet shows the difference between my new POM and the one I posted last night:

            <!-- assemble a zip file with the deps in lib/ -->
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <id>dist-zip</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <descriptors>
                                <descriptor>assembly.xml</descriptor>
                            </descriptors>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>

        <extensions>
            <extension>
                <groupId>org.apache.maven.wagon</groupId>
                <artifactId>wagon-ssh</artifactId>
                <version>2.4</version>
            </extension>
        </extensions>
    </build>

    <distributionManagement>
        <repository>
            <id>chalcodes</id>
            <url>${chalcodes.url}</url>
        </repository>
    </distributionManagement>

There are a few things to note here. The ${chalcodes.url} property is injected by my ~/.m2/settings.xml. The SSH wagon extension requires that the URL contains the absolute path of the repository on the host, e.g., scp://example.com:1234/home/user/public_html/maven. If you supply a path relative to your home directory like you normally would with scp, it tries to create the directory /public_html, which of course fails. Also note that the assembly plugin goal must be single and not assembly.

There was one other hitch that took me a while to figure out. The wagon was able to create the subdirectories it needed, but it couldn't upload the files. It was failing with this error:

org.apache.maven.wagon.CommandExecutionException: Exit code: 0 - stdin: is not a tty

It turned out that my .bash_profile was including a file that contained the command stty. This doesn't cause any problems for scp, but apparently it screws up the Java implementation that Maven uses.

And here's the result: a sweet directory full of sweet files. It even generates the checksums automatically!

Wednesday, March 13, 2013

Bending Maven to my will

Sometimes you have to let your subconscious chew on something for a month or two before you can digest it. Such is my relationship with Maven.

Last night I dove into Maven: The Complete Reference again with one very specific goal in mind: creating an artifact similar to what Eclipse produces when you export an executable jar with its dependencies in a directory next to it. I came up gasping for air with this solution clenched in my teeth.

The main class is a "Hello, World!" program. It prints its message through slf4j just so I'd have an excuse to bring in some dependencies. Here's the POM:

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

    <groupId>com.chalcodes.fnord</groupId>
    <artifactId>HelloMaven</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.2</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.9</version>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
            </plugin>

            <!-- make an executable jar *without* deps -->
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>fnord.Hello</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <!-- assemble a zip file with the deps in lib/ -->
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <descriptors>
                        <descriptor>assembly.xml</descriptor>
                    </descriptors>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

And here's the assembly descriptor it references:

<assembly>
    <id>complete</id>
    <formats>
        <format>zip</format>
    </formats>

    <files>
        <file>
            <source>${project.build.directory}/${project.build.finalName}.${project.packaging}</source>
            <destName>${project.artifactId}.${project.packaging}</destName>
            <fileMode>0644</fileMode>
        </file>
    </files>

    <dependencySets>
        <dependencySet>
            <useProjectArtifact>false</useProjectArtifact>
            <outputDirectory>lib</outputDirectory>
        </dependencySet>
    </dependencySets>
</assembly>
This produces a file called HelloMaven-1.0-complete.zip. It contains a directory called HelloMaven-1.0, which in turn contains the executable HelloMaven.jar and the lib/ directory full of dependencies. The classpath in the manifest of the executable jar correctly references each dependency by its relative path.

I still need to figure out how to attach the zip file to the Maven project (if that's the right term) and automatically deploy it to my repo. But I feel like the tables have turned in my war with Maven. This will make building and deploying Weapon M a breeze, not only for me but for anyone who wants to customize it.