jeka-dev / jeka

Build and Run Java code for Everywhere
https://jeka.dev
Apache License 2.0
111 stars 15 forks source link

Feature request: add arbitrary files to jars #37

Closed cuchaz closed 7 years ago

cuchaz commented 7 years ago

Properly adding license/readme files to jars is tricky because license/readme files belong in the project root, not in any source/resources folder. So they don't get "compiled" into the classes folder and picked up by the usual jar packer methods.

Here's some workaround code I have that's working for now:

    @Override
    protected JkJavaPacker createPacker() {
        return JkJavaPacker.builder(this)
            .includeVersion(true)
            .doJar(false)
            .doSources(false)
            .doFatJar(true)
            .extraAction(new JkExtraPacking() {

                private File licenseFile = new File("LICENSE.txt");

                @Override
                public void process(JkJavaBuild build) {

                    // move the fat jar to a temp file
                    JkJavaPacker packer = createPacker();
                    File tempFile = build.ouputDir("temp.jar");
                    packer.fatJarFile().renameTo(tempFile);

                    // make a new fat jar with the stuff we want to keep
                    try (ZipFile zipIn = new ZipFile(tempFile)) {
                        try (ZipOutputStream zipOut = JkUtilsZip.createZipOutputStream(packer.fatJarFile(), Deflater.DEFAULT_COMPRESSION)) {

                            // add new entries
                            JkUtilsZip.addZipEntry(zipOut, licenseFile, licenseFile.getName(), false);
                        }
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }

                    // cleanup
                    tempFile.delete();
                }
            })
            .build();
    }

Maybe there's a way to tell the default packer to grab extra files while it's zipping the classes folder?

Thanks!

djeang commented 7 years ago

I think this can make it. This is the proper way to add extra resources in your project.

@Override
public JkFileTreeSet editedResources() {
        return super.editedResources().and(new File("../myRootProject/LICENSE.TXT"));
}
cuchaz commented 7 years ago

that doesn't seem to work =(

java.lang.IllegalArgumentException: LICENSE.txt is not a directory.
    at org.jerkar.api.file.JkFileTree.<init>(JkFileTree.java:57)
    at org.jerkar.api.file.JkFileTree.<init>(JkFileTree.java:44)
    at org.jerkar.api.file.JkFileTree.of(JkFileTree.java:36)
    at org.jerkar.api.file.JkFileTreeSet.and(JkFileTreeSet.java:82)
    at cuchaz.login.server.Build.editedResources(Build.java:74)
    at org.jerkar.tool.builtins.javabuild.JkJavaBuild.resources(JkJavaBuild.java:182)
    at org.jerkar.tool.builtins.javabuild.JkJavaBuild.resourceProcessor(JkJavaBuild.java:323)
    at org.jerkar.tool.builtins.javabuild.JkJavaBuild.processResources(JkJavaBuild.java:498)
    at org.jerkar.tool.builtins.javabuild.JkJavaBuild.compile(JkJavaBuild.java:385)
    at org.jerkar.tool.builtins.javabuild.JkJavaBuild.doCompile(JkJavaBuild.java:630)
    at org.jerkar.tool.builtins.javabuild.JkJavaBuild.doUnitTest(JkJavaBuild.java:636)
    at org.jerkar.tool.builtins.javabuild.JkJavaBuild.doPack(JkJavaBuild.java:643)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.jerkar.api.utils.JkUtilsReflect.invoke(JkUtilsReflect.java:185)
    at org.jerkar.tool.JkBuild.invoke(JkBuild.java:165)
    at org.jerkar.tool.JkBuild.invoke(JkBuild.java:291)
    at org.jerkar.tool.JkBuild.execute(JkBuild.java:180)
    at org.jerkar.tool.Project.runProject(Project.java:250)
    at org.jerkar.tool.Project.launch(Project.java:239)
    at org.jerkar.tool.Project.execute(Project.java:137)
    at org.jerkar.tool.Main.main(Main.java:32)
djeang commented 7 years ago

Indeed. Sorry I put you on the wrong track. Resources are managed as file tree so you can decide their relative path to the root. This should work

@Override
public JkFileTreeSet editedResources() {
        JkFileTree licenceFiles = baseDir().from("../myRootProject").include("license.txt");   
        return super.editedResources().and(licenceFiles);
}

Have a look at the JkTreeFile API. It's very powerfull to deal with set of files. It's close to ANT fileset

cuchaz commented 7 years ago

That worked, thanks! Your solution is much better than mine. =P

Maybe a mention of this trick in the documentation would be helpful to new jerkar users. It's not obvious to new users that the editedXXX() methods can use full file trees/forests and not just a single folder.

djeang commented 7 years ago

Ok, did you find it by yourself if the method was named xith other than 'editedResources' ? I am trying to find the most intuitive way to do it ....

cuchaz commented 7 years ago

I think the code is fine, but the documentation should just show a few examples of common use cases for overriding editedXXX() methods so users can learn how to do them. Adding README.txt and LICENSE.txt files to a jar or fat jar would be a good example. Or any other case where you want to add a file to a jar file for humans to read, rather than the JVM.

cuchaz commented 7 years ago

implemented by #45