{"id":15873757,"url":"https://github.com/luchiniatwork/cambada","last_synced_at":"2025-04-05T06:05:55.154Z","repository":{"id":62431702,"uuid":"136821062","full_name":"luchiniatwork/cambada","owner":"luchiniatwork","description":"Packager for Clojure based on deps.edn (AKA tools.deps). Supporting jar, uberjar and GraalVM's native-image.","archived":false,"fork":false,"pushed_at":"2020-12-24T00:42:23.000Z","size":48,"stargazers_count":222,"open_issues_count":23,"forks_count":28,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-29T05:04:48.937Z","etag":null,"topics":["clojure","graalvm","packager","tools","tools-deps"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luchiniatwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-10T15:31:27.000Z","updated_at":"2024-09-12T02:14:39.000Z","dependencies_parsed_at":"2022-11-01T20:46:40.528Z","dependency_job_id":null,"html_url":"https://github.com/luchiniatwork/cambada","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fcambada","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fcambada/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fcambada/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fcambada/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luchiniatwork","download_url":"https://codeload.github.com/luchiniatwork/cambada/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247294537,"owners_count":20915340,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","graalvm","packager","tools","tools-deps"],"created_at":"2024-10-06T01:06:08.070Z","updated_at":"2025-04-05T06:05:55.125Z","avatar_url":"https://github.com/luchiniatwork.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cambada\n\n[![Clojars Project](https://img.shields.io/clojars/v/luchiniatwork/cambada.svg)](https://clojars.org/luchiniatwork/cambada)\n\nCambada is a packager for Clojure based on `deps.edn` (AKA `tools.deps`). It is\nheavily inspired by Leiningen's jar and uberjar tasks and also supports\nGraalVM's new native-image making it a one-stop shop for any packaging needed\nfor your Clojure project.\n\n## Motivation\n\nLeiningen has laid the foundations of what many of us have come to accept as the\nstandard for Clojure projects. Clojure's `tools.deps` potentially brings new\nideas to the Clojure workflow. Cambada brings some of the great features of\nLeiningen to the `tools.deps` workflow.\n\nCambada's sole focus is packaging. It doesn't have plugins, templates or Clojars\nintegration. It packages your `deps.edn` progject as one - or all - of:\n\n1. jar\n2. uberjar\n3. GraalVM native image\n\nOn top of Phil Hagelberg's (and so many others') great Leiningen, many thanks to\nDominic Monroe and his work on `pack` as well as Taylor Wood and his\n`clj.native-image`. These projects offered a lot of inspiration (and, in some\ncases, donor code too).\n\n## Table of Contents\n\n* [Getting Started](#getting-started)\n* [Easy Aliases](#easy-aliases)\n* [Packaging as a Jar](#packaging-as-a-jar)\n* [Packaging as an Uberjar](#packaging-as-an-uberjar)\n* [Packaging as a Native Image](#packaging-as-a-native-image)\n* [Caveats](#caveats)\n* [Performance Comparison](#performance-comparison)\n* [Bugs](#bugs)\n* [Help!](#help)\n\n## Getting Started\n\nCambada is a simple set of main functions that can be called from a `deps.edn`\nalias. The simplest way to have it available in your project is to add an alias\nwith `extra-deps` to your `deps.edn` file:\n\n``` clojure\n{:aliases {:cambada\n           {:extra-deps\n            {luchiniatwork/cambada\n             {:mvn/version \"1.0.5\"}}}}}\n```\n\nCambada has three main entry points, `cambada.jar`, `cambada.uberjar` and\n`cambada.native-image`. Let's say you simply want to create an uberjar:\n\n``` shell\n$ clj -R:cambada -m cambada.uberjar\nCleaning target\nCreating target/classes\n  Compiling ...\nCreating target/project-name-1.0.0-SNAPSHOT.jar\nUpdating pom.xml\nCreating target/project-name-1.0.0-SNAPSHOT-standalone.jar\n  Including ...\nDone!\n```\n\nYour files will be located at `target/` by default.\n\nAll entry points have a few extra configuration options you might be interested\nin. For instance:\n\n``` shell\n$ clj -R:cambada -m cambada.uberjar --help\nPackage up the project files and all dependencies into a jar file.\n\nUsage: clj -m cambada.uberjar [options]\n\nOptions:\n  -m, --main NS_NAME                            The namespace with the -main function\n      --app-group-id STRING     project-name    Application Maven group ID\n      --app-artifact-id STRING  project-name    Application Maven artifact ID\n      --app-version STRING      1.0.0-SNAPSHOT  Application version\n      --[no-]copy-source                        Copy source files by default\n  -a, --aot NS_NAMES            all             Namespaces to be AOT-compiled or `all` (default)\n  -d, --deps FILE_PATH          deps.edn        Location of deps.edn file\n  -o, --out PATH                target          Output directory\n  -h, --help                                    Shows this help\n```\n\nDo try `--help` for `cambada.jar` and `cambada.native-image` if you are\ninterested or refer to the sections below.\n\n## Easy Aliases\n\nOne of the powers-in-simplicity of `tools.deps` is the ability to define aliases\non `deps.edn`. When we used the alias `cambada` on the section above, we simply\nspecified it as an dependency to be resolved (therefore the `-R` when calling\n`clj`).\n\nYou can also be a lot more prescriptive in your aliases by making them do more\nwork for you. For instance, the alias below will create a versioned uberjar:\n\n``` clojure\n{:aliases {:uberjar\n           {:extra-deps\n            {luchiniatwork/cambada {:mvn/version \"1.0.0\"}}\n            :main-opts [\"-m\" \"cambada.uberjar\"\n                        \"--app-version\" \"0.5.3\"]}}}\n```\n\nBy having an alias like this `uberjar` one in your `deps.edn` you can simply run\nit by using `$ clj -A:uberjar` making it very familiar to those used with `$\nlein uberjar`:\n\n``` shell\n$ clj -A:uberjar\nCleaning target\nCreating target/classes\n  Compiling ...\nCreating target/project-name-0.5.3.jar\nUpdating pom.xml\nCreating target/project-name-0.5.3-standalone.jar\n  Including ...\nDone!\n```\n\n## Packaging as a Jar\n\nLet's start with an empty project folder:\n\n``` shell\n$ mkdir -p myproj/src/myproj/\n$ cd myproj\n```\n\nCreate a `deps.edn` at the root of your project with `cambada.jar` as an alias:\n\n``` clojure\n{:aliases {:jar\n           {:extra-deps\n            {luchiniatwork/cambada {:mvn/version \"1.0.2\"}}\n            :main-opts [\"-m\" \"cambada.jar\"\n                        \"-m\" \"myproj.core\"]}}}\n```\n\nCreate a simple hello world on a `-main` function at `src/myproj/core.clj`:\n\n``` clojure\n(ns myproj.core\n  (:gen-class))\n\n(defn -main [\u0026 args]\n  (println \"Hello World!\"))\n```\n\nOf course, just for safe measure, let's run this hello world via `clj`:\n\n``` shell\n$ clj -m myproj.core\nHello World!\n```\n\nThen just call the alias from the project's root:\n\n``` shell\n$ clj -A:jar\nCleaning target\nCreating target/classes\n  Compiling myproj.core\nCreating target/myproj-1.0.0-SNAPSHOT.jar\nUpdating pom.xml\nDone!\n```\n\nOnce Cambada is done, you'll have a jar package at `target/`. In order to run\nit, you'll need to add Clojure and spec to your class path. The paths will vary\non your system:\n\n``` shell\n$ java -cp target/myproj-1.0.0-SNAPSHOT.jar myproj.core\nHello World!\n```\nFor a standalone jar file see the uberjar option on the next section.\n\nYou can specify the following options for `cambada.jar`:\n\n``` text\n  -m, --main NS_NAME                            The namespace with the -main function\n      --app-group-id STRING     project-name    Application Maven group ID\n      --app-artifact-id STRING  project-name    Application Maven artifact ID\n      --app-version STRING      1.0.0-SNAPSHOT  Application version\n      --[no-]copy-source                        Copy source files by default\n  -a, --aot NS_NAMES            all             Namespaces to be AOT-compiled or `all` (default)\n  -d, --deps FILE_PATH          deps.edn        Location of deps.edn file\n  -o, --out PATH                target          Output directory\n  -h, --help                                    Shows this help\n```\n\nThese options should be quite self-explanatory and the defaults are\nhopefully sensible enough for most of the basic cases. By default\neverything gets AOT-compiled and sources are copied to the resulting jar.\n\nFor those used to Leiningen, the application's group ID, artifact ID\nand version are not extracted from `project.clj` (since it's assumed\nyou don't have a `project.clj` in a `deps.edn` workflow). Therefore,\nyou must specify these expressively as options.\n\n## Packaging as an Uberjar\n\nLet's start with an empty project folder:\n\n``` shell\n$ mkdir -p myproj/src/myproj/\n$ cd myproj\n```\n\nCreate a `deps.edn` at the root of your project with `cambada.jar` as an alias:\n\n``` clojure\n{:aliases {:uberjar\n           {:extra-deps\n            {luchiniatwork/cambada {:mvn/version \"1.0.0\"}}\n            :main-opts [\"-m\" \"cambada.uberjar\"\n                        \"-m\" \"myproj.core\"]}}}\n```\n\nCreate a simple hello world on a `-main` function at `src/myproj/core.clj`:\n\n``` clojure\n(ns myproj.core\n  (:gen-class))\n\n(defn -main [\u0026 args]\n  (println \"Hello World!\"))\n```\n\nOf course, just for safe measure, let's run this hello world via `clj`:\n\n``` shell\n$ clj -m myproj.core\nHello World!\n```\n\nThen just call the alias from the project's root:\n\n``` shell\n$ clj -A:uberjar\nCleaning target\nCreating target/classes\n  Compiling myproj.core\nCreating target/myproj-1.0.0-SNAPSHOT.jar\nUpdating pom.xml\nCreating target/myproj-1.0.0-SNAPSHOT-standalone.jar\n  Including myproj-1.0.0-SNAPSHOT.jar\n  Including clojure-1.9.0.jar\n  Including spec.alpha-0.1.143.jar\n  Including core.specs.alpha-0.1.24.jar\nDone!\n```\n\nOnce Cambada is done, you'll have two jar packages at `target/`. One for a basic\njar and one standalone with all dependencies in it. In order to run it, simply\ncall it:\n\n``` shell\n$ java -jar target/myproj-1.0.0-SNAPSHOT-standalone.jar\nHello World!\n```\n\n`cambada.uberjar` has exactly the same options and defaults as\n`cambada.jar` (see above for more details).\n\n## Caveats\n\nIf any of your transitive dependencies has a Maven Central dependency,\n`cambada` may fail on you (investigations under way). Therefore, it is\nrecommended that you explicitly add your repos (Central included) to\nyour `deps.edn` file i.e.:\n\n``` clojure\n{:deps {...}\n\n :mvn/repos {\"central\" {:url \"https://repo1.maven.org/maven2/\"}\n             \"clojars\" {:url \"https://repo.clojars.org/\"}}}\n```\n\n## Packaging as a Native Image\n\nBy using GraalVM we now have the option of packaging everything AOT\ncompiled as a native image.\n\nIf you want to use this feature, make sure to [download and install\nGraalVM](https://www.graalvm.org/).\n\nIf you are a MacOS user, GraalVM CE is available as a brew cask:\n\n``` shell\n$ brew cask install graalvm/tap/graalvm-ce\n```\n\nGraalVM's `native-image` is a package that needs to be installed\nmanually with the following command (attention that `gu` is at\n`$GRAALVM_HOME/bin/` if it is not on your `PATH`):\n\n``` shell\n$ gu install native-image\n```\n\nYou will need to set your `GRAALVM_HOME` environment variable to point\nto where GraalVM is installed. Alternatevely you can call\n`cambada.native-image` with the argument `--graalvm-home` pointing to it.\n\nThe entry point for native image packaging is\n`cambada.native-image`. Let's assume your `GRAALVM_HOME` variable is\nset (if you don't, use `--graalvm-home`).\n\nLet's start with an empty project folder:\n\n``` shell\n$ mkdir -p myproj/src/myproj/\n$ cd myproj\n```\n\nCreate a `deps.edn` at the root of your project with `cambada.jar` as an alias:\n\n``` clojure\n{:aliases {:native-image\n           {:extra-deps\n            {luchiniatwork/cambada {:mvn/version \"1.0.0\"}}\n            :main-opts [\"-m\" \"cambada.native-image\"\n                        \"-m\" \"myproj.core\"]}}}\n```\n\nCreate a simple hello world on a `-main` function at `src/myproj/core.clj`:\n\n``` clojure\n(ns myproj.core\n  (:gen-class))\n\n(defn -main [\u0026 args]\n  (println \"Hello World!\"))\n```\n\nOf course, just for safe measure, let's run this hello world via `clj`:\n\n``` shell\n$ clj -m myproj.native-image\nHello World!\n```\n\nThen just call the alias from the project's root:\n\n``` shell\n$ clj -A:native-image\nCleaning target\nCreating target/classes\n  Compiling myproj.core\nCreating target/myproj\n   classlist:   2,810.07 ms\n       (cap):   1,469.31 ms\n       setup:   2,561.28 ms\n  (typeflow):   5,802.45 ms\n   (objects):   2,644.17 ms\n  (features):      40.54 ms\n    analysis:   8,609.18 ms\n    universe:     314.28 ms\n     (parse):   1,834.84 ms\n    (inline):   2,338.45 ms\n   (compile):  16,824.24 ms\n     compile:  21,435.77 ms\n       image:   1,862.44 ms\n       write:   1,276.55 ms\n     [total]:  38,942.48 ms\n\nDone!\n```\n\nOnce Cambada is done, you'll have an executable package at `target/`:\n\n``` shell\n$ ./target/myproj\nHello World!\n```\n\nExtra options can be sent to GraalVM's packager by using Cambada's\n`--graalvm-opt` option i.e., to include `FILE` as a resource, simply\nuse `--graalvm-opt H:IncludeResources=FILE`.\n\n## Performance Comparison\n\nA quick comparison of the `myproj` hello world as described previously and ran\nacross different packaging options:\n\nStraight with `clj`:\n\n``` shell\n$ time clj -m myproj.core\nHello World!\n1.160 secs\n```\n\nAs a standalone uberjar:\n\n``` shell\n$ time java -jar target/myproj-1.0.0-SNAPSHOT-standalone.jar\nHello World!\n0.850 secs\n```\n\nAs a native image:\n\n``` shell\n$ time ./target/myproj\nHello World!\n0.054 secs\n```\n\nComparing with `clj` as a baseline:\n\n| Method         | Speed in secs | Speed relative to `clj` |\n| -------------- | ------------- | ----------------------- |\n| `clj`          | `1.160 secs`  | `1x`                    |\n| `uberjar`      | `0.850 secs`  | `1.36x`                 |\n| `native-image` | `0.054 secs`  | `21.48x`                |\n\n## Bugs\n\nIf you find a bug, submit a\n[Github issue](https://github.com/luchiniatwork/cambada/issues).\n\n## Help\n\nThis project is looking for team members who can help this project succeed!\nIf you are interested in becoming a team member please open an issue.\n\n## License\n\nCopyright © 2018 Tiago Luchini\n\nDistributed under the MIT License. See LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluchiniatwork%2Fcambada","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluchiniatwork%2Fcambada","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluchiniatwork%2Fcambada/lists"}