Git Product home page Git Product logo

cambada's Introduction

Cambada

Clojars Project

Cambada is a packager for Clojure based on deps.edn (AKA tools.deps). It is heavily inspired by Leiningen's jar and uberjar tasks and also supports GraalVM's new native-image making it a one-stop shop for any packaging needed for your Clojure project.

Motivation

Leiningen has laid the foundations of what many of us have come to accept as the standard for Clojure projects. Clojure's tools.deps potentially brings new ideas to the Clojure workflow. Cambada brings some of the great features of Leiningen to the tools.deps workflow.

Cambada's sole focus is packaging. It doesn't have plugins, templates or Clojars integration. It packages your deps.edn progject as one - or all - of:

  1. jar
  2. uberjar
  3. GraalVM native image

On top of Phil Hagelberg's (and so many others') great Leiningen, many thanks to Dominic Monroe and his work on pack as well as Taylor Wood and his clj.native-image. These projects offered a lot of inspiration (and, in some cases, donor code too).

Table of Contents

Getting Started

Cambada is a simple set of main functions that can be called from a deps.edn alias. The simplest way to have it available in your project is to add an alias with extra-deps to your deps.edn file:

{:aliases {:cambada
           {:extra-deps
            {luchiniatwork/cambada
             {:mvn/version "1.0.5"}}}}}

Cambada has three main entry points, cambada.jar, cambada.uberjar and cambada.native-image. Let's say you simply want to create an uberjar:

$ clj -R:cambada -m cambada.uberjar
Cleaning target
Creating target/classes
  Compiling ...
Creating target/project-name-1.0.0-SNAPSHOT.jar
Updating pom.xml
Creating target/project-name-1.0.0-SNAPSHOT-standalone.jar
  Including ...
Done!

Your files will be located at target/ by default.

All entry points have a few extra configuration options you might be interested in. For instance:

$ clj -R:cambada -m cambada.uberjar --help
Package up the project files and all dependencies into a jar file.

Usage: clj -m cambada.uberjar [options]

Options:
  -m, --main NS_NAME                            The namespace with the -main function
      --app-group-id STRING     project-name    Application Maven group ID
      --app-artifact-id STRING  project-name    Application Maven artifact ID
      --app-version STRING      1.0.0-SNAPSHOT  Application version
      --[no-]copy-source                        Copy source files by default
  -a, --aot NS_NAMES            all             Namespaces to be AOT-compiled or `all` (default)
  -d, --deps FILE_PATH          deps.edn        Location of deps.edn file
  -o, --out PATH                target          Output directory
  -h, --help                                    Shows this help

Do try --help for cambada.jar and cambada.native-image if you are interested or refer to the sections below.

Easy Aliases

One of the powers-in-simplicity of tools.deps is the ability to define aliases on deps.edn. When we used the alias cambada on the section above, we simply specified it as an dependency to be resolved (therefore the -R when calling clj).

You can also be a lot more prescriptive in your aliases by making them do more work for you. For instance, the alias below will create a versioned uberjar:

{:aliases {:uberjar
           {:extra-deps
            {luchiniatwork/cambada {:mvn/version "1.0.0"}}
            :main-opts ["-m" "cambada.uberjar"
                        "--app-version" "0.5.3"]}}}

By having an alias like this uberjar one in your deps.edn you can simply run it by using $ clj -A:uberjar making it very familiar to those used with $ lein uberjar:

$ clj -A:uberjar
Cleaning target
Creating target/classes
  Compiling ...
Creating target/project-name-0.5.3.jar
Updating pom.xml
Creating target/project-name-0.5.3-standalone.jar
  Including ...
Done!

Packaging as a Jar

Let's start with an empty project folder:

$ mkdir -p myproj/src/myproj/
$ cd myproj

Create a deps.edn at the root of your project with cambada.jar as an alias:

{:aliases {:jar
           {:extra-deps
            {luchiniatwork/cambada {:mvn/version "1.0.2"}}
            :main-opts ["-m" "cambada.jar"
                        "-m" "myproj.core"]}}}

Create a simple hello world on a -main function at src/myproj/core.clj:

(ns myproj.core
  (:gen-class))

(defn -main [& args]
  (println "Hello World!"))

Of course, just for safe measure, let's run this hello world via clj:

$ clj -m myproj.core
Hello World!

Then just call the alias from the project's root:

$ clj -A:jar
Cleaning target
Creating target/classes
  Compiling myproj.core
Creating target/myproj-1.0.0-SNAPSHOT.jar
Updating pom.xml
Done!

Once Cambada is done, you'll have a jar package at target/. In order to run it, you'll need to add Clojure and spec to your class path. The paths will vary on your system:

$ java -cp target/myproj-1.0.0-SNAPSHOT.jar myproj.core
Hello World!

For a standalone jar file see the uberjar option on the next section.

You can specify the following options for cambada.jar:

  -m, --main NS_NAME                            The namespace with the -main function
      --app-group-id STRING     project-name    Application Maven group ID
      --app-artifact-id STRING  project-name    Application Maven artifact ID
      --app-version STRING      1.0.0-SNAPSHOT  Application version
      --[no-]copy-source                        Copy source files by default
  -a, --aot NS_NAMES            all             Namespaces to be AOT-compiled or `all` (default)
  -d, --deps FILE_PATH          deps.edn        Location of deps.edn file
  -o, --out PATH                target          Output directory
  -h, --help                                    Shows this help

These options should be quite self-explanatory and the defaults are hopefully sensible enough for most of the basic cases. By default everything gets AOT-compiled and sources are copied to the resulting jar.

For those used to Leiningen, the application's group ID, artifact ID and version are not extracted from project.clj (since it's assumed you don't have a project.clj in a deps.edn workflow). Therefore, you must specify these expressively as options.

Packaging as an Uberjar

Let's start with an empty project folder:

$ mkdir -p myproj/src/myproj/
$ cd myproj

Create a deps.edn at the root of your project with cambada.jar as an alias:

{:aliases {:uberjar
           {:extra-deps
            {luchiniatwork/cambada {:mvn/version "1.0.0"}}
            :main-opts ["-m" "cambada.uberjar"
                        "-m" "myproj.core"]}}}

Create a simple hello world on a -main function at src/myproj/core.clj:

(ns myproj.core
  (:gen-class))

(defn -main [& args]
  (println "Hello World!"))

Of course, just for safe measure, let's run this hello world via clj:

$ clj -m myproj.core
Hello World!

Then just call the alias from the project's root:

$ clj -A:uberjar
Cleaning target
Creating target/classes
  Compiling myproj.core
Creating target/myproj-1.0.0-SNAPSHOT.jar
Updating pom.xml
Creating target/myproj-1.0.0-SNAPSHOT-standalone.jar
  Including myproj-1.0.0-SNAPSHOT.jar
  Including clojure-1.9.0.jar
  Including spec.alpha-0.1.143.jar
  Including core.specs.alpha-0.1.24.jar
Done!

Once Cambada is done, you'll have two jar packages at target/. One for a basic jar and one standalone with all dependencies in it. In order to run it, simply call it:

$ java -jar target/myproj-1.0.0-SNAPSHOT-standalone.jar
Hello World!

cambada.uberjar has exactly the same options and defaults as cambada.jar (see above for more details).

Caveats

If any of your transitive dependencies has a Maven Central dependency, cambada may fail on you (investigations under way). Therefore, it is recommended that you explicitly add your repos (Central included) to your deps.edn file i.e.:

{:deps {...}

 :mvn/repos {"central" {:url "https://repo1.maven.org/maven2/"}
             "clojars" {:url "https://repo.clojars.org/"}}}

Packaging as a Native Image

By using GraalVM we now have the option of packaging everything AOT compiled as a native image.

If you want to use this feature, make sure to download and install GraalVM.

If you are a MacOS user, GraalVM CE is available as a brew cask:

$ brew cask install graalvm/tap/graalvm-ce

GraalVM's native-image is a package that needs to be installed manually with the following command (attention that gu is at $GRAALVM_HOME/bin/ if it is not on your PATH):

$ gu install native-image

You will need to set your GRAALVM_HOME environment variable to point to where GraalVM is installed. Alternatevely you can call cambada.native-image with the argument --graalvm-home pointing to it.

The entry point for native image packaging is cambada.native-image. Let's assume your GRAALVM_HOME variable is set (if you don't, use --graalvm-home).

Let's start with an empty project folder:

$ mkdir -p myproj/src/myproj/
$ cd myproj

Create a deps.edn at the root of your project with cambada.jar as an alias:

{:aliases {:native-image
           {:extra-deps
            {luchiniatwork/cambada {:mvn/version "1.0.0"}}
            :main-opts ["-m" "cambada.native-image"
                        "-m" "myproj.core"]}}}

Create a simple hello world on a -main function at src/myproj/core.clj:

(ns myproj.core
  (:gen-class))

(defn -main [& args]
  (println "Hello World!"))

Of course, just for safe measure, let's run this hello world via clj:

$ clj -m myproj.native-image
Hello World!

Then just call the alias from the project's root:

$ clj -A:native-image
Cleaning target
Creating target/classes
  Compiling myproj.core
Creating target/myproj
   classlist:   2,810.07 ms
       (cap):   1,469.31 ms
       setup:   2,561.28 ms
  (typeflow):   5,802.45 ms
   (objects):   2,644.17 ms
  (features):      40.54 ms
    analysis:   8,609.18 ms
    universe:     314.28 ms
     (parse):   1,834.84 ms
    (inline):   2,338.45 ms
   (compile):  16,824.24 ms
     compile:  21,435.77 ms
       image:   1,862.44 ms
       write:   1,276.55 ms
     [total]:  38,942.48 ms

Done!

Once Cambada is done, you'll have an executable package at target/:

$ ./target/myproj
Hello World!

Extra options can be sent to GraalVM's packager by using Cambada's --graalvm-opt option i.e., to include FILE as a resource, simply use --graalvm-opt H:IncludeResources=FILE.

Performance Comparison

A quick comparison of the myproj hello world as described previously and ran across different packaging options:

Straight with clj:

$ time clj -m myproj.core
Hello World!
1.160 secs

As a standalone uberjar:

$ time java -jar target/myproj-1.0.0-SNAPSHOT-standalone.jar
Hello World!
0.850 secs

As a native image:

$ time ./target/myproj
Hello World!
0.054 secs

Comparing with clj as a baseline:

Method Speed in secs Speed relative to clj
clj 1.160 secs 1x
uberjar 0.850 secs 1.36x
native-image 0.054 secs 21.48x

Bugs

If you find a bug, submit a Github issue.

Help

This project is looking for team members who can help this project succeed! If you are interested in becoming a team member please open an issue.

License

Copyright © 2018 Tiago Luchini

Distributed under the MIT License. See LICENSE

cambada's People

Contributors

l0st3d avatar luchiniatwork avatar xfthhxk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cambada's Issues

Feature request: lein-v (infer version numbers from git tags)

Cambada looks very cool, and I think it would help my team migrate from leiningen to tools.deps. One thing it's missing from our current workflow, though, is functionality like that of lein-v, which lets us specify what versions our projects are on using git tags.

I thought I'd check in and see if you had any thoughts on either including this functionality in cambada, or else accessing it through some sort of extension point if it was available as an external library. Essentially what I'd want is for the --app-version argument to be set from (some.ns/function), where some.ns is included on the classpath via my deps.edn alias.

I'd be happy to work on this if it seems like a good idea, let me know.

native-image --graalvm-opts is wrong

I tried to pass the --enable-url-protocols=http but it failed miserably.

After some digging in the code it turns out that the implementation takes the opts and prepends them with the "-" character.

So I had to "hack" it by passing --graalvm-opt "-enable-url-protocols=http" so it works correctly. (note the absence of one "-" at the beginning of the option)

API change in tools.deps

Just wanted to drop a note that the newest version of tools.deps.alpha contains some api changes (moving towards getting out of alpha). So when you bump to tools.deps.alpha >= 0.9.745, your use of clojure.tools.deps.alpha.reader in cambda.cli needs to be updated - same function slurp-deps exists, but is now in clojure.tools.deps.alpha instead so just need to update the ns requires.

Allow filtering of bad stuff (signatures etc) from input jars

Thanks for this, it is a nice tool.

I don't know if this is out of scope for your project, but when merging jars there are certain files which should probably be filtered out. For example, many jars have signatures in the META-INF folder which will fail to validate in the merged jar, causing Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes.

Similarly you may find files in META-INF/services which need concatenating.

boot has some waffle about this that might be helpful.

AOT required for all jars

Currently there is no way to run the jar task without AOT compiling at least one namespace, or if there is it is not properly documented.

I suggest adding a --no-aot flag to allow a jar to be created which only copies source, and does not have any AOT compilation, since this is the normal method used for the creation of libraries.

uberjar fails when using git libs

If deps.edn has a :git/url or :local/root such as the following then there is an exception thrown by ZipFile.open while generating the uberjar.

{:paths ["src" "resources"]
 :deps
  {commons   {:git/url "https://github.com/user/commons"
              :sha "caffa137fa1c865ee12f02ed100f11917e839c11"}}
 :aliases
 {:uberjar {:extra-deps
            {luchiniatwork/cambada {:mvn/version "1.0.0"}}
            :main-opts ["-m" "cambada.uberjar" "--main" "program.main"]}}
 ...
  Including httpcore-nio-4.4.6.jar
  Including component-0.3.2.jar
  Including clj
Exception in thread "main" java.io.FileNotFoundException: /home/user/.gitlibs/libs/commons/caffa137fa1c865ee12f02ed100f11917e839c11/src/clj (Is a directory)
	at java.util.zip.ZipFile.open(Native Method)
	at java.util.zip.ZipFile.<init>(ZipFile.java:225)
	at java.util.zip.ZipFile.<init>(ZipFile.java:155)
	at java.util.zip.ZipFile.<init>(ZipFile.java:169)
	at sun.reflect.GeneratedConstructorAccessor15.newInstance(Unknown Source)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at clojure.lang.Reflector.invokeConstructor(Reflector.java:180)
	at cambada.uberjar$include_dep.invokeStatic(uberjar.clj:103)
	at cambada.uberjar$include_dep.invoke(uberjar.clj:102)
	at clojure.core$partial$fn__5563.invoke(core.clj:2624)
	at clojure.core.protocols$naive_seq_reduce.invokeStatic(protocols.clj:62)
	at clojure.core.protocols$interface_or_naive_reduce.invokeStatic(protocols.clj:72)
	at clojure.core.protocols$fn__7852.invokeStatic(protocols.clj:169)
	at clojure.core.protocols$fn__7852.invoke(protocols.clj:124)
	at clojure.core.protocols$fn__7807$G__7802__7816.invoke(protocols.clj:19)
	at clojure.core.protocols$seq_reduce.invokeStatic(protocols.clj:31)
	at clojure.core.protocols$fn__7835.invokeStatic(protocols.clj:75)
	at clojure.core.protocols$fn__7835.invoke(protocols.clj:75)
	at clojure.core.protocols$fn__7781$G__7776__7794.invoke(protocols.clj:13)
	at clojure.core$reduce.invokeStatic(core.clj:6748)
	at clojure.core$reduce.invoke(core.clj:6730)
	at cambada.uberjar$write_components.invokeStatic(uberjar.clj:118)
	at cambada.uberjar$write_components.invoke(uberjar.clj:113)
	at cambada.uberjar$apply_BANG_.invokeStatic(uberjar.clj:138)
	at cambada.uberjar$apply_BANG_.invoke(uberjar.clj:129)
	at cambada.cli$runner.invokeStatic(cli.clj:120)
	at cambada.cli$runner.invoke(cli.clj:115)
	at cambada.uberjar$_main.invokeStatic(uberjar.clj:142)
	at cambada.uberjar$_main.doInvoke(uberjar.clj:140)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.main$main_opt.invokeStatic(main.clj:317)
	at clojure.main$main_opt.invoke(main.clj:313)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)

SAXParseException when buliding uberjar

I'm getting the following stacktrace when I attempt to build an uberjar:

[Fatal Error] :1:628: The content of elements must consist of well-formed character data or markup.
ERROR:  'The content of elements must consist of well-formed character data or markup.'
Exception in thread "main" javax.xml.transform.TransformerException: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 628; The content of elements must consist of well-formed character data or markup.
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:740)
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:343)
        at clojure.data.xml.jvm.pprint$indent_xml.invokeStatic(pprint.clj:27)
        at clojure.data.xml.jvm.pprint$indent_xml.invoke(pprint.clj:23)
        at clojure.data.xml$indent.invokeStatic(xml.clj:158)
        at clojure.data.xml$indent.doInvoke(xml.clj:153)
        at clojure.lang.RestFn.invoke(RestFn.java:425)
        at clojure.lang.AFn.applyToHelper(AFn.java:156)
        at clojure.lang.RestFn.applyTo(RestFn.java:132)
        at clojure.core$apply.invokeStatic(core.clj:661)
        at clojure.core$apply.invoke(core.clj:652)
        at clojure.data.xml$indent_str.invokeStatic(xml.clj:164)
        at clojure.data.xml$indent_str.doInvoke(xml.clj:160)
        at clojure.lang.RestFn.invoke(RestFn.java:410)
        at clojure.tools.deps.alpha.gen.pom$sync_pom.invokeStatic(pom.clj:135)
        at clojure.tools.deps.alpha.gen.pom$sync_pom.invoke(pom.clj:123)
        at cambada.jar$sync_pom.invokeStatic(jar.clj:225)
        at cambada.jar$sync_pom.invoke(jar.clj:222)
        at cambada.jar$apply_BANG_.invokeStatic(jar.clj:237)
        at cambada.jar$apply_BANG_.invoke(jar.clj:233)
        at cambada.uberjar$apply_BANG_.invokeStatic(uberjar.clj:130)
        at cambada.uberjar$apply_BANG_.invoke(uberjar.clj:129)
        at cambada.cli$runner.invokeStatic(cli.clj:120)
        at cambada.cli$runner.invoke(cli.clj:115)
        at cambada.uberjar$_main.invokeStatic(uberjar.clj:142)
        at cambada.uberjar$_main.doInvoke(uberjar.clj:140)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:702)
        at clojure.core$apply.invokeStatic(core.clj:657)
        at clojure.main$main_opt.invokeStatic(main.clj:317)
        at clojure.main$main_opt.invoke(main.clj:313)
        at clojure.main$main.invokeStatic(main.clj:424)
        at clojure.main$main.doInvoke(main.clj:387)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:702)
        at clojure.main.main(main.java:37)
Caused by: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 628; The content of elements must consist of well-formed character data or markup.
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1239)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:643)
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:632)
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:728)
        ... 35 more
---------
org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 628; The content of elements must consist of well-formed character data or markup.
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1239)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:643)
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:632)
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:728)
        at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:343)
        at clojure.data.xml.jvm.pprint$indent_xml.invokeStatic(pprint.clj:27)
        at clojure.data.xml.jvm.pprint$indent_xml.invoke(pprint.clj:23)
        at clojure.data.xml$indent.invokeStatic(xml.clj:158)
        at clojure.data.xml$indent.doInvoke(xml.clj:153)
        at clojure.lang.RestFn.invoke(RestFn.java:425)
        at clojure.lang.AFn.applyToHelper(AFn.java:156)
        at clojure.lang.RestFn.applyTo(RestFn.java:132)
        at clojure.core$apply.invokeStatic(core.clj:661)
        at clojure.core$apply.invoke(core.clj:652)
        at clojure.data.xml$indent_str.invokeStatic(xml.clj:164)
        at clojure.data.xml$indent_str.doInvoke(xml.clj:160)
        at clojure.lang.RestFn.invoke(RestFn.java:410)
        at clojure.tools.deps.alpha.gen.pom$sync_pom.invokeStatic(pom.clj:135)
        at clojure.tools.deps.alpha.gen.pom$sync_pom.invoke(pom.clj:123)
        at cambada.jar$sync_pom.invokeStatic(jar.clj:225)
        at cambada.jar$sync_pom.invoke(jar.clj:222)
        at cambada.jar$apply_BANG_.invokeStatic(jar.clj:237)
        at cambada.jar$apply_BANG_.invoke(jar.clj:233)
        at cambada.uberjar$apply_BANG_.invokeStatic(uberjar.clj:130)
        at cambada.uberjar$apply_BANG_.invoke(uberjar.clj:129)
        at cambada.cli$runner.invokeStatic(cli.clj:120)
        at cambada.cli$runner.invoke(cli.clj:115)
        at cambada.uberjar$_main.invokeStatic(uberjar.clj:142)
        at cambada.uberjar$_main.doInvoke(uberjar.clj:140)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:702)
        at clojure.core$apply.invokeStatic(core.clj:657)
        at clojure.main$main_opt.invokeStatic(main.clj:317)
        at clojure.main$main_opt.invoke(main.clj:313)
        at clojure.main$main.invokeStatic(main.clj:424)
        at clojure.main$main.doInvoke(main.clj:387)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:702)
        at clojure.main.main(main.java:37)

Exception in thread "main" java.lang.NoClassDefFoundError: clojure/data/xml/node/Element

This is such a cool project.. Thanks for working on this.. I am executing clj -R:cambada -m cambada.uberjar

Getting the following exception.

Cleaning target
Creating target/classes
  Compiling inject-github-id.core
Creating target/inject_github_id-1.0.0-SNAPSHOT.jar
Updating pom.xml
Exception in thread "main" java.lang.NoClassDefFoundError: clojure/data/xml/node/Element
	at cambada.jar$parse_xml$fn__2572.invoke(jar.clj:194)
	at clojure.core$filter$fn__5614.invoke(core.clj:2813)
	at clojure.lang.LazySeq.sval(LazySeq.java:40)
	at clojure.lang.LazySeq.seq(LazySeq.java:49)
	at clojure.lang.LazySeq.first(LazySeq.java:71)
	at clojure.lang.RT.first(RT.java:685)
	at clojure.core$first__5106.invokeStatic(core.clj:55)
	at clojure.core$first__5106.invoke(core.clj:55)
	at cambada.jar$parse_xml.invokeStatic(jar.clj:194)
	at cambada.jar$parse_xml.invoke(jar.clj:190)
	at cambada.jar$sync_pom$fn__2580.invoke(jar.clj:230)
	at cambada.jar$sync_pom.invokeStatic(jar.clj:227)
	at cambada.jar$sync_pom.invoke(jar.clj:222)
	at cambada.jar$apply_BANG_.invokeStatic(jar.clj:237)
	at cambada.jar$apply_BANG_.invoke(jar.clj:233)
	at cambada.uberjar$apply_BANG_.invokeStatic(uberjar.clj:130)
	at cambada.uberjar$apply_BANG_.invoke(uberjar.clj:129)
	at cambada.cli$runner.invokeStatic(cli.clj:120)
	at cambada.cli$runner.invoke(cli.clj:115)
	at cambada.uberjar$_main.invokeStatic(uberjar.clj:142)
	at cambada.uberjar$_main.doInvoke(uberjar.clj:140)
	at clojure.lang.RestFn.invoke(RestFn.java:397)
	at clojure.lang.AFn.applyToHelper(AFn.java:152)
	at clojure.lang.RestFn.applyTo(RestFn.java:132)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.core$apply.invokeStatic(core.clj:657)
	at clojure.main$main_opt.invokeStatic(main.clj:317)
	at clojure.main$main_opt.invoke(main.clj:313)
	at clojure.main$main.invokeStatic(main.clj:424)
	at clojure.main$main.doInvoke(main.clj:387)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:702)
	at clojure.main.main(main.java:37)
Caused by: java.lang.ClassNotFoundException: clojure.data.xml.node.Element
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
	... 33 more

install and config deps.edn are not considered

When working with deps.edn, normally the files from the installation directory, from the config directory (usually ~/.clojure/deps.edn) and the local deps.edn are merged (using essentially a merge-with merge). Here only the local deps.edn file is considered.

Is this by choice or is it a bug?

resources folder is not included, but should be in uberjar.

Hello.

I want use cambada to build executable uberjar file.
This is my config
:uberjar {:extra-deps {luchiniatwork/cambada {:mvn/version "1.0.0"}} :main-opts ["-m" "cambada.uberjar" "-m" "my.app.core" "--no-copy-source"]}
I expect, that cambada will create uberjar file and resources folder will be included in it like leiningen does "lein uberjar". But in practice, cambada is ignoring resource folder and not include it in uberjar.

If I delete "--no-copy-source" parameter, then resources folder will be included in uberjar, but with source code (which is unwanted) and resource folder will be in wrong place.

Mike.

uberjar missing deps

Using deps.edn the uberjar ends up missing deps.

when my deps looks like:

:deps {org.clojure/clojure {:mvn/version "1.11.1"}
        org.clojure/tools.cli {:mvn/version "1.0.206"}
        com.github.mwiede/jsch {:mvn/version "0.2.3"}
        nrepl/bencode {:mvn/version "1.1.0"}}
$ clj -X:deps tree
org.clojure/clojure 1.11.1
  . org.clojure/spec.alpha 0.3.218
  . org.clojure/core.specs.alpha 0.2.62
org.clojure/tools.cli 1.0.206
com.github.mwiede/jsch 0.2.3
nrepl/bencode 1.1.0

but my uberjar is built without org.clojure/spec.alpha

Creating target/bbssh-0.1.0-SNAPSHOT-standalone.jar
  Including bbssh-0.1.0-SNAPSHOT.jar
  Including jsch-0.2.3.jar
  Including bencode-1.1.0.jar
  Including clojure-1.11.1.jar
  Including tools.cli-1.0.206.jar
  Including core.specs.alpha-0.2.62.jar
Done!

Notice it's missing here. Then when I run the uberjar:

java -cp target/bbssh-0.1.0-SNAPSHOT-standalone.jar clojure.main -m bbssh.core
Exception in thread "main" java.lang.ExceptionInInitializerError
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:398)
	at clojure.lang.RT.classForName(RT.java:2209)
	at clojure.lang.RT.classForName(RT.java:2218)
	at clojure.lang.RT.loadClassForName(RT.java:2237)
	at clojure.lang.RT.load(RT.java:449)
	at clojure.lang.RT.load(RT.java:424)
	at clojure.core$load$fn__6908.invoke(core.clj:6161)
	at clojure.core$load.invokeStatic(core.clj:6160)
	at clojure.core$load.doInvoke(core.clj:6144)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5933)
	at clojure.core$load_one.invoke(core.clj:5928)
	at clojure.core$load_lib$fn__6850.invoke(core.clj:5975)
	at clojure.core$load_lib.invokeStatic(core.clj:5974)
	at clojure.core$load_lib.doInvoke(core.clj:5953)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$load_libs.invokeStatic(core.clj:6016)
	at clojure.core$load_libs.doInvoke(core.clj:6000)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$require.invokeStatic(core.clj:6038)
	at clojure.core.server$loading__6789__auto____8961.invoke(server.clj:9)
	at clojure.core.server__init.load(Unknown Source)
	at clojure.core.server__init.<clinit>(Unknown Source)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:398)
	at clojure.lang.RT.classForName(RT.java:2209)
	at clojure.lang.RT.classForName(RT.java:2218)
	at clojure.lang.RT.loadClassForName(RT.java:2237)
	at clojure.lang.RT.load(RT.java:449)
	at clojure.lang.RT.load(RT.java:424)
	at clojure.core$load$fn__6908.invoke(core.clj:6161)
	at clojure.core$load.invokeStatic(core.clj:6160)
	at clojure.core$load.doInvoke(core.clj:6144)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5933)
	at clojure.core$load_one.invoke(core.clj:5928)
	at clojure.core$load_lib$fn__6850.invoke(core.clj:5975)
	at clojure.core$load_lib.invokeStatic(core.clj:5974)
	at clojure.core$load_lib.doInvoke(core.clj:5953)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$load_libs.invokeStatic(core.clj:6016)
	at clojure.core$load_libs.doInvoke(core.clj:6000)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$require.invokeStatic(core.clj:6038)
	at clojure.core$require.doInvoke(core.clj:6038)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.lang.Var.invoke(Var.java:384)
	at clojure.lang.RT.doInit(RT.java:491)
	at clojure.lang.RT.init(RT.java:467)
	at clojure.main.main(main.java:38)
Caused by: java.io.FileNotFoundException: Could not locate clojure/spec/alpha__init.class, clojure/spec/alpha.clj or clojure/spec/alpha.cljc on classpath.
	at clojure.lang.RT.load(RT.java:462)
	at clojure.lang.RT.load(RT.java:424)
	at clojure.core$load$fn__6908.invoke(core.clj:6161)
	at clojure.core$load.invokeStatic(core.clj:6160)
	at clojure.core$load.doInvoke(core.clj:6144)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5933)
	at clojure.core$load_one.invoke(core.clj:5928)
	at clojure.core$load_lib$fn__6850.invoke(core.clj:5975)
	at clojure.core$load_lib.invokeStatic(core.clj:5974)
	at clojure.core$load_lib.doInvoke(core.clj:5953)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$load_libs.invokeStatic(core.clj:6016)
	at clojure.core$load_libs.doInvoke(core.clj:6000)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$require.invokeStatic(core.clj:6038)
	at clojure.main$loading__6789__auto____9094.invoke(main.clj:11)
	at clojure.main__init.load(Unknown Source)
	at clojure.main__init.<clinit>(Unknown Source)
	... 55 more

If I add the dep explicitly, making my deps:

 :deps {org.clojure/clojure {:mvn/version "1.11.1"}
        org.clojure/spec.alpha {:mvn/version "0.3.218"}
        org.clojure/tools.cli {:mvn/version "1.0.206"}
        com.github.mwiede/jsch {:mvn/version "0.2.3"}
        nrepl/bencode {:mvn/version "1.1.0"}}

It still doesn't include it:

   Creating target/bbssh-0.1.0-SNAPSHOT-standalone.jar
  Including bbssh-0.1.0-SNAPSHOT.jar
  Including jsch-0.2.3.jar
  Including bencode-1.1.0.jar
  Including clojure-1.11.1.jar
  Including tools.cli-1.0.206.jar
  Including core.specs.alpha-0.2.62.jar
Done!

If I add both spec deps now, making deps:

      :deps {org.clojure/clojure {:mvn/version "1.11.1"}
        org.clojure/spec.alpha {:mvn/version "0.3.218"}
        org.clojure/core.specs.alpha {:mvn/version "0.2.62"}
        org.clojure/tools.cli {:mvn/version "1.0.206"}
        com.github.mwiede/jsch {:mvn/version "0.2.3"}
        nrepl/bencode {:mvn/version "1.1.0"}}

now both spec libs are included, but suddenly org.clojure/tools.cli is missing!

Creating target/bbssh-0.1.0-SNAPSHOT-standalone.jar
  Including bbssh-0.1.0-SNAPSHOT.jar
  Including jsch-0.2.3.jar
  Including bencode-1.1.0.jar
  Including clojure-1.11.1.jar
  Including core.specs.alpha-0.2.62.jar
  Including spec.alpha-0.3.218.jar
Done!

How does cambada determine the deps to include in the uberjar? Something very strange is happening here.

Error: Could not find or load main class

Hi! Thanks very much for putting this together. It's the most familiar and complete of the various deps based tools out there for packaging. I have been trying to get some of my lein based projects to use deps and am discovering some issues along the way.

After building the jar with the patch in #10 I ran into this error:

$ java -jar ./target/app-1.0.0-SNAPSHOT-standalone.jar
Error: Could not find or load main class app.main

I inspected the jar's contents and the class is indeed there. After some investigation discovered that if the jar contains cryptography libraries ie buddy and/or Bouncy Castle then there are various files present in META-INF/ with .SF, .RSA or .DSA suffixes which I believe are from signed jars. lein by default excludes these files when building uberjars.

Helpful Stackoverflow post: https://stackoverflow.com/questions/25842559/valid-jar-signature-for-javafx-projects/30922181

clojure is always included in uberjar

While this is unlikely a problem for most, it is possible to build an uberjar with lein that does not include Clojure. The one use case I do have is with flyway. Vendor drivers that also depend on other libs can be uberjarred and droped in flyway's lib directory so that you can run migrations from the command line.

Properly attribute code to leiningen

I noticed the layout of the namespaces, the algorithms, and even the symbol naming in this repository bears uncanny resemblance to leiningen's code.

I would suggest that you ensure that you have @technomancy's permission, that you attribute the maintainers in the Copyright clause, and that you ensure you can redistribute EPL derivative works under an MIT license (this might be alright, but I really have no idea).

I really like the approach you've taken with this library, and I look forward to using it as soon as I'm sure that these issues have been addressed.

Examples:

lein ns: leiningen.jar

(defmethod copy-to-jar :path [project jar-os acc spec]
  (let [root-file (io/file (:path spec))
        root-dir-path (unix-path (dir-string root-file))
        paths (for [child (file-seq root-file)
                    :let [path (relativize-path
                                 (full-path child (unix-path (str child)))
                                 root-dir-path)]]
                (when-not (or (skip-file? child path root-file
                                          (:jar-exclusions project)
                                          (:jar-inclusions project))
                              (added-file? child path acc))
                  (put-jar-entry! jar-os child path)
                  path))]
    (into acc paths)))

(defmethod copy-to-jar :paths [project jar-os acc spec]
  (reduce (partial copy-to-jar project jar-os) acc
          (for [path (:paths spec)]
            {:type :path :path path})))

(defmethod copy-to-jar :bytes [project jar-os acc spec]
  (let [path (unix-path (:path spec))]
    (when-not (some #(re-find % path) (:jar-exclusions project))
      (.putNextEntry jar-os (JarEntry. path))
      (let [bytes (if (string? (:bytes spec))
                    (.getBytes (:bytes spec))
                    (:bytes spec))]
        (io/copy (ByteArrayInputStream. bytes) jar-os)))
    (conj acc path)))

(defmethod copy-to-jar :fn [project jar-os acc spec]
  (let [f (eval (:fn spec))
        dynamic-spec (f project)]
    (copy-to-jar project jar-os acc dynamic-spec)))

cambada ns: cambada.jar

(defmethod copy-to-jar :path [project jar-os acc spec]
  (let [root-file (io/file (:path spec))
        root-dir-path (utils/unix-path (utils/dir-string root-file))
        paths (for [child (file-seq root-file)
                    :let [path (utils/relativize-path
                                (utils/full-path child (utils/unix-path (str child)))
                                root-dir-path)]]
                (when-not (or (skip-file? child path root-file
                                          (:jar-exclusions project)
                                          (:jar-inclusions project))
                              (added-file? child path acc))
                  (put-jar-entry! jar-os child path)
                  path))]
    (into acc paths)))

(defmethod copy-to-jar :paths [project jar-os acc spec]
  (reduce (partial copy-to-jar project jar-os) acc
          (for [path (:paths spec)]
            {:type :path :path path})))

(defmethod copy-to-jar :bytes [project jar-os acc spec]
  (let [path (utils/unix-path (:path spec))]
    (when-not (some #(re-find % path) (:jar-exclusions project))
      (.putNextEntry jar-os (JarEntry. path))
      (let [bytes (if (string? (:bytes spec))
                    (.getBytes (:bytes spec))
                    (:bytes spec))]
        (io/copy (ByteArrayInputStream. bytes) jar-os)))
    (conj acc path)))

(defmethod copy-to-jar :fn [project jar-os acc spec]
  (let [f (eval (:fn spec))
        dynamic-spec (f project)]
    (copy-to-jar project jar-os acc dynamic-spec)))

lein ns: leiningen.uberjar

(defn- merger-match? [[pattern] filename]
  (boolean
   (condp instance? pattern
     String (= pattern filename)
     Pattern (re-find pattern filename))))

(def ^:private skip-merger
  [(constantly ::skip)
   (constantly nil)])

(def ^:private default-merger
  [(fn [in out file prev]
     (when-not prev
       (.setCompressedSize file -1)
       (.putNextEntry out file)
       (io/copy (.getInputStream in file) out)
       (.closeEntry out))
     ::skip)
   (constantly nil)])

(defn- make-merger [fns]
  {:pre [(sequential? fns) (= 3 (count fns)) (every? ifn? fns)]}
  (let [[read-fn merge-fn write-fn] fns]
    [(fn [in out file prev]
       (with-open [ins (.getInputStream in file)]
         (let [new (read-fn ins)]
           (if-not prev
             new
             (merge-fn new prev)))))
     (fn [out filename result]
       (.putNextEntry out (ZipEntry. filename))
       (write-fn (CloseShieldOutputStream. out) result)
       (.closeEntry out))]))

cambada ns: cambada.uberjar

(defn ^:private  merger-match? [[pattern] filename]
  (boolean
   (condp instance? pattern
     String (= pattern filename)
     Pattern (re-find pattern filename))))

(def ^:private default-merger
  [(fn [in out file prev]
     (when-not prev
       (.setCompressedSize file -1)
       (.putNextEntry out file)
       (io/copy (.getInputStream in file) out)
       (.closeEntry out))
     ::skip)
   (constantly nil)])

(defn ^:private make-merger [fns]
  {:pre [(sequential? fns) (= 3 (count fns)) (every? ifn? fns)]}
  (let [[read-fn merge-fn write-fn] fns]
    [(fn [in out file prev]
       (with-open [ins (.getInputStream in file)]
         (let [new (read-fn ins)]
           (if-not prev
             new
             (merge-fn new prev)))))
     (fn [out filename result]
       (.putNextEntry out (ZipEntry. filename))
       (write-fn (CloseShieldOutputStream. out) result)
       (.closeEntry out))]))

(defn ^:private map-vals
  "Like 'update', but for all values in a map."
  [m f & args]
  (zipmap (keys m) (map #(apply f % args) (vals m))))

(def ^:private skip-merger
  [(constantly ::skip)
   (constantly nil)])

lein ns: leiningen.clean

(defn real-directory?
  "Returns true if this file is a real directory, false if it is a symlink or a
  normal file."
  [f]
  (if (= :windows (utils/get-os))
    (.isDirectory f)
    (and (.isDirectory f)
         (not (utils/symlink? f)))))

(defn delete-file-recursively
  "Delete file f. If it's a directory, recursively delete all its contents.
  Raise an exception if any deletion fails unless silently is true."
  [f & [silently]]
  (let [f (io/file f)]
    (when (real-directory? f)
      (doseq [child (.listFiles f)]
        (delete-file-recursively child silently)))
    (.setWritable f true)
    (io/delete-file f silently)))

cambada ns: cambada.utils (cambada.clean is basically a proxy to these functions)

(defn real-directory?
  "Returns true if this file is a real directory, false if it is a symlink or a
  normal file."
  [f]
  (if (= :windows (get-os))
    (.isDirectory f)
    (and (.isDirectory f)
         (not (symlink? f)))))

(defn delete-file-recursively
  "Delete file f. If it's a directory, recursively delete all its contents.
  Raise an exception if any deletion fails unless silently is true."
  [f & [silently]]
  (let [f (io/file f)]
    (when (real-directory? f)
      (doseq [child (.listFiles f)]
        (delete-file-recursively child silently)))
    (.setWritable f true)
    (io/delete-file f silently)))

ERROR! Error: Main entry point class not found.

com.oracle.svm.core.util.UserError$UserException: Main entry point class 'cvfetcher.fetch-linkedin' not found.
        at com.oracle.svm.core.util.UserError.abort(UserError.java:65)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:260)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:448)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:113)
Error: Image build request failed with exit status 1

This happens when I try to compile a native image.

Invalid sourceDirectory in generated pom

My deps.edn looks like this:

{
 :paths   ["src/cljc"]
 :aliases {
           :jar {:extra-deps {luchiniatwork/cambada {:mvn/version "1.0.2"}}
                 :main-opts  ["-m" "cambada.jar"
                              "-m" "xyz.core"
                              "--app-group-id" "abc"
                              "--app-artifact-id" "xyz"]}}}

Now clj -A:jar --app-version 0.0.1 generates pom.xml that includes this:

  <build>
    <sourceDirectory>src</sourceDirectory>
  </build>

while clj -Spom results with this:

  <build>
    <sourceDirectory>src/cljc</sourceDirectory>
  </build>

The jar built by cambada includes both cljs/xyz/core.cljc and xyz/core.cljc. I think that only the latter is correct and it might be result of bad sourceDirectory evaluation.

NoSuchFileException when performing uberjar with spec-alpha2 in deps.

Description

Attempting to build uberjar for a project that includes Clojure's unreleased spec-alpha2 as a git reference. It reaches a step that says "Including java" and throws an exception:

Cleaning target
Creating target/classes
Creating target/repro-case-1.0.0.jar
Updating pom.xml
Skipping paths: resources
Skipping coordinate: {:git/url https://github.com/clojure/spec-alpha2.git, :sha 2f84e3a37cab76d44c58785ff4481597429bc1d3}
Warning: The Main-Class specified does not exist within the jar. It may not be executable as expected. A gen-class directive may be missing in the namespace which contains the main method, or the namespace has not been AOT-compiled.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Creating target/repro-case-1.0.0-standalone.jar
  Including repro-case-1.0.0.jar
  Including java
Execution error (NoSuchFileException) at sun.nio.fs.UnixException/translateToIOException (UnixException.java:92).
/root/.gitlibs/libs/org.clojure/spec-alpha2/2f84e3a37cab76d44c58785ff4481597429bc1d3/${project.basedir}/src/main/java

Full report at:
/tmp/clojure-17151214060258308065.edn

A minimal reproduction case is at https://gist.github.com/mtnygard/aaec1ec5ebb50c2405afd40bde40c5b9. That gist includes a deps.edn file that is sufficient to produce the error without any other sources required. The full report produced by cambada is included in that gist.

ArtifactNotFoundException for a pom artifact, when attempting to create uberjar

Hi there,

I'm trying to make an uberjar for a project that depends on metosin/spec-tools version 0.8.0, and I am getting the following error:

alex@deneb:/mnt/d/Projects/myproj$ clj -A:uberjar
Cleaning target
Creating target/classes
  Compiling myproj.core
Creating target/myproj-1.0.0-SNAPSHOT.jar
Updating pom.xml
Exception in thread "main" org.eclipse.aether.resolution.ArtifactDescriptorException: Failed to read artifact descriptor for com.fasterxml.jackson.core:jackson-databind:jar:2.9.7
        at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.loadPom(DefaultArtifactDescriptorReader.java:323)
        at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.readArtifactDescriptor(DefaultArtifactDescriptorReader.java:192)
        at org.eclipse.aether.internal.impl.DefaultRepositorySystem.readArtifactDescriptor(DefaultRepositorySystem.java:253)
        at clojure.tools.deps.alpha.extensions.maven$eval2624$fn__2626.invoke(maven.clj:79)
        at clojure.lang.MultiFn.invoke(MultiFn.java:243)
        at clojure.tools.deps.alpha$expand_deps.invokeStatic(alpha.clj:170)
        at clojure.tools.deps.alpha$expand_deps.invoke(alpha.clj:152)
        at clojure.tools.deps.alpha$resolve_deps.invokeStatic(alpha.clj:215)
        at clojure.tools.deps.alpha$resolve_deps.invoke(alpha.clj:197)
        at cambada.uberjar$get_dep_jars.invokeStatic(uberjar.clj:109)
        at cambada.uberjar$get_dep_jars.invoke(uberjar.clj:107)
        at cambada.uberjar$apply_BANG_.invokeStatic(uberjar.clj:132)
        at cambada.uberjar$apply_BANG_.invoke(uberjar.clj:129)
        at cambada.cli$runner.invokeStatic(cli.clj:120)
        at cambada.cli$runner.invoke(cli.clj:115)
        at cambada.uberjar$_main.invokeStatic(uberjar.clj:142)
        at cambada.uberjar$_main.doInvoke(uberjar.clj:140)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:702)
        at clojure.core$apply.invokeStatic(core.clj:657)
        at clojure.main$main_opt.invokeStatic(main.clj:317)
        at clojure.main$main_opt.invoke(main.clj:313)
        at clojure.main$main.invokeStatic(main.clj:424)
        at clojure.main$main.doInvoke(main.clj:387)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:702)
        at clojure.main.main(main.java:37)
Caused by: org.apache.maven.model.resolution.UnresolvableModelException: Could not find artifact com.fasterxml.jackson:jackson-base:pom:2.9.7
        at org.apache.maven.repository.internal.DefaultModelResolver.resolveModel(DefaultModelResolver.java:178)
        at org.apache.maven.repository.internal.DefaultModelResolver.resolveModel(DefaultModelResolver.java:224)
        at org.apache.maven.model.building.DefaultModelBuilder.readParentExternally(DefaultModelBuilder.java:1051)
        at org.apache.maven.model.building.DefaultModelBuilder.readParent(DefaultModelBuilder.java:829)
        at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:331)
        at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.loadPom(DefaultArtifactDescriptorReader.java:314)
        ... 26 more
Caused by: org.eclipse.aether.resolution.ArtifactResolutionException: Could not find artifact com.fasterxml.jackson:jackson-base:pom:2.9.7
        at org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:422)
        at org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolveArtifacts(DefaultArtifactResolver.java:224)
        at org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolveArtifact(DefaultArtifactResolver.java:201)
        at org.apache.maven.repository.internal.DefaultModelResolver.resolveModel(DefaultModelResolver.java:174)
        ... 31 more
Caused by: org.eclipse.aether.transfer.ArtifactNotFoundException: Could not find artifact com.fasterxml.jackson:jackson-base:pom:2.9.7
        at org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:412)
        ... 34 more

Why is it unable to find this pom artifact? I can see com.fasterxml.jackson:jackson-base:pom:2.9.7 in my ~/.m2 directory.

My deps.edn:

{:deps {metosin/spec-tools {:mvn/version "0.8.0"}}
 :aliases {:uberjar
           {:extra-deps
            {luchiniatwork/cambada {:mvn/version "1.0.0"}}
            :main-opts ["-m" "cambada.uberjar"
                        "-m" "myproj.core"]}}}

The dependency tree:

alex@deneb:/mnt/d/Projects/myproj$ clj -Stree
org.clojure/clojure 1.9.0
  org.clojure/core.specs.alpha 0.1.24
metosin/spec-tools 0.8.0
  org.clojure/spec.alpha 0.2.176
  com.fasterxml.jackson.core/jackson-databind 2.9.7
    com.fasterxml.jackson.core/jackson-annotations 2.9.0
    com.fasterxml.jackson.core/jackson-core 2.9.7

ArtifactDescriptorException: Failed to read artifact descriptor when running uberjar

I'm experiencing an exception

Exception in thread "main" org.eclipse.aether.resolution.ArtifactDescriptorException: Failed to read artifact descriptor for org.apache.httpcomponents:httpcore:jar:4.4.9
	at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.loadPom(DefaultArtifactDescriptorReader.java:323)
...
	at clojure.tools.deps.alpha$resolve_deps.invokeStatic(alpha.clj:215)
	at clojure.tools.deps.alpha$resolve_deps.invoke(alpha.clj:197)
	at cambada.uberjar$get_dep_jars.invokeStatic(uberjar.clj:109)
	at cambada.uberjar$get_dep_jars.invoke(uberjar.clj:107)
...

when running clj -A:uberjar -Srepro -Sforce on OS X High Sierra.

I've attached the simplest repro I could make for this.

This reproduces from a clean install:

  1. empty ~/.m2 and ~/.clojure,
  2. brew install clojure
  3. run:
clj -Sdeps '{:deps
              {seancorfield/clj-new
                {:git/url "https://github.com/seancorfield/clj-new"
                 :sha "21ca1b27f46dc324be084ba839beca555aeda387"}}}' \
  -m clj-new.create \
  app \
  krukow/artifact-exception
  1. Add to deps.edn
{:paths ["resources" "src"]
 :deps {org.clojure/clojure {:mvn/version "1.9.0"}
        clj-http {:mvn/version "3.8.0"}}
 :aliases
 {:uberjar
  {:extra-deps
   {luchiniatwork/cambada {:mvn/version "1.0.0"}}
   :main-opts ["-m" "cambada.uberjar"
               "-m" "krukow.artifact-exception"]}}
 }

However, if I add

:mvn/repos {"central" {:url "http://central.maven.org/maven2/"}
             "clojars" {:url "https://repo.clojars.org/"}}

things work as expected (I'm not interested in this since it's using http).

simple-repro.zip

Problems with finding main entry class for uberjar and native-image

Got a simple cljfx project that i want to get built with cambada (ideally with native-image, but i'm kind of stuck on uberjar now).

Project repo: https://github.com/jumski/tone-collector/tree/add-cambada

My deps.edn file:

{:deps
 {org.clojure/clojure {:mvn/version "1.10.0"}
  cljfx {:mvn/version "1.6.7"}
  overtone/midi-clj {:mvn/version "0.5.0"}}
 :aliases
 {:uberjar {:extra-deps {luchiniatwork/cambada {:mvn/version "1.0.2"}}
            :main-opts ["-m" "cambada.uberjar"
                        "-m" "jumski.tone-collector.core"]}}}

Building uberjar

It mainly works with small warnings (that i assume are insignificant because they are about some logger)

warnings

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

But when i try to run the uberjar with

java -jar target/tone-collector-1.0.0-SNAPSHOT-standalone.jar

I got this error:

Error: Could not find or load main class jumski.tone-collector.core
Caused by: java.lang.ClassNotFoundException: jumski.tone-collector.core

By trial and error i figured out that when i provide the -m as a slash/underscore path like this, it will work:

-                        "-m" "jumski.tone-collector.core"]}}}
+                        "-m" "jumski/tone_collector/core"]}}}

I do not really understand what is the problem here.

Building native-image

When i try to use native-image support it fails with ERROR! Error: Main entry point class 'jumski.tone-collector.core not found for both jumski.tone-collector.core and jumski/tone_collector/core variants of main entry point spec.

Error for `jumski.tone-collector.core`

ERROR! Error: Main entry point class 'jumski.tone-collector.core' not found.
com.oracle.svm.core.util.UserError$UserException: Main entry point class 'jumski.tone-collector.core' not found.
        at com.oracle.svm.core.util.UserError.abort(UserError.java:68)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:300)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:501)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:115)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner$JDK9Plus.main(NativeImageGeneratorRunner.java:528)
Error: Image build request failed with exit status 1

Error for `jumski/tone_collector/core`

ERROR! Error: Main entry point class 'jumski/tone_collector/core' not found.
com.oracle.svm.core.util.UserError$UserException: Main entry point class 'jumski/tone_collector/core' not found.
        at com.oracle.svm.core.util.UserError.abort(UserError.java:68)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:300)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:501)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:115)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner$JDK9Plus.main(NativeImageGeneratorRunner.java:528)
Error: Image build request failed with exit status 1

Am i missing something or there is some kind or problem in cambada?

Versions

› clj --help
Version: 1.10.1.536

› gu --help
GraalVM Component Updater v2.0.0

› native-image --version
GraalVM Version 20.0.0 CE

› java --version
openjdk 11.0.6 2020-01-14
OpenJDK Runtime Environment GraalVM CE 20.0.0 (build 11.0.6+9-jvmci-20.0-b02)
OpenJDK 64-Bit Server VM GraalVM CE 20.0.0 (build 11.0.6+9-jvmci-20.0-b02, mixed mode, sharing)

omit source files

Hi!

Nice tool. How to omit source files in uberjar?
Analog in Leiningen
;; Leave the contents of :source-paths out of jars (for AOT projects).
:omit-source true

Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.