├── test-data ├── nses │ └── src │ │ ├── c.clj │ │ ├── d.clj │ │ ├── a.clj │ │ └── b.clj ├── uber-conflict │ ├── j1 │ │ ├── append.txt │ │ ├── my │ │ │ └── j1.txt │ │ ├── ignore.txt │ │ ├── overwrite.txt │ │ ├── META-INF │ │ │ └── LICENSE.txt │ │ ├── data_readers.clj │ │ └── data_readers.cljc │ ├── j2 │ │ ├── append.txt │ │ ├── my │ │ │ └── j2.txt │ │ ├── ignore.txt │ │ ├── overwrite.txt │ │ ├── data_readers.clj │ │ ├── META-INF │ │ │ └── LICENSE.txt │ │ └── data_readers.cljc │ └── j3 │ │ └── META-INF │ │ └── LICENSE.txt ├── case-sensitive-collision │ ├── j1 │ │ └── FOO │ └── j2 │ │ └── foo │ │ └── hi.txt ├── p1 │ ├── resources │ │ ├── data.edn │ │ └── test.png │ ├── java │ │ └── foo │ │ │ ├── Demo1.java │ │ │ └── Demo2.java │ ├── src │ │ └── foo │ │ │ └── bar.clj │ └── deps.edn ├── p2 │ ├── resources │ │ └── data.edn │ ├── src │ │ └── foo │ │ │ └── bar.clj │ ├── deps.edn │ └── pom.xml ├── p3 │ ├── resources │ │ └── data.edn │ ├── src │ │ └── foo │ │ │ └── bar.clj │ └── deps.edn ├── p4 │ ├── resources │ │ └── data.edn │ ├── src │ │ └── foo │ │ │ └── bar.clj │ ├── deps.edn │ └── pom.xml ├── bad-zip │ ├── deps.edn │ └── bad.jar ├── reflecting │ ├── deps.edn │ └── src │ │ └── foo │ │ └── bar.clj └── assert │ ├── deps.edn │ └── src │ └── foo │ └── check_assert.clj ├── docs ├── build.graffle ├── css │ ├── highlight.css │ └── default.css ├── index.html ├── js │ ├── page_effects.js │ └── highlight.min.js └── clojure.tools.build.api.html ├── .clj-kondo └── config.edn ├── .gitignore ├── .github ├── workflows │ ├── snapshot.yml │ ├── release.yml │ ├── ci.yml │ └── build_docs.yml └── PULL_REQUEST_TEMPLATE ├── src ├── main │ └── clojure │ │ └── clojure │ │ └── tools │ │ └── build │ │ ├── api │ │ └── specs.clj │ │ ├── util │ │ ├── log.clj │ │ ├── zip.clj │ │ └── file.clj │ │ └── tasks │ │ ├── create_basis.clj │ │ ├── zip.clj │ │ ├── jar.clj │ │ ├── install.clj │ │ ├── javac.clj │ │ ├── copy.clj │ │ ├── compile_clj.clj │ │ ├── process.clj │ │ ├── write_pom.clj │ │ └── uber.clj └── test │ └── clojure │ └── clojure │ └── tools │ └── build │ ├── tasks │ ├── test_basis.clj │ ├── test_delete.clj │ ├── test_javac.clj │ ├── test_write_file.clj │ ├── test_process.clj │ ├── test_install.clj │ ├── test_jar.clj │ ├── test_zip.clj │ ├── test_copy.clj │ ├── test_compile_clj.clj │ ├── test_uber.clj │ └── test_pom.clj │ ├── test_project_root.clj │ └── test_util.clj ├── CONTRIBUTING.md ├── README.md ├── deps.edn ├── pom.xml ├── CHANGELOG.md ├── LICENSE └── epl-v10.html /test-data/nses/src/c.clj: -------------------------------------------------------------------------------- 1 | (ns c) -------------------------------------------------------------------------------- /test-data/nses/src/d.clj: -------------------------------------------------------------------------------- 1 | (ns d) -------------------------------------------------------------------------------- /test-data/uber-conflict/j1/append.txt: -------------------------------------------------------------------------------- 1 | j1 2 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j1/my/j1.txt: -------------------------------------------------------------------------------- 1 | j1 file 2 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j2/append.txt: -------------------------------------------------------------------------------- 1 | j2 2 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j2/my/j2.txt: -------------------------------------------------------------------------------- 1 | j2 file 2 | -------------------------------------------------------------------------------- /test-data/nses/src/a.clj: -------------------------------------------------------------------------------- 1 | (ns a 2 | (:require b)) -------------------------------------------------------------------------------- /test-data/nses/src/b.clj: -------------------------------------------------------------------------------- 1 | (ns b 2 | (:require c)) -------------------------------------------------------------------------------- /test-data/uber-conflict/j1/ignore.txt: -------------------------------------------------------------------------------- 1 | j1 file 2 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j1/overwrite.txt: -------------------------------------------------------------------------------- 1 | j1 file 2 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j2/ignore.txt: -------------------------------------------------------------------------------- 1 | j2 file 2 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j2/overwrite.txt: -------------------------------------------------------------------------------- 1 | j2 file 2 | -------------------------------------------------------------------------------- /test-data/case-sensitive-collision/j1/FOO: -------------------------------------------------------------------------------- 1 | I'm a foo 2 | -------------------------------------------------------------------------------- /test-data/case-sensitive-collision/j2/foo/hi.txt: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /test-data/p1/resources/data.edn: -------------------------------------------------------------------------------- 1 | {:version "$version"} 2 | -------------------------------------------------------------------------------- /test-data/p2/resources/data.edn: -------------------------------------------------------------------------------- 1 | {:version "$version"} 2 | -------------------------------------------------------------------------------- /test-data/p3/resources/data.edn: -------------------------------------------------------------------------------- 1 | {:version "$version"} 2 | -------------------------------------------------------------------------------- /test-data/p4/resources/data.edn: -------------------------------------------------------------------------------- 1 | {:version "$version"} 2 | -------------------------------------------------------------------------------- /test-data/bad-zip/deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {my/bad {:local/root "bad.jar"}}} 2 | -------------------------------------------------------------------------------- /test-data/p2/src/foo/bar.clj: -------------------------------------------------------------------------------- 1 | (ns foo.bar) 2 | 3 | (defn hello [] (println "hello")) 4 | -------------------------------------------------------------------------------- /test-data/p3/src/foo/bar.clj: -------------------------------------------------------------------------------- 1 | (ns foo.bar) 2 | 3 | (defn hello [] (println "hello")) 4 | -------------------------------------------------------------------------------- /test-data/p4/src/foo/bar.clj: -------------------------------------------------------------------------------- 1 | (ns foo.bar) 2 | 3 | (defn hello [] (println "hello")) 4 | -------------------------------------------------------------------------------- /docs/build.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clojure/tools.build/master/docs/build.graffle -------------------------------------------------------------------------------- /test-data/uber-conflict/j1/META-INF/LICENSE.txt: -------------------------------------------------------------------------------- 1 | this is a dummy license 2 | that will be appended -------------------------------------------------------------------------------- /test-data/uber-conflict/j1/data_readers.clj: -------------------------------------------------------------------------------- 1 | {j1a my.foo/j1a-reader 2 | j1b my.bar/j1b-reader} 3 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j2/data_readers.clj: -------------------------------------------------------------------------------- 1 | {j2a my.foo/j2a-reader 2 | j2b my.bar/j2b-reader} 3 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j3/META-INF/LICENSE.txt: -------------------------------------------------------------------------------- 1 | this is a dummy license 2 | that will be appended -------------------------------------------------------------------------------- /test-data/bad-zip/bad.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clojure/tools.build/master/test-data/bad-zip/bad.jar -------------------------------------------------------------------------------- /test-data/uber-conflict/j2/META-INF/LICENSE.txt: -------------------------------------------------------------------------------- 1 | this is a different license 2 | so should be included 3 | -------------------------------------------------------------------------------- /test-data/reflecting/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src"] 2 | :deps 3 | {org.clojure/clojure {:mvn/version "1.12.0"}} 4 | } 5 | -------------------------------------------------------------------------------- /test-data/p1/resources/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clojure/tools.build/master/test-data/p1/resources/test.png -------------------------------------------------------------------------------- /test-data/assert/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["target/classes" "src"] 2 | 3 | :deps 4 | {org.clojure/clojure {:mvn/version "1.10.3"}}} 5 | -------------------------------------------------------------------------------- /test-data/assert/src/foo/check_assert.clj: -------------------------------------------------------------------------------- 1 | (ns foo.check-assert) 2 | 3 | (defn f 4 | [x] 5 | {:pre [(keyword? x)]} 6 | x) 7 | -------------------------------------------------------------------------------- /.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:skip-comments true 2 | :linters 3 | {:unused-binding {:level :off} 4 | :cond-else {:level :off} 5 | :refer-all {:level :off}}} 6 | -------------------------------------------------------------------------------- /test-data/reflecting/src/foo/bar.clj: -------------------------------------------------------------------------------- 1 | (ns foo.bar 2 | (:gen-class)) 3 | 4 | (defn foo [s] (.length s)) 5 | 6 | (defn -main [& args] 7 | (println (foo "abc"))) 8 | -------------------------------------------------------------------------------- /test-data/uber-conflict/j1/data_readers.cljc: -------------------------------------------------------------------------------- 1 | {j1a #?(:cljs my.cljs.foo/j1a-reader :clj my.clj.foo/j1a-reader) 2 | j1b #?(:cljs my.cljs.foo/j1b-reader :clj my.clj.foo/j1b-reader)} -------------------------------------------------------------------------------- /test-data/uber-conflict/j2/data_readers.cljc: -------------------------------------------------------------------------------- 1 | {j2a #?(:cljs my.cljs.foo/j2a-reader :clj my.clj.foo/j2a-reader) 2 | j2b #?(:cljs my.cljs.foo/j2b-reader :clj my.clj.foo/j2b-reader)} -------------------------------------------------------------------------------- /test-data/p1/java/foo/Demo1.java: -------------------------------------------------------------------------------- 1 | package foo; 2 | 3 | public class Demo1 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println("Hello"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test-data/p1/java/foo/Demo2.java: -------------------------------------------------------------------------------- 1 | package foo; 2 | 3 | public class Demo2 { 4 | 5 | public static void main(String[] args) { 6 | System.out.println("Hello"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test-data/p1/src/foo/bar.clj: -------------------------------------------------------------------------------- 1 | (ns foo.bar 2 | "__REPLACE__" 3 | (:gen-class)) 4 | 5 | (defn hello [] (println "hello")) 6 | 7 | (defn -main 8 | [& args] 9 | (hello)) 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | target/ 4 | .nrepl* 5 | .cpcache 6 | .lein* 7 | .java-version 8 | project.clj 9 | out* 10 | test-out 11 | .clj-kondo/.cache 12 | .vscode 13 | .lsp 14 | .calva/mcp-server/port 15 | .portal/vs-code.edn 16 | -------------------------------------------------------------------------------- /.github/workflows/snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Snapshot on demand 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: [workflow_dispatch] 7 | 8 | jobs: 9 | call-snapshot: 10 | uses: clojure/build.ci/.github/workflows/snapshot.yml@master 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /test-data/p1/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths [:clj-paths :resource-paths] 2 | 3 | :deps 4 | {org.clojure/clojure {:mvn/version "1.10.1"} 5 | org.clojure/core.cache {:mvn/version "1.0.207"}} 6 | 7 | :aliases 8 | {:clj-paths ["src"] 9 | :resource-paths ["resources"]}} 10 | -------------------------------------------------------------------------------- /test-data/p2/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths [:clj-paths :resource-paths] 2 | 3 | :deps 4 | {org.clojure/clojure {:mvn/version "1.10.1"} 5 | org.clojure/core.cache {:mvn/version "1.0.207"}} 6 | 7 | :aliases 8 | {:clj-paths ["src"] 9 | :resource-paths ["resources"]}} 10 | -------------------------------------------------------------------------------- /test-data/p4/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths [:clj-paths :resource-paths] 2 | 3 | :deps 4 | {org.clojure/clojure {:mvn/version "1.10.1"} 5 | org.clojure/core.cache {:mvn/version "1.0.207"}} 6 | 7 | :aliases 8 | {:clj-paths ["src"] 9 | :resource-paths ["resources"]}} 10 | -------------------------------------------------------------------------------- /test-data/p3/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths [:clj-paths :resource-paths] 2 | 3 | :deps 4 | {org.clojure/clojure {:mvn/version "1.10.1"} 5 | org.clojure/core.async {:mvn/version "1.1.587" :optional true}} 6 | 7 | :aliases 8 | {:clj-paths ["src"] 9 | :resource-paths ["resources"]}} 10 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/api/specs.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.tools.build.api.specs 2 | (:require [clojure.spec.alpha :as s])) 3 | 4 | (s/def ::lib qualified-ident?) 5 | (s/def ::path string?) 6 | (s/def ::paths (s/coll-of string?)) 7 | 8 | ;; there are better specs in clojure.tools.deps.specs, but no basis spec yet 9 | ;; just doing a simple check here 10 | (s/def ::basis (s/nilable map?)) -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | This is a [Clojure contrib] project. 2 | 3 | Under the Clojure contrib [guidelines], this project cannot accept 4 | pull requests. All patches must be submitted via [JIRA]. 5 | 6 | See [Contributing] on the Clojure website for 7 | more information on how to contribute. 8 | 9 | [Clojure contrib]: https://clojure.org/community/contrib_libs 10 | [Contributing]: https://clojure.org/community/contributing 11 | [JIRA]: https://clojure.atlassian.net/browse/TBUILD 12 | [guidelines]: https://clojure.org/community/contrib_howto 13 | -------------------------------------------------------------------------------- /test-data/p2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | jar 5 | foo 6 | bar 7 | 0.0.1 8 | name 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_basis.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.tools.build.tasks.test-basis 2 | (:require 3 | [clojure.test :refer :all] 4 | [clojure.tools.build.api :as api] 5 | [clojure.tools.build.test-util :refer :all]) 6 | (:import 7 | [java.nio.file Files] 8 | [java.nio.file.attribute FileAttribute])) 9 | 10 | (deftest test-missing-project-deps-file 11 | (let [path (Files/createTempDirectory "abc" (make-array FileAttribute 0))] 12 | (with-test-dir (.toString path) 13 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 14 | (api/create-basis {})))) 15 | 16 | (comment 17 | (run-tests) 18 | ) 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release on demand 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | releaseVersion: 10 | description: "Version to release" 11 | required: true 12 | snapshotVersion: 13 | description: "Snapshot version after release" 14 | required: true 15 | 16 | jobs: 17 | call-release: 18 | uses: clojure/build.ci/.github/workflows/release.yml@master 19 | with: 20 | releaseVersion: ${{ github.event.inputs.releaseVersion }} 21 | snapshotVersion: ${{ github.event.inputs.snapshotVersion }} 22 | centralDeployServer: "s01" 23 | secrets: inherit 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | Hi! Thanks for your interest in contributing to this project. 2 | 3 | Clojure contrib projects do not use GitHub issues or pull requests, and 4 | require a signed Contributor Agreement. If you would like to contribute, 5 | please read more about the CA and sign that first (this can be done online). 6 | 7 | Then go to this project's issue tracker in JIRA to create tickets, update 8 | tickets, or submit patches. For help in creating tickets and patches, 9 | please see: 10 | 11 | - Signing the CA: https://clojure.org/community/contributing 12 | - Creating Tickets: https://clojure.org/community/creating_tickets 13 | - Developing Patches: https://clojure.org/community/developing_patches 14 | - Contributing FAQ: https://clojure.org/community/contributing 15 | -------------------------------------------------------------------------------- /test-data/p4/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | jar 5 | foo 6 | bar 7 | 0.0.1 8 | name 9 | 10 | 11 | 12 | 13 | 14 | scm:git:git@github.com:clojure/tools.build.git 15 | scm:git:git@github.com:clojure/tools.build.git 16 | git@github.com:clojure/tools.build.git 17 | HEAD 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/util/log.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.util.log 10 | (:require 11 | [clojure.pprint :as pprint])) 12 | 13 | (defmacro log 14 | [verbose & msgs] 15 | `(when ~verbose 16 | (println ~@msgs))) 17 | 18 | (defmacro log-map 19 | [verbose m] 20 | `(when ~verbose 21 | (binding [*print-namespace-maps* false] 22 | (pprint/pprint ~m)))) 23 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/test_project_root.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.tools.build.test-project-root 2 | (:require 3 | [clojure.java.io :as jio] 4 | [clojure.test :refer :all] 5 | [clojure.tools.build.api :as api]) 6 | (:import 7 | [java.nio.file Files] 8 | [java.nio.file.attribute FileAttribute])) 9 | 10 | (deftest test-with-project-root 11 | (let [path (Files/createTempDirectory "p" (make-array FileAttribute 0)) 12 | root-dir (jio/file (.toString path)) 13 | p-dir (jio/file root-dir "p") 14 | deps (jio/file p-dir "deps.edn")] 15 | (jio/make-parents deps) 16 | (spit deps "{:deps {org.clojure/data.json {:mvn/version \"2.3.0\"}}}") 17 | (api/set-project-root! (.toString path)) 18 | (api/with-project-root (.getPath (jio/file (.toString path) "p")) 19 | (is (contains? (-> (api/create-basis) :libs keys set) 'org.clojure/data.json))))) 20 | 21 | (comment 22 | (test-with-project-root) 23 | ) -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: [push] 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | java-version: ["11", "17", "21"] 13 | os: [ubuntu-latest, macOS-latest, windows-latest] 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - name: Prepare java 17 | uses: actions/setup-java@v4 18 | with: 19 | distribution: "adopt" 20 | java-version: ${{ matrix.java-version }} 21 | 22 | - name: Setup Clojure 23 | uses: DeLaGuardo/setup-clojure@13.1 24 | with: 25 | cli: latest 26 | 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | 30 | - name: Run tests not Windows 31 | if: ${{ matrix.os != 'windows-latest' }} 32 | run: clojure -X:test 33 | shell: bash 34 | 35 | - name: Run tests on Windows 36 | if: ${{ matrix.os == 'windows-latest' }} 37 | run: clojure -X:test 38 | shell: powershell 39 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_delete.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-delete 10 | (:require 11 | [clojure.test :refer :all :as test] 12 | [clojure.java.io :as jio] 13 | [clojure.tools.build.api :as api] 14 | [clojure.tools.build.test-util :refer :all])) 15 | 16 | (deftest test-delete 17 | (with-test-dir "test-data/p1" 18 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 19 | ;; copy src into target, then delete, and check target dir is gone 20 | (api/copy-dir {:target-dir "target/classes" 21 | :src-dirs ["src"]}) 22 | (api/delete {:path "target"}) 23 | (is (false? (.exists (jio/file (project-path "target/classes"))))))) 24 | 25 | (comment 26 | (run-tests) 27 | ) 28 | -------------------------------------------------------------------------------- /.github/workflows/build_docs.yml: -------------------------------------------------------------------------------- 1 | name: Build Docs 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | 15 | - name: Set up Java 16 | uses: actions/setup-java@v3 17 | with: 18 | java-version: 8 19 | distribution: 'temurin' 20 | 21 | - name: Set up Clojure 22 | uses: DeLaGuardo/setup-clojure@11.0 23 | with: 24 | cli: 'latest' 25 | 26 | - name: Cache clojure dependencies 27 | uses: actions/cache@v3 28 | with: 29 | path: | 30 | ~/.m2/repository 31 | ~/.gitlibs 32 | key: cljdeps-${{ hashFiles('deps.edn') }} 33 | restore-keys: cljdeps- 34 | 35 | - name: Clone the repo 36 | uses: actions/checkout@v4 37 | 38 | - name: Install rlwrap 39 | run: sudo apt-get install -y rlwrap 40 | 41 | - name: Execute doc build 42 | run: | 43 | clj -X:docs 44 | 45 | - name: Commit and push 46 | run: | 47 | git config --global user.name clojure-build 48 | git config --global user.email "clojure-build@users.noreply.github.com" 49 | git add -u -v 50 | git commit -m "Action doc commit" 51 | git push origin master 52 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/create_basis.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.create-basis 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.tools.build.api :as api] 13 | [clojure.tools.deps :as deps] 14 | [clojure.tools.deps.util.dir :as dir])) 15 | 16 | (defn create-basis 17 | "Wrapper for deps/create-basis, but ensure relative paths are resolved 18 | relative to *project-root*. 19 | 20 | Options (note, paths resolved via *project-root*): 21 | :root - dep source, default = :standard 22 | :user - dep source, default = nil 23 | :project - dep source, default = :standard (\"./deps.edn\") 24 | :extra - dep source, default = nil 25 | :aliases - coll of aliases of argmaps to apply to subprocesses" 26 | ([] 27 | (create-basis nil)) 28 | ([params] 29 | (dir/with-dir (jio/file api/*project-root*) 30 | (deps/create-basis (merge {:user nil} params))))) 31 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_javac.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-javac 10 | (:require 11 | [clojure.string :as str] 12 | [clojure.test :refer :all :as test] 13 | [clojure.java.io :as jio] 14 | [clojure.tools.build.api :as api] 15 | [clojure.tools.build.test-util :refer :all])) 16 | 17 | (deftest test-javac 18 | (with-test-dir "test-data/p1" 19 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 20 | (api/javac {:class-dir "target/classes" 21 | :src-dirs ["java"]}) 22 | (is (true? (.exists (jio/file (project-path "target/classes/foo/Demo1.class"))))) 23 | (is (true? (.exists (jio/file (project-path "target/classes/foo/Demo2.class"))))) 24 | (let [class-path (.getPath (jio/file (project-path "target/classes")))] 25 | (is (= "Hello" (str/trim (:out (api/process {:command-args ["java" "-cp" class-path "foo.Demo1"] :out :capture}))))) 26 | (is (= "Hello" (str/trim (:out (api/process {:command-args ["java" "-cp" class-path "foo.Demo2"] :out :capture})))))))) 27 | 28 | (comment 29 | (run-tests) 30 | ) 31 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/test_util.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.test-util 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.string :as str] 13 | [clojure.test :as test] 14 | [clojure.tools.build.util.file :as file]) 15 | (:import 16 | [java.io File])) 17 | 18 | (def ^:dynamic ^File *test-dir* nil) 19 | 20 | (defmacro with-test-dir 21 | [test-project & body] 22 | `(let [name# (-> test/*testing-vars* last symbol str) 23 | dir# (jio/file "test-out" name#)] 24 | (file/delete dir#) 25 | (.mkdirs dir#) 26 | (file/copy-contents (jio/file ~test-project) dir#) 27 | (binding [*test-dir* dir#] 28 | ~@body))) 29 | 30 | (defn project-path [& parts] 31 | (str/join "/" (cons (.getAbsolutePath *test-dir*) parts))) 32 | 33 | (defn submap? 34 | "Is m1 a subset of m2?" 35 | [m1 m2] 36 | (if (and (map? m1) (map? m2)) 37 | (every? (fn [[k v]] (and (contains? m2 k) 38 | (submap? v (get m2 k)))) 39 | m1) 40 | (= m1 m2))) 41 | 42 | (def windows? (str/starts-with? (System/getProperty "os.name") "Windows")) 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tools.build 2 | ======================================== 3 | 4 | A library for building artifacts in Clojure projects. 5 | 6 | ## Docs 7 | 8 | * [API](https://clojure.github.io/tools.build) 9 | * [Guide](https://clojure.org/guides/tools_build) 10 | 11 | # Release Information 12 | 13 | Latest release: 14 | 15 | [deps.edn](https://clojure.org/reference/deps_and_cli) dependency information: 16 | 17 | As a git dep: 18 | 19 | ```clojure 20 | io.github.clojure/tools.build {:git/tag "v0.10.11" :git/sha "c6c670a"} 21 | ``` 22 | 23 | As a Maven dep: 24 | 25 | ```clojure 26 | io.github.clojure/tools.build {:mvn/version "0.10.11"} 27 | ``` 28 | 29 | # Developer Information 30 | 31 | [![Tests](https://github.com/clojure/tools.build/actions/workflows/ci.yml/badge.svg)](https://github.com/clojure/tools.build/actions/workflows/ci.yml) 32 | 33 | * [GitHub project](https://github.com/clojure/tools.build) 34 | * [How to contribute](https://clojure.org/community/contributing) 35 | * [Bug Tracker](https://clojure.atlassian.net/browse/TBUILD) 36 | 37 | # Copyright and License 38 | 39 | Copyright © Rich Hickey, Alex Miller, and contributors 40 | 41 | All rights reserved. The use and 42 | distribution terms for this software are covered by the 43 | [Eclipse Public License 1.0] which can be found in the file 44 | epl-v10.html at the root of this distribution. By using this software 45 | in any fashion, you are agreeing to be bound by the terms of this 46 | license. You must not remove this notice, or any other, from this 47 | software. 48 | 49 | [Eclipse Public License 1.0]: https://opensource.org/license/epl-1-0 50 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | github.com style (c) Vasily Polovnyov 3 | */ 4 | 5 | .hljs { 6 | display: block; 7 | overflow-x: auto; 8 | padding: 0.5em; 9 | color: #333; 10 | background: #f8f8f8; 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-quote { 15 | color: #998; 16 | font-style: italic; 17 | } 18 | 19 | .hljs-keyword, 20 | .hljs-selector-tag, 21 | .hljs-subst { 22 | color: #333; 23 | font-weight: bold; 24 | } 25 | 26 | .hljs-number, 27 | .hljs-literal, 28 | .hljs-variable, 29 | .hljs-template-variable, 30 | .hljs-tag .hljs-attr { 31 | color: #008080; 32 | } 33 | 34 | .hljs-string, 35 | .hljs-doctag { 36 | color: #d14; 37 | } 38 | 39 | .hljs-title, 40 | .hljs-section, 41 | .hljs-selector-id { 42 | color: #900; 43 | font-weight: bold; 44 | } 45 | 46 | .hljs-subst { 47 | font-weight: normal; 48 | } 49 | 50 | .hljs-type, 51 | .hljs-class .hljs-title { 52 | color: #458; 53 | font-weight: bold; 54 | } 55 | 56 | .hljs-tag, 57 | .hljs-name, 58 | .hljs-attribute { 59 | color: #000080; 60 | font-weight: normal; 61 | } 62 | 63 | .hljs-regexp, 64 | .hljs-link { 65 | color: #009926; 66 | } 67 | 68 | .hljs-symbol, 69 | .hljs-bullet { 70 | color: #990073; 71 | } 72 | 73 | .hljs-built_in, 74 | .hljs-builtin-name { 75 | color: #0086b3; 76 | } 77 | 78 | .hljs-meta { 79 | color: #999; 80 | font-weight: bold; 81 | } 82 | 83 | .hljs-deletion { 84 | background: #fdd; 85 | } 86 | 87 | .hljs-addition { 88 | background: #dfd; 89 | } 90 | 91 | .hljs-emphasis { 92 | font-style: italic; 93 | } 94 | 95 | .hljs-strong { 96 | font-weight: bold; 97 | } 98 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/zip.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.zip 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.tools.build.api :as api] 13 | [clojure.tools.build.util.file :as file] 14 | [clojure.tools.build.util.zip :as zip]) 15 | (:import 16 | [java.util.zip ZipOutputStream])) 17 | 18 | (set! *warn-on-reflection* true) 19 | 20 | (defn zip 21 | [{:keys [src-dirs zip-file] :as params}] 22 | (let [zip-file (api/resolve-path zip-file)] 23 | (file/ensure-dir (.getParent zip-file)) 24 | (with-open [zos (ZipOutputStream. (jio/output-stream zip-file))] 25 | (doseq [zpath src-dirs] 26 | (let [zip-from (file/ensure-dir (api/resolve-path zpath))] 27 | ;(println "Zipping from" (.getPath zip-from) "to" (.getPath zip-file)) 28 | (zip/copy-to-zip zos zip-from)))))) 29 | 30 | (defn unzip 31 | [{:keys [zip-file target-dir] :as params}] 32 | (let [{:keys [zip-file target-dir]} params 33 | ret (zip/unzip (api/resolve-path zip-file) (api/resolve-path target-dir))] 34 | (when-not ret 35 | (throw (ex-info (format "Zip file does not exist: %s" zip-file) {}))))) 36 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src/main/clojure" "src/main/resources"] 2 | 3 | :deps 4 | {org.clojure/clojure {:mvn/version "1.12.0"} 5 | org.clojure/tools.deps {:mvn/version "0.22.1492"} 6 | ;org.clojure/tools.deps {:git/url "https://github.com/clojure/tools.deps.git" 7 | ; :git/sha "459222ca6e4fce91cf5838435589a028cedbc784"} 8 | org.clojure/tools.namespace {:mvn/version "1.5.0"} 9 | org.slf4j/slf4j-nop {:mvn/version "1.7.36"}} 10 | 11 | :aliases 12 | {;; Run all tests 13 | ;; clj -X:test 14 | :test {:extra-paths ["src/test/clojure"] 15 | ;;:extra-deps {io.github.cognitect-labs/test-runner 16 | ;; {:local/root "../test-runner"}} 17 | :extra-deps {io.github.cognitect-labs/test-runner 18 | {:git/url "https://github.com/cognitect-labs/test-runner.git" 19 | :git/tag "v0.5.1" :git/sha "dfb30dd"}} 20 | :exec-fn cognitect.test-runner.api/test 21 | :exec-args {:dirs ["src/test/clojure"] 22 | :patterns [".*"]} 23 | :jvm-opts ["-XX:-OmitStackTraceInFastThrow"]} 24 | 25 | ;; Regenerate docs in docs/ 26 | ;; clj -X:docs 27 | :docs {:extra-deps {codox/codox {:mvn/version "0.10.8"}} 28 | :exec-fn codox.main/generate-docs 29 | :exec-args {:source-paths ["src/main/clojure"] 30 | :namespaces [clojure.tools.build.api] 31 | :output-path "docs"}} 32 | 33 | ;; Lint the source 34 | ;; clj -M:lint 35 | :lint {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2024.11.14"}} 36 | :main-opts ["-m" "clj-kondo.main" "--lint" "src"]} 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/jar.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.jar 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.tools.build.api :as api] 13 | [clojure.tools.build.util.file :as file] 14 | [clojure.tools.build.util.zip :as zip] 15 | [clojure.string :as str]) 16 | (:import 17 | [java.util.jar Manifest JarOutputStream])) 18 | 19 | (defn jar 20 | [{mf-attrs :manifest, :keys [class-dir jar-file main] :as params}] 21 | (let [jar-file (api/resolve-path jar-file) 22 | class-dir-file (file/ensure-dir (api/resolve-path class-dir)) 23 | mf-attr-strs (reduce-kv (fn [m k v] (assoc m (str k) (str v))) nil mf-attrs)] 24 | (file/ensure-dir (.getParent jar-file)) 25 | (let [manifest (Manifest.)] 26 | (zip/fill-manifest! manifest 27 | (merge 28 | (cond-> 29 | {"Manifest-Version" "1.0" 30 | "Created-By" "org.clojure/tools.build" 31 | "Build-Jdk-Spec" (System/getProperty "java.specification.version")} 32 | main (assoc "Main-Class" (str/replace (str main) \- \_))) 33 | mf-attr-strs)) 34 | (with-open [jos (JarOutputStream. (jio/output-stream jar-file) manifest)] 35 | (zip/copy-to-zip jos class-dir-file))))) 36 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/install.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.install 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.tools.deps.util.maven :as mvn] 13 | [clojure.tools.build.api :as api]) 14 | (:import 15 | [org.eclipse.aether.artifact DefaultArtifact] 16 | [org.eclipse.aether.installation InstallRequest])) 17 | 18 | (set! *warn-on-reflection* true) 19 | 20 | (defn install 21 | [{:keys [basis lib classifier version jar-file class-dir] :as params}] 22 | (let [{:mvn/keys [local-repo]} basis 23 | group-id (namespace lib) 24 | artifact-id (name lib) 25 | jar-file-file (api/resolve-path jar-file) 26 | pom-dir (jio/file (api/resolve-path class-dir) "META-INF" "maven" group-id artifact-id) 27 | pom (jio/file pom-dir "pom.xml") 28 | system (mvn/make-system) 29 | session (mvn/make-session system (or local-repo @mvn/cached-local-repo)) 30 | jar-artifact (.setFile (DefaultArtifact. group-id artifact-id classifier "jar" version) jar-file-file) 31 | artifacts (cond-> [jar-artifact] 32 | (and pom-dir (.exists pom)) (conj (.setFile (DefaultArtifact. group-id artifact-id classifier "pom" version) pom))) 33 | install-request (.setArtifacts (InstallRequest.) artifacts)] 34 | (.install system session install-request) 35 | nil)) 36 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/javac.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.javac 10 | (:require 11 | [clojure.string :as str] 12 | [clojure.tools.build.api :as api] 13 | [clojure.tools.build.util.file :as file]) 14 | (:import 15 | [java.io File] 16 | [javax.tools ToolProvider DiagnosticListener])) 17 | 18 | (set! *warn-on-reflection* true) 19 | 20 | (defn javac 21 | [{:keys [basis javac-opts class-dir src-dirs] :as params}] 22 | (let [{:keys [libs]} basis] 23 | (when (seq src-dirs) 24 | (let [class-dir (file/ensure-dir (api/resolve-path class-dir)) 25 | compiler (ToolProvider/getSystemJavaCompiler) 26 | listener (reify DiagnosticListener (report [_ diag] (println (str diag)))) 27 | file-mgr (.getStandardFileManager compiler listener nil nil) 28 | class-dir-path (.getPath class-dir) 29 | classpath (str/join File/pathSeparator (conj (mapcat :paths (vals libs)) class-dir-path)) 30 | options (concat ["-classpath" classpath "-d" class-dir-path] javac-opts) 31 | java-files (mapcat #(file/collect-files (api/resolve-path %) :collect (file/suffixes ".java")) src-dirs) 32 | file-objs (.getJavaFileObjectsFromFiles file-mgr java-files) 33 | task (.getTask compiler nil file-mgr listener options nil file-objs) 34 | success (.call task)] 35 | (when-not success 36 | (throw (ex-info "Java compilation failed" {}))))))) 37 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_write_file.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-write-file 10 | (:require 11 | [clojure.test :refer :all :as test] 12 | [clojure.edn :as edn] 13 | [clojure.java.io :as jio] 14 | [clojure.tools.build.api :as api] 15 | [clojure.tools.build.test-util :refer :all])) 16 | 17 | (deftest test-touch 18 | (with-test-dir "test-data/p1" 19 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 20 | (api/write-file {:path "target/out.txt"}) 21 | (is (.exists (jio/file (project-path "target/out.txt")))))) 22 | 23 | (deftest test-write-data 24 | (with-test-dir "test-data/p1" 25 | (let [data {:abc "abc" :def [1 2 3]}] 26 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 27 | (api/write-file {:path "target/out.txt" 28 | :content data}) 29 | (let [out-file (jio/file (project-path "target/out.txt"))] 30 | (is (.exists out-file)) 31 | (is (= data (edn/read-string (slurp out-file)))))))) 32 | 33 | (deftest test-write-string 34 | (with-test-dir "test-data/p1" 35 | (let [string "abcd/nef/n"] 36 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 37 | (api/write-file {:path "target/out.txt" 38 | :string string}) 39 | (let [out-file (jio/file (project-path "target/out.txt"))] 40 | (is (.exists out-file)) 41 | (is (= string (slurp out-file))))))) 42 | 43 | (comment 44 | (run-tests) 45 | ) 46 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_process.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-process 10 | (:require 11 | [clojure.test :refer :all] 12 | [clojure.set :as set] 13 | [clojure.tools.build.api :as api] 14 | [clojure.tools.build.tasks.process :as process])) 15 | 16 | (deftest test-large-process-output 17 | (is (string? (api/git-process {:git-args ["log"]})))) 18 | 19 | (deftest test-capture 20 | (is (string? (:err (api/process {:command-args ["java" "-version"] 21 | :err :capture}))))) 22 | 23 | (deftest test-env-and-capture 24 | (when-not (#'process/windows?) 25 | (is (= "hi\n" 26 | (:out 27 | (api/process {:env {"FOO" "hi"} 28 | :command-args ["/bin/bash" "-c" "echo $FOO"] 29 | :out :capture})))))) 30 | 31 | (deftest test-need-cp-file 32 | (let [f #'process/need-cp-file] 33 | (are [os java length expected] 34 | (= expected (f os java length)) 35 | "Windows 10" "9.0.1" 10000 true 36 | "Windows 10" "9.0.1" 5000 false 37 | "Windows 10" "1.8.0_261" 10000 false 38 | "Mac OS X" "17" 10000 false))) 39 | 40 | (deftest test-java-process-uses-and-merges-basis-jvm-opts 41 | (let [basis (api/create-basis {:extra {:aliases {:opts {:jvm-opts ["-Dhi=there"]}}} 42 | :aliases [:opts]}) 43 | command (api/java-command {:basis basis, :main 'clojure.main, :java-opts ["-Dfoo=bar"]})] 44 | (is (set/subset? #{"-Dhi=there" "-Dfoo=bar"} (set (:command-args command)))))) 45 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_install.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-install 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.test :as test :refer [deftest is]] 13 | [clojure.tools.build.api :as api] 14 | [clojure.tools.build.test-util :refer [with-test-dir *test-dir* project-path]])) 15 | 16 | (def test-org (str (gensym "ORG"))) 17 | (def test-lib (str (gensym "LIB"))) 18 | (def lib (symbol test-org test-lib)) 19 | (def version "1.0.0") 20 | 21 | (deftest test-install-no-pom 22 | (with-test-dir "test-data/p1" 23 | (let [classes "target/classes" 24 | jar-path "target/output.jar" 25 | local-repo (project-path "tmp-repo") 26 | basis (api/create-basis {:project "deps.edn", :extra {:mvn/local-repo local-repo}})] 27 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 28 | (api/copy-dir {:src-dirs ["src"], :target-dir classes}) 29 | (api/write-pom {:basis basis, :class-dir classes, :lib lib, :version version}) 30 | (api/jar {:class-dir classes, :jar-file jar-path}) 31 | (api/install {:basis basis 32 | :jar-file jar-path 33 | :lib lib 34 | :class-dir classes 35 | :version version}) 36 | (let [expected-dir (jio/file local-repo test-org) 37 | expected-jar (jio/file expected-dir test-lib version (str test-lib "-1.0.0.jar")) 38 | expected-pom (jio/file expected-dir test-lib version (str test-lib "-1.0.0.pom"))] 39 | (is (.exists expected-dir)) 40 | (is (.exists expected-jar)) 41 | (is (.exists expected-pom)))))) 42 | 43 | (comment 44 | (test/run-tests) 45 | ) 46 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_jar.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-jar 10 | (:require 11 | [clojure.string :as str] 12 | [clojure.test :refer :all] 13 | [clojure.java.io :as jio] 14 | [clojure.tools.build.api :as api] 15 | [clojure.tools.build.test-util :refer :all] 16 | [clojure.tools.build.util.zip :as zip]) 17 | (:import [java.util.zip ZipFile ZipEntry])) 18 | 19 | (defn slurp-manifest 20 | [z] 21 | (let [zip-file (jio/file z)] 22 | (with-open [zip (ZipFile. zip-file)] 23 | (let [^ZipEntry ze (.getEntry zip "META-INF/MANIFEST.MF")] 24 | (when ze 25 | (slurp (.getInputStream zip ze))))))) 26 | 27 | (deftest test-jar 28 | (let [jar-path "target/output.jar"] 29 | (with-test-dir "test-data/p1" 30 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 31 | (api/jar {:class-dir "src" 32 | :jar-file jar-path}) 33 | (is (true? (.exists (jio/file (project-path jar-path))))) 34 | (is (= #{"META-INF/MANIFEST.MF" "foo/" "foo/bar.clj"} 35 | (set (map :name (zip/list-zip (project-path jar-path))))))))) 36 | 37 | (deftest test-jar-custom-manifest 38 | (let [jar-path "target/output.jar"] 39 | (with-test-dir "test-data/p1" 40 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 41 | (api/jar {:class-dir "src" 42 | :jar-file jar-path 43 | :manifest {"Abc" 100}}) 44 | (is (true? (.exists (jio/file (project-path jar-path))))) 45 | (is (= #{"META-INF/MANIFEST.MF" "foo/" "foo/bar.clj"} 46 | (set (map :name (zip/list-zip (project-path jar-path)))))) 47 | (let [manifest-out (slurp-manifest (project-path jar-path))] 48 | (is (str/includes? manifest-out "Abc: 100")))))) 49 | 50 | (comment 51 | (run-tests) 52 | ) 53 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_zip.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-zip 10 | (:require 11 | [clojure.test :refer :all :as test] 12 | [clojure.java.io :as jio] 13 | [clojure.tools.build.api :as api] 14 | [clojure.tools.build.test-util :refer :all] 15 | [clojure.tools.build.util.zip :as zip] 16 | [clojure.tools.build.util.file :as file]) 17 | (:import 18 | [java.io File] 19 | [java.nio.file Files LinkOption] 20 | [java.util.concurrent TimeUnit])) 21 | 22 | (deftest test-zip 23 | (let [zip-path "target/output.zip"] 24 | (with-test-dir "test-data/p1" 25 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 26 | (api/zip {:src-dirs ["src"] 27 | :zip-file zip-path}) 28 | (is (true? (.exists (jio/file (project-path zip-path))))) 29 | (is (= #{"foo/" "foo/bar.clj"} 30 | (set (map :name (zip/list-zip (project-path zip-path))))))))) 31 | 32 | (deftest test-unzip 33 | (let [zip-path "target/output.zip"] 34 | (with-test-dir "test-data/p1" 35 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 36 | (api/zip {:src-dirs ["src"] 37 | :zip-file zip-path}) 38 | (api/unzip {:zip-file zip-path :target-dir "target/unzipped"}) 39 | (let [original (file/collect-files (jio/file (project-path "src")) ) 40 | roundtrip (file/collect-files (jio/file (project-path "target/unzipped")))] 41 | (is (= 1 (count original) (count roundtrip))) 42 | (let [f1 ^File (first original) 43 | f2 ^File (first roundtrip)] 44 | (is (= (.getName f1) (.getName f2))) 45 | (is (= (.length f1) (.length f2))) 46 | (is (= (.to (Files/getLastModifiedTime (.toPath f1) (into-array LinkOption [])) TimeUnit/SECONDS) 47 | (.to (Files/getLastModifiedTime (.toPath f2) (into-array LinkOption [])) TimeUnit/SECONDS)))))))) 48 | 49 | (comment 50 | (run-tests) 51 | ) 52 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 3 |

Namespaces

-------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_copy.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-copy 10 | (:require 11 | [clojure.test :refer :all] 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.tools.build.api :as api] 15 | [clojure.tools.build.util.file :as file] 16 | [clojure.tools.build.util.zip :as zip] 17 | [clojure.tools.build.test-util :refer :all]) 18 | (:import 19 | [java.io File FileInputStream ByteArrayOutputStream] 20 | [java.nio.file Files LinkOption FileSystems] 21 | [java.nio.file.attribute PosixFilePermission] 22 | [java.util UUID])) 23 | 24 | (defn slurp-binary 25 | [^File f] 26 | (let [fis (FileInputStream. f) 27 | os (ByteArrayOutputStream.) 28 | buffer (byte-array 4096)] 29 | (zip/copy-stream! fis os buffer) 30 | (.toByteArray os))) 31 | 32 | (deftest test-copy 33 | (with-test-dir "test-data/p1" 34 | (let [txt (str (UUID/randomUUID))] 35 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 36 | (api/copy-dir {:target-dir "target/classes" 37 | :src-dirs ["src" "resources"] 38 | :replace {"__REPLACE__" txt}}) 39 | (let [source-file (jio/file (project-path "target/classes/foo/bar.clj")) 40 | contents (slurp source-file)] 41 | (is (.exists source-file)) 42 | (is (str/includes? contents txt))) 43 | 44 | ;; binary files in replaced exts should be copied but not replaced 45 | (let [binary-in (jio/file (project-path "resources/test.png")) 46 | binary-out (jio/file (project-path "target/classes/test.png"))] 47 | (is (.exists binary-out)) 48 | (is (= (seq (slurp-binary binary-in)) (seq (slurp-binary binary-out)))))))) 49 | 50 | (deftest test-replace-retains-perms 51 | (when (contains? (.supportedFileAttributeViews (FileSystems/getDefault)) "posix") 52 | (with-test-dir "test-data/p1" 53 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 54 | (let [start-file (jio/file (project-path "target/x/f")) 55 | _start (file/ensure-file start-file "abc") 56 | start-path (.toPath start-file)] 57 | (Files/setPosixFilePermissions start-path #{PosixFilePermission/OWNER_READ PosixFilePermission/GROUP_READ PosixFilePermission/OWNER_EXECUTE}) 58 | (api/copy-dir {:src-dirs [(project-path "target/x")] :target-dir (project-path "target/y") :replace {"abc" "xyz"}}) 59 | (let [end (jio/file (project-path "target/y/f")) 60 | end-path (.toPath end) 61 | perms (Files/getPosixFilePermissions end-path (into-array LinkOption [LinkOption/NOFOLLOW_LINKS]))] 62 | (is (= (slurp end) "xyz")) 63 | (is (contains? perms PosixFilePermission/OWNER_EXECUTE))))))) 64 | 65 | (comment 66 | (run-tests) 67 | ) 68 | -------------------------------------------------------------------------------- /docs/js/page_effects.js: -------------------------------------------------------------------------------- 1 | function visibleInParent(element) { 2 | var position = $(element).position().top 3 | return position > -50 && position < ($(element).offsetParent().height() - 50) 4 | } 5 | 6 | function hasFragment(link, fragment) { 7 | return $(link).attr("href").indexOf("#" + fragment) != -1 8 | } 9 | 10 | function findLinkByFragment(elements, fragment) { 11 | return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first() 12 | } 13 | 14 | function scrollToCurrentVarLink(elements) { 15 | var elements = $(elements); 16 | var parent = elements.offsetParent(); 17 | 18 | if (elements.length == 0) return; 19 | 20 | var top = elements.first().position().top; 21 | var bottom = elements.last().position().top + elements.last().height(); 22 | 23 | if (top >= 0 && bottom <= parent.height()) return; 24 | 25 | if (top < 0) { 26 | parent.scrollTop(parent.scrollTop() + top); 27 | } 28 | else if (bottom > parent.height()) { 29 | parent.scrollTop(parent.scrollTop() + bottom - parent.height()); 30 | } 31 | } 32 | 33 | function setCurrentVarLink() { 34 | $('.secondary a').parent().removeClass('current') 35 | $('.anchor'). 36 | filter(function(index) { return visibleInParent(this) }). 37 | each(function(index, element) { 38 | findLinkByFragment(".secondary a", element.id). 39 | parent(). 40 | addClass('current') 41 | }); 42 | scrollToCurrentVarLink('.secondary .current'); 43 | } 44 | 45 | var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }()) 46 | 47 | function scrollPositionId(element) { 48 | var directory = window.location.href.replace(/[^\/]+\.html$/, '') 49 | return 'scroll::' + $(element).attr('id') + '::' + directory 50 | } 51 | 52 | function storeScrollPosition(element) { 53 | if (!hasStorage) return; 54 | localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft()) 55 | localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop()) 56 | } 57 | 58 | function recallScrollPosition(element) { 59 | if (!hasStorage) return; 60 | $(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x")) 61 | $(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y")) 62 | } 63 | 64 | function persistScrollPosition(element) { 65 | recallScrollPosition(element) 66 | $(element).scroll(function() { storeScrollPosition(element) }) 67 | } 68 | 69 | function sidebarContentWidth(element) { 70 | var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() }) 71 | return Math.max.apply(Math, widths) 72 | } 73 | 74 | function calculateSize(width, snap, margin, minimum) { 75 | if (width == 0) { 76 | return 0 77 | } 78 | else { 79 | return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2)) 80 | } 81 | } 82 | 83 | function resizeSidebars() { 84 | var primaryWidth = sidebarContentWidth('.primary') 85 | var secondaryWidth = 0 86 | 87 | if ($('.secondary').length != 0) { 88 | secondaryWidth = sidebarContentWidth('.secondary') 89 | } 90 | 91 | // snap to grid 92 | primaryWidth = calculateSize(primaryWidth, 32, 13, 160) 93 | secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160) 94 | 95 | $('.primary').css('width', primaryWidth) 96 | $('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1) 97 | 98 | if (secondaryWidth > 0) { 99 | $('#content').css('left', primaryWidth + secondaryWidth + 2) 100 | } 101 | else { 102 | $('#content').css('left', primaryWidth + 1) 103 | } 104 | } 105 | 106 | $(window).ready(resizeSidebars) 107 | $(window).ready(setCurrentVarLink) 108 | $(window).ready(function() { persistScrollPosition('.primary')}) 109 | $(window).ready(function() { 110 | $('#content').scroll(setCurrentVarLink) 111 | $(window).resize(setCurrentVarLink) 112 | }) 113 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/copy.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.copy 10 | (:require 11 | [clojure.string :as str] 12 | [clojure.tools.build.api :as api] 13 | [clojure.tools.build.util.file :as file]) 14 | (:import 15 | [java.io File] 16 | [java.nio.file FileSystems FileVisitor FileVisitResult Files Path LinkOption])) 17 | 18 | (set! *warn-on-reflection* true) 19 | 20 | ;; copy spec: 21 | ;; :from (coll of dirs), default = ["."] 22 | ;; ;include (glob), default = "**" 23 | ;; :replace (map of replacements) - performed while copying 24 | 25 | (defn- match-paths 26 | "Match glob to paths under root and return a collection of Path objects" 27 | [^File root glob] 28 | (let [root-path (.toPath root) 29 | matcher (.getPathMatcher (FileSystems/getDefault) (str "glob:" glob)) 30 | paths (volatile! []) 31 | visitor (reify FileVisitor 32 | (visitFile [_ path _attrs] 33 | (when (.matches matcher (.relativize root-path ^Path path)) 34 | (vswap! paths conj path)) 35 | FileVisitResult/CONTINUE) 36 | (visitFileFailed [_ _path _ex] FileVisitResult/CONTINUE) 37 | (preVisitDirectory [_ _ _] FileVisitResult/CONTINUE) 38 | (postVisitDirectory [_ _ _] FileVisitResult/CONTINUE))] 39 | (Files/walkFileTree root-path visitor) 40 | @paths)) 41 | 42 | (def default-ignores 43 | [".*~$" 44 | "^#.*#$" 45 | "^\\.#.*" 46 | "^.DS_Store$"]) 47 | 48 | (defn ignore? [name ignore-regexes] 49 | (boolean (some #(re-matches % name) ignore-regexes))) 50 | 51 | (def default-non-replaced-exts 52 | ["jpg" "jpeg" "png" "gif" "bmp"]) 53 | 54 | (defn- ends-with-ext? 55 | [exts path] 56 | (loop [[ext & es] exts] 57 | (if ext 58 | (if (str/ends-with? path ext) 59 | true 60 | (recur es)) 61 | false))) 62 | 63 | (defn copy 64 | [{:keys [target-dir src-dirs include replace ignores non-replaced-exts] 65 | :or {include "**", ignores default-ignores, non-replaced-exts default-non-replaced-exts} 66 | :as _params}] 67 | (let [to-path (.toPath (file/ensure-dir (api/resolve-path target-dir))) 68 | ignore-regexes (map re-pattern ignores) 69 | non-replaced (map #(str "." %) default-non-replaced-exts)] 70 | (doseq [dir src-dirs] 71 | ;(println "from" dir) 72 | (let [from-file (api/resolve-path dir) 73 | paths (match-paths from-file include)] 74 | (doseq [^Path path paths] 75 | (let [path-file (.toFile path) 76 | path-file-name (.getName path-file) 77 | target-file (.toFile (.resolve to-path (.relativize (.toPath from-file) path)))] 78 | (when-not (ignore? path-file-name ignore-regexes) 79 | ;(println "copying" (.toString path-file) (.toString target-file) (boolean (not (empty? replace)))) 80 | (if (or (empty? replace) (ends-with-ext? non-replaced (.getName path-file))) 81 | (file/copy-file path-file target-file) 82 | (let [contents (slurp path-file) 83 | replaced (reduce (fn [s [find replace]] (str/replace s find replace)) 84 | contents replace) 85 | perms (when (contains? (.supportedFileAttributeViews (FileSystems/getDefault)) "posix") 86 | (Files/getPosixFilePermissions (.toPath path-file) 87 | (into-array LinkOption [LinkOption/NOFOLLOW_LINKS])))] 88 | (file/ensure-file target-file replaced :append false) 89 | (when perms 90 | (Files/setPosixFilePermissions (.toPath target-file) perms) 91 | nil)))))))))) 92 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/util/zip.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.util.zip 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.tools.build.util.file :as file] 13 | [clojure.string :as str]) 14 | (:import 15 | [java.io File InputStream OutputStream] 16 | [java.nio.file Files LinkOption] 17 | [java.nio.file.attribute BasicFileAttributes] 18 | [java.util.zip ZipFile ZipInputStream ZipOutputStream ZipEntry] 19 | [java.util.jar Manifest Attributes$Name])) 20 | 21 | (set! *warn-on-reflection* true) 22 | 23 | (defn- add-zip-entry 24 | [^ZipOutputStream output-stream ^String path ^File file] 25 | (let [dir (.isDirectory file) 26 | attrs (Files/readAttributes (.toPath file) BasicFileAttributes ^"[Ljava.nio.file.LinkOption;" (into-array LinkOption [])) 27 | path (if (and dir (not (.endsWith path "/"))) (str path "/") path) 28 | path (str/replace path \\ \/) ;; only use unix-style paths in jars 29 | entry (doto (ZipEntry. path) 30 | ;(.setSize (.size attrs)) 31 | ;(.setLastAccessTime (.lastAccessTime attrs)) 32 | (.setLastModifiedTime (.lastModifiedTime attrs)))] 33 | (.putNextEntry output-stream entry) 34 | (when-not dir 35 | (with-open [fis (jio/input-stream file)] 36 | (jio/copy fis output-stream))) 37 | 38 | (.closeEntry output-stream))) 39 | 40 | (defn copy-to-zip 41 | [^ZipOutputStream jos ^File root] 42 | (let [root-path (.toPath root) 43 | files (file/collect-files root :dirs true)] 44 | (run! (fn [^File f] 45 | (let [rel-path (.toString (.relativize root-path (.toPath f)))] 46 | (when-not (= rel-path "") 47 | ;(println " Adding" rel-path) 48 | (add-zip-entry jos rel-path f)))) 49 | files))) 50 | 51 | (defn fill-manifest! 52 | [^Manifest manifest props] 53 | (let [attrs (.getMainAttributes manifest)] 54 | (run! 55 | (fn [[name value]] 56 | (.put attrs (Attributes$Name. ^String name) value)) props))) 57 | 58 | (defn list-zip 59 | [^String zip-path] 60 | (let [zip-file (jio/file zip-path)] 61 | (when (.exists zip-file) 62 | (with-open [zip (ZipFile. zip-file)] 63 | (let [entries (enumeration-seq (.entries zip))] 64 | (sort-by :name 65 | (into [] (map (fn [^ZipEntry entry] 66 | {:name (.getName entry) 67 | :created (.getCreationTime entry) 68 | :modified (.getLastModifiedTime entry) 69 | :size (.getSize entry)})) 70 | entries))))))) 71 | 72 | (defn copy-stream! 73 | "Copy input stream to output stream using buffer. 74 | Caller is responsible for passing buffered streams and closing streams." 75 | [^InputStream is ^OutputStream os ^bytes buffer] 76 | (loop [] 77 | (let [size (.read is buffer)] 78 | (if (pos? size) 79 | (do 80 | (.write os buffer 0 size) 81 | (recur)) 82 | (.close os))))) 83 | 84 | (defn unzip 85 | [^String zip-path ^String target-dir] 86 | (let [buffer (byte-array 4096) 87 | zip-file (jio/file zip-path)] 88 | (if (.exists zip-file) 89 | (with-open [zis (ZipInputStream. (jio/input-stream zip-file))] 90 | (loop [] 91 | (if-let [entry (.getNextEntry zis)] 92 | ;(println "entry:" (.getName entry) (.isDirectory entry)) 93 | (let [out-file (jio/file target-dir (.getName entry))] 94 | (jio/make-parents out-file) 95 | (when-not (.isDirectory entry) 96 | (with-open [output (jio/output-stream out-file)] 97 | (copy-stream! zis output buffer) 98 | (Files/setLastModifiedTime (.toPath out-file) (.getLastModifiedTime entry)))) 99 | (recur)) 100 | true))) 101 | false))) 102 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | io.github.clojure 4 | tools.build 5 | 0.10.12-SNAPSHOT 6 | tools.build 7 | Clojure builds as Clojure programs. 8 | 9 | 10 | org.clojure 11 | pom.contrib 12 | 1.3.0 13 | 14 | 15 | 16 | 17 | puredanger 18 | Alex Miller 19 | 20 | 21 | 22 | 23 | 24 | true 25 | 1.12.0 26 | 27 | 28 | 29 | 30 | org.clojure 31 | clojure 32 | ${clojure.version} 33 | 34 | 35 | org.clojure 36 | tools.deps 37 | 0.22.1492 38 | 39 | 40 | org.clojure 41 | tools.namespace 42 | 1.5.0 43 | 44 | 45 | org.slf4j 46 | slf4j-nop 47 | 1.7.36 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | sonatype-nexus-staging 56 | https://s01.oss.sonatype.org/content/repositories/snapshots 57 | 58 | 59 | 60 | 61 | 62 | 63 | src/main/resources 64 | true 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-resources-plugin 71 | 3.1.0 72 | 73 | 74 | 77 | com.theoryinpractise 78 | clojure-maven-plugin 79 | 1.7.1 80 | true 81 | 82 | ${clojure.warnOnReflection} 83 | true 84 | 85 | 86 | 87 | clojure-compile 88 | none 89 | 90 | 91 | clojure-test 92 | test 93 | 94 | test 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.sonatype.plugins 103 | nexus-staging-maven-plugin 104 | 1.6.8 105 | true 106 | 107 | 108 | sonatype-nexus-staging 109 | https://s01.oss.sonatype.org/ 110 | true 111 | 112 | 113 | 114 | 115 | 116 | 117 | scm:git:git@github.com:clojure/tools.build.git 118 | scm:git:git@github.com:clojure/tools.build.git 119 | git@github.com:clojure/tools.build.git 120 | HEAD 121 | 122 | 123 | 124 | 125 | clojars 126 | https://clojars.org/repo/ 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/util/file.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.util.file 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.string :as str]) 13 | (:import 14 | [java.io File] 15 | [java.nio.file Path Files LinkOption CopyOption StandardCopyOption] 16 | [clojure.lang PersistentQueue])) 17 | 18 | (set! *warn-on-reflection* true) 19 | 20 | (defn collect-files 21 | "Recursively collect all paths under path, starting from root. 22 | Options: 23 | :dirs - whether to collect directories (default false) 24 | :collect - function for whether to collect a path (default yes)" 25 | [^File root & {:keys [dirs collect] 26 | :or {dirs false 27 | collect (constantly true)}}] 28 | (when (.exists root) 29 | (loop [queue (conj PersistentQueue/EMPTY root) 30 | collected []] 31 | (let [^File file (peek queue)] 32 | (if file 33 | (let [path (.toPath file) 34 | is-dir (Files/isDirectory path (into-array LinkOption [LinkOption/NOFOLLOW_LINKS])) 35 | children (when is-dir (with-open [entries (Files/newDirectoryStream path)] 36 | (mapv #(.toFile ^Path %) entries))) 37 | collect? (and (if is-dir dirs true) (collect file))] 38 | (recur (into (pop queue) children) (if collect? (conj collected file) collected))) 39 | (when (seq collected) collected)))))) 40 | 41 | (defn suffixes 42 | "Returns a predicate matching suffixes" 43 | [& suffixes] 44 | (apply some-fn 45 | (map #(fn [^File f] (str/ends-with? (.toString f) ^String %)) suffixes))) 46 | 47 | (defn delete 48 | "Recursively delete file, where file is coerced with clojure.java.io/file" 49 | [file] 50 | (run! #(.delete ^File %) (reverse (collect-files (jio/file file) :dirs true)))) 51 | 52 | (def ^{:private true, :tag "[Ljava.nio.file.CopyOption;"} 53 | copy-options 54 | (into-array CopyOption [StandardCopyOption/COPY_ATTRIBUTES StandardCopyOption/REPLACE_EXISTING])) 55 | 56 | (defn copy-file 57 | "Copy file from src to target, retaining file attributes. Returns nil." 58 | [^File src-file ^File target-file] 59 | (.mkdirs target-file) 60 | (Files/copy (.toPath src-file) (.toPath target-file) copy-options) 61 | nil) 62 | 63 | (defn copy-contents 64 | "Copy files in src dir to target dir, optionally filtering by prefix paths" 65 | ([^File src-dir ^File target-dir] 66 | (let [source-path (.toPath src-dir) 67 | target-path (.toPath target-dir) 68 | source-files (collect-files src-dir)] 69 | ;(println "source" (str source-path)) 70 | ;(println "target" (str target-path)) 71 | ;(println "source-files" (map str source-files)) 72 | (run! 73 | (fn [^File f] 74 | (let [p (.toPath f) 75 | new-path (.resolve target-path (.relativize source-path p))] 76 | ;(println "copying" (str p) (str new-path)) 77 | (copy-file f (.toFile new-path)))) 78 | source-files))) 79 | ([^File src-dir ^File target-dir prefixes] 80 | (when (.exists src-dir) 81 | (let [root (.toPath src-dir) 82 | target (.toPath target-dir)] 83 | (loop [queue (conj PersistentQueue/EMPTY src-dir)] 84 | (let [^File file (peek queue)] 85 | (when file 86 | (let [path (.toPath file) 87 | relative (.relativize root path)] 88 | ;(println "consider" (.toString file) "match" (some #(str/starts-with? (.toString relative) %) prefixes) "dir" (Files/isDirectory path (into-array LinkOption [LinkOption/NOFOLLOW_LINKS]))) 89 | (cond 90 | ;; match, copy this file/dir 91 | (some #(str/starts-with? (.toString relative) %) prefixes) 92 | (let [end-path (.resolve target relative)] 93 | (copy-contents file (.toFile end-path)) 94 | (recur (pop queue))) 95 | 96 | ;; no match, but continue looking in this directory if it could match later 97 | (and 98 | (Files/isDirectory path (into-array LinkOption [LinkOption/NOFOLLOW_LINKS])) 99 | (some #(str/starts-with? % (.toString relative)) prefixes)) 100 | (recur (into (pop queue) 101 | (with-open [entries (Files/newDirectoryStream path)] 102 | (mapv #(.toFile ^Path %) entries)))) 103 | 104 | ;; work the queue 105 | :else 106 | (recur (pop queue))))))))))) 107 | 108 | (defn ensure-dir 109 | "Ensure dir exists by making all parent directories and return it" 110 | ^File [dir] 111 | (let [d (jio/file dir)] 112 | (if (.exists d) 113 | d 114 | (if (.mkdirs d) 115 | d 116 | (throw (ex-info (str "Can't create directory " dir) {})))))) 117 | 118 | (defn ensure-file 119 | ([file] (ensure-file file "")) 120 | ([file contents & opts] 121 | (let [file (jio/file file) 122 | parent (.getParent file)] 123 | (if (.exists (jio/file parent)) 124 | (apply spit file contents opts) 125 | (do 126 | (ensure-dir parent) 127 | (apply spit file contents opts)))))) 128 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_compile_clj.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-compile-clj 10 | (:require 11 | [clojure.test :refer :all :as test] 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.tools.build.api :as api] 15 | [clojure.tools.build.tasks.compile-clj :as compile-clj] 16 | [clojure.tools.build.test-util :refer :all])) 17 | 18 | (deftest test-topo 19 | ;; deps: a -> b -> c, d 20 | ;; expect: c b a (reverse topo sort), then d at the end 21 | (is (= '[c b a d] 22 | (#'compile-clj/nses-in-topo [(jio/file "test-data/nses/src")])))) 23 | 24 | (deftest test-compile 25 | (with-test-dir "test-data/p1" 26 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 27 | (api/compile-clj {:class-dir "target/classes" 28 | :src-dirs ["src"] 29 | :basis (api/create-basis nil)}) 30 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar.class"))))) 31 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar__init.class"))))) 32 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar$hello.class"))))))) 33 | 34 | ;; use :src-dirs from basis paths 35 | (deftest test-compile-basis-paths 36 | (with-test-dir "test-data/p1" 37 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 38 | (api/compile-clj {:class-dir "target/classes" 39 | :basis (api/create-basis nil)}) 40 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar.class"))))) 41 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar__init.class"))))) 42 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar$hello.class"))))))) 43 | 44 | (defn find-java [] 45 | (-> (api/process {:command-args [(if windows? "where" "which") "java"] 46 | :out :capture}) 47 | :out 48 | str/split-lines 49 | first)) 50 | 51 | (deftest test-compile-passthrough-opts 52 | (when-not (str/starts-with? (System/getProperty "java.version") "1.") 53 | (let [java-cmd (find-java)] 54 | (with-test-dir "test-data/p1" 55 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 56 | (api/compile-clj {:class-dir "target/classes" 57 | :src-dirs ["src"] 58 | :basis (api/create-basis nil) 59 | ;; pass these through to java command 60 | :java-opts ["-Dhi=there"] 61 | :use-cp-file :always 62 | :java-cmd java-cmd}) 63 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar.class"))))) 64 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar__init.class"))))) 65 | (is (true? (.exists (jio/file (project-path "target/classes/foo/bar$hello.class"))))))))) 66 | 67 | (deftest test-turn-off-assert-with-bindings 68 | (with-test-dir "test-data/assert" 69 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 70 | (let [basis (api/create-basis nil) 71 | invoke #(-> {:basis basis :main 'clojure.main :main-args ["-e" "((requiring-resolve 'foo.check-assert/f) 100)"]} 72 | api/java-command 73 | (merge {:out :capture, :err :ignore}) 74 | api/process) 75 | compile-params {:class-dir "target/classes" :src-dirs ["src"] :basis basis}] 76 | 77 | ;; by default, assertions are on when compiling, then invocation fails (assertion expects keyword) 78 | (api/compile-clj compile-params) ;; no :bindings set 79 | (is (= {:exit 1} (invoke))) 80 | 81 | ;; recompile with binding to turn off assertions, then it passes (assertion not checked) 82 | (api/delete {:path "target/classes"}) 83 | (api/compile-clj (assoc compile-params :bindings {#'clojure.core/*assert* false})) ;; turn off asserts 84 | (is (= {:exit 0, :out (str "100" (System/lineSeparator))} (invoke)))))) 85 | 86 | (deftest test-capture-reflection 87 | (with-test-dir "test-data/reflecting" 88 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 89 | (let [basis (api/create-basis nil) 90 | compile-params {:class-dir "target/classes" 91 | :src-dirs ["src"] 92 | :basis basis 93 | :ns-compile ['foo.bar]}] 94 | 95 | ;; by default, reflection does not warn 96 | (is (nil? (api/compile-clj compile-params))) ;; no :bindings set 97 | 98 | ;; compile with reflection warnings and capture the error output 99 | (api/delete {:path "target/classes"}) 100 | (is (str/starts-with? 101 | (:err 102 | (api/compile-clj (merge compile-params 103 | {:bindings {#'clojure.core/*warn-on-reflection* true} 104 | :err :capture}))) 105 | "Reflection warning"))))) 106 | 107 | (deftest test-accidental-basis-delay 108 | (with-test-dir "test-data/p1" 109 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 110 | (is (thrown? clojure.lang.ExceptionInfo 111 | (api/compile-clj {:class-dir "target/classes" 112 | :src-dirs ["src"] 113 | :basis (delay (api/create-basis nil))}))))) 114 | 115 | (comment 116 | (run-tests) 117 | ) 118 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/compile_clj.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.compile-clj 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.pprint :as pprint] 13 | [clojure.string :as str] 14 | [clojure.tools.build.api :as api] 15 | [clojure.tools.build.util.file :as file] 16 | [clojure.tools.build.tasks.process :as process] 17 | [clojure.tools.namespace.find :as find] 18 | [clojure.tools.namespace.dependency :as dependency] 19 | [clojure.tools.namespace.parse :as parse]) 20 | (:import 21 | [java.io File] 22 | [java.nio.file Files] 23 | [java.nio.file.attribute FileAttribute])) 24 | 25 | (set! *warn-on-reflection* true) 26 | 27 | (defn- write-compile-script! 28 | ^File [^File script-file ^File compile-dir nses compiler-opts bindings] 29 | (let [compile-bindings (merge bindings 30 | {#'*compile-path* (.toString compile-dir) 31 | #'*compiler-options* compiler-opts}) 32 | binding-nses (->> compile-bindings 33 | keys 34 | (map #(-> % symbol namespace symbol)) ;; Var->namespace 35 | distinct 36 | (remove #(= % 'clojure.core))) 37 | requires (map (fn [n] `(require '~n)) binding-nses) 38 | do-compile `(with-bindings ~compile-bindings 39 | ~@(map (fn [n] `(~'compile '~n)) nses) 40 | (System/exit 0)) 41 | script (->> (conj (vec requires) do-compile) 42 | (map #(with-out-str (pprint/pprint %))) 43 | (str/join (System/lineSeparator)))] 44 | (spit script-file script))) 45 | 46 | (defn- ns->path 47 | [ns-sym] 48 | (str/replace (clojure.lang.Compiler/munge (str ns-sym)) \. \/)) 49 | 50 | (defn- nses-in-bfs 51 | [dirs] 52 | (mapcat #(find/find-namespaces-in-dir % find/clj) dirs)) 53 | 54 | (defn- nses-in-topo 55 | [dirs] 56 | (let [ns-decls (mapcat find/find-ns-decls-in-dir dirs) 57 | ns-candidates (set (map parse/name-from-ns-decl ns-decls)) 58 | graph (reduce 59 | (fn [graph decl] 60 | (let [sym (parse/name-from-ns-decl decl)] 61 | (reduce 62 | (fn [graph dep] (dependency/depend graph sym dep)) 63 | graph 64 | (parse/deps-from-ns-decl decl)))) 65 | (dependency/graph) 66 | ns-decls)] 67 | (->> graph 68 | dependency/topo-sort 69 | (filter ns-candidates) ;; only keep stuff in these dirs 70 | (#(concat % ns-candidates)) ;; but make sure everything is in there at least once 71 | distinct))) 72 | 73 | (defn- basis-paths 74 | "Extract all path entries from basis, in classpath order" 75 | [{:keys [classpath classpath-roots]}] 76 | (let [path-set (->> classpath 77 | (filter #(contains? (val %) :path-key)) 78 | (map key) 79 | set)] 80 | (filter path-set classpath-roots))) 81 | 82 | (defn compile-clj 83 | [{:keys [basis src-dirs compile-opts ns-compile filter-nses class-dir sort bindings] :as params 84 | :or {sort :topo}}] 85 | (let [working-dir (.toFile (Files/createTempDirectory "compile-clj" (into-array FileAttribute []))) 86 | compile-dir-file (file/ensure-dir (api/resolve-path class-dir)) 87 | clj-paths (map api/resolve-path (or src-dirs (basis-paths basis))) 88 | nses (cond 89 | (seq ns-compile) ns-compile 90 | (= sort :topo) (nses-in-topo clj-paths) 91 | (= sort :bfs) (nses-in-bfs clj-paths) 92 | :else (throw (ex-info "Missing :ns-compile or :sort order in compile-clj task" {}))) 93 | working-compile-dir (file/ensure-dir (jio/file working-dir "compile-clj")) 94 | compile-script (jio/file working-dir "compile.clj") 95 | _ (write-compile-script! compile-script working-compile-dir nses compile-opts bindings) 96 | 97 | ;; java-command will run in context of *project-dir* - basis, classpaths, etc 98 | ;; should all be relative to that (or absolute like working-compile-dir) 99 | process-args (merge 100 | (process/java-command 101 | (merge 102 | (select-keys params [:java-cmd :java-opts :use-cp-file]) 103 | {:cp [(.getPath working-compile-dir) class-dir] 104 | :basis basis 105 | :main 'clojure.main 106 | :main-args [(.getCanonicalPath compile-script)]})) 107 | (select-keys params [:out :err :out-file :err-file])) 108 | _ (spit (jio/file working-dir "compile.args") (str/join " " (:command-args process-args))) 109 | {exit :exit, ps-out :out, ps-err :err} (process/process process-args) 110 | ret (cond-> nil 111 | ps-out (assoc :out ps-out) 112 | ps-err (assoc :err ps-err))] 113 | (if (zero? exit) 114 | (do 115 | (if (seq filter-nses) 116 | (file/copy-contents working-compile-dir compile-dir-file (map ns->path filter-nses)) 117 | (file/copy-contents working-compile-dir compile-dir-file)) 118 | ;; only delete on success, otherwise leave the evidence! 119 | (file/delete working-dir) 120 | ret) 121 | (throw (ex-info (str "Clojure compilation failed, working dir preserved: " (.toString working-dir)) ret))))) 122 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/process.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.process 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.java.process :as proc] 13 | [clojure.tools.deps :as deps] 14 | [clojure.tools.build.api :as api] 15 | [clojure.string :as str]) 16 | (:import 17 | [java.io InputStream StringWriter File])) 18 | 19 | (set! *warn-on-reflection* true) 20 | 21 | (defn- trim-blank [^String s] 22 | (if (str/blank? s) nil s)) 23 | 24 | (defn process 25 | "Exec the command made from command-args, redirect out and err as directed, 26 | and return {:exit exit-code, :out captured-out, :err captured-err} 27 | 28 | Options: 29 | :command-args - required, coll of string args 30 | :dir - directory to run the command from, default current directory 31 | :out - one of :inherit :capture :write :append :ignore 32 | :err - one of :inherit :capture :write :append :ignore 33 | :out-file - file path to write if :out is :write or :append 34 | :err-file - file path to write if :err is :write or :append 35 | :env - map of environment variables to set 36 | 37 | The :out and :err input flags take one of the following options: 38 | :inherit - inherit the stream and write the subprocess io to this process's stream (default) 39 | :capture - capture the stream to a string and return it 40 | :write - write to :out-file or :err-file 41 | :append - append to :out-file or :err-file 42 | :ignore - ignore the stream" 43 | [{:keys [command-args dir env out err out-file err-file] 44 | :or {dir ".", out :inherit, err :inherit} :as opts}] 45 | (when (not (seq command-args)) 46 | (throw (ex-info "process missing required arg :command-args" opts))) 47 | (let [stream-opt (fn [opt file] 48 | (case opt 49 | :inherit :inherit 50 | :write (proc/to-file (api/resolve-path file)) 51 | :append (proc/to-file (api/resolve-path file) :append true) 52 | :ignore :discard 53 | (:capture :pipe) :pipe)) 54 | proc-opts {:dir (api/resolve-path (or dir ".")) 55 | :out (stream-opt out out-file) 56 | :err (stream-opt err err-file) 57 | :env env} 58 | proc (apply proc/start proc-opts command-args) 59 | out-f (when (= out :capture) (proc/io-task #(slurp (proc/stdout proc)))) 60 | err-f (when (= err :capture) (proc/io-task #(slurp (proc/stderr proc)))) 61 | exit (deref (proc/exit-ref proc)) 62 | out-str (when out-f (trim-blank @out-f)) 63 | err-str (when err-f (trim-blank @err-f))] 64 | (cond-> {:exit exit} 65 | out-str (assoc :out out-str) 66 | err-str (assoc :err err-str)))) 67 | 68 | (comment 69 | (api/process {:command-args ["ls" "-l"]}) 70 | (api/process {:command-args ["git" "log"] :out :ignore}) 71 | (api/process {:command-args ["java" "-version"] :err :capture}) 72 | (api/process {:command-args ["java" "--version"] :out :capture}) 73 | (api/process {:env {"FOO" "hi"} 74 | :command-args ["echo" "$FOO"] 75 | :out :capture}) 76 | ) 77 | 78 | (defn- need-cp-file 79 | [os-name java-version command-length] 80 | (and 81 | ;; this is only an issue on Windows 82 | (str/starts-with? os-name "Win") 83 | ;; CLI support only exists in Java 9+, for Java <= 1.8, the version number is 1.x 84 | (not (str/starts-with? java-version "1.")) 85 | ;; the actual limit on windows is 8191 (<8k), but giving some room 86 | (> command-length 8000))) 87 | 88 | (defn- make-java-args 89 | [java-cmd java-opts cp main main-args use-cp-file] 90 | (let [full-args (vec (concat [java-cmd] java-opts ["-cp" cp (name main)] main-args)) 91 | arg-str (str/join " " full-args)] 92 | (if (or (= use-cp-file :always) 93 | (and (= use-cp-file :auto) 94 | (need-cp-file (System/getProperty "os.name") (System/getProperty "java.version") (count arg-str)))) 95 | (let [cp-file (doto (File/createTempFile "tbuild-" ".cp") (.deleteOnExit))] 96 | (spit cp-file cp) 97 | (vec (concat [java-cmd] java-opts ["-cp" (str "@" (.getAbsolutePath cp-file)) (name main)] main-args))) 98 | full-args))) 99 | 100 | (defn which 101 | "Given the name of an executable, return either a full path to 102 | its location on the system PATH or nil if not found" 103 | [cmd] 104 | (when-let [path (System/getenv "PATH")] 105 | (let [paths (str/split path (re-pattern File/pathSeparator))] 106 | (loop [paths paths] 107 | (when-first [p paths] 108 | (let [f (jio/file p cmd)] 109 | (if (and (.isFile f) (.canExecute f)) 110 | (.getCanonicalPath f) 111 | (recur (rest paths))))))))) 112 | 113 | (defn- windows? 114 | [] 115 | (str/starts-with? (System/getProperty "os.name") "Windows")) 116 | 117 | (defn- java-exe 118 | [] 119 | (if (windows?) "java.exe" "java")) 120 | 121 | (defn- java-home-bin 122 | "Returns path $JAVA_HOME/bin/java if JAVA_HOME set, or nil" 123 | [] 124 | (when-let [jhome (System/getenv "JAVA_HOME")] 125 | (let [exe (jio/file jhome "bin" (java-exe))] 126 | (when (and (.exists exe) (.canExecute exe)) 127 | (.getCanonicalPath exe))))) 128 | 129 | (defn java-executable 130 | "Given the environment, emulate the Clojure CLI logic to determine the 131 | Java executable path and return it by trying in order: 132 | $JAVA_CMD 133 | java on the PATH 134 | $JAVA_HOME/bin/java" 135 | [] 136 | (or 137 | (System/getenv "JAVA_CMD") 138 | (which (java-exe)) 139 | (java-home-bin) 140 | (throw (ex-info "Couldn't find java executable via $JAVA_CMD, $PATH, or $JAVA_HOME" {})))) 141 | 142 | (defn java-command 143 | "Create Java command line args. The classpath will be the combination of 144 | :cp followed by the classpath from the basis, both are optional. 145 | 146 | Options: 147 | :java-cmd - Java command, default = $JAVA_CMD or 'java' on $PATH, or $JAVA_HOME/bin/java 148 | :cp - coll of string classpath entries, used first (if provided) 149 | :basis - runtime basis used for classpath, used last (if provided) 150 | :java-opts - coll of string jvm opts 151 | :main - required, main class symbol 152 | :main-args - coll of main class args 153 | :use-cp-file - one of: 154 | :auto (default) - use only if os=windows && Java >= 9 && command length >= 8k 155 | :always - always write classpath to temp file and include 156 | :never - never write classpath to temp file (pass on command line) 157 | 158 | Returns: 159 | :command-args - coll of command arg strings" 160 | [{:keys [java-cmd cp basis java-opts main main-args use-cp-file] 161 | :or {use-cp-file :auto} :as _params}] 162 | (let [cmd (or java-cmd (java-executable)) 163 | {:keys [classpath-roots argmap]} basis 164 | cp-entries (concat cp classpath-roots) 165 | cp-str (deps/join-classpath cp-entries) 166 | combined-java-opts (concat java-opts (:jvm-opts argmap))] 167 | {:command-args (make-java-args cmd combined-java-opts cp-str main main-args use-cp-file)})) 168 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | =========== 3 | 4 | * v0.10.11 c6c670a on Oct 28, 2025 5 | * write-pom - TBUILD-41 - add :src-pom :none option 6 | * v0.10.10 deedd62 on Jul 29, 2025 7 | * compile-clj - TBUILD-46 Capture out and error if process is unsuccessful 8 | * v0.10.9 e405aac on May 13, 2025 9 | * write-pom - TBUILD-44 - build.properties file should use `SOURCE_DATE_EPOCH` if set for reproducible builds 10 | * v0.10.8 2fdfd66 on Mar 25, 2025 11 | * process - TBUILD-1 Fix blocked buffering when capturing large output 12 | * v0.10.7 573711e on Feb 8, 2025 13 | * write-pom - add warning if pom-data ignored because src-pom template exists 14 | * Update deps to latest 15 | * v0.10.6 52cf7d6 on Nov 22, 2024 16 | * compile-clj - add stream control args for compilation so out and err can be captured 17 | * java-command - remove assert that :basis is required (that is no longer true) 18 | * v0.10.5 2a21b7a on July 12, 2024 19 | * compile-clj - fix ordering of namespaces not included in topo sort 20 | * v0.10.4 31388ff on Jun 8, 2024 21 | * uber, jar, zip - TBUILD-42 Use buffered output streams everywhere 22 | * v0.10.3 15ead66 on May 1, 2024 23 | * compile-clj - add simple spec for :basis arg 24 | * v0.10.1 5e3b8f3 on Apr 28, 2024 25 | * Update deps to latest 26 | * compile-clj - validate that basis is non-nil 27 | * v0.10.0 3a2c484 on Mar 8, 2024 28 | * Updated deps to latest tools.deps and Clojure 1.11.2 29 | * v0.9.6 8e78bcc on Oct 6, 2023 30 | * write-pom - add :pom-data to supply extra pom data when generating a new pom 31 | * uber - fix exclusions and conflict handling when including local deps on windows 32 | * v0.9.5 24f2894 on Aug 11, 2023 33 | * java-command - TBUILD-14 Use :jvm-opts from aliases in basis, if provided 34 | * Update to latest deps 35 | * v0.9.4 76b78fe on Mar 6, 2023 36 | * uber - exclude Emacs backup files from uberjar inclusion (useful with local/git deps) 37 | * uber - improve error message when file in local or git lib can't be read 38 | * write-pom - improve docstring 39 | * Switch to tools.deps 0.17.1297 40 | * v0.9.3 e537cd1 on Feb 1, 2023 41 | * NO CHANGES - just moved Maven artifact to io.github.clojure groupId to match git dep 42 | * v0.9.2 fe6b140 on Jan 17, 2023 43 | * uber - fix from TBUILD-30 to close copied file streams 44 | * v0.9.1 27ff8a4 on Jan 13, 2023 45 | * uber - TBUILD-35 Fix error on exploding jar with / entry 46 | * uber - TBUILD-30 Apply exclusions and conflict handlers for local and git libs 47 | * v0.9.0 8c93e0c on Dec 22, 2022 48 | * Add clojure.tools.build.api/with-project-root macro 49 | * java-command, compile-clj - TBUILD-34 - Use Clojure CLI logic in finding Java executable 50 | * Switch to tools.deps 0.16.1264 51 | * v0.8.5 9c738da on Nov 14, 2022 52 | * Add support for snapshot and release policies on :mvn/repos (see TDEPS-101) 53 | * v0.8.4 8c3cd69 on Nov 3, 2022 54 | * TBUILD-26 Released as a Maven artifact 55 | * v0.8.3 0d20256 on Jun 28, 2022 56 | * uber - TBUILD-32 - Preserve reader conditionals when merging data\_readers.cljc 57 | * uber - TBUILD-33 - Adjust regex for data\_readers to omit .cljs 58 | * Update to tools.deps.alpha 0.14.1212 59 | * v0.8.2 ba1a2bf on May 6, 2022 60 | * Update deps to latest 61 | * v0.8.1 7d40500 on Mar 11, 2022 62 | * compile-clj - TBUILD-29 - add support for setting bindings during compilation 63 | * v0.8.0 e3e3532 on Feb 24, 2022 64 | * compile-clj - always create classpath entries relative to `*project-root*` 65 | * java-command - don't resolve classpath entries, leave them relative to `*project-root*` 66 | * v0.7.7 1474ad6 on Feb 18, 2022 67 | * compile-clj - TBUILD-27 - Fix bug in prior impl 68 | * v0.7.6 3549b5f on Feb 18, 2022 69 | * compile-clj - TBUILD-27 - Use basis as default src dirs 70 | * Update to tools.deps.alpha 0.12.1148 71 | * v0.7.5 34727f7 on Jan 5, 2022 72 | * Update to tools.deps.alpha 0.12.1109 73 | * v0.7.4 ac442da on Dec 23, 2021 74 | * Update to tools.deps.alpha 0.12.1104 75 | * v0.7.3 2699924 on Dec 22, 2021 76 | * Update to tools.deps.alpha 0.12.1098 (fix occasional race condition in parallel load of S3TransporterFactory) 77 | * v0.7.2 0361dde on Dec 13, 2021 78 | * copy-dir - copy posix file permissions only when posix permissions are supported 79 | * v0.7.1 13f0fec on Dec 12, 2021 80 | * copy-dir - TBUILD-24 - retain file permissions when doing string replace 81 | * v0.7.0 16eddbf on Dec 12, 2021 82 | * write-pom - TBUILD-23 - specify explicit output path with :target 83 | * Update to tools.namespace 1.2.0 84 | * Update to tools.deps.alpha 0.12.1090 85 | * v0.6.8 d79ae84 on Nov 26, 2021 86 | * uber - fix service append regex 87 | * v0.6.7 8cca4f4 on Nov 24, 2021 88 | * git-process - remove debug output 89 | * v0.6.6 4d41c26 on Nov 14, 2021 90 | * install - fix use of deprecated local-repo default in install - thanks @borkdude! 91 | * v0.6.5 a0c3ff6 on Nov 12, 2021 92 | * git-process - NEW task to run an arbitrary git process and return the output 93 | * git-rev-count - updated to use git-process, added :git-command attribute 94 | * v0.6.4 ea76dff on Nov 12, 2021 95 | * java-command - add control over using classpath file with :use-cp-file (default=:auto) 96 | * compile-clj - can now accept java-command passthrough args :java-cmd, :java-opts, :use-cp-file 97 | * v0.6.3 4a1b53a on Nov 8, 2021 98 | * Update to tools.deps 0.12.1071 99 | * v0.6.2 226fb52 on Oct 12, 2021 100 | * Update to tools.deps 0.12.1053 101 | * v0.6.1 515b334 on Oct 10, 2021 102 | * copy-dir - update attribute name added in v0.6.0 103 | * v0.6.0 b139316 on Oct 10, 2021 104 | * compile-clj - TBUILD-20 - fix regression with including class dir on classpath 105 | * copy-dir - add option to copy but not replace in binary files by extension 106 | * v0.5.1 21da7d4 on Sep 21, 2021 107 | * Update to latest tools.deps 0.12.1048 108 | * v0.5.0 7d77952 on Sep 16, 2021 109 | * create-basis - do not include user deps.edn by default 110 | * v0.4.1 452db44 on Sep 16, 2021 111 | * Add some param spec checking 112 | * v0.4.0 801a22f on Sep 15, 2021 113 | * uber - TBUILD-2 - add support for configurable conflict handlers 114 | * uber - TBUILD-11 - detect file and dir with same name in uber 115 | * uber - TBUILD-16 - expand default exclusions 116 | * uber - add support for custom exclusions 117 | * v0.3.0 e418fc9 on Sep 11, 2021 118 | * Bump to latest tools.deps - 0.12.1036 119 | * v0.2.2 3049217 on Sep 7, 2021 120 | * unzip - new task to unzip a zip file in a dir 121 | * v0.2.1 dd64636 on Sep 4, 2021 122 | * jar, uber - replace - with _ in main class name 123 | * v0.2.0 7cbb94b on Aug 31, 2021 124 | * compile-clj - fix docs and code to not require :src-dirs 125 | * compile-clj - TBUILD-7 - sort namespaces using topological sort by default 126 | * v0.1.9 6736c83 on Aug 22, 2021 127 | * git-count-revs - add :path option 128 | * pom-path - new task that computes the path to the pom.xml in a jar 129 | * v0.1.8 38d2780 on Aug 13, 2021 130 | * write-file - TBUILD-15 - add :string option 131 | * write-pom - TBUILD-13 - add :scm options to write scm properties 132 | * v0.1.7 8a3abc2 on July 28, 2021 133 | * TBUILD-10 - fix missing assertions in tests 134 | * Remove unnecessary resource file that overrides tools.deps 135 | * v0.1.6 5636e61 on July 21, 2021 136 | * copy-dir - Fix TBUILD-4 - set up default and overridable file ignore patterns 137 | * v0.1.5 1cd59e6 on July 21, 2021 138 | * jar, uber - Fix TBUILD-8 - jar files built on Windows had bad paths 139 | * v0.1.4 169fef9 on July 20, 2021 140 | * jar - add support for custom :manifest attributes 141 | * uber - add support for custom :manifest attributes 142 | * create-basis - make more tolerant of missing deps.edn file 143 | * update tools.deps.alpha dependency to latest 144 | * v0.1.3 660a71f on July 13, 2021 145 | * write-pom - Fix TBUILD-3 - now takes deps from output of basis libs, so includes alias effects 146 | * uber - exclude META-INF/\*.MF files 147 | * v0.1.2 81f05b7 on July 9, 2021 148 | * Update tools.deps.alpha dependency, public release 149 | * v0.1.1 on July 7, 2021 150 | * Add `java-command` take to create Java command line from basis 151 | * v0.1.0 on July 5, 2021 152 | * Renaming things towards release 153 | -------------------------------------------------------------------------------- /docs/js/highlight.min.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.6.0 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return R(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||R(i))return i}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){l+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):E(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"===e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":y.classPrefix,i='',i+n+o}function p(){var e,t,r,a;if(!E.k)return n(B);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)a+=n(B.substr(t,r.index-t)),e=g(E,r),e?(M+=e[1],a+=h(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return a+n(B.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=""}function v(e){k+=e.cN?h(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?B+=n:(a.rE||a.eE||(B+=n),b(),a.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,E=i||N,L={},k="";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var B="",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substr(O,I.index-O),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},a=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&"\n"===e?"
":y.tabReplace?n.replace(/\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n,t,r,o,s,p=i(e);a(p)||(y.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||"").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",y={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},I={"&":"&","<":"<",">":">"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("clojure",function(e){var t={"builtin-name":"def defonce cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"},r="a-zA-Z_\\-!.?+*=<>&#'",n="["+r+"]["+r+"0-9/;:]*",a="[-+]?\\d+(\\.\\d+)?",o={b:n,r:0},s={cN:"number",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(";","$",{r:0}),d={cN:"literal",b:/\b(true|false|nil)\b/},l={b:"[\\[\\{]",e:"[\\]\\}]"},m={cN:"comment",b:"\\^"+n},p=e.C("\\^\\{","\\}"),u={cN:"symbol",b:"[:]{1,2}"+n},f={b:"\\(",e:"\\)"},h={eW:!0,r:0},y={k:t,l:n,cN:"name",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C("comment",""),y,h],h.c=b,l.c=b,{aliases:["clj"],i:/\S/,c:[f,i,m,p,c,u,l,s,d]}});hljs.registerLanguage("clojure-repl",function(e){return{c:[{cN:"meta",b:/^([\w.-]+|\s*#_)=>/,starts:{e:/$/,sL:"clojure"}}]}}); -------------------------------------------------------------------------------- /docs/css/default.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Arial, sans-serif; 3 | font-size: 15px; 4 | } 5 | 6 | pre, code { 7 | font-family: Monaco, DejaVu Sans Mono, Consolas, monospace; 8 | font-size: 9pt; 9 | margin: 15px 0; 10 | } 11 | 12 | h1 { 13 | font-weight: normal; 14 | font-size: 29px; 15 | margin: 10px 0 2px 0; 16 | padding: 0; 17 | } 18 | 19 | h2 { 20 | font-weight: normal; 21 | font-size: 25px; 22 | } 23 | 24 | h5.license { 25 | margin: 9px 0 22px 0; 26 | color: #555; 27 | font-weight: normal; 28 | font-size: 12px; 29 | font-style: italic; 30 | } 31 | 32 | .document h1, .namespace-index h1 { 33 | font-size: 32px; 34 | margin-top: 12px; 35 | } 36 | 37 | #header, #content, .sidebar { 38 | position: fixed; 39 | } 40 | 41 | #header { 42 | top: 0; 43 | left: 0; 44 | right: 0; 45 | height: 22px; 46 | color: #f5f5f5; 47 | padding: 5px 7px; 48 | } 49 | 50 | #content { 51 | top: 32px; 52 | right: 0; 53 | bottom: 0; 54 | overflow: auto; 55 | background: #fff; 56 | color: #333; 57 | padding: 0 18px; 58 | } 59 | 60 | .sidebar { 61 | position: fixed; 62 | top: 32px; 63 | bottom: 0; 64 | overflow: auto; 65 | } 66 | 67 | .sidebar.primary { 68 | background: #e2e2e2; 69 | border-right: solid 1px #cccccc; 70 | left: 0; 71 | width: 250px; 72 | } 73 | 74 | .sidebar.secondary { 75 | background: #f2f2f2; 76 | border-right: solid 1px #d7d7d7; 77 | left: 251px; 78 | width: 200px; 79 | } 80 | 81 | #content.namespace-index, #content.document { 82 | left: 251px; 83 | } 84 | 85 | #content.namespace-docs { 86 | left: 452px; 87 | } 88 | 89 | #content.document { 90 | padding-bottom: 10%; 91 | } 92 | 93 | #header { 94 | background: #3f3f3f; 95 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); 96 | z-index: 100; 97 | } 98 | 99 | #header h1 { 100 | margin: 0; 101 | padding: 0; 102 | font-size: 18px; 103 | font-weight: lighter; 104 | text-shadow: -1px -1px 0px #333; 105 | } 106 | 107 | #header h1 .project-version { 108 | font-weight: normal; 109 | } 110 | 111 | .project-version { 112 | padding-left: 0.15em; 113 | } 114 | 115 | #header a, .sidebar a { 116 | display: block; 117 | text-decoration: none; 118 | } 119 | 120 | #header a { 121 | color: #f5f5f5; 122 | } 123 | 124 | .sidebar a { 125 | color: #333; 126 | } 127 | 128 | #header h2 { 129 | float: right; 130 | font-size: 9pt; 131 | font-weight: normal; 132 | margin: 4px 3px; 133 | padding: 0; 134 | color: #bbb; 135 | } 136 | 137 | #header h2 a { 138 | display: inline; 139 | } 140 | 141 | .sidebar h3 { 142 | margin: 0; 143 | padding: 10px 13px 0 13px; 144 | font-size: 19px; 145 | font-weight: lighter; 146 | } 147 | 148 | .sidebar h3 a { 149 | color: #444; 150 | } 151 | 152 | .sidebar h3.no-link { 153 | color: #636363; 154 | } 155 | 156 | .sidebar ul { 157 | padding: 7px 0 6px 0; 158 | margin: 0; 159 | } 160 | 161 | .sidebar ul.index-link { 162 | padding-bottom: 4px; 163 | } 164 | 165 | .sidebar li { 166 | display: block; 167 | vertical-align: middle; 168 | } 169 | 170 | .sidebar li a, .sidebar li .no-link { 171 | border-left: 3px solid transparent; 172 | padding: 0 10px; 173 | white-space: nowrap; 174 | } 175 | 176 | .sidebar li .no-link { 177 | display: block; 178 | color: #777; 179 | font-style: italic; 180 | } 181 | 182 | .sidebar li .inner { 183 | display: inline-block; 184 | padding-top: 7px; 185 | height: 24px; 186 | } 187 | 188 | .sidebar li a, .sidebar li .tree { 189 | height: 31px; 190 | } 191 | 192 | .depth-1 .inner { padding-left: 2px; } 193 | .depth-2 .inner { padding-left: 6px; } 194 | .depth-3 .inner { padding-left: 20px; } 195 | .depth-4 .inner { padding-left: 34px; } 196 | .depth-5 .inner { padding-left: 48px; } 197 | .depth-6 .inner { padding-left: 62px; } 198 | 199 | .sidebar li .tree { 200 | display: block; 201 | float: left; 202 | position: relative; 203 | top: -10px; 204 | margin: 0 4px 0 0; 205 | padding: 0; 206 | } 207 | 208 | .sidebar li.depth-1 .tree { 209 | display: none; 210 | } 211 | 212 | .sidebar li .tree .top, .sidebar li .tree .bottom { 213 | display: block; 214 | margin: 0; 215 | padding: 0; 216 | width: 7px; 217 | } 218 | 219 | .sidebar li .tree .top { 220 | border-left: 1px solid #aaa; 221 | border-bottom: 1px solid #aaa; 222 | height: 19px; 223 | } 224 | 225 | .sidebar li .tree .bottom { 226 | height: 22px; 227 | } 228 | 229 | .sidebar li.branch .tree .bottom { 230 | border-left: 1px solid #aaa; 231 | } 232 | 233 | .sidebar.primary li.current a { 234 | border-left: 3px solid #a33; 235 | color: #a33; 236 | } 237 | 238 | .sidebar.secondary li.current a { 239 | border-left: 3px solid #33a; 240 | color: #33a; 241 | } 242 | 243 | .namespace-index h2 { 244 | margin: 30px 0 0 0; 245 | } 246 | 247 | .namespace-index h3 { 248 | font-size: 16px; 249 | font-weight: bold; 250 | margin-bottom: 0; 251 | } 252 | 253 | .namespace-index .topics { 254 | padding-left: 30px; 255 | margin: 11px 0 0 0; 256 | } 257 | 258 | .namespace-index .topics li { 259 | padding: 5px 0; 260 | } 261 | 262 | .namespace-docs h3 { 263 | font-size: 18px; 264 | font-weight: bold; 265 | } 266 | 267 | .public h3 { 268 | margin: 0; 269 | float: left; 270 | } 271 | 272 | .usage { 273 | clear: both; 274 | } 275 | 276 | .public { 277 | margin: 0; 278 | border-top: 1px solid #e0e0e0; 279 | padding-top: 14px; 280 | padding-bottom: 6px; 281 | } 282 | 283 | .public:last-child { 284 | margin-bottom: 20%; 285 | } 286 | 287 | .members .public:last-child { 288 | margin-bottom: 0; 289 | } 290 | 291 | .members { 292 | margin: 15px 0; 293 | } 294 | 295 | .members h4 { 296 | color: #555; 297 | font-weight: normal; 298 | font-variant: small-caps; 299 | margin: 0 0 5px 0; 300 | } 301 | 302 | .members .inner { 303 | padding-top: 5px; 304 | padding-left: 12px; 305 | margin-top: 2px; 306 | margin-left: 7px; 307 | border-left: 1px solid #bbb; 308 | } 309 | 310 | #content .members .inner h3 { 311 | font-size: 12pt; 312 | } 313 | 314 | .members .public { 315 | border-top: none; 316 | margin-top: 0; 317 | padding-top: 6px; 318 | padding-bottom: 0; 319 | } 320 | 321 | .members .public:first-child { 322 | padding-top: 0; 323 | } 324 | 325 | h4.type, 326 | h4.dynamic, 327 | h4.added, 328 | h4.deprecated { 329 | float: left; 330 | margin: 3px 10px 15px 0; 331 | font-size: 15px; 332 | font-weight: bold; 333 | font-variant: small-caps; 334 | } 335 | 336 | .public h4.type, 337 | .public h4.dynamic, 338 | .public h4.added, 339 | .public h4.deprecated { 340 | font-size: 13px; 341 | font-weight: bold; 342 | margin: 3px 0 0 10px; 343 | } 344 | 345 | .members h4.type, 346 | .members h4.added, 347 | .members h4.deprecated { 348 | margin-top: 1px; 349 | } 350 | 351 | h4.type { 352 | color: #717171; 353 | } 354 | 355 | h4.dynamic { 356 | color: #9933aa; 357 | } 358 | 359 | h4.added { 360 | color: #508820; 361 | } 362 | 363 | h4.deprecated { 364 | color: #880000; 365 | } 366 | 367 | .namespace { 368 | margin-bottom: 30px; 369 | } 370 | 371 | .namespace:last-child { 372 | margin-bottom: 10%; 373 | } 374 | 375 | .index { 376 | padding: 0; 377 | font-size: 80%; 378 | margin: 15px 0; 379 | line-height: 16px; 380 | } 381 | 382 | .index * { 383 | display: inline; 384 | } 385 | 386 | .index p { 387 | padding-right: 3px; 388 | } 389 | 390 | .index li { 391 | padding-right: 5px; 392 | } 393 | 394 | .index ul { 395 | padding-left: 0; 396 | } 397 | 398 | .type-sig { 399 | clear: both; 400 | color: #088; 401 | } 402 | 403 | .type-sig pre { 404 | padding-top: 10px; 405 | margin: 0; 406 | } 407 | 408 | .usage code { 409 | display: block; 410 | color: #008; 411 | margin: 2px 0; 412 | } 413 | 414 | .usage code:first-child { 415 | padding-top: 10px; 416 | } 417 | 418 | p { 419 | margin: 15px 0; 420 | } 421 | 422 | .public p:first-child, .public pre.plaintext { 423 | margin-top: 12px; 424 | } 425 | 426 | .doc { 427 | margin: 0 0 26px 0; 428 | clear: both; 429 | } 430 | 431 | .public .doc { 432 | margin: 0; 433 | } 434 | 435 | .namespace-index .doc { 436 | margin-bottom: 20px; 437 | } 438 | 439 | .namespace-index .namespace .doc { 440 | margin-bottom: 10px; 441 | } 442 | 443 | .markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td { 444 | line-height: 22px; 445 | } 446 | 447 | .markdown li { 448 | padding: 2px 0; 449 | } 450 | 451 | .markdown h2 { 452 | font-weight: normal; 453 | font-size: 25px; 454 | margin: 30px 0 10px 0; 455 | } 456 | 457 | .markdown h3 { 458 | font-weight: normal; 459 | font-size: 20px; 460 | margin: 30px 0 0 0; 461 | } 462 | 463 | .markdown h4 { 464 | font-size: 15px; 465 | margin: 22px 0 -4px 0; 466 | } 467 | 468 | .doc, .public, .namespace .index { 469 | max-width: 680px; 470 | overflow-x: visible; 471 | } 472 | 473 | .markdown pre > code { 474 | display: block; 475 | padding: 10px; 476 | } 477 | 478 | .markdown pre > code, .src-link a { 479 | border: 1px solid #e4e4e4; 480 | border-radius: 2px; 481 | } 482 | 483 | .markdown code:not(.hljs), .src-link a { 484 | background: #f6f6f6; 485 | } 486 | 487 | pre.deps { 488 | display: inline-block; 489 | margin: 0 10px; 490 | border: 1px solid #e4e4e4; 491 | border-radius: 2px; 492 | padding: 10px; 493 | background-color: #f6f6f6; 494 | } 495 | 496 | .markdown hr { 497 | border-style: solid; 498 | border-top: none; 499 | color: #ccc; 500 | } 501 | 502 | .doc ul, .doc ol { 503 | padding-left: 30px; 504 | } 505 | 506 | .doc table { 507 | border-collapse: collapse; 508 | margin: 0 10px; 509 | } 510 | 511 | .doc table td, .doc table th { 512 | border: 1px solid #dddddd; 513 | padding: 4px 6px; 514 | } 515 | 516 | .doc table th { 517 | background: #f2f2f2; 518 | } 519 | 520 | .doc dl { 521 | margin: 0 10px 20px 10px; 522 | } 523 | 524 | .doc dl dt { 525 | font-weight: bold; 526 | margin: 0; 527 | padding: 3px 0; 528 | border-bottom: 1px solid #ddd; 529 | } 530 | 531 | .doc dl dd { 532 | padding: 5px 0; 533 | margin: 0 0 5px 10px; 534 | } 535 | 536 | .doc abbr { 537 | border-bottom: 1px dotted #333; 538 | font-variant: none; 539 | cursor: help; 540 | } 541 | 542 | .src-link { 543 | margin-bottom: 15px; 544 | } 545 | 546 | .src-link a { 547 | font-size: 70%; 548 | padding: 1px 4px; 549 | text-decoration: none; 550 | color: #5555bb; 551 | } 552 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/write_pom.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.write-pom 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.string :as str] 13 | [clojure.data.xml :as xml] 14 | [clojure.data.xml.tree :as tree] 15 | [clojure.data.xml.event :as event] 16 | [clojure.walk :as walk] 17 | [clojure.zip :as zip] 18 | [clojure.tools.deps.util.maven :as maven] 19 | [clojure.tools.deps.util.io :refer [printerrln]] 20 | [clojure.tools.build.api :as api] 21 | [clojure.tools.build.util.file :as file]) 22 | (:import [clojure.data.xml.node Element] 23 | [java.io Reader] 24 | [java.time Instant ZoneId ZonedDateTime] 25 | [java.time.format DateTimeFormatter])) 26 | 27 | (set! *warn-on-reflection* true) 28 | 29 | (xml/alias-uri 'pom "http://maven.apache.org/POM/4.0.0") 30 | (def ^:private pom-ns (name (.-name ^clojure.lang.Namespace (get (ns-aliases *ns*) 'pom)))) 31 | 32 | 33 | (defn- to-dep 34 | [[lib {:keys [mvn/version exclusions optional] :as coord}]] 35 | (let [[group-id artifact-id classifier] (maven/lib->names lib)] 36 | (if version 37 | (cond-> 38 | [::pom/dependency 39 | [::pom/groupId group-id] 40 | [::pom/artifactId artifact-id] 41 | [::pom/version version]] 42 | 43 | classifier 44 | (conj [::pom/classifier classifier]) 45 | 46 | (seq exclusions) 47 | (conj [::pom/exclusions 48 | (map (fn [excl] 49 | [::pom/exclusion 50 | [::pom/groupId (or (namespace excl) (name excl))] 51 | [::pom/artifactId (name excl)]]) 52 | exclusions)]) 53 | 54 | optional 55 | (conj [::pom/optional "true"])) 56 | (printerrln "Skipping coordinate:" coord)))) 57 | 58 | (defn- gen-deps 59 | [deps] 60 | [::pom/dependencies 61 | (map to-dep deps)]) 62 | 63 | (defn- gen-source-dir 64 | [path] 65 | [::pom/sourceDirectory path]) 66 | 67 | (defn- to-resource 68 | [resource] 69 | [::pom/resource 70 | [::pom/directory resource]]) 71 | 72 | (defn- gen-resources 73 | [rpaths] 74 | [::pom/resources 75 | (map to-resource rpaths)]) 76 | 77 | (defn- to-repo-policy 78 | [parent-tag {:keys [enabled update checksum]}] 79 | [parent-tag 80 | (when (some? enabled) [::pom/enabled (str enabled)]) 81 | (when update [::pom/updatePolicy (if (keyword? update) (name update) (str "interval:" update))]) 82 | (when checksum [::pom/checksumPolicy (name checksum)])]) 83 | 84 | (defn- to-repo 85 | [[name {:keys [url snapshots releases]}]] 86 | [::pom/repository 87 | [::pom/id name] 88 | [::pom/url url] 89 | (when releases (to-repo-policy ::pom/releases releases)) 90 | (when snapshots (to-repo-policy ::pom/snapshots snapshots))]) 91 | 92 | (defn- gen-repos 93 | [repos] 94 | [::pom/repositories 95 | (map to-repo repos)]) 96 | 97 | (defn- pomify 98 | [val] 99 | (if (and (vector? val) (keyword? (first val))) 100 | (into [(keyword pom-ns (name (first val)))] (rest val)) 101 | val)) 102 | 103 | (defn- gen-pom 104 | [{:keys [deps src-paths resource-paths repos group artifact version scm pom-data] 105 | :or {version "0.1.0"}}] 106 | (let [[path & paths] src-paths 107 | {:keys [connection developerConnection tag url]} scm] 108 | (xml/sexp-as-element 109 | (into 110 | [::pom/project 111 | {:xmlns "http://maven.apache.org/POM/4.0.0" 112 | (keyword "xmlns:xsi") "http://www.w3.org/2001/XMLSchema-instance" 113 | (keyword "xsi:schemaLocation") "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"} 114 | [::pom/modelVersion "4.0.0"] 115 | [::pom/packaging "jar"] 116 | [::pom/groupId group] 117 | [::pom/artifactId artifact] 118 | [::pom/version version] 119 | [::pom/name artifact] 120 | (gen-deps deps) 121 | (when (or path (seq resource-paths)) 122 | (when (seq paths) (apply printerrln "Skipping paths:" paths)) 123 | [::pom/build 124 | (when path (gen-source-dir path)) 125 | (when (seq resource-paths) (gen-resources resource-paths))]) 126 | (gen-repos repos) 127 | (when scm 128 | [::pom/scm 129 | (when connection [::pom/connection connection]) 130 | (when developerConnection [::pom/developerConnection developerConnection]) 131 | (when tag [::pom/tag tag]) 132 | (when url [::pom/url url])])] 133 | (walk/postwalk pomify pom-data))))) 134 | 135 | (defn- make-xml-element 136 | [{:keys [tag attrs] :as node} children] 137 | (with-meta 138 | (apply xml/element tag attrs children) 139 | (meta node))) 140 | 141 | (defn- xml-update 142 | [root tag-path replace-node] 143 | (let [z (zip/zipper xml/element? :content make-xml-element root)] 144 | (zip/root 145 | (loop [[tag & more-tags :as tags] tag-path, parent z, child (zip/down z)] 146 | (if child 147 | (if (= tag (:tag (zip/node child))) 148 | (if (seq more-tags) 149 | (recur more-tags child (zip/down child)) 150 | (zip/edit child (constantly replace-node))) 151 | (if-let [next-sibling (zip/right child)] 152 | (recur tags parent next-sibling) 153 | (if (seq more-tags) 154 | (let [new-parent (zip/append-child parent (xml/sexp-as-element tag)) 155 | new-child (zip/rightmost (zip/down new-parent))] 156 | (recur more-tags new-child (zip/down new-child))) 157 | (zip/append-child parent replace-node)))) 158 | (if (seq more-tags) 159 | (let [new-parent (zip/append-child parent (xml/sexp-as-element tag)) 160 | new-child (zip/rightmost (zip/down new-parent))] 161 | (recur more-tags new-child (zip/down new-child))) 162 | (zip/append-child parent replace-node))))))) 163 | 164 | (defn- replace-deps 165 | [pom deps] 166 | (xml-update pom [::pom/dependencies] (xml/sexp-as-element (gen-deps deps)))) 167 | 168 | (defn- replace-paths 169 | [pom [path & paths]] 170 | (if path 171 | (do 172 | (when (seq paths) (apply printerrln "Skipping paths:" paths)) 173 | (xml-update pom [::pom/build ::pom/sourceDirectory] (xml/sexp-as-element (gen-source-dir path)))) 174 | pom)) 175 | 176 | (defn- replace-resources 177 | [pom resource-paths] 178 | (if (seq resource-paths) 179 | (xml-update pom [::pom/build ::pom/resources] (xml/sexp-as-element (gen-resources resource-paths))) 180 | pom)) 181 | 182 | (defn- replace-repos 183 | [pom repos] 184 | (if (seq repos) 185 | (xml-update pom [::pom/repositories] (xml/sexp-as-element (gen-repos repos))) 186 | pom)) 187 | 188 | (defn- replace-lib 189 | [pom lib] 190 | (if lib 191 | (-> pom 192 | (xml-update [::pom/groupId] (xml/sexp-as-element [::pom/groupId (namespace lib)])) 193 | (xml-update [::pom/artifactId] (xml/sexp-as-element [::pom/artifactId (name lib)])) 194 | (xml-update [::pom/name] (xml/sexp-as-element [::pom/name (name lib)]))) 195 | pom)) 196 | 197 | (defn- replace-version 198 | [pom version] 199 | (if version 200 | (xml-update pom [::pom/version] (xml/sexp-as-element [::pom/version version])) 201 | pom)) 202 | 203 | (defn- replace-scm 204 | [pom {:keys [connection developerConnection tag url] :as scm}] 205 | (if scm 206 | (cond-> pom 207 | connection (xml-update [::pom/scm ::pom/connection] (xml/sexp-as-element [::pom/connection connection])) 208 | developerConnection (xml-update [::pom/scm ::pom/developerConnection] (xml/sexp-as-element [::pom/developerConnection developerConnection])) 209 | tag (xml-update [::pom/scm ::pom/tag] (xml/sexp-as-element [::pom/tag tag])) 210 | url (xml-update [::pom/scm ::pom/url] (xml/sexp-as-element [::pom/url url]))) 211 | pom)) 212 | 213 | (defn- parse-xml 214 | [^Reader rdr] 215 | (let [roots (tree/seq-tree event/event-element event/event-exit? event/event-node 216 | (xml/event-seq rdr {:include-node? #{:element :characters :comment} 217 | :skip-whitespace true}))] 218 | (first (filter #(instance? Element %) (first roots))))) 219 | 220 | (defn- libs->deps 221 | "Convert libmap to root deps" 222 | [libs] 223 | (reduce-kv 224 | (fn [ret lib {:keys [dependents] :as coord}] 225 | (if (seq dependents) 226 | ret 227 | (assoc ret lib coord))) 228 | {} libs)) 229 | 230 | (defn meta-maven-path 231 | [params] 232 | (let [{:keys [lib]} params 233 | pom-file (jio/file "META-INF" "maven" (namespace lib) (name lib))] 234 | (.toString pom-file))) 235 | 236 | (defn write-pom 237 | [params] 238 | (let [{:keys [basis class-dir target src-pom lib version scm src-dirs resource-dirs repos pom-data]} params 239 | {:keys [libs]} basis 240 | root-deps (libs->deps libs) 241 | src-pom-file (when-not (= :none src-pom) 242 | (api/resolve-path (or src-pom "pom.xml"))) 243 | repos (or repos (remove #(= "https://repo1.maven.org/maven2/" (-> % val :url)) (:mvn/repos basis))) 244 | pom (if (and src-pom-file (.exists src-pom-file)) 245 | (do 246 | (when pom-data 247 | (println "Warning in write-pom: pom-data supplied but not used because pom template exists at" (or src-pom "pom.xml"))) 248 | (with-open [rdr (jio/reader src-pom-file)] 249 | (-> rdr 250 | parse-xml 251 | (replace-deps root-deps) 252 | (replace-paths src-dirs) 253 | (replace-resources resource-dirs) 254 | (replace-repos repos) 255 | (replace-lib lib) 256 | (replace-version version) 257 | (replace-scm scm)))) 258 | (gen-pom 259 | (cond-> 260 | {:deps root-deps 261 | :src-paths src-dirs 262 | :resource-paths resource-dirs 263 | :repos repos 264 | :group (namespace lib) 265 | :artifact (name lib)} 266 | version (assoc :version version) 267 | scm (assoc :scm scm) 268 | pom-data (assoc :pom-data pom-data)))) 269 | pom-dir-file (file/ensure-dir 270 | (cond 271 | class-dir (jio/file (api/resolve-path class-dir) (meta-maven-path {:lib lib})) 272 | target (-> target api/resolve-path jio/file file/ensure-dir) 273 | :else (throw (ex-info "write-pom requires either :class-dir or :target" {}))))] 274 | (spit (jio/file pom-dir-file "pom.xml") (xml/indent-str pom)) 275 | (spit (jio/file pom-dir-file "pom.properties") 276 | (str/join (System/lineSeparator) 277 | ["# Generated by org.clojure/tools.build" 278 | (let [dtf (DateTimeFormatter/ofPattern "E MMM d HH:mm:ss 'UTC' u") 279 | inst (or (some-> "SOURCE_DATE_EPOCH" 280 | System/getenv 281 | parse-long 282 | Instant/ofEpochSecond) 283 | (Instant/now))] 284 | (str "# " (.format dtf (ZonedDateTime/ofInstant inst (ZoneId/of "Z"))))) 285 | (format "version=%s" version) 286 | (format "groupId=%s" (namespace lib)) 287 | (format "artifactId=%s" (name lib))])))) 288 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | 205 | 206 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_uber.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-uber 10 | (:require 11 | [clojure.set :as set] 12 | [clojure.string :as str] 13 | [clojure.test :refer :all] 14 | [clojure.java.io :as jio] 15 | [clojure.tools.build.api :as api] 16 | [clojure.tools.build.tasks.uber :as uber] 17 | [clojure.tools.build.test-util :refer :all] 18 | [clojure.tools.build.util.zip :as zip] 19 | [clojure.tools.build.tasks.test-jar :as test-jar]) 20 | (:import 21 | [clojure.lang ExceptionInfo] 22 | [java.io ByteArrayInputStream])) 23 | 24 | (defn- string->stream 25 | [^String s] 26 | (ByteArrayInputStream. (.getBytes s "UTF-8"))) 27 | 28 | (deftest string-stream-rt 29 | (are [s] (= s (#'uber/stream->string (string->stream s))) 30 | "" 31 | "abc")) 32 | 33 | (deftest test-uber 34 | (let [uber-path "target/p1-uber.jar"] 35 | (with-test-dir "test-data/p1" 36 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 37 | (api/javac {:class-dir "target/classes" 38 | :src-dirs ["java"]}) 39 | (api/copy-dir {:target-dir "target/classes" 40 | :src-dirs ["src"]}) 41 | (api/uber {:class-dir "target/classes" 42 | :basis (api/create-basis nil) 43 | :uber-file uber-path 44 | :main 'foo.bar}) 45 | (let [uf (jio/file (project-path uber-path))] 46 | (is (true? (.exists uf))) 47 | (is (set/subset? 48 | #{"META-INF/MANIFEST.MF" "foo/" "foo/bar.clj" "foo/Demo2.class" "foo/Demo1.class"} 49 | (set (map :name (zip/list-zip (project-path uber-path)))))) 50 | (is (str/includes? (test-jar/slurp-manifest uf) "Main-Class: foo.bar")))))) 51 | 52 | (deftest test-custom-manifest 53 | (let [uber-path "target/p1-uber.jar"] 54 | (with-test-dir "test-data/p1" 55 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 56 | (api/javac {:class-dir "target/classes" 57 | :src-dirs ["java"]}) 58 | (api/copy-dir {:target-dir "target/classes" 59 | :src-dirs ["src"]}) 60 | (api/uber {:class-dir "target/classes" 61 | :uber-file uber-path 62 | :main 'foo.bar 63 | :manifest {"Main-Class" "baz" ;; overrides :main 64 | 'Custom-Thing 100}}) ;; stringify kvs 65 | (let [uf (jio/file (project-path uber-path))] 66 | (is (true? (.exists uf))) 67 | (is (= #{"META-INF/MANIFEST.MF" "foo/" "foo/bar.clj" "foo/Demo2.class" "foo/Demo1.class"} 68 | (set (map :name (zip/list-zip (project-path uber-path)))))) 69 | (let [manifest-out (test-jar/slurp-manifest uf)] 70 | (is (str/includes? manifest-out "Main-Class: baz")) 71 | (is (str/includes? manifest-out "Custom-Thing: 100"))))))) 72 | 73 | (deftest test-conflicts 74 | (with-test-dir "test-data/uber-conflict" 75 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 76 | 77 | ;; make "jars" 78 | (doseq [j ["j1" "j2" "j3"]] 79 | (let [classes (format "target/%s/classes" j) 80 | jar-file (format "target/%s.jar" j)] 81 | (api/copy-dir {:target-dir classes :src-dirs [j]}) 82 | (api/jar {:class-dir classes :jar-file jar-file}))) 83 | 84 | ;; uber including j1, j2, j3 85 | (api/uber {:class-dir "target/classes" 86 | :basis (api/create-basis {:root nil 87 | :project {:deps {'dummy/j1 {:local/root "target/j1.jar"} 88 | 'dummy/j2 {:local/root "target/j2.jar"} 89 | 'dummy/j3 {:local/root "target/j3.jar"}}}}) 90 | :uber-file "target/conflict.jar" 91 | :conflict-handlers {"ignore.txt" :ignore 92 | "overwrite.txt" :overwrite 93 | "append.txt" :append}}) 94 | 95 | ;; unzip 96 | (api/unzip {:zip-file "target/conflict.jar" :target-dir "target/unzip"}) 97 | 98 | ;; non-conflicting files are combined, conflicting files are reconciled 99 | (let [fs (map :name (zip/list-zip (project-path "target/conflict.jar")))] 100 | (is 101 | (set/subset? 102 | #{"META-INF/LICENSE.txt" "META-INF/MANIFEST.MF" 103 | "data_readers.clj" 104 | "my/j1.txt" "my/j2.txt" 105 | "ignore.txt" "overwrite.txt" "append.txt"} 106 | (set fs)))) 107 | 108 | ;; data_readers.clj merge 109 | (is (= '{j1a my.foo/j1a-reader, j1b my.bar/j1b-reader, 110 | j2a my.foo/j2a-reader, j2b my.bar/j2b-reader} 111 | (read-string (slurp (project-path "target/unzip/data_readers.clj"))))) 112 | 113 | ;; data_readers.cljc merge 114 | (is (= {'j1a (reader-conditional '(:cljs my.cljs.foo/j1a-reader :clj my.clj.foo/j1a-reader) false) 115 | 'j1b (reader-conditional '(:cljs my.cljs.foo/j1b-reader :clj my.clj.foo/j1b-reader) false) 116 | 'j2a (reader-conditional '(:cljs my.cljs.foo/j2a-reader :clj my.clj.foo/j2a-reader) false) 117 | 'j2b (reader-conditional '(:cljs my.cljs.foo/j2b-reader :clj my.clj.foo/j2b-reader) false)} 118 | (read-string {:read-cond :preserve :features #{:clj}} 119 | (slurp (project-path "target/unzip/data_readers.cljc"))))) 120 | 121 | ;; ignore files ignore, so first one wins 122 | (is (= (slurp (project-path "j1/ignore.txt")) 123 | (slurp (project-path "target/unzip/ignore.txt")))) 124 | 125 | ;; overwrite files overwrite, so last wins 126 | (is (= (slurp (project-path "j2/overwrite.txt")) 127 | (slurp (project-path "target/unzip/overwrite.txt")))) 128 | 129 | ;; append files append 130 | (is (= (str (slurp (project-path "j1/append.txt")) "\n" 131 | (slurp (project-path "j2/append.txt"))) 132 | (slurp (project-path "target/unzip/append.txt")))) 133 | 134 | ;; LICENSE files append but no dupes - include j1 and j2, but not j3 (dupe of j1) 135 | (is (= (str (slurp (project-path "j1/META-INF/LICENSE.txt")) "\n" 136 | (slurp (project-path "j2/META-INF/LICENSE.txt"))) 137 | (slurp (project-path "target/unzip/META-INF/LICENSE.txt")))))) 138 | 139 | (deftest test-conflicts-but-files 140 | (with-test-dir "test-data/uber-conflict" 141 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 142 | 143 | ;; make dirs 144 | (doseq [j ["j1" "j2" "j3"]] 145 | (let [classes (format "target/%s/classes" j)] 146 | (api/copy-dir {:target-dir classes :src-dirs [j]}) 147 | (spit (project-path (format "target/%s/classes/deps.edn" j)) "{:paths [\".\"]}"))) 148 | 149 | ;; uber including j1, j2, j3 as local dirs 150 | (api/uber {:class-dir "target/classes" 151 | :basis (api/create-basis {:root nil 152 | :project {:deps {'dummy/j1 {:local/root "target/j1/classes"} 153 | 'dummy/j2 {:local/root "target/j2/classes"} 154 | 'dummy/j3 {:local/root "target/j3/classes"}}}}) 155 | :uber-file "target/conflict.jar" 156 | :conflict-handlers {"ignore.txt" :ignore 157 | "overwrite.txt" :overwrite 158 | "append.txt" :append}}) 159 | 160 | ;; unzip 161 | (api/unzip {:zip-file "target/conflict.jar" :target-dir "target/unzip"}) 162 | 163 | ;; non-conflicting files are combined, conflicting files are reconciled 164 | (let [fs (map :name (zip/list-zip (project-path "target/conflict.jar")))] 165 | (is 166 | (set/subset? 167 | #{"META-INF/LICENSE.txt" "META-INF/MANIFEST.MF" 168 | "data_readers.clj" 169 | "my/j1.txt" "my/j2.txt" 170 | "ignore.txt" "overwrite.txt" "append.txt"} 171 | (set fs)))) 172 | 173 | ;; data_readers.clj merge 174 | (is (= '{j1a my.foo/j1a-reader, j1b my.bar/j1b-reader, 175 | j2a my.foo/j2a-reader, j2b my.bar/j2b-reader} 176 | (read-string (slurp (project-path "target/unzip/data_readers.clj"))))) 177 | 178 | ;; data_readers.cljc merge 179 | (is (= {'j1a (reader-conditional '(:cljs my.cljs.foo/j1a-reader :clj my.clj.foo/j1a-reader) false) 180 | 'j1b (reader-conditional '(:cljs my.cljs.foo/j1b-reader :clj my.clj.foo/j1b-reader) false) 181 | 'j2a (reader-conditional '(:cljs my.cljs.foo/j2a-reader :clj my.clj.foo/j2a-reader) false) 182 | 'j2b (reader-conditional '(:cljs my.cljs.foo/j2b-reader :clj my.clj.foo/j2b-reader) false)} 183 | (read-string {:read-cond :preserve :features #{:clj}} 184 | (slurp (project-path "target/unzip/data_readers.cljc"))))) 185 | 186 | ;; ignore files ignore, so first one wins 187 | (is (= (slurp (project-path "j1/ignore.txt")) 188 | (slurp (project-path "target/unzip/ignore.txt")))) 189 | 190 | ;; overwrite files overwrite, so last wins 191 | (is (= (slurp (project-path "j2/overwrite.txt")) 192 | (slurp (project-path "target/unzip/overwrite.txt")))) 193 | 194 | ;; append files append 195 | (is (= (str (slurp (project-path "j1/append.txt")) "\n" 196 | (slurp (project-path "j2/append.txt"))) 197 | (slurp (project-path "target/unzip/append.txt")))) 198 | 199 | ;; LICENSE files append but no dupes - include j1 and j2, but not j3 (dupe of j1) 200 | (is (= (str (slurp (project-path "j1/META-INF/LICENSE.txt")) "\n" 201 | (slurp (project-path "j2/META-INF/LICENSE.txt"))) 202 | (slurp (project-path "target/unzip/META-INF/LICENSE.txt")))))) 203 | 204 | (deftest test-case-sensitive-dir-file-collision 205 | (with-test-dir "test-data/case-sensitive-collision" 206 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 207 | 208 | ;; make "jars" 209 | (doseq [j ["j1" "j2"]] 210 | (let [classes (format "target/%s/classes" j) 211 | jar-file (format "target/%s.jar" j)] 212 | (api/copy-dir {:target-dir classes :src-dirs [j]}) 213 | (api/jar {:class-dir classes :jar-file jar-file}))) 214 | 215 | ;; uber including j1, j2 - should fail with conflict 216 | (let [basis (api/create-basis {:root nil 217 | :project {:deps {'dummy/j1 {:local/root "target/j1.jar"} 218 | 'dummy/j2 {:local/root "target/j2.jar"}}}})] 219 | (try 220 | (api/uber {:class-dir "target/classes", :basis basis, :uber-file "target/collision.jar"}) 221 | (catch ExceptionInfo ex 222 | (= "Cannot write foo/hi.txt from dummy/j2 as parent dir is a file from another lib. One of them must be excluded." 223 | (ex-message ex)))) 224 | 225 | ;; uber including j1, j2 but excluding one of the conflicts 226 | (api/uber {:class-dir "target/classes", :basis basis, :uber-file "target/collision.jar" 227 | :exclude ["FOO"]}) 228 | 229 | ;; after exclusion, only foo/hi.txt 230 | (let [fs (map :name (zip/list-zip (project-path "target/collision.jar")))] 231 | (is (= #{"META-INF/MANIFEST.MF" "foo/" "foo/hi.txt"} (set fs))))))) 232 | 233 | ;; TBUILD-35 - it is rare but possible for a jar to contain a / dir entry 234 | (deftest test-bad-zip-with-root-dir 235 | (with-test-dir "test-data/bad-zip" 236 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 237 | (let [basis (api/create-basis {:root nil})] 238 | (api/uber {:class-dir "target/classes" :basis basis, :uber-file "target/out.jar"}) 239 | (.exists (jio/file (api/resolve-path "target/out.jar")))))) 240 | 241 | (comment 242 | (test-conflicts) 243 | (test-conflicts-but-files) 244 | (test-case-sensitive-dir-file-collision) 245 | (run-tests) 246 | ) 247 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/build/tasks/uber.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.uber 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.pprint :as pprint] 13 | [clojure.set :as set] 14 | [clojure.string :as str] 15 | [clojure.tools.build.api :as api] 16 | [clojure.tools.build.util.file :as file] 17 | [clojure.tools.build.util.zip :as zip]) 18 | (:import 19 | [java.io File InputStream IOException OutputStream ByteArrayOutputStream] 20 | [java.nio.file Files] 21 | [java.nio.file.attribute FileAttribute FileTime] 22 | [java.util.jar JarEntry JarInputStream JarOutputStream Manifest])) 23 | 24 | (set! *warn-on-reflection* true) 25 | 26 | (def ^:private uber-exclusions 27 | [#"project.clj" 28 | #"META-INF/.*\.(?:SF|RSA|DSA|MF)" 29 | #"module-info\.class" 30 | #"(.*/)?\.DS_Store" ;; Mac metadata 31 | #".+~" ;; emacs backup files 32 | #".#.*" ;; emacs 33 | #"(.*/)?\.keep" ;; convention in dirs to keep that are empty 34 | #".*\.pom" 35 | #"(?i)META-INF/(?:INDEX\.LIST|DEPENDENCIES)(?:\.txt)?"]) 36 | 37 | (defn- exclude-from-uber? 38 | [exclude-patterns ^String path] 39 | (loop [[re & res] exclude-patterns] 40 | (if re 41 | (if (re-matches re path) 42 | true 43 | (recur res)) 44 | false))) 45 | 46 | (defn- copy-stream! 47 | "Copy input stream to output stream using buffer. 48 | Caller is responsible for passing buffered streams and closing streams." 49 | [^InputStream is ^OutputStream os ^bytes buffer] 50 | (loop [] 51 | (let [size (.read is buffer)] 52 | (if (pos? size) 53 | (do 54 | (.write os buffer 0 size) 55 | (recur)) 56 | (.close os))))) 57 | 58 | (defn- stream->string 59 | [^InputStream is] 60 | (let [baos (ByteArrayOutputStream. 4096) 61 | _ (copy-stream! is baos (byte-array 4096))] 62 | (.toString baos "UTF-8"))) 63 | 64 | (defn conflict-overwrite 65 | [{:keys [path in]}] 66 | {:write {path {:stream in}}}) 67 | 68 | (defn- conflict-append 69 | [{:keys [path in]}] 70 | {:write {path {:string (str "\n" (stream->string in)), :append true}}}) 71 | 72 | (defn- conflict-append-dedupe 73 | [{:keys [path in ^File existing state] :as _params}] 74 | (let [existing-content (slurp existing) 75 | existing-lower (str/lower-case existing-content) 76 | new-content (stream->string in) 77 | new-content-lower (str/lower-case new-content) 78 | seen (or (get-in state [:append-dedupe path]) #{existing-lower})] 79 | (if (contains? seen new-content-lower) 80 | ;; already seen 81 | {:state (assoc-in state [:append-dedupe path] seen)} 82 | ;; record and append 83 | {:state (assoc-in state [:append-dedupe path] (conj seen new-content)) 84 | :write {path {:string (str "\n" new-content), :append true}}}))) 85 | 86 | (defn conflict-data-readers 87 | [{:keys [path in ^File existing]}] 88 | (binding [*read-eval* false] 89 | (let [existing-str (slurp existing) 90 | existing-reader-fns (read-string 91 | {:read-cond :preserve :features #{:clj}} 92 | existing-str) 93 | append-reader-fns (read-string 94 | {:read-cond :preserve :features #{:clj}} 95 | (stream->string in)) 96 | reader-str (with-out-str (pprint/pprint (merge existing-reader-fns append-reader-fns)))] 97 | {:write {path {:string reader-str}}}))) 98 | 99 | (defn- conflict-warn 100 | [{:keys [path lib]}] 101 | (println "Conflicting path at" path "from" lib)) 102 | 103 | (defn- conflict-error 104 | [{:keys [path lib]}] 105 | (throw (ex-info (str "Conflicting path at " path " from " lib) {}))) 106 | 107 | (defn- handler-emit 108 | [^FileTime last-modified-time buffer out-dir path write-spec] 109 | (let [{:keys [string stream append] :or {append false}} write-spec 110 | out-file (jio/file out-dir path)] 111 | (if string 112 | (spit out-file string :append ^boolean append) 113 | (copy-stream! ^InputStream stream (jio/output-stream out-file :append append) buffer)) 114 | (Files/setLastModifiedTime (.toPath out-file) last-modified-time))) 115 | 116 | (defn- handle-conflict 117 | [handlers last-modified-time buffer out-dir {:keys [state path] :as handler-params}] 118 | (let [use-handler (loop [[[re handler] & hs] (dissoc handlers :default)] 119 | (if re 120 | (if (re-matches re path) 121 | handler 122 | (recur hs)) 123 | (:default handlers)))] 124 | (if use-handler 125 | (let [{new-state :state, write :write} (use-handler handler-params)] 126 | (when write 127 | (doseq [[path write-spec] write] 128 | (handler-emit last-modified-time buffer out-dir path write-spec))) 129 | (or new-state state)) 130 | (throw (ex-info (format "No handler found for conflict at %s" path) {}))))) 131 | 132 | (defn- ensure-dir 133 | "Returns true if parent dir exists, false if exists but is not a file, 134 | and throws if it cannot be created." 135 | [^File parent ^File child] 136 | (if (.exists parent) 137 | (.isDirectory parent) 138 | (if (jio/make-parents child) 139 | true 140 | (throw (ex-info (str "Unable to create parent dirs for: " (.toString child)) {}))))) 141 | 142 | (defn- explode1 143 | "Given one entry/src file, copy to target pursuant to excludes and handlers. 144 | Returns possibly updated state for further exploding." 145 | [^InputStream is ^String path dir? ^FileTime last-modified-time 146 | ^File out-file lib {:keys [out-dir buffer exclude handlers] :as context} state] 147 | (cond 148 | ;; excluded or directory - do nothing 149 | (or (exclude-from-uber? exclude path) dir?) 150 | state 151 | 152 | ;; conflict, same file from multiple sources - handle 153 | (.exists out-file) 154 | (handle-conflict handlers last-modified-time buffer out-dir 155 | {:lib lib, :path path, :in is, :existing out-file, :state state}) 156 | 157 | ;; write new file, parent dir exists for writing 158 | (ensure-dir (.getParentFile out-file) out-file) 159 | (do 160 | (copy-stream! ^InputStream is (jio/output-stream out-file) buffer) 161 | (Files/setLastModifiedTime (.toPath out-file) last-modified-time) 162 | state) 163 | 164 | :parent-dir-is-a-file 165 | (throw (ex-info (format "Cannot write %s from %s as parent dir is a file from another lib. One of them must be excluded." 166 | path lib) {})))) 167 | 168 | (defn- explode 169 | [^File lib-file lib {:keys [out-dir buffer exclude handlers] :as context} state] 170 | (cond 171 | (not (.exists lib-file)) 172 | state 173 | 174 | (str/ends-with? (.getPath lib-file) ".jar") 175 | (with-open [jis (JarInputStream. (jio/input-stream lib-file))] 176 | (loop [the-state state] 177 | (if-let [entry (.getNextJarEntry jis)] 178 | (let [path (.getName entry) 179 | ;; should rarely happen (except /), but chop to make relative: 180 | path (if (str/starts-with? path "/") (subs path 1) path) 181 | out-file (jio/file out-dir path)] 182 | (recur 183 | (explode1 jis path (.isDirectory entry) (.getLastModifiedTime ^JarEntry entry) 184 | out-file lib context the-state))) 185 | the-state))) 186 | 187 | (.isDirectory lib-file) 188 | (let [source-dir (.getAbsoluteFile lib-file) 189 | source-path (.toPath source-dir) 190 | fs (file/collect-files source-dir :dirs true)] 191 | (loop [[^File f & restf] fs, the-state state] 192 | (if f 193 | (let [is (when (.isFile f) 194 | (try 195 | (jio/input-stream f) 196 | (catch IOException e 197 | (throw (ex-info (str "Uber task found file but can't read its content in " lib " at path " (.getPath f)) 198 | {:path (.getPath f)} e))))) 199 | new-state (try 200 | (let [path (str/replace (.toString (.relativize source-path (.toPath f))) \\ \/) 201 | source-time (FileTime/fromMillis (.lastModified f)) 202 | out-file (jio/file out-dir path)] 203 | (explode1 is path (.isDirectory f) source-time out-file lib context the-state)) 204 | (finally 205 | (when is (.close ^InputStream is))))] 206 | (recur restf new-state)) 207 | the-state))) 208 | 209 | :else 210 | (throw (ex-info (format "Unexpected lib file: %s" (.toString lib-file)) {})))) 211 | 212 | (defn- remove-optional 213 | "Remove optional libs and their transitive dependencies from the lib tree. 214 | Only remove transitive if all dependents are optional." 215 | [libs] 216 | (let [by-opt (group-by (fn [[_lib coord]] (boolean (:optional coord))) libs) 217 | optional (apply conj {} (get by-opt true))] 218 | (if (seq optional) 219 | (loop [req (get by-opt false) 220 | opt optional] 221 | (let [under-opts (group-by (fn [[_lib {:keys [dependents]}]] 222 | (boolean 223 | (and (seq dependents) 224 | (set/subset? (set dependents) (set (keys opt)))))) 225 | req) 226 | trans-opt (get under-opts true)] 227 | (if (seq trans-opt) 228 | (recur (get under-opts false) (into opt trans-opt)) 229 | (apply conj {} req)))) 230 | libs))) 231 | 232 | (defn- built-ins 233 | [kw] 234 | (or 235 | (get {:ignore (fn [_]) 236 | :overwrite conflict-overwrite 237 | :append conflict-append 238 | :append-dedupe conflict-append-dedupe 239 | :data-readers conflict-data-readers 240 | :warn conflict-warn 241 | :error conflict-error} 242 | kw) 243 | (throw (ex-info (str "Invalid handler: " kw) {})))) 244 | 245 | (defn- prep-handler 246 | "Convert user handler to fn" 247 | [handler] 248 | (cond 249 | (keyword? handler) (built-ins handler) 250 | (symbol? handler) (deref (requiring-resolve handler)) 251 | (ifn? handler) handler 252 | :else (throw (ex-info (str "Invalid handler: " handler) {})))) 253 | 254 | (def ^:private default-handlers 255 | {"^data_readers.clj[c]?$" :data-readers 256 | "^META-INF/services/.*" :append 257 | "(?i)^(META-INF/)?(COPYRIGHT|NOTICE|LICENSE)(\\.(txt|md))?$" :append-dedupe 258 | :default :ignore}) 259 | 260 | (defn- prep-handlers 261 | "Transform user handler map into a map of re->fn" 262 | [handlers] 263 | (reduce-kv 264 | (fn [m pattern handler] 265 | (assoc m (if (= pattern :default) :default (re-pattern pattern)) 266 | (prep-handler handler))) 267 | {} (merge default-handlers handlers))) 268 | 269 | (defn uber 270 | [{mf-attrs :manifest, :keys [basis class-dir uber-file main exclude conflict-handlers]}] 271 | (let [working-dir (.toFile (Files/createTempDirectory "uber" (into-array FileAttribute []))) 272 | context {:out-dir working-dir 273 | :buffer (byte-array 4096) 274 | :handlers (prep-handlers conflict-handlers) 275 | :exclude (map re-pattern (into uber-exclusions exclude))}] 276 | (try 277 | (let [{:keys [libs]} basis 278 | compile-dir (api/resolve-path class-dir) 279 | manifest (Manifest.) 280 | uber-file (api/resolve-path uber-file) 281 | mf-attr-strs (reduce-kv (fn [m k v] (assoc m (str k) (str v))) nil mf-attrs)] 282 | (reduce 283 | (fn [state [lib coord]] 284 | (reduce 285 | (fn [state path] (explode (jio/file path) lib context state)) 286 | state (:paths coord))) 287 | nil ;; initial state, usable by handlers if needed 288 | (assoc (remove-optional libs) nil {:paths [compile-dir]})) 289 | (zip/fill-manifest! manifest 290 | (merge 291 | (cond-> 292 | {"Manifest-Version" "1.0" 293 | "Created-By" "org.clojure/tools.build" 294 | "Build-Jdk-Spec" (System/getProperty "java.specification.version")} 295 | main (assoc "Main-Class" (str/replace (str main) \- \_)) 296 | (.exists (jio/file working-dir "META-INF" "versions")) (assoc "Multi-Release" "true")) 297 | mf-attr-strs)) 298 | (file/ensure-dir (.getParent uber-file)) 299 | (with-open [jos (JarOutputStream. (jio/output-stream uber-file) manifest)] 300 | (zip/copy-to-zip jos working-dir))) 301 | (finally 302 | (file/delete working-dir))))) 303 | -------------------------------------------------------------------------------- /epl-v10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Eclipse Public License - Version 1.0 8 | 25 | 26 | 27 | 28 | 29 | 30 |

Eclipse Public License - v 1.0

31 | 32 |

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 33 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR 34 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS 35 | AGREEMENT.

36 | 37 |

1. DEFINITIONS

38 | 39 |

"Contribution" means:

40 | 41 |

a) in the case of the initial Contributor, the initial 42 | code and documentation distributed under this Agreement, and

43 |

b) in the case of each subsequent Contributor:

44 |

i) changes to the Program, and

45 |

ii) additions to the Program;

46 |

where such changes and/or additions to the Program 47 | originate from and are distributed by that particular Contributor. A 48 | Contribution 'originates' from a Contributor if it was added to the 49 | Program by such Contributor itself or anyone acting on such 50 | Contributor's behalf. Contributions do not include additions to the 51 | Program which: (i) are separate modules of software distributed in 52 | conjunction with the Program under their own license agreement, and (ii) 53 | are not derivative works of the Program.

54 | 55 |

"Contributor" means any person or entity that distributes 56 | the Program.

57 | 58 |

"Licensed Patents" mean patent claims licensable by a 59 | Contributor which are necessarily infringed by the use or sale of its 60 | Contribution alone or when combined with the Program.

61 | 62 |

"Program" means the Contributions distributed in accordance 63 | with this Agreement.

64 | 65 |

"Recipient" means anyone who receives the Program under 66 | this Agreement, including all Contributors.

67 | 68 |

2. GRANT OF RIGHTS

69 | 70 |

a) Subject to the terms of this Agreement, each 71 | Contributor hereby grants Recipient a non-exclusive, worldwide, 72 | royalty-free copyright license to reproduce, prepare derivative works 73 | of, publicly display, publicly perform, distribute and sublicense the 74 | Contribution of such Contributor, if any, and such derivative works, in 75 | source code and object code form.

76 | 77 |

b) Subject to the terms of this Agreement, each 78 | Contributor hereby grants Recipient a non-exclusive, worldwide, 79 | royalty-free patent license under Licensed Patents to make, use, sell, 80 | offer to sell, import and otherwise transfer the Contribution of such 81 | Contributor, if any, in source code and object code form. This patent 82 | license shall apply to the combination of the Contribution and the 83 | Program if, at the time the Contribution is added by the Contributor, 84 | such addition of the Contribution causes such combination to be covered 85 | by the Licensed Patents. The patent license shall not apply to any other 86 | combinations which include the Contribution. No hardware per se is 87 | licensed hereunder.

88 | 89 |

c) Recipient understands that although each Contributor 90 | grants the licenses to its Contributions set forth herein, no assurances 91 | are provided by any Contributor that the Program does not infringe the 92 | patent or other intellectual property rights of any other entity. Each 93 | Contributor disclaims any liability to Recipient for claims brought by 94 | any other entity based on infringement of intellectual property rights 95 | or otherwise. As a condition to exercising the rights and licenses 96 | granted hereunder, each Recipient hereby assumes sole responsibility to 97 | secure any other intellectual property rights needed, if any. For 98 | example, if a third party patent license is required to allow Recipient 99 | to distribute the Program, it is Recipient's responsibility to acquire 100 | that license before distributing the Program.

101 | 102 |

d) Each Contributor represents that to its knowledge it 103 | has sufficient copyright rights in its Contribution, if any, to grant 104 | the copyright license set forth in this Agreement.

105 | 106 |

3. REQUIREMENTS

107 | 108 |

A Contributor may choose to distribute the Program in object code 109 | form under its own license agreement, provided that:

110 | 111 |

a) it complies with the terms and conditions of this 112 | Agreement; and

113 | 114 |

b) its license agreement:

115 | 116 |

i) effectively disclaims on behalf of all Contributors 117 | all warranties and conditions, express and implied, including warranties 118 | or conditions of title and non-infringement, and implied warranties or 119 | conditions of merchantability and fitness for a particular purpose;

120 | 121 |

ii) effectively excludes on behalf of all Contributors 122 | all liability for damages, including direct, indirect, special, 123 | incidental and consequential damages, such as lost profits;

124 | 125 |

iii) states that any provisions which differ from this 126 | Agreement are offered by that Contributor alone and not by any other 127 | party; and

128 | 129 |

iv) states that source code for the Program is available 130 | from such Contributor, and informs licensees how to obtain it in a 131 | reasonable manner on or through a medium customarily used for software 132 | exchange.

133 | 134 |

When the Program is made available in source code form:

135 | 136 |

a) it must be made available under this Agreement; and

137 | 138 |

b) a copy of this Agreement must be included with each 139 | copy of the Program.

140 | 141 |

Contributors may not remove or alter any copyright notices contained 142 | within the Program.

143 | 144 |

Each Contributor must identify itself as the originator of its 145 | Contribution, if any, in a manner that reasonably allows subsequent 146 | Recipients to identify the originator of the Contribution.

147 | 148 |

4. COMMERCIAL DISTRIBUTION

149 | 150 |

Commercial distributors of software may accept certain 151 | responsibilities with respect to end users, business partners and the 152 | like. While this license is intended to facilitate the commercial use of 153 | the Program, the Contributor who includes the Program in a commercial 154 | product offering should do so in a manner which does not create 155 | potential liability for other Contributors. Therefore, if a Contributor 156 | includes the Program in a commercial product offering, such Contributor 157 | ("Commercial Contributor") hereby agrees to defend and 158 | indemnify every other Contributor ("Indemnified Contributor") 159 | against any losses, damages and costs (collectively "Losses") 160 | arising from claims, lawsuits and other legal actions brought by a third 161 | party against the Indemnified Contributor to the extent caused by the 162 | acts or omissions of such Commercial Contributor in connection with its 163 | distribution of the Program in a commercial product offering. The 164 | obligations in this section do not apply to any claims or Losses 165 | relating to any actual or alleged intellectual property infringement. In 166 | order to qualify, an Indemnified Contributor must: a) promptly notify 167 | the Commercial Contributor in writing of such claim, and b) allow the 168 | Commercial Contributor to control, and cooperate with the Commercial 169 | Contributor in, the defense and any related settlement negotiations. The 170 | Indemnified Contributor may participate in any such claim at its own 171 | expense.

172 | 173 |

For example, a Contributor might include the Program in a commercial 174 | product offering, Product X. That Contributor is then a Commercial 175 | Contributor. If that Commercial Contributor then makes performance 176 | claims, or offers warranties related to Product X, those performance 177 | claims and warranties are such Commercial Contributor's responsibility 178 | alone. Under this section, the Commercial Contributor would have to 179 | defend claims against the other Contributors related to those 180 | performance claims and warranties, and if a court requires any other 181 | Contributor to pay any damages as a result, the Commercial Contributor 182 | must pay those damages.

183 | 184 |

5. NO WARRANTY

185 | 186 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 187 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 188 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 189 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 190 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 191 | responsible for determining the appropriateness of using and 192 | distributing the Program and assumes all risks associated with its 193 | exercise of rights under this Agreement , including but not limited to 194 | the risks and costs of program errors, compliance with applicable laws, 195 | damage to or loss of data, programs or equipment, and unavailability or 196 | interruption of operations.

197 | 198 |

6. DISCLAIMER OF LIABILITY

199 | 200 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT 201 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 202 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 203 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 204 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 205 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 206 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 207 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

208 | 209 |

7. GENERAL

210 | 211 |

If any provision of this Agreement is invalid or unenforceable under 212 | applicable law, it shall not affect the validity or enforceability of 213 | the remainder of the terms of this Agreement, and without further action 214 | by the parties hereto, such provision shall be reformed to the minimum 215 | extent necessary to make such provision valid and enforceable.

216 | 217 |

If Recipient institutes patent litigation against any entity 218 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 219 | Program itself (excluding combinations of the Program with other 220 | software or hardware) infringes such Recipient's patent(s), then such 221 | Recipient's rights granted under Section 2(b) shall terminate as of the 222 | date such litigation is filed.

223 | 224 |

All Recipient's rights under this Agreement shall terminate if it 225 | fails to comply with any of the material terms or conditions of this 226 | Agreement and does not cure such failure in a reasonable period of time 227 | after becoming aware of such noncompliance. If all Recipient's rights 228 | under this Agreement terminate, Recipient agrees to cease use and 229 | distribution of the Program as soon as reasonably practicable. However, 230 | Recipient's obligations under this Agreement and any licenses granted by 231 | Recipient relating to the Program shall continue and survive.

232 | 233 |

Everyone is permitted to copy and distribute copies of this 234 | Agreement, but in order to avoid inconsistency the Agreement is 235 | copyrighted and may only be modified in the following manner. The 236 | Agreement Steward reserves the right to publish new versions (including 237 | revisions) of this Agreement from time to time. No one other than the 238 | Agreement Steward has the right to modify this Agreement. The Eclipse 239 | Foundation is the initial Agreement Steward. The Eclipse Foundation may 240 | assign the responsibility to serve as the Agreement Steward to a 241 | suitable separate entity. Each new version of the Agreement will be 242 | given a distinguishing version number. The Program (including 243 | Contributions) may always be distributed subject to the version of the 244 | Agreement under which it was received. In addition, after a new version 245 | of the Agreement is published, Contributor may elect to distribute the 246 | Program (including its Contributions) under the new version. Except as 247 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no 248 | rights or licenses to the intellectual property of any Contributor under 249 | this Agreement, whether expressly, by implication, estoppel or 250 | otherwise. All rights in the Program not expressly granted under this 251 | Agreement are reserved.

252 | 253 |

This Agreement is governed by the laws of the State of New York and 254 | the intellectual property laws of the United States of America. No party 255 | to this Agreement will bring a legal action under this Agreement more 256 | than one year after the cause of action arose. Each party waives its 257 | rights to a jury trial in any resulting litigation.

258 | 259 | 260 | 261 | 262 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/build/tasks/test_pom.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.tools.build.tasks.test-pom 10 | (:require 11 | [clojure.test :refer :all :as test] 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.zip :as zip] 15 | [clojure.data.xml :as xml] 16 | [clojure.tools.build.api :as api] 17 | [clojure.tools.build.tasks.write-pom :as write-pom] 18 | [clojure.tools.build.test-util :refer :all]) 19 | (:import 20 | [java.io File InputStream] 21 | [java.util Properties] 22 | [clojure.lang ExceptionInfo])) 23 | 24 | (set! *warn-on-reflection* true) 25 | 26 | (xml/alias-uri 'pom "http://maven.apache.org/POM/4.0.0") 27 | 28 | (defn read-xml 29 | [^File f] 30 | (when (.exists f) 31 | (#'write-pom/parse-xml (jio/reader f)))) 32 | 33 | (defn xml-path-val 34 | [root tag-path] 35 | (let [z (zip/zipper xml/element? :content nil root)] 36 | (loop [[tag & more-tags :as tags] tag-path, parent z, child (zip/down z)] 37 | (if child 38 | (let [node (zip/node child)] 39 | (if (= tag (:tag node)) 40 | (if (seq more-tags) 41 | (recur more-tags child (zip/down child)) 42 | (:content node)) 43 | (recur tags parent (zip/right child)))) 44 | nil)))) 45 | 46 | (defn read-props 47 | [^File f] 48 | (let [props (Properties.)] 49 | (when (.exists f) 50 | (doto props 51 | (.load ^InputStream (jio/input-stream f)))))) 52 | 53 | (deftest test-new-pom 54 | (with-test-dir "test-data/p1" 55 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 56 | (api/delete {:path "target"}) 57 | (api/write-pom {;; NO :src-pom 58 | :lib 'test/p1 59 | :version "1.2.3" 60 | :class-dir "target/classes" 61 | :src-dirs ["src"] 62 | :resource-dirs ["resources"] 63 | :basis (api/create-basis nil) 64 | :scm {:tag "v1.2.3" :url "https://github.com/example/p1"}}) 65 | (let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p1")) 66 | pom-out (jio/file pom-dir "pom.xml") 67 | pom (read-xml pom-out) 68 | prop-out (jio/file pom-dir "pom.properties") 69 | props (read-props prop-out)] 70 | ;; check xml out 71 | (is (.exists pom-out)) 72 | (are [path val] (= val (xml-path-val pom path)) 73 | [::pom/packaging] ["jar"] 74 | [::pom/groupId] ["test"] 75 | [::pom/artifactId] ["p1"] 76 | [::pom/version] ["1.2.3"] 77 | [::pom/name] ["p1"] 78 | [::pom/build ::pom/sourceDirectory] ["src"] 79 | [::pom/build ::pom/resources ::pom/resource ::pom/directory] ["resources"] 80 | [::pom/scm ::pom/tag] ["v1.2.3"] 81 | [::pom/scm ::pom/url] ["https://github.com/example/p1"]) 82 | (is (= 2 (count (xml-path-val pom [::pom/dependencies])))) 83 | (is (= 1 (count (xml-path-val pom [::pom/repositories])))) 84 | ;; check properties out 85 | (is (.exists prop-out)) 86 | (is (submap? {"groupId" "test", "artifactId" "p1", "version" "1.2.3"} props))))) 87 | 88 | (deftest test-update-existing-pom 89 | (with-test-dir "test-data/p2" 90 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 91 | (api/delete {:path "target"}) 92 | (api/write-pom {:lib 'test/p2 93 | :version "1.2.3" 94 | :class-dir "target/classes" 95 | :src-dirs ["src"] 96 | :src-pom "pom.xml" 97 | :resource-dirs ["resources"] 98 | :basis (api/create-basis nil) 99 | :scm {:tag "v1.2.3" 100 | :url "https://github.com/example/p1"}}) 101 | (let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p2")) 102 | pom-out (jio/file pom-dir "pom.xml") 103 | pom (read-xml pom-out) 104 | prop-out (jio/file pom-dir "pom.properties") 105 | props (read-props prop-out)] 106 | ;; check xml out 107 | (is (.exists pom-out)) 108 | (are [path val] (= val (xml-path-val pom path)) 109 | [::pom/packaging] ["jar"] 110 | [::pom/groupId] ["test"] 111 | [::pom/artifactId] ["p2"] 112 | [::pom/version] ["1.2.3"] 113 | [::pom/name] ["p2"] 114 | [::pom/build ::pom/sourceDirectory] ["src"] 115 | [::pom/build ::pom/resources ::pom/resource ::pom/directory] ["resources"] 116 | [::pom/scm ::pom/tag] ["v1.2.3"]) 117 | (is (= 2 (count (xml-path-val pom [::pom/dependencies])))) 118 | (is (= 1 (count (xml-path-val pom [::pom/repositories])))) 119 | ;; check properties out 120 | (is (.exists prop-out)) 121 | (is (submap? {"groupId" "test", "artifactId" "p2", "version" "1.2.3"} props))))) 122 | 123 | (deftest test-ignore-existing-pom 124 | ;; verify existing scm info is preserved if src-pom is supplied: 125 | (with-test-dir "test-data/p4" 126 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 127 | (api/delete {:path "target"}) 128 | (api/write-pom {:lib 'test/p4 129 | :version "1.2.3" 130 | :class-dir "target/classes" 131 | :src-dirs ["src"] 132 | :src-pom "pom.xml" 133 | :resource-dirs ["resources"] 134 | :basis (api/create-basis nil)}) 135 | (let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p4")) 136 | pom-out (jio/file pom-dir "pom.xml") 137 | pom (read-xml pom-out) 138 | prop-out (jio/file pom-dir "pom.properties") 139 | props (read-props prop-out)] 140 | ;; check xml out 141 | (is (.exists pom-out)) 142 | (are [path val] (= val (xml-path-val pom path)) 143 | [::pom/packaging] ["jar"] 144 | [::pom/groupId] ["test"] 145 | [::pom/artifactId] ["p4"] 146 | [::pom/version] ["1.2.3"] 147 | [::pom/name] ["p4"] 148 | [::pom/build ::pom/sourceDirectory] ["src"] 149 | [::pom/build ::pom/resources ::pom/resource ::pom/directory] ["resources"] 150 | [::pom/scm ::pom/tag] ["HEAD"] 151 | [::pom/scm ::pom/url] ["git@github.com:clojure/tools.build.git"]) 152 | (is (= 2 (count (xml-path-val pom [::pom/dependencies])))) 153 | (is (= 1 (count (xml-path-val pom [::pom/repositories])))) 154 | ;; check properties out 155 | (is (.exists prop-out)) 156 | (is (submap? {"groupId" "test", "artifactId" "p4", "version" "1.2.3"} props)))) 157 | ;; verify no scm info is present if src-pom is :none: 158 | (with-test-dir "test-data/p4" 159 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 160 | (api/delete {:path "target"}) 161 | (api/write-pom {:lib 'test/p4 162 | :version "1.2.3" 163 | :class-dir "target/classes" 164 | :src-dirs ["src"] 165 | :src-pom :none 166 | :resource-dirs ["resources"] 167 | :basis (api/create-basis nil)}) 168 | (let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p4")) 169 | pom-out (jio/file pom-dir "pom.xml") 170 | pom (read-xml pom-out) 171 | prop-out (jio/file pom-dir "pom.properties") 172 | props (read-props prop-out)] 173 | ;; check xml out 174 | (is (.exists pom-out)) 175 | (are [path val] (= val (xml-path-val pom path)) 176 | [::pom/packaging] ["jar"] 177 | [::pom/groupId] ["test"] 178 | [::pom/artifactId] ["p4"] 179 | [::pom/version] ["1.2.3"] 180 | [::pom/name] ["p4"] 181 | [::pom/build ::pom/sourceDirectory] ["src"] 182 | [::pom/build ::pom/resources ::pom/resource ::pom/directory] ["resources"] 183 | [::pom/scm ::pom/tag] nil 184 | [::pom/scm ::pom/url] nil) 185 | (is (= 2 (count (xml-path-val pom [::pom/dependencies])))) 186 | (is (= 1 (count (xml-path-val pom [::pom/repositories])))) 187 | ;; check properties out 188 | (is (.exists prop-out)) 189 | (is (submap? {"groupId" "test", "artifactId" "p4", "version" "1.2.3"} props))))) 190 | 191 | ;; check that optional deps are marked optional 192 | (deftest test-optional 193 | (with-test-dir "test-data/p3" 194 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 195 | (api/delete {:path "target"}) 196 | (api/write-pom {:lib 'test/p3 197 | :version "1.2.3" 198 | :class-dir "target/classes" 199 | :src-dirs ["src"] 200 | :src-pom "pom.xml" 201 | :resource-dirs ["resources"] 202 | :basis (api/create-basis nil)}) 203 | (let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p3")) 204 | pom-out (jio/file pom-dir "pom.xml")] 205 | (is (.exists pom-out)) 206 | (let [generated (slurp pom-out)] 207 | (is (str/includes? generated "core.async")) 208 | (is (str/includes? generated "true")))))) 209 | 210 | ;; check that supplying an empty repo map removes repos from generated pom 211 | (deftest test-omit-repos 212 | (with-test-dir "test-data/p1" 213 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 214 | (api/delete {:path "target"}) 215 | (api/write-pom {:lib 'test/p1 216 | :version "1.2.3" 217 | :class-dir "target/classes" 218 | :src-dirs ["src"] 219 | :src-pom "pom.xml" 220 | :resource-dirs ["resources"] 221 | :repos {} ;; replace repo map from deps.edn 222 | :basis (api/create-basis nil)}) 223 | (let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p1")) 224 | pom-out (jio/file pom-dir "pom.xml")] 225 | (is (.exists pom-out)) 226 | (let [generated (slurp pom-out)] 227 | (is (not (str/includes? generated "clojars"))))))) 228 | 229 | ;; check that :extra-deps in basis aliases are reflected in pom deps 230 | (deftest test-extra-deps 231 | (with-test-dir "test-data/p1" 232 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 233 | (api/delete {:path "target"}) 234 | (api/write-pom {:lib 'test/p1 235 | :version "1.2.3" 236 | :class-dir "target/classes" 237 | :src-dirs ["src"] 238 | :src-pom "pom.xml" 239 | :resource-dirs ["resources"] 240 | :basis (api/create-basis '{:extra {:aliases {:ex {:extra-deps {org.clojure/data.json {:mvn/version "2.3.1"}}}}} 241 | :aliases [:ex]})}) 242 | (let [pom-dir (jio/file (project-path "target/classes/META-INF/maven/test/p1")) 243 | pom-out (jio/file pom-dir "pom.xml")] 244 | (is (.exists pom-out)) 245 | (let [generated (slurp pom-out)] 246 | (is (str/includes? generated "data.json")))))) 247 | 248 | (deftest test-pom-path 249 | (is (= (jio/file "META-INF/maven/a.b/c/pom.xml") (jio/file (api/pom-path {:lib 'a.b/c})))) 250 | 251 | (let [prior api/*project-root*] 252 | (api/set-project-root! ".") 253 | (is (= (jio/file "./foo/META-INF/maven/a.b/c/pom.xml") 254 | (jio/file (api/pom-path {:class-dir "foo" :lib 'a.b/c})))) 255 | (api/set-project-root! prior))) 256 | 257 | (deftest test-validate-lib 258 | (with-test-dir "test-data/p1" 259 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 260 | (api/delete {:path "target"}) 261 | (try 262 | (api/write-pom {:lib 'unqualified-lib ;; invalid 263 | :version "1.2.3" 264 | :class-dir "target/classes" 265 | :src-dirs ["src"] 266 | :src-pom "pom.xml" 267 | :resource-dirs ["resources"] 268 | :basis (api/create-basis nil)}) 269 | (catch ExceptionInfo e 270 | (let [m (ex-message e)] 271 | (is (str/includes? m ":lib")) 272 | (is (str/includes? m "unqualified-lib")) 273 | (is (str/includes? m "qualified-ident?"))))))) 274 | 275 | (deftest test-target 276 | (with-test-dir "test-data/p1" 277 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 278 | (api/delete {:path "target"}) 279 | (api/write-pom {:lib 'test/p1 280 | :version "1.2.3" 281 | :target "target/output-pom.xml" 282 | :src-dirs ["src"] 283 | :resource-dirs ["resources"] 284 | :basis (api/create-basis nil)}) 285 | (is (.exists (jio/file (project-path "target/output-pom.xml")))))) 286 | 287 | (deftest test-target-or-class-dir 288 | (with-test-dir "test-data/p1" 289 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 290 | (api/delete {:path "target"}) 291 | (try 292 | (api/write-pom {:lib 'test/p1 293 | :version "1.2.3" 294 | ;; NO :target or :class-dir 295 | :src-dirs ["src"] 296 | :src-pom "pom.xml" 297 | :resource-dirs ["resources"] 298 | :basis (api/create-basis nil)}) 299 | (is false) 300 | (catch Throwable t 301 | (is true))))) 302 | 303 | (deftest test-pom-data 304 | (with-test-dir "test-data/p1" 305 | (api/set-project-root! (.getAbsolutePath *test-dir*)) 306 | (api/delete {:path "target"}) 307 | (api/write-pom {:lib 'test/p1 308 | :version "1.2.3" 309 | :target "target" 310 | :src-dirs ["src"] 311 | :resource-dirs ["resources"] 312 | :basis (api/create-basis nil) 313 | :pom-data [[:licenses 314 | [:license 315 | [:name "Apache-2.0"] 316 | [:url "https://www.apache.org/licenses/LICENSE-2.0.txt"] 317 | [:distribution "repo"] 318 | [:comments "OSS license"]]] 319 | [:foo 320 | [:bar "hello"]]]}) 321 | (is (.exists (jio/file (project-path "target/pom.xml")))) 322 | (let [written-pom (slurp (project-path "target/pom.xml"))] 323 | (is (str/includes? written-pom "Apache-2.0")) 324 | (is (str/includes? written-pom "hello"))))) 325 | 326 | (comment 327 | (run-tests) 328 | (test-validate-lib) 329 | ) 330 | -------------------------------------------------------------------------------- /docs/clojure.tools.build.api.html: -------------------------------------------------------------------------------- 1 | 3 | clojure.tools.build.api documentation

clojure.tools.build.api

*project-root*

dynamic

Project root path, defaults to current directory.
  4 | Use `resolve-path` to resolve relative paths in terms of the *project-root*.
  5 | Use `set-project-root!` to override the default for all tasks.

compile-clj

(compile-clj params)
Compile Clojure source to classes in :class-dir.
  6 | 
  7 | Clojure source files are found in :basis :paths by default, or override with :src-dirs.
  8 | 
  9 | Namespaces and order of compilation are one of:
 10 |   * :ns-compile - compile these namespaces, in this order
 11 |   * :sort - find all namespaces in source files and use either :topo (default)
 12 |             or :bfs to order them for compilation
 13 | 
 14 | Options:
 15 |   :basis - required, basis to use when compiling
 16 |   :class-dir - required, dir to write classes, will be created if needed
 17 |   :src-dirs - coll of Clojure source dirs, used to find all Clojure nses to compile
 18 |   :ns-compile - coll of specific namespace symbols to compile
 19 |   :sort - :topo (default) or :bfs for breadth-first search
 20 |   :compile-opts - map of Clojure compiler options:
 21 |     {:disable-locals-clearing false
 22 |      :elide-meta [:doc :file :line ...]
 23 |      :direct-linking false}
 24 |   :bindings - map of Var to value to be set during compilation, for example:
 25 |     {#'clojure.core/*assert* false
 26 |      #'clojure.core/*warn-on-reflection* true}
 27 |   :filter-nses - coll of symbols representing a namespace prefix to include
 28 | 
 29 | Additional options flow to the forked process doing the compile:
 30 |   :java-cmd - Java command, default = $JAVA_CMD or 'java' on $PATH, or $JAVA_HOME/bin/java
 31 |   :java-opts - coll of string jvm opts
 32 |   :use-cp-file - one of:
 33 |                    :auto (default) - use only if os=windows && Java >= 9 && command length >= 8k
 34 |                    :always - always write classpath to temp file and include
 35 |                    :never - never write classpath to temp file (pass on command line)
 36 |   :out - one of :inherit :capture :write :append :ignore
 37 |   :err - one of :inherit :capture :write :append :ignore
 38 |   :out-file - file path to write if :out is :write or :append
 39 |   :err-file - file path to write if :err is :write or :append
 40 | 
 41 | Returns nil, or if needed a map with keys:
 42 |   :out captured-out
 43 |   :err captured-err

copy-dir

(copy-dir params)
Copy the contents of the src-dirs to the target-dir, optionally do text replacement.
 44 | Returns nil.
 45 | 
 46 | Globs are wildcard patterns for specifying sets of files in a directory
 47 | tree, as specified in the glob syntax of java.nio.file.FileSystem:
 48 | https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String)
 49 | 
 50 | Options:
 51 |   :target-dir - required, dir to write files, will be created if it doesn't exist
 52 |   :src-dirs   - required, coll of dirs to copy from
 53 |   :include    - glob of files to include, default = "**"
 54 |   :ignores    - collection of ignore regex patterns (applied only to file names),
 55 |                 see clojure.tools.build.tasks.copy/default-ignores for defaults
 56 |   :replace    - map of source to replacement string in files
 57 |   :non-replaced-exts - coll of extensions to skip when replacing (still copied)
 58 |                 default = ["jpg" "jpeg" "png" "gif" "bmp"]

copy-file

(copy-file {:keys [src target], :as params})
Copy one file from source to target, creating target dirs if needed.
 59 | Returns nil.
 60 | 
 61 | Options:
 62 |   :src - required, source path
 63 |   :target - required, target path

create-basis

(create-basis)(create-basis params)
Create a basis from a set of deps sources and a set of aliases. By default, use
 64 | root, user, and project deps and no aliases (essentially the same classpath you
 65 | get by default from the Clojure CLI).
 66 | 
 67 | Each dep source value can be :standard, a string path, a deps edn map, or nil.
 68 | Sources are merged in the order - :root, :user, :project, :extra.
 69 | 
 70 | Options (note, paths resolved via *project-root*):
 71 |   :dir     - directory root path, defaults to current directory
 72 |   :root    - dep source, default = :standard
 73 |   :user    - dep source, default = nil (for reproducibility, not included)
 74 |   :project - dep source, default = :standard ("./deps.edn")
 75 |   :extra   - dep source, default = nil
 76 |   :aliases - coll of aliases of argmaps to apply to subprocesses
 77 | 
 78 | Returns a runtime basis, which is the initial merged deps edn map plus these keys:
 79 |  :resolve-args - the resolve args passed in, if any
 80 |  :classpath-args - the classpath args passed in, if any
 81 |  :libs - lib map, per resolve-deps
 82 |  :classpath - classpath map per make-classpath-map
 83 |  :classpath-roots - vector of paths in classpath order

delete

(delete {:keys [path], :as params})
Delete file or directory recursively, if it exists. Returns nil.
 84 | 
 85 | Options:
 86 |   :path - required, path to file or directory

git-count-revs

(git-count-revs {:keys [dir git-command path], :or {git-command "git"}, :as params})
Shells out to git and returns count of commits on this branch:
 87 |   git rev-list HEAD --count
 88 | 
 89 | Options:
 90 |   :dir - dir to invoke this command from, default = current directory
 91 |   :git-command - git command to use, default = "git"
 92 |   :path - path to count commits for relative to dir

git-process

(git-process params)
Run git process in the specified dir using git-command with git-args (which should not
 93 | start with "git"). git-args may either be a string (split on whitespace) or a vector
 94 | of strings. By default, stdout is captured, trimmed, and returned.
 95 | 
 96 | Options:
 97 |   :dir - dir to invoke this command from, default = current directory
 98 |   :git-command - git command to use, default = "git"
 99 |   :git-args - required, coll of git-arg strings forming a command line OR
100 |               a string (do not use if args may have embedded spaces)
101 |   :capture - :out (default) or :err, else nothing
102 | 
103 | Examples:
104 |   (api/git-process {:git-args "rev-list HEAD --count"})
105 |   (api/git-process {:git-args "branch --show-current"})
106 |   (api/git-process {:git-args "rev-parse --short HEAD"})
107 |   (api/git-process {:git-args "push", :capture nil})

install

(install params)
Install pom and jar to local Maven repo.
108 | Returns nil.
109 | 
110 | Options:
111 |   :basis - required, used for :mvn/local-repo
112 |   :lib - required, lib symbol
113 |   :classifier - classifier string, if needed
114 |   :version - required, string version
115 |   :jar-file - required, path to jar file
116 |   :class-dir - required, used to find the pom file

jar

(jar params)
Create jar file containing contents of class-dir. Use main in the manifest
117 | if provided. Returns nil.
118 | 
119 | Options:
120 |   :class-dir - required, dir to include in jar
121 |   :jar-file - required, jar to write
122 |   :main - main class symbol
123 |   :manifest - map of manifest attributes, merged last over defaults+:main

java-command

(java-command params)
Create Java command line args. The classpath will be the combination of
124 | :cp followed by the classpath from the basis, both are optional.
125 | 
126 | Note that 'java-command' will NOT resolve any relative paths from basis
127 | or cp in terms of *project-root*, you will get a classpath with the same
128 | relative paths. 'process' (if run with this output), will run in the
129 | context of the *project-root* directory.
130 | 
131 | Options:
132 |   :java-cmd - Java command, default = $JAVA_CMD or 'java' on $PATH, or $JAVA_HOME/bin/java
133 |   :cp - coll of string classpath entries, used first (if provided)
134 |   :basis - runtime basis used for classpath and jvm opts from aliases, used last (if provided)
135 |   :java-opts - coll of string jvm opts
136 |   :main - required, main class symbol
137 |   :main-args - coll of main class args
138 |   :use-cp-file - one of:
139 |                    :auto (default) - use only if os=windows && Java >= 9 && command length >= 8k
140 |                    :always - always write classpath to temp file and include
141 |                    :never - never write classpath to temp file (pass on command line)
142 | 
143 | Returns map suitable for passing to 'process' with keys:
144 |   :command-args - coll of command arg strings

javac

(javac params)
Compile Java source to classes. Returns nil.
145 | 
146 | Options:
147 |   :src-dirs - required, coll of Java source dirs
148 |   :class-dir - required, dir to write classes, will be created if needed
149 |   :basis - classpath basis to use when compiling
150 |   :javac-opts - coll of string opts, like ["-source" "8" "-target" "8"]

pom-path

(pom-path params)
Calculate path to pom.xml in jar meta (same path used by write-pom).
151 | Relative path in jar is:
152 |   META-INF/maven/<groupId>/<artifactId>/pom.xml
153 | 
154 | If :class-dir provided, return path will start with resolved class-dir
155 | (which may be either absolute or relative), otherwise just relative
156 | path in jar.
157 | 
158 | Options:
159 |   :lib - required, used to form the relative path in jar to pom.xml
160 |   :class-dir - optional, if provided will be resolved and form the root of the path

process

(process params)
Exec the command made from command-args, redirect out and err as directed,
161 | and return {:exit exit-code, :out captured-out, :err captured-err}.
162 | 
163 | Options:
164 |   :command-args - required, coll of string args
165 |   :dir - directory to run the command from, default *project-root*
166 |   :out - one of :inherit :capture :write :append :ignore
167 |   :err - one of :inherit :capture :write :append :ignore
168 |   :out-file - file path to write if :out is :write or :append
169 |   :err-file - file path to write if :err is :write or :append
170 |   :env - map of environment variables to set
171 | 
172 | The :out and :err input flags take one of the following options:
173 |   :inherit - inherit the stream and write the subprocess io to this process's stream (default)
174 |   :capture - capture the stream to a string and return it
175 |   :write - write to :out-file or :err-file
176 |   :append - append to :out-file or :err-file
177 |   :ignore - ignore the stream

resolve-path

(resolve-path path)
If path is absolute or root-path is nil then return path,
178 | otherwise resolve relative to *project-root*.

set-project-root!

(set-project-root! root)
Set *project-root* dir (default is ".")
179 | 

uber

(uber params)
Create uberjar file. An uberjar is a self-contained jar file containing
180 | both the project contents AND the contents of all dependencies.
181 | 
182 | The project contents are represented by the class-dir. Use other tasks to
183 | put Clojure source, class files, a pom file, or other resources in the
184 | class-dir. In particular, see the copy-dir, write-pom, compile-clj, and
185 | javac tasks.
186 | 
187 | The dependencies are pulled from the basis. All transitive deps will be
188 | included. Dependency jars are expanded for inclusion in the uberjar.
189 | Use :exclude to exclude specific paths from the expanded deps. Use
190 | conflict-handlers to handle conflicts that may occur if two dependency
191 | jar files include a file at the same path. See below for more detail.
192 | 
193 | If a main class or manifest are provided, those are put in the uberjar
194 | META-INF/MANIFEST.MF file. Providing a main allows the jar to be
195 | invoked with java -jar.
196 | 
197 | Returns nil.
198 | 
199 | Options:
200 |   :uber-file - required, uber jar file to create
201 |   :class-dir - required, local class dir to include
202 |   :basis - used to pull dep jars
203 |   :main - main class symbol
204 |   :manifest - map of manifest attributes, merged last over defaults + :main
205 |   :exclude - coll of string patterns (regex) to exclude from deps
206 |   :conflict-handlers - map of string pattern (regex) to built-in handlers,
207 |                        symbols to eval, or function instances
208 | 
209 | When combining jar files into an uber jar, multiple jars may contain a file
210 | at the same path. The conflict handlers are a map of string regex pattern
211 | to:
212 |   a keyword (to use a built-in handler) or
213 |   a symbol (to resolve and invoke) or
214 |   a function instance
215 | The special key `:default` specifies the default behavior if not matched.
216 | 
217 | Conflict handler signature (fn [params]) => effect-map:
218 |   params:
219 |     :path     - String, path in uber jar, matched by regex
220 |     :in       - InputStream to incoming file (see stream->string if needed)
221 |     :existing - File, existing File at path
222 |     :lib      - symbol, lib source for incoming conflict
223 |     :state    - map, available for retaining state during uberjar process
224 | 
225 | Handler should return effect-map with optional keys:
226 |   :state      - updated state map
227 |   :write      - map of string path to map of :string (string) or
228 |                 :stream (InputStream) to write and optional :append
229 |                 flag. Omit if no files to write.
230 | 
231 | Available built-in conflict handlers:
232 |   :ignore - don't do anything (default)
233 |   :overwrite - overwrite (replaces prior file)
234 |   :append - append the file with a blank line separator
235 |   :append-dedupe - append the file but dedupe appended sections
236 |   :data-readers - merge data_readers.clj
237 |   :warn - print a warning
238 |   :error - throw an error
239 | 
240 | Default conflict handlers map:
241 |   {"^data_readers.clj[c]?$" :data-readers
242 |    "^META-INF/services/.*" :append
243 |    "(?i)^(META-INF/)?(COPYRIGHT|NOTICE|LICENSE)(\\.(txt|md))?$" :append-dedupe
244 |    :default :ignore}

unzip

(unzip params)
Unzip zip file to target-dir. Returns nil.
245 | 
246 | Options:
247 |   :zip-file - required, zip file to unzip
248 |   :target-dir - required, directory to unzip in

with-project-root

macro

(with-project-root path & forms)
Execute forms in a bound project path (string) other than the default (".")
249 | 

write-file

(write-file {:keys [path content string opts], :as params})
Writes a file at path, will create parent dirs if needed. Returns nil.
250 | File contents may be specified either with :content (for data, that
251 | will be pr-str'ed) or with :string for the string to write. If
252 | neither is specified, an empty file is created (like touch).
253 | 
254 | Options:
255 |   :path - required, file path
256 |   :content - val to write, will pr-str
257 |   :string - string to write
258 |   :opts - coll of writer opts like :append and :encoding (per clojure.java.io)

write-pom

(write-pom params)
Write pom.xml and pom.properties files to the class dir under
259 | META-INF/maven/group-id/artifact-id/ (where Maven typically writes
260 | these files), or to target (exactly one of :class-dir and :target must
261 | be provided).
262 | 
263 | Optionally use :src-pom to provide a pom template (or a default will
264 | be generated from the provided attributes). The pom deps, dirs, and
265 | repos from the basis will replace those sections of the template. Note
266 | that the :src-pom template is not validated and should contain required
267 | elements such as modelVersion.
268 | 
269 | If a repos map is provided it supersedes the repos in the basis.
270 | 
271 | Returns nil.
272 | 
273 | Options:
274 |   :basis - required, used to pull deps, repos
275 |   :src-pom - source pom.xml to synchronize from, default = "./pom.xml"
276 |              may be :none to ignore any existing pom.xml file
277 |   :class-dir - root dir for writing pom files, created if needed
278 |   :target - file path to write pom if no :class-dir specified
279 |   :lib - required, project lib symbol
280 |   :version - required, project version
281 |   :scm - map of scm properties to write in pom
282 |          keys:  :connection, :developerConnection, :tag, :url
283 |          See: https://maven.apache.org/pom.html#SCM for details
284 |   :src-dirs - coll of src dirs
285 |   :resource-dirs - coll of resource dirs
286 |   :repos - map of repo name to repo config, replaces repos from deps.edn
287 |   :pom-data - vector of hiccup-style extra pom top elements to include when
288 |     :src-pom is :none or the source pom.xml does not exist:
289 |      [[:licenses
290 |        [:license
291 |         [:name "Eclipse Public License 1.0"]
292 |         [:url "https://opensource.org/license/epl-1-0/";]
293 |         [:distribution "repo"]]]
294 |       [:organization "Super Corp"]]
295 |     The pom-data MUST NOT include:
296 |       :modelVersion, :packaging, :groupId, :artifactId, :version, :name,
297 |       :deps, :repositories, :build, or :scm

zip

(zip params)
Create zip file containing contents of src dirs. Returns nil.
298 | 
299 | Options:
300 |   :src-dirs - required, coll of source directories to include in zip
301 |   :zip-file - required, zip file to create
--------------------------------------------------------------------------------