adding MetadataBuilderMojo
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/FatJar.java b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/FatJar.java
index 25119c0..7fe34a4 100644
--- a/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/FatJar.java
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/FatJar.java
@@ -14,25 +14,19 @@
 package org.apache.winegrower.extension.build.common;
 
 import static java.util.Objects.requireNonNull;
-import static java.util.stream.Collectors.joining;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.Properties;
 import java.util.Set;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
 import java.util.zip.ZipEntry;
 
 public class FatJar implements Runnable {
@@ -49,21 +43,12 @@
 
         try (final JarOutputStream outputStream = new JarOutputStream(
                 new BufferedOutputStream(new FileOutputStream(configuration.output)))) {
-            final Properties manifests = new Properties();
-            final Properties index = new Properties();
+            final MetadataBuilder metadataBuilder = new MetadataBuilder();
             byte[] buffer = new byte[8192];
             final Set<String> alreadyAdded = new HashSet<>();
             configuration.jars.forEach(shadedJar -> {
-                final Collection<String> files = new ArrayList<>();
                 try (final JarInputStream inputStream = new JarInputStream(new BufferedInputStream(new FileInputStream(shadedJar)))) {
-                    final Manifest manifest = inputStream.getManifest();
-                    if (manifest != null) {
-                        try (final ByteArrayOutputStream manifestStream = new ByteArrayOutputStream()) {
-                            manifest.write(manifestStream);
-                            manifestStream.flush();
-                            manifests.put(shadedJar.getName(), new String(manifestStream.toByteArray(), StandardCharsets.UTF_8));
-                        }
-                    }
+                    metadataBuilder.onJar(shadedJar.getName(), inputStream);
 
                     ZipEntry nextEntry;
                     while ((nextEntry = inputStream.getNextEntry()) != null) {
@@ -71,7 +56,7 @@
                         if (!alreadyAdded.add(name)) {
                             continue;
                         }
-                        files.add(name);
+                        metadataBuilder.onFile(name);
                         outputStream.putNextEntry(nextEntry);
                         int count;
                         while ((count = inputStream.read(buffer, 0, buffer.length)) >= 0) {
@@ -84,19 +69,21 @@
                 } catch (final IOException e) {
                     throw new IllegalStateException(e);
                 }
-                index.put(shadedJar.getName(), files.stream().collect(joining(",")));
+                metadataBuilder.afterJar();
             });
 
             outputStream.putNextEntry(new JarEntry("WINEGROWER-INF/"));
             outputStream.closeEntry();
 
-            outputStream.putNextEntry(new JarEntry("WINEGROWER-INF/index.properties"));
-            index.store(outputStream, "index");
-            outputStream.closeEntry();
-
-            outputStream.putNextEntry(new JarEntry("WINEGROWER-INF/manifests.properties"));
-            manifests.store(outputStream, "manifests");
-            outputStream.closeEntry();
+            metadataBuilder.getMetadata().forEach((key, value) -> {
+                try {
+                    outputStream.putNextEntry(new JarEntry("WINEGROWER-INF/" + key + ".properties"));
+                    value.store(outputStream, "index");
+                    outputStream.closeEntry();
+                } catch (final IOException ioe) {
+                    throw new IllegalStateException(ioe);
+                }
+            });
         } catch (final IOException e) {
             throw new IllegalStateException(e);
         }
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/MetadataBuilder.java b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/MetadataBuilder.java
new file mode 100644
index 0000000..9191cdc
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/MetadataBuilder.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.winegrower.extension.build.common;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+public class MetadataBuilder {
+    private final Properties manifests = new Properties();
+    private final Properties index = new Properties();
+
+    private String currentJar;
+    private List<String> files;
+
+    public Map<String, Properties> getMetadata() {
+        final HashMap<String, Properties> meta = new HashMap<>();
+        meta.put("index", index);
+        meta.put("manifests", manifests);
+        return meta;
+    }
+
+    public void onJar(final String jarName, final JarInputStream jarInputStream) {
+        final Manifest manifest = jarInputStream.getManifest();
+        if (manifest != null) {
+            try (final ByteArrayOutputStream manifestStream = new ByteArrayOutputStream()) {
+                manifest.write(manifestStream);
+                manifestStream.flush();
+                manifests.put(jarName, new String(manifestStream.toByteArray(), StandardCharsets.UTF_8));
+            } catch (final IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+        this.currentJar = jarName;
+        this.files = new ArrayList<>();
+    }
+
+    public void onFile(final String name) {
+        files.add(name);
+    }
+
+    public void afterJar() {
+        index.put(currentJar, String.join(",", files));
+        currentJar = null;
+        files = null;
+    }
+}
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/asciidoc/index.adoc b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/asciidoc/index.adoc
index 6fa116b..ecceb10 100644
--- a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/asciidoc/index.adoc
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/asciidoc/index.adoc
@@ -121,6 +121,28 @@
 |workDir|File|winegrower.workDir|${project.build.directory}/${project.artifactId}-distribution|Where the distribution is built during the build.
 |===
 
+== Generate Winegrower metadata
+
+When you build a Fatjar, Winegrower uses metadata to respect the jar bundle modularity.
+It can be neat to generate these metadata without generating a fatjar - for GraalVM for example.
+To do that, you can use `metadata` mojo.
+
+[source,sh]
+----
+mvn winegrower:metadata
+----
+
+=== Configuration
+
+[cols="e,m,m,m,a",headers]
+|===
+|Name|Type|Property|Default|Description
+|buildArtifact|File|winegrower.buildArtifact|${project.build.directory}/${project.build.finalName}.${project.packaging}.|Path of the project artifact if any.
+|includeScopes|Collection<String>|winegrower.includeScopes|provided,compile,runtime|The scopes included in the produced artifact.
+|output|File|winegrower.metadata.output|${project.build.outputDirectory}|Where to dump metadata.
+|namingPattern|String|winegrower.metadata.namingPattern|WINEGROWER-INF/%s.properties|Naming pattern for each metadata - relative to output directory.
+|===
+
 == From Winegrower no-manifest deployment to OSGi deployments
 
 Winegrower supports some custom API not requiring a full OSGi packaging
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/BaseClasspathMojo.java b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/BaseClasspathMojo.java
new file mode 100644
index 0000000..339e0b6
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/BaseClasspathMojo.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.winegrower.extension.build.maven;
+
+import static java.util.stream.Collectors.toList;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+
+public abstract class BaseClasspathMojo extends AbstractMojo {
+    @Parameter(defaultValue = "${project}", readonly = true, required = true)
+    protected MavenProject project;
+
+    @Parameter(defaultValue = "provided,compile,runtime", property = "winegrower.includeScopes")
+    private Collection<String> includeScopes;
+
+    @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}", property = "winegrower.buildArtifact")
+    private File buildArtifact;
+
+    protected Collection<File> collectJars() {
+        return Stream.concat(
+                    project.getArtifacts().stream()
+                        .filter(it -> includeScopes.contains(it.getScope()))
+                        .map(Artifact::getFile),
+                    Stream.of(buildArtifact))
+                .filter(Objects::nonNull)
+                .filter(File::exists)
+                .collect(toList());
+    }
+}
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/FatJarMojo.java b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/FatJarMojo.java
index f06b11a..4721ff8 100644
--- a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/FatJarMojo.java
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/FatJarMojo.java
@@ -13,31 +13,18 @@
  */
 package org.apache.winegrower.extension.build.maven;
 
-import static java.util.stream.Collectors.toList;
 import static org.apache.maven.plugins.annotations.ResolutionScope.RUNTIME_PLUS_SYSTEM;
 
 import java.io.File;
-import java.util.Collection;
-import java.util.Objects;
-import java.util.stream.Stream;
 
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugins.annotations.Component;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
-import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.apache.winegrower.extension.build.common.FatJar;
 
 @Mojo(name = "fatjar", requiresDependencyResolution = RUNTIME_PLUS_SYSTEM)
-public class FatJarMojo extends AbstractMojo {
-    @Parameter(defaultValue = "${project}", readonly = true, required = true)
-    private MavenProject project;
-
-    @Parameter(defaultValue = "provided,compile,runtime", property = "winegrower.includeScopes")
-    private Collection<String> includeScopes;
-
+public class FatJarMojo extends BaseClasspathMojo {
     @Parameter(defaultValue = "${project.build.directory}/${project.artifactId}-fatjar.jar", property = "winegrower.output")
     private File output;
 
@@ -47,9 +34,6 @@
     @Parameter(defaultValue = "fatjar", property = "winegrower.classifier")
     private String classifier;
 
-    @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}", property = "winegrower.buildArtifact")
-    private File buildArtifact;
-
     @Component
     private MavenProjectHelper helper;
 
@@ -60,15 +44,4 @@
             helper.attachArtifact(project, output, classifier);
         }
     }
-
-    private Collection<File> collectJars() {
-        return Stream.concat(
-                    project.getArtifacts().stream()
-                        .filter(it -> includeScopes.contains(it.getScope()))
-                        .map(Artifact::getFile),
-                    Stream.of(buildArtifact))
-                .filter(Objects::nonNull)
-                .filter(File::exists)
-                .collect(toList());
-    }
 }
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/MetadataBuilderMojo.java b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/MetadataBuilderMojo.java
new file mode 100644
index 0000000..d38ecfe
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/MetadataBuilderMojo.java
@@ -0,0 +1,79 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.winegrower.extension.build.maven;
+
+import static org.apache.maven.plugins.annotations.ResolutionScope.RUNTIME_PLUS_SYSTEM;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+import java.util.zip.ZipEntry;
+
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.winegrower.extension.build.common.MetadataBuilder;
+
+@Mojo(name = "metadata", requiresDependencyResolution = RUNTIME_PLUS_SYSTEM)
+public class MetadataBuilderMojo extends BaseClasspathMojo {
+    @Parameter(defaultValue = "${project.build.outputDirectory}", property = "winegrower.metadata.output")
+    private File output;
+
+    @Parameter(defaultValue = "WINEGROWER-INF/%s.properties", property = "winegrower.metadata.namingPattern")
+    private String namingPattern;
+
+    @Override
+    public void execute() {
+        final MetadataBuilder metadataBuilder = new MetadataBuilder();
+        final Set<String> alreadyAdded = new HashSet<>();
+        collectJars().forEach(jar -> {
+            try (final JarInputStream inputStream = new JarInputStream(new BufferedInputStream(new FileInputStream(jar)))) {
+                metadataBuilder.onJar(jar.getName(), inputStream);
+
+                ZipEntry nextEntry;
+                while ((nextEntry = inputStream.getNextEntry()) != null) {
+                    final String name = nextEntry.getName();
+                    if (!alreadyAdded.add(name)) {
+                        continue;
+                    }
+                    metadataBuilder.onFile(name);
+                }
+            } catch (final IOException e) {
+                throw new IllegalStateException(e);
+            }
+            metadataBuilder.afterJar();
+        });
+
+        metadataBuilder.getMetadata().forEach((key, value) -> {
+            final Path target = output.toPath().resolve(String.format(namingPattern, key));
+            try {
+                if (!Files.exists(target.getParent())) {
+                    Files.createDirectories(target.getParent());
+                }
+                try (final OutputStream out = Files.newOutputStream(target)) {
+                    value.store(out, "index");
+                }
+                getLog().info("Generated '" + target + "'");
+            } catch (final IOException ioe) {
+                throw new IllegalStateException(ioe);
+            }
+        });
+    }
+}