Cannot access 'kotlin.ranges.OpenEndRange' in IDEA and Kotlin 1.7.0 - kotlin

I have a Kotlin + Maven project which uses Kotlin 1.7.0. When I import the project in IDEA I see an 'error' (more on that later) in the in operator when used with an IntRange:
My IDEA version is the latest as of this writing (Build #IU-223.7571.182, built on November 29, 2022).
It looks like for some reason IDEA is trying to find the OpenEndRange type introduced in 1.7.20 (https://kotlinlang.org/docs/whatsnew1720.html#preview-of-the-operator-for-creating-open-ended-ranges), but my project is using 1.7.0 and the type doesn't exist.
I noticed that in IDEA preferences it uses a newer plugin and analyzer versions than 1.7.0:
When the project is compiled on the CLI with just maven, all works. Actually, it also builds and runs in IDEA, so the red underscore is somehow bogus and confusing.
Here is my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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.example</groupId>
<artifactId>idea_kotlin_test</artifactId>
<version>0.0.1</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<kotlin.version>1.7.0</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
and the sample code that shows the issue:
package com.example
import java.time.LocalDate
import java.time.temporal.ChronoField
fun foo(month: Int): Boolean {
val start = LocalDate.now().get(ChronoField.MONTH_OF_YEAR) // as Int
return month in (start..12) // <=== here
}
fun main() {
println(foo(1))
}
I imported the project into IDEA by just choosing 'File' -> 'Open' and choosing the project folder, it detects everything automatically.
It looks as though IDEA somehow doesn't like the LocalDate.get(TemporalField field) call, or rather its return value. When I add as Int, the 'error' goes away but then as Int is greyed out as 'needless cast' (which I expect); the 'error' is also not shown when I just assign a hardcoded Int to the start val. I tried calling different Java methods returning int but to no avail, I couldn't reproduce the issue.
What is going on and how can I tell IDEA to stop showing this strange 'error'? Why is the LocalDate.get(TemporalField) method special?

Related

Kotlin: Possible to modify functions during compile time through metaprogramming?

In dynamic languages like JavaScript/Python, it's possible to overwrite or "modify" functions during run-time. For example, in order to modify the alert function in JS, one could do:
const _prev_alert = window.alert;
window.alert = function() {
_prev_alert.apply(this, arguments);
console.log("Alert function was called!");
}
This would output "Alert function was called!" to the console every time the alert function is called.
Now, obviously something like this would be impossible during runtime in Kotlin-JVM or Kotlin-Native due to their static nature. However, in regards to those same languages, is it possible to perhaps modify a non-compiled function during compile time? I don't mean pre-compiled functions from libraries, but instead functions I have written in the same project I'm developing on.
For example, let's say I have a function I wrote called get_number. Could I modify get_number to return a different number without changing how its called in main and without modifying its code directly? (Or is there a way I COULD write the original get_number so modification IS possible down the line?)
fun main(args: Array<String>) {
println(get_number())
}
fun get_number(): Int {
return 3
}
// Without modifying the code above, can I get main to print something besides 3?
I've been reading into Kotlin's metaprogramming with Annotations and Reflections, so perhaps those could control the compiler's behavior and overwrite get_number's code? Or is this complete lunacy and the only way something of this nature would be possible is through developing my own, separate, metaprogramming wrapper over Kotlin?
Also, just to double-clarify, this question is not about Kotlin-JS and the answer (if it exists) should be applicable to Kotlin-JVM or Native.
As stated in my comment: in almost all cases, it's more desirable to use an appropriate design pattern than to start relying on things like dynamic proxies, reflection, or AOP to address this kind of problem.
That being said, the question asks whether it's possible to modify Kotlin functions at compile time through meta-programming, and the answer is "Yes". To demonstrate, below is a complete example that uses AspectJ.
Project structure
I set up a small Maven-based project with the following structure:
.
├── pom.xml
└── src
└── main
└── kotlin
├── Aop.kt
└── Main.kt
I'll reproduce the contents of all files in the sections below.
Application code
The actual application code is in the file named Main.kt, and—except for the fact that I renamed your function to be in line with Kotlin naming rules—it's identical to the code provided in your question. The getNumber() method is designed to return 3.
fun main(args: Array<String>) {
println(getNumber())
}
fun getNumber(): Int {
return 3
}
AOP code
The AOP-related code is in Aop.kt, and is very simple. It has an #Around advice with a point cut that matches the execution of the getNumber() function. The advice will intercept the call to the getNumber() method and return 42 (instead of 3).
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
#Aspect
class Aop {
#Around("execution(* MainKt.getNumber(..))")
fun getRealNumber(joinPoint: ProceedingJoinPoint): Any {
return 42
}
}
(Note how the name of the generated class for the Main.kt file is MainKt.)
POM file
The POM file puts everything together. I'm using 4 plugins:
The kotlin-maven-plugin takes care of compiling the Kotline files. The configuration includes the execution of the kapt plugin to process the AspectJ annotations.
The jcabi-maven-plugin executes the AspectJ compiler/weaver to weave the aspects into the binary classes.
The maven-jar-plugin builds the JAR file with a manifest that references the main class.
The maven-shade-plugin builds a fat JAR that includes all library dependencies.
This is the complete POM file:
<?xml version="1.0" encoding="UTF-8"?>
<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>x.y.z</groupId>
<artifactId>kotlin-aop</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<kotlin.version>1.2.61</kotlin.version>
<aspectj.version>1.9.1</aspectj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
</execution>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<version>0.14.1</version>
<executions>
<execution>
<goals>
<goal>ajc</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>MainKt</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Building and executing
To build, as with any Maven project, you just need to run:
mvn clean package
This will build a fat JAR at the target/kotlin-aop-1.0-SNAPSHOT.jar location. This JAR can then be executed using the java command:
java -jar target/kotlin-aop-1.0-SNAPSHOT.jar
Execution then gives us the following result, demonstrating that everything worked as expected:
42
(Application was built and executed using the most recent Oracle Java 8 JDK at the time of writing—1.8.0_181)
Conclusion
As the example above demonstrates, it's certainly possible to redefine Kotlin functions, but—to reiterate my original point—in almost all cases, there are more elegant solutions available to achieve what you need.

How to add multiple source directories to pom.xml

I am getting package org.testng.annotations does not exist error when I am trying to compile multiple source directories using maven. I am running webdriver tests and all of my tests are importing import org.testng.annotations. I am using IntelliJ 12 and my src directory looks like this -
src
-->main
--> java
--> package1
--> file1.java
--> package2
--> file2.java
-->test
--> java
--> package1
--> file1.java
--> file2.java
--> package2
--> package3
--> package4
--> package5
--> package6
and the build plugin I am using in the pom.xml looks like this -
<?xml version="1.0" encoding="UTF-8"?>
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">
<parent>
<artifactId>core-xxxxx</artifactId>
<groupId>core-xxxxx</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>tests</artifactId>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-api</artifactId>
<version>2.32.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<directory>target</directory>
<outputDirectory>target/classes</outputDirectory>
<testOutputDirectory>target/test-classes</testOutputDirectory>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/java</source>
<source>src/test/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
why am I getting the org.testng.annotations not found error?
The default layout for a maven project is the following:
src
main
java
test
java
within the src/main/java/ directory the package name for you productive java class source files should added. within the src/test/java/ the package name for the unit tests java class source files.
And you should NOT change the layout if you don't have really really good reasons to do so.
Furthermore redefining the defaults of Maven defaults (target, target/classes, target/test-classes etc.) is against the convention over configuration paradigm.
Be aware that you need to follow a naming convention for unit tests which means you unit tests should be named like the following:
<includes>
<include>**/*Test*.java</include>
<include>**/*Test.java</include>
<include>**/*TestCase.java</include>
</includes>
But in your case I assume that we are talking about integration tests which means you need to use the following naming convention
<includes>
<include>**/IT*.java</include>
<include>**/*IT.java</include>
<include>**/*ITCase.java</include>
</includes>
Apart from that you should now that unit tests in Maven will be executed by the maven-surefire-plugin whereas the integration tests will be executed by the maven-failsafe-plugin.
To get around the testng annotations not found errors I ended up moving the base class from src/main/java/package1/baseclass.java to src/main/test/package1/baseclass.java and the testng annotations errors resolved

Maven does not resolve a local Grails plug-in

My goal is to take a Grails web application and build it into a Web ARchive (WAR file) using Maven, and the key is that it must populate the "plugins" folder without live access to the internet. An "out of the box" Grails webapp will already have the plugins folder populated with JAR files, but the maven build script should take care of populating it, just like it does for any traditional WAR projects (such as WEB-INF/lib/ if it's empty)
This is an error when executing mvn grails:run-app with Grails 1.1 using Maven 2.0.10 and org.grails:grails-maven-plugin:1.0. (This "hibernate-1.1" plugin is needed to do GORM.)
[INFO] [grails:run-app]
Running pre-compiled script
Environment set to development
Plugin [hibernate-1.1] not installed, resolving..
Reading remote plugin list ...
Error reading remote plugin list [svn.codehaus.org], building locally...
Unable to list plugins, please check you have a valid internet connection: svn.codehaus.org
Reading remote plugin list ...
Error reading remote plugin list [plugins.grails.org], building locally...
Unable to list plugins, please check you have a valid internet connection: plugins.grails.org
Plugin 'hibernate' was not found in repository. If it is not stored in a configured repository you will need to install it manually. Type 'grails list-plugins' to find out what plugins are available.
The build machine does not have access to the internet and must use an internal/enterprise repository, so this error is just saying that maven can't find the required artifact anywhere. That dependency is already included with the stock Grails software that's installed locally, so I just need to figure out how to get my POM file to unpackage that ZIP file into my webapp's "plugins" folder.
I've tried installing the plugin manually to my local repository and making it an explicit dependency in POM.xml, but it's still not being recognized. Maybe you can't pull down grails plugins like you would a standard maven reference?
mvn install:install-file -DgroupId=org.grails -DartifactId=grails-hibernate -Dversion=1.1 -Dpackaging=zip -Dfile=%GRAILS_HOME%/plugins/grails-hibernate-1.1.zip
I can manually setup the Grails webapp from the command-line, which creates that local ./plugins folder properly. This is a step in the right direction, so maybe the question is: how can I incorporate this goal into my POM?
mvn grails:install-plugin -DpluginUrl=%GRAILS_HOME%/plugins/grails-hibernate-1.1.zip
Here is a copy of my POM.xml file, which was generated using an archetype.
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.samples</groupId>
<artifactId>sample-grails</artifactId>
<packaging>war</packaging>
<name>Sample Grails webapp</name>
<properties>
<sourceComplianceLevel>1.5</sourceComplianceLevel>
</properties>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.grails</groupId>
<artifactId>grails-crud</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.grails</groupId>
<artifactId>grails-gorm</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>opensymphony</groupId>
<artifactId>oscache</artifactId>
<version>2.4</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--
<dependency>
<groupId>org.grails</groupId>
<artifactId>grails-hibernate</artifactId>
<version>1.1</version>
<type>zip</type>
</dependency>
-->
</dependencies>
<build>
<pluginManagement />
<plugins>
<plugin>
<groupId>org.grails</groupId>
<artifactId>grails-maven-plugin</artifactId>
<version>1.0</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>init</goal>
<goal>maven-clean</goal>
<goal>validate</goal>
<goal>config-directories</goal>
<goal>maven-compile</goal>
<goal>maven-test</goal>
<goal>maven-war</goal>
<goal>maven-functional-test</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${sourceComplianceLevel}</source>
<target>${sourceComplianceLevel}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
This is a tricky problem. I was going to suggest using Grails 1.3, which allows you to pull Grails plugins from Maven-compatible repositories, but I don't think this helps with Maven (at the moment).
So, I'm going to suggest something I haven't tried myself, but may work. I have some confidence because I wrote the relevant code in the Grails Maven plugin ;) No guarantees though.
With that out of the way, let's get started. First, you need to grab the code for the relevant Grails plugins. For example, you can get Hibernate from here:
http://svn.codehaus.org/grails/trunk/grails-plugins/grails-hibernate/tags/RELEASE_1_1/
You just need a copy of the code, so a read-only checkout will be fine.
Once you have the code, run mvn grails:create-pom -DgroupId=org.grails.plugins from the root of the plugin project. This will generate a POM. Next, you will need to edit the POM and change the packaging to "grails-plugin". You should also be able to remove the <executions> block from the Grails Plugin configuration.
The POM will now allow you to build and package the Hibernate plugin, but you still have to deploy it. So add your local repository to the POM's distribution management and run mvn deploy. Once that's done, you should be able to add the plugin as a standard dependency in your application's POM.
It's hard work, but at least you should only have to do it once per version of the plugin!
I was able to come up with a workaround just to get up and running.
This requires Grails be installed locally and that GRAILS_HOME be set. It will clear out and then populate the project's "plugins" folder during the maven "validate" phase. (Insert this into the POM above.)
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<!-- clear out this project's plugins folder if it exists, otherwise you will get prompted to upgrade it after re-building -->
<delete dir="${basedir}/plugins/" includeemptydirs="true"/>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.grails</groupId>
<artifactId>grails-maven-plugin</artifactId>
<version>1.0</version>
<extensions>true</extensions>
<executions>
<execution>
<id>create plugins folder</id>
<phase>validate</phase>
<goals>
<goal>install-plugin</goal>
</goals>
<configuration>
<pluginUrl>${env.GRAILS_HOME}/plugins/grails-hibernate-1.1.zip</pluginUrl>
</configuration>
</execution>
</executions>
</plugin>
</plugins>

How to filter resource in Maven, replacing with a dependencies artifactId?

I'm trying to build a jar that has an xml file as a resource. I'd like to apply a filter to that xml to insert the name of a dependency into the xml. The filtering is working, because I was able to drop in ${project.build.finalName} and get it replaced. I found one hint that the property I'm looking for might be
${project.dependencies[0].artifactId}
but that doesn't seem to work. I'm looking to replace
<fileName>${project.dependencies[0].artifactId}</fileName>
with
<fileName>OtherLibrary</fileName>
Is that possible?
xml, which is in src/main/resources:
<somenode>
<fileName>${project.dependencies[0].artifactId}</fileName>
</somenode>
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.foo</groupId>
<artifactId>Thing</artifactId>
<version>1.0-SNAPSHOT</version>
<name>Thing</name>
<url>http://maven.apache.org</url>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<dependencies>
<dependency>
<groupId>com.pts</groupId>
<artifactId>OtherLibrary</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Damn, you're right, this property doesn't get replaced during the filtering of resources. That's weird and it sounds like a bug in the Maven Resources Plugin because this property is correctly interpolated during the process-resources phase as I'll demonstrate in the workaround I'm suggesting below (based on the maven-antrun-plugin and the replace task).
First, add the following to your POM:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-resources</phase>
<configuration>
<tasks>
<echo>${project.dependencies[0].artifactId}</echo><!-- I'm a test -->
<replace file="${project.build.outputDirectory}/myxmlfile.xml"
token="###" value="${project.dependencies[0].artifactId}"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
Then, update your XML file into:
<somenode>
<fileName>###</fileName>
</somenode>
With these changes, running mvn process-resources would produce the following result:
$ cat target/classes/myxmlfile.xml
<somenode>
<fileName>OtherLibrary</fileName>
</somenode>
Which proves the property is interpolated (but not set during maven filtering of resources)1. And if you need to filter more than one file, the replace task can take a fileset. Adapt it to suit your needs.
1 Actually, it would be nice to create a new Jira for this bug in the Maven 2.x Resources Plugin. I've created MRESOURCES-118.
The indexed properties will only be available inside plugin configuration due to the way Maven interpolates the POM - so it is available to antrun's replace task, but not the filtering.
However, accessing dependencies by index is not very robust - it is susceptible to changes in the parent. You might instead use the following in pom.xml:
<properties>
<fileName>some-name</fileName>
</properties>
...
<dependency>
<groupId>your.group.id</groupId>
<artifactId>${fileName}</artifactId>
...
</dependency>
You can then continue to filter using the property name:
<somenode>
<fileName>${fileName}</fileName>
</somenode>

How to create source distribution with self sustainable maven build?

What I want to do is to create source code distribution of my application with all dependencies and burn it on DVD. So that I could build it in 100 years (well, ok, you know what I mean...). No online dependencies on libraries or maven plugins!
I know that Ant would be better for this, but I'm using maven in my project. I'm not going to switch to Ant just for that, I'm asking how to do this with maven. Or, if there is a way how to generate self sustainable Ant build that I could put on DVD that would be great too.
(there is ant:ant plugin but it just generates Ant build.xml that points dependencies to local maven repo)
The approach I've taken is that I wanted to create special local repository that I can put on DVD and then build project with mvn -o -Dmaven.repo.local=repo/on/dvd. I was trying to make such repository with dependency:copy-dependencies anduseRepositoryLayout param set to true. But it doesn't copy freaking maven plugins that my build depends on...
The only way I can think of to include the plugins is to specify a different local repository for the build on the command line and ensure all the dependency sources etc are downloaded, then create an archive including the project's contents and the custom repository.
Here is a pom that downloads the sources and javadocs (it downloads them to the project's target directory, which we exclude from the archive because they will also be in the local repository). The assembly descriptor bundles the project's contents and the local repository into a single (pretty large) archive.
Note the processing is all in a profile because you really don't want this running on every build. If temporary local repository is in the target directory you can easily clean the mess up afterwards with a mvn clean.
To activate the profile do something like the following:
mvn package -Parchive -Dmaven.repo.local=.\target\repo
Here's the pom:
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>name.seller.rich</groupId>
<artifactId>test-archive</artifactId>
<version>0.0.1</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>archive</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>sources</id>
<phase>pre-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<classifier>sources</classifier>
<failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
<!--the target directory won't be included, but the sources will be in the repository-->
<outputDirectory>${project.build.directory}/sources</outputDirectory>
</configuration>
</execution>
<execution>
<id>javadocs</id>
<phase>pre-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<classifier>javadoc</classifier> <failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
<outputDirectory>${project.build.directory}/javadocs</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/archive.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
And here's the assembly:
<assembly>
<id>archive</id>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<outputDirectory>/</outputDirectory>
<excludes>
<exclude>target/**</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${maven.repo.local}</directory>
<outputDirectory>repo</outputDirectory>
</fileSet>
</fileSets>
</assembly>
Watch this:
Maven Assembly Plugin
Quote from the homepage:
Do you want to create a binary
distribution from a Maven project that
includes supporting scripts,
configuration files, and all runtime
dependencies? You need to use the
Assembly Plugin to create a
distribution for your project.
It's well configurable. I used it especially for making self-running demo versions of web-applications with an embedded jetty server and user documentation.
I don't have a complete answer. Last time I looked at this, I thought that cleaning out the localRepository at the start of the build (or using a separate one) and the running mvn dependency:go-offline.
If you're really keen, you'll also want to bundle maven itself and a JDK into the distribution. This likely takes it out of scope of a pure maven build.