├── VERSION_TEMPLATE
├── .clj-kondo
└── config.edn
├── design
└── clojure-cli.graffle
├── .gitignore
├── .github
├── workflows
│ ├── snapshot.yml
│ ├── doc-build.yml
│ ├── release.yml
│ └── test.yml
└── PULL_REQUEST_TEMPLATE
├── src
├── test
│ └── clojure
│ │ └── clojure
│ │ └── tools
│ │ └── deps
│ │ ├── script
│ │ ├── test_parse.clj
│ │ └── test_make_classpath2.clj
│ │ ├── test_specs.clj
│ │ ├── util.clj
│ │ ├── util
│ │ ├── dir_test.clj
│ │ └── test_s3_transporter.clj
│ │ ├── gen
│ │ └── test_pom.clj
│ │ ├── extensions
│ │ ├── faken.clj
│ │ └── test_git.clj
│ │ └── test_deps.clj
└── main
│ ├── resources
│ └── clojure
│ │ └── tools
│ │ └── deps
│ │ ├── deps.edn
│ │ └── license-abbrev.edn
│ ├── clojure
│ └── clojure
│ │ └── tools
│ │ └── deps
│ │ ├── util
│ │ ├── concurrent.clj
│ │ ├── s3_aws_client.clj
│ │ ├── session.clj
│ │ ├── io.clj
│ │ ├── dir.clj
│ │ ├── s3_transporter.clj
│ │ └── maven.clj
│ │ ├── script
│ │ ├── parse.clj
│ │ ├── generate_manifest2.clj
│ │ ├── resolve_tags.clj
│ │ └── make_classpath2.clj
│ │ ├── extensions
│ │ ├── deps.clj
│ │ ├── local.clj
│ │ ├── pom.clj
│ │ ├── git.clj
│ │ └── maven.clj
│ │ ├── tool.clj
│ │ ├── tree.clj
│ │ ├── specs.clj
│ │ ├── extensions.clj
│ │ └── gen
│ │ └── pom.clj
│ └── java
│ └── clojure
│ └── tools
│ └── deps
│ └── util
│ └── S3TransporterFactory.java
├── script
└── version
├── CONTRIBUTING.md
├── deps.edn
├── README.md
├── pom.xml
└── LICENSE
/VERSION_TEMPLATE:
--------------------------------------------------------------------------------
1 | 0.27.GENERATED_VERSION
2 |
--------------------------------------------------------------------------------
/.clj-kondo/config.edn:
--------------------------------------------------------------------------------
1 | {:skip-comments true
2 | :linters
3 | {:unused-binding {:level :off}}}
4 |
--------------------------------------------------------------------------------
/design/clojure-cli.graffle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure/tools.deps/master/design/clojure-cli.graffle
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea/
3 | .calva/
4 | .lsp/
5 | target/
6 | .nrepl*
7 | .cpcache
8 | .lein*
9 | project.clj
10 | .clj-kondo/.cache
11 | test-out/
12 | .vscode/
13 | .clj-watson/
14 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.github/workflows/doc-build.yml:
--------------------------------------------------------------------------------
1 | name: Build API Docs
2 |
3 | permissions:
4 | contents: write
5 |
6 | on:
7 | workflow_dispatch:
8 |
9 | jobs:
10 | call-doc-build-workflow:
11 | uses: clojure/build.ci/.github/workflows/doc-build.yml@master
12 | with:
13 | project: clojure/tools.deps
14 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/script/test_parse.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.deps.script.test-parse
2 | (:require
3 | [clojure.test :refer [deftest is]]
4 | [clojure.tools.deps.script.parse :as parse]))
5 |
6 | (deftest test-parse-config
7 | (is (= {:paths ["x"]} (parse/parse-config "{:paths [\"x\"]}")))
8 | (is (= "deps.edn" (parse/parse-config "deps.edn"))))
--------------------------------------------------------------------------------
/script/version:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | version_template=`cat VERSION_TEMPLATE`
6 | commit_count=`git rev-list HEAD --count`
7 |
8 | if [[ "$version_template" =~ ^[0-9]+\.[0-9]+\.GENERATED_VERSION(-[a-zA-Z0-9]+)?$ ]]; then
9 |
10 | echo ${version_template/GENERATED_VERSION/$commit_count}
11 |
12 | else
13 | echo "Invalid version template string: $version_template" >&2
14 | exit -1
15 | fi
16 |
17 |
18 |
--------------------------------------------------------------------------------
/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/TDEPS
12 | [guidelines]: https://clojure.org/community/contrib_howto
13 |
--------------------------------------------------------------------------------
/src/main/resources/clojure/tools/deps/deps.edn:
--------------------------------------------------------------------------------
1 | {
2 | :paths ["src"]
3 |
4 | :deps {
5 | org.clojure/clojure {:mvn/version "1.12.4"}
6 | }
7 |
8 | :aliases {
9 | :deps {:replace-paths []
10 | :replace-deps {org.clojure/tools.deps.cli {:mvn/version "0.14.121"}}
11 | :ns-default clojure.tools.deps.cli.api
12 | :ns-aliases {help clojure.tools.deps.cli.help}}
13 | :test {:extra-paths ["test"]}
14 | }
15 |
16 | :mvn/repos {
17 | "central" {:url "https://repo1.maven.org/maven2/"}
18 | "clojars" {:url "https://repo.clojars.org/"}
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 | secrets: inherit
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | permissions:
4 | contents: read
5 |
6 | on: [push]
7 |
8 | jobs:
9 | test:
10 | strategy:
11 | matrix:
12 | os: [ubuntu-latest] # macOS-latest, windows-latest]
13 | java-version: ["8", "11", "17", "21", "25"]
14 | clojure-version: ["1.10.3", "1.11.1"]
15 | runs-on: ${{ matrix.os }}
16 | steps:
17 | - uses: actions/checkout@v5
18 | - name: Set up Java
19 | uses: actions/setup-java@v4
20 | with:
21 | java-version: ${{ matrix.java-version }}
22 | distribution: 'temurin'
23 | cache: 'maven'
24 | - name: Build with Maven
25 | run: mvn -ntp -B -Dclojure.version=${{ matrix.clojure-version }} clean test
26 |
--------------------------------------------------------------------------------
/.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 | - Contributing FAQ: https://clojure.org/dev
12 | - Signing the CA: https://clojure.org/dev/contributor_agreement
13 | - Creating Tickets: https://clojure.org/dev/creating_tickets
14 | - Developing Patches: https://clojure.org/dev/developing_patches
15 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/test_specs.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.deps.test-specs
2 | (:require
3 | [clojure.test :refer [deftest is]]
4 | [clojure.tools.deps.specs :as specs]))
5 |
6 |
7 | (deftest test-explain-deps
8 | (let [deps-map {:mvn/repos {"myrepo" {:url "https://repo1.maven.org/maven2/"
9 | :snapshots true}}}]
10 | (is (= (specs/explain-deps deps-map)
11 | "Found: true, expected: map?, in: [:mvn/repos \"myrepo\" 1 :snapshots]")))
12 |
13 | (let [deps-map {:tools/usage {:ns-default "some.ns"}}]
14 | (is (= (specs/explain-deps deps-map)
15 | "Found: \"some.ns\", expected: simple-symbol?, in: [:tools/usage :ns-default]"))))
16 |
17 | (comment
18 | (test-explain-deps)
19 | )
20 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/util.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.deps.util)
2 |
3 | (defn submap?
4 | "Is m1 a subset of m2?"
5 | [m1 m2]
6 | (if (and (map? m1) (map? m2))
7 | (every? (fn [[k v]] (and (contains? m2 k)
8 | (submap? v (get m2 k))))
9 | m1)
10 | (= m1 m2)))
11 |
12 | (defn submap-debug?
13 | "Is m1 a subset of m2?
14 | Print missing keys or mismatched values."
15 | [m1 m2]
16 | (if (and (map? m1) (map? m2))
17 | (every? (fn [[k v]]
18 | (when (not (contains? m2 k)) (println "m1 has key, m2 does not: " k))
19 | (and (contains? m2 k) (submap? v (get m2 k))))
20 | m1)
21 | (if (= m1 m2)
22 | true
23 | (do
24 | (println "Nested values don't match, m1 val=" m1 "m2 val=" m2)
25 | false))))
26 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/util/dir_test.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.deps.util.dir-test
10 | (:require
11 | [clojure.tools.deps.util.dir :as dir]
12 | [clojure.test :refer [deftest is]])
13 | (:import
14 | [java.io File]))
15 |
16 | (deftest test-canonicalize
17 | (let [base (.getCanonicalFile (File. "."))]
18 | (binding [dir/*the-dir* base]
19 | (let [abs (File. "/a/b")]
20 | (is (= abs (dir/canonicalize abs)))
21 | (is (= (File. base "xyz") (dir/canonicalize (File. "xyz"))))))))
--------------------------------------------------------------------------------
/src/main/resources/clojure/tools/deps/license-abbrev.edn:
--------------------------------------------------------------------------------
1 | ;; Map of "name found in the wild" => abbreviated name
2 | ;; not exhaustive, just trying to catch 80%
3 | ;; useful resource: https://github.com/spdx/license-list-data
4 | {"3-Clause BSD License" "BSD-3-Clause-Attribution"
5 | "Apache 2.0" "Apache-2.0"
6 | "Apache License 2.0" "Apache-2.0"
7 | "Apache License Version 2.0" "Apache-2.0"
8 | "Apache License, Version 2.0" "Apache-2.0"
9 | "Apache Software License - Version 2.0" "Apache-2.0"
10 | "Apache v2" "Apache-2.0"
11 | "BSD 3-Clause License" "BSD-3-Clause-Attribution"
12 | "Eclipse Public License" "EPL-1.0"
13 | "Eclipse Public License (EPL)" "EPL-1.0"
14 | "Eclipse Public License - v 1.0" "EPL-1.0"
15 | "Eclipse Public License 1.0" "EPL-1.0"
16 | "Eclipse Public License, Version 1.0" "EPL-1.0"
17 | "Eclipse Public License 2.0" "EPL-2.0"
18 | "Eclipse Public License version 2" "EPL-2.0"
19 | "GNU Affero General Public License (AGPL) version 3.0" "AGPL-3.0"
20 | "GNU General Public License, version 2 (GPL2), with the classpath exception" "GPL-2.0-with-classpath-exception"
21 | "GNU General Public License, version 2 with the GNU Classpath Exception" "GPL-2.0-with-classpath-exception"
22 | "GNU General Public License, version 2" "GPL-2.0"
23 | "GNU Lesser General Public License (LGPL)" "LGPL"
24 | "JSON License" "JSON"
25 | "MIT License" "MIT"
26 | "MIT license" "MIT"
27 | "Mozilla Public License" "MPL"
28 | "The Apache Software License, Version 2.0" "Apache-2.0"
29 | "The BSD 3-Clause License (BSD3)" "BSD-3-Clause-Attribution"
30 | "The MIT License" "MIT"}
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/util/concurrent.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 ^{:skip-wiki true}
10 | clojure.tools.deps.util.concurrent
11 | (:import
12 | [java.util.concurrent Callable Future ThreadFactory ExecutorService Executors TimeUnit]))
13 |
14 | (set! *warn-on-reflection* true)
15 |
16 | (defonce thread-factory
17 | (let [counter (atom 0)]
18 | (reify ThreadFactory
19 | (newThread [_ r]
20 | (doto (Thread. r)
21 | (.setName (format "tools.deps worker %s" (swap! counter inc)))
22 | (.setDaemon true))))))
23 |
24 | (defn new-executor
25 | ^ExecutorService [^long n]
26 | (Executors/newFixedThreadPool n ^ThreadFactory thread-factory))
27 |
28 | (def processors (long (.availableProcessors (Runtime/getRuntime))))
29 |
30 | (defn submit-task
31 | ^Future [^ExecutorService executor f]
32 | (let [bindings (get-thread-bindings)
33 | task (bound-fn* f)]
34 | (.submit executor ^Callable task)))
35 |
36 | (defn shutdown-on-error
37 | [^ExecutorService executor]
38 | (.shutdownNow executor)
39 | (.awaitTermination executor 1 TimeUnit/SECONDS))
40 |
41 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/script/parse.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 ^{:skip-wiki true}
10 | clojure.tools.deps.script.parse
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.string :as str]
14 | [clojure.edn :as edn]
15 | [clojure.tools.deps :as deps])
16 | (:import
17 | [java.io File]))
18 |
19 | (defn parse-files
20 | "Parses a string of comma-delimited files into a collection of
21 | Files, filtering only those that exist."
22 | [s]
23 | (->> (str/split s #",")
24 | (map jio/file)
25 | (filter #(.exists ^File %))))
26 |
27 | (defn parse-kws
28 | "Parses a concatenated string of keywords into a collection of keywords
29 | Ex: (parse-kws \":a:b:c\") ;; returns: (:a :b :c)"
30 | [s]
31 | (->> (str/split (or s "") #":")
32 | (remove str/blank?)
33 | (map
34 | #(if-let [i (str/index-of % \/)]
35 | (keyword (subs % 0 i) (subs % (inc i)))
36 | (keyword %)))))
37 |
38 | (defn parse-config
39 | "Parses a string of edn into a deps map."
40 | [s]
41 | (#'deps/canonicalize-all-syms ;; to be removed in the future
42 | (cond
43 | (str/blank? s) (throw (ex-info "-Sdeps must be non-blank" {}))
44 | (str/starts-with? (str/trim s) "{") (edn/read-string {:default tagged-literal} s)
45 | :else s)))
46 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/util/s3_aws_client.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 ^{:skip-wiki true}
10 | clojure.tools.deps.util.s3-aws-client
11 | (:require
12 | [cognitect.aws.client.api :as aws]
13 | [cognitect.aws.credentials :as creds]))
14 |
15 | (defn- aws-creds-provider
16 | [user pw]
17 | (reify creds/CredentialsProvider
18 | (fetch [_]
19 | {:aws/access-key-id user
20 | :aws/secret-access-key pw})))
21 |
22 | (defn- get-bucket-loc
23 | [config bucket]
24 | (let [s3-client (aws/client (merge {:region "us-east-1"} config))
25 | resp (try
26 | (aws/invoke s3-client {:op :GetBucketLocation
27 | :request {:Bucket bucket}})
28 | (catch Throwable _ nil))
29 | region (:LocationConstraint resp)]
30 | (cond
31 | (nil? region) nil
32 | (= region "") "us-east-1"
33 | :else region)))
34 |
35 | (defn new-s3-client
36 | [user pw region bucket]
37 | (let [cred-provider (when (and user pw)
38 | (aws-creds-provider user pw))
39 | config (cond-> {:api :s3}
40 | cred-provider (assoc :credentials-provider cred-provider))
41 | use-region (or region (get-bucket-loc config bucket) "us-east-1")]
42 | (aws/client (assoc config :region use-region))))
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/util/test_s3_transporter.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.deps.util.test-s3-transporter
2 | (:require
3 | [clojure.test :refer [are deftest]]
4 | [clojure.tools.deps.util.s3-transporter :as s3t]
5 | [clojure.tools.deps.util.maven :as mvn])
6 | (:import
7 | [java.io File]
8 | [java.net URI]
9 | [org.eclipse.aether.spi.connector.transport Transporter GetTask TransportListener]))
10 |
11 | (set! *warn-on-reflection* true)
12 |
13 | (deftest test-parse
14 | (are [u r b p] (= (merge {:region nil, :bucket nil, :repo-path nil}
15 | (s3t/parse-url (mvn/remote-repo [nil {:url u}])))
16 | {:region r :bucket b :repo-path p})
17 | "s3://BUCKET/PATH1/PATH2" nil "BUCKET" "PATH1/PATH2"
18 | "s3://BUCKET/PATH1/PATH2?region=REGION" "REGION" "BUCKET" "PATH1/PATH2"))
19 |
20 | (defn downloader
21 | [repo url path]
22 | (let [system (mvn/make-system)
23 | settings (mvn/get-settings)
24 | session (mvn/make-session system settings "/Users/alex/.m2/repository")
25 | remote-repo (mvn/remote-repo [repo {:url url}])
26 | transporter (s3t/new-transporter session remote-repo)
27 | task (GetTask. (URI/create path))
28 | temp (File/createTempFile "dload-" nil)]
29 | (.setDataFile task temp)
30 | (.setListener task (proxy [TransportListener] []
31 | (transportStarted [_ _])
32 | (transportProgressed [_])))
33 | (.get ^Transporter transporter task)
34 | (slurp temp)))
35 |
36 | (comment
37 | (downloader "datomic" "s3://datomic-releases-1fc2183a/maven/releases" "com/datomic/ion/0.9.35/ion-0.9.35.pom")
38 | (downloader "datomic" "s3://datomic-releases-1fc2183a/maven/releases?region=us-east-1" "com/datomic/ion/0.9.35/ion-0.9.35.pom")
39 | )
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/util/session.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 ^{:skip-wiki true}
10 | clojure.tools.deps.util.session
11 | "Maintains session resources during or across runs of the resolver"
12 | (:import
13 | [java.util.concurrent ConcurrentMap ConcurrentHashMap]
14 | [java.util.function Function]))
15 |
16 | (def session (ConcurrentHashMap.)) ;; should never be nil
17 |
18 | (defn retrieve
19 | "Read the current value of key from the session. If absent, and if-absent-fn
20 | is supplied, invoke the fn, set it in the session (if there is one),
21 | and return the value."
22 | ([key]
23 | (.get ^ConcurrentMap session key))
24 | ([key if-absent-fn]
25 | (.computeIfAbsent ^ConcurrentMap session key
26 | (reify Function
27 | (apply [_f _k]
28 | (if-absent-fn))))))
29 |
30 | (defn retrieve-local
31 | "Like retrieve, but scoped to a thread-specific key, so never shared across threads."
32 | ([key]
33 | (retrieve {:thread (.getId (Thread/currentThread)) :key key}))
34 | ([key if-absent-fn]
35 | (retrieve {:thread (.getId (Thread/currentThread)) :key key} if-absent-fn)))
36 |
37 | (defmacro with-session
38 | "Create a new empty session and execute the body"
39 | [& body]
40 | `(let [prior# session]
41 | (alter-var-root #'session (constantly (ConcurrentHashMap.)))
42 | (try
43 | ~@body
44 | (finally
45 | (alter-var-root #'session (constantly prior#))))))
46 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/gen/test_pom.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.deps.gen.test-pom
2 | (:require
3 | [clojure.test :refer [deftest is]]
4 | [clojure.java.io :as jio]
5 | [clojure.string :as str]
6 | [clojure.tools.deps.gen.pom :as gen-pom]
7 | [clojure.tools.deps :as deps])
8 | (:import
9 | [java.io File]))
10 |
11 | ;; simple check that pom gen is working - gen a pom.xml from tda's deps.edn
12 | (deftest test-pom-gen
13 | (let [temp-dir (.getParentFile (File/createTempFile "dummy" nil))
14 | pom (jio/file temp-dir "pom.xml")
15 | {:keys [root-edn user-edn project-edn]} (deps/find-edn-maps)
16 | master-edn (deps/merge-edns [root-edn user-edn project-edn])]
17 | (.delete pom)
18 | (gen-pom/sync-pom master-edn temp-dir)
19 | (is (.exists pom))
20 | (is (not (str/blank? (slurp pom))))))
21 |
22 | ;; check that optional deps are marked optional
23 | (deftest test-optional
24 | (let [temp-dir (.getParentFile (File/createTempFile "dummy" nil))
25 | pom (jio/file temp-dir "pom.xml")]
26 | (.delete pom)
27 | (gen-pom/sync-pom
28 | '{:deps {org.clojure/core.async {:mvn/version "1.1.587" :optional true}}}
29 | temp-dir)
30 | (is (.exists pom))
31 | (let [generated (slurp pom)]
32 | (is (str/includes? generated "core.async"))
33 | (is (str/includes? generated "true")))))
34 |
35 | (deftest test-add-src-dir
36 | (let [temp-dir (.getParentFile (File/createTempFile "dummy" nil))
37 | pom (jio/file temp-dir "pom.xml")]
38 | (.delete pom)
39 | (spit pom "\n\n 4.0.0\n foo\n foo\n 0.1.0\n foo\n")
40 | (gen-pom/sync-pom (deps/root-deps) temp-dir)
41 | (let [new-pom (slurp pom)]
42 | (is (str/includes? new-pom ""))
43 | (is (str/includes? new-pom "src")))))
44 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/util/io.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 ^{:skip-wiki true}
10 | clojure.tools.deps.util.io
11 | (:require
12 | [clojure.edn :as edn]
13 | [clojure.java.io :as jio]
14 | [clojure.string :as str])
15 | (:import
16 | [java.io Reader FileReader PushbackReader]))
17 |
18 | (defonce ^:private nl (System/getProperty "line.separator"))
19 |
20 | (defn printerrln
21 | "println to *err*"
22 | [& msgs]
23 | (binding [*out* *err*
24 | *print-readably* nil]
25 | (pr (str (str/join " " msgs) nl))
26 | (flush)))
27 |
28 | (defn read-edn
29 | "Read the edn file from the specified `reader`.
30 | This file should contain a single edn value. Empty files return nil.
31 | The reader will be read to EOF and closed."
32 | [^Reader reader]
33 | (let [EOF (Object.)]
34 | (with-open [rdr (PushbackReader. reader)]
35 | (let [val (edn/read {:default tagged-literal :eof EOF} rdr)]
36 | (if (identical? EOF val)
37 | nil
38 | (if (not (identical? EOF (edn/read {:eof EOF} rdr)))
39 | (throw (ex-info "Invalid file, expected edn to contain a single value." {}))
40 | val))))))
41 |
42 | (defn slurp-edn
43 | "Read the edn file specified by f, a string or File.
44 | An empty file will return nil."
45 | [f]
46 | (read-edn (FileReader. (jio/file f))))
47 |
48 | (defn write-file
49 | "Write the string s to file f. Creates parent directories for f if they don't exist."
50 | [f s]
51 | (let [the-file (jio/file f)
52 | parent (.getParentFile the-file)]
53 | (when-not (.exists parent)
54 | (when-not (.mkdirs parent)
55 | (let [parent-name (.getCanonicalPath parent)]
56 | (throw (ex-info (str "Can't create directory: " parent-name) {:dir parent-name})))))
57 | (spit the-file s)))
58 |
59 | (comment
60 | (slurp-edn "deps.edn")
61 | )
62 |
--------------------------------------------------------------------------------
/src/main/java/clojure/tools/deps/util/S3TransporterFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Rich Hickey. All rights reserved.
3 | * The use and distribution terms for this software are covered by the
4 | * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
5 | * which can be found in the file epl-v10.html at the root of this distribution.
6 | * By using this software in any fashion, you are agreeing to be bound by
7 | * the terms of this license.
8 | * You must not remove this notice, or any other, from this software.
9 | */
10 | package clojure.tools.deps.util;
11 |
12 | import javax.inject.Named;
13 | import org.eclipse.aether.RepositorySystemSession;
14 | import org.eclipse.aether.repository.RemoteRepository;
15 | import org.eclipse.aether.spi.connector.transport.Transporter;
16 | import org.eclipse.aether.spi.connector.transport.TransporterFactory;
17 | import org.eclipse.aether.spi.locator.Service;
18 | import org.eclipse.aether.spi.locator.ServiceLocator;
19 | import clojure.java.api.Clojure;
20 | import clojure.lang.IFn;
21 | import org.eclipse.aether.transfer.NoTransporterException;
22 |
23 | /**
24 | * Transporter factory for repositories using the s3 protocol.
25 | */
26 | @Named("s3")
27 | public final class S3TransporterFactory implements TransporterFactory, Service {
28 |
29 | private static class DelayedInstance {
30 | private static final IFn NEW_TRANSPORTER;
31 |
32 | static {
33 | IFn REQUIRE = Clojure.var("clojure.core", "require");
34 | REQUIRE.invoke(Clojure.read("clojure.tools.deps.util.s3-transporter"));
35 | NEW_TRANSPORTER = Clojure.var("clojure.tools.deps.util.s3-transporter", "new-transporter");
36 | }
37 | }
38 |
39 | private S3TransporterFactory() {}
40 |
41 | public void initService(ServiceLocator locator) {
42 | }
43 |
44 | public Transporter newInstance(RepositorySystemSession session, RemoteRepository repository) throws NoTransporterException {
45 | String protocol = repository.getProtocol();
46 | if("s3".equals(protocol)) {
47 | return (Transporter) DelayedInstance.NEW_TRANSPORTER.invoke(session, repository);
48 | } else {
49 | throw new NoTransporterException(repository);
50 | }
51 | }
52 |
53 | public float getPriority() {
54 | return 5.0f;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/util/dir.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 ^{:skip-wiki true}
10 | clojure.tools.deps.util.dir
11 | (:require
12 | [clojure.java.io :as jio])
13 | (:import
14 | [java.io File]
15 | [java.nio.file Files Path]))
16 |
17 | (set! *warn-on-reflection* true)
18 |
19 | (def ^:dynamic *the-dir*
20 | "Thread-local directory context for resolving relative directories.
21 | Defaults to current directory. Should always hold an absolute directory
22 | java.io.File, never null."
23 | (jio/file (System/getProperty "user.dir")))
24 |
25 | (defn canonicalize
26 | "Make canonical File in terms of the current directory context.
27 | f may be either absolute or relative."
28 | ^File [^File f]
29 | (.getCanonicalFile
30 | (if (.isAbsolute f)
31 | f
32 | (jio/file *the-dir* f))))
33 |
34 | (defmacro with-dir
35 | "Push directory into current directory context for execution of body."
36 | [^File dir & body]
37 | `(binding [*the-dir* (canonicalize ~dir)]
38 | ~@body))
39 |
40 | (defn- same-file?
41 | "If a file can't be read (most common reason is directory does not exist), then
42 | treat this as not the same file (ie unknown)."
43 | [^Path p1 ^Path p2]
44 | (try
45 | (Files/isSameFile p1 p2)
46 | (catch Exception _ false)))
47 |
48 | (defn sub-path?
49 | "True if the path is a sub-path of the current directory context.
50 | path may be either absolute or relative. Will return true if path
51 | has a parent that is the current directory context, false otherwise.
52 | Handles relative paths, .., ., etc. The sub-path does not need to
53 | exist on disk (but the current directory context must)."
54 | [^File path]
55 | (if (nil? path)
56 | false
57 | (let [root-path (.toPath ^File *the-dir*)]
58 | (loop [check-path (.toPath (canonicalize path))]
59 | (cond
60 | (nil? check-path) false
61 | (same-file? root-path check-path) true
62 | :else (recur (.getParent check-path)))))))
63 |
64 | ;; DEPRECATED
65 | (defn as-canonical
66 | ^File [^File dir]
67 | (canonicalize dir))
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 | {:paths ["src/main/clojure" "src/main/resources"]
2 | :deps {
3 | org.clojure/clojure {:mvn/version "1.12.4"}
4 | org.apache.maven.resolver/maven-resolver-api {:mvn/version "1.8.2"}
5 | org.apache.maven.resolver/maven-resolver-spi {:mvn/version "1.8.2" :exclusions [org.apache.commons/commons-lang3]}
6 | ;; override for CVE
7 | org.apache.commons/commons-lang3 {:mvn/version "3.18.0"}
8 | org.apache.maven.resolver/maven-resolver-impl {:mvn/version "1.8.2"}
9 | org.apache.maven.resolver/maven-resolver-util {:mvn/version "1.8.2"}
10 | org.apache.maven.resolver/maven-resolver-connector-basic {:mvn/version "1.8.2"}
11 | org.apache.maven.resolver/maven-resolver-transport-file {:mvn/version "1.8.2"}
12 | org.apache.maven.resolver/maven-resolver-transport-http {:mvn/version "1.8.2"}
13 | org.apache.maven/maven-resolver-provider {:mvn/version "3.8.8"}
14 | ;; exclude due to CVE-2020-8908
15 | org.apache.maven/maven-core {:mvn/version "3.8.8" :exclusions [com.google.guava/guava]}
16 | org.clojure/data.xml {:mvn/version "0.2.0-alpha9"}
17 | org.clojure/tools.gitlibs {:mvn/version "2.6.206"}
18 | org.clojure/tools.cli {:mvn/version "1.1.230"}
19 | com.cognitect.aws/api {:mvn/version "0.8.762"}
20 | ;; override jetty stuff under http-client for CVEs
21 | com.cognitect/http-client {:mvn/version "1.0.127" :exclusions [org.eclipse.jetty/jetty-http org.eclipse.jetty/jetty-client org.eclipse.jetty/jetty-util]}
22 | org.eclipse.jetty/jetty-http {:mvn/version "9.4.58.v20250814"}
23 | org.eclipse.jetty/jetty-client {:mvn/version "9.4.58.v20250814"}
24 | org.eclipse.jetty/jetty-util {:mvn/version "9.4.58.v20250814"}
25 | com.cognitect.aws/endpoints {:mvn/version "871.2.32.21"}
26 | com.cognitect.aws/s3 {:mvn/version "871.2.32.2"}
27 | javax.inject/javax.inject {:mvn/version "1"}
28 | }
29 | :aliases {
30 | :test {:extra-paths ["src/test/clojure"]}
31 |
32 | ;; clj -M:lint
33 | :lint {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2025.07.28"}}
34 | :main-opts ["-m" "clj-kondo.main" "--lint" "src/main/clojure" "--lint" "src/test/clojure"]}
35 |
36 | ;; clj -M:cve
37 | :cve {:extra-deps {io.github.clj-holmes/clj-watson {:git/tag "v6.0.1" :git/sha "b520351"}}
38 | :extra-paths [".clj-watson"]
39 | :jvm-opts ["--illegal-access=deny"]
40 | :main-opts ["-m" "clj-watson.cli" "scan" "-p" "deps.edn"]}
41 |
42 | ;; clj -M:outdated
43 | :outdated {:extra-deps {com.github.liquidz/antq {:mvn/version "RELEASE"}}
44 | :main-opts ["-m" "antq.core"]}
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/extensions/faken.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.deps.extensions.faken
10 | (:require
11 | [clojure.string :as str]
12 | [clojure.tools.deps.extensions :as ext])
13 | (:import
14 | ;; maven-resolver-util
15 | [org.eclipse.aether.util.version GenericVersionScheme]))
16 |
17 | ;; Fake Maven extension for testing dependency resolution
18 |
19 | ;; Use the functions to construct a faux Maven repo
20 |
21 | ;; {lib {coord [dep1 ...]}}
22 | (def ^:dynamic repo {})
23 |
24 | (defmacro with-libs
25 | [libs & body]
26 | `(binding [repo ~libs]
27 | ~@body))
28 |
29 | (defmethod ext/coord-type-keys :fkn [_type] #{:fkn/version})
30 |
31 | (defmethod ext/dep-id :fkn
32 | [_lib coord _config]
33 | (select-keys coord [:fkn/version]))
34 |
35 | (defmethod ext/manifest-type :fkn
36 | [_lib _coord _config]
37 | {:deps/manifest :fkn})
38 |
39 | (defonce ^:private version-scheme (GenericVersionScheme.))
40 |
41 | (defn- parse-version [{version :fkn/version :as _coord}]
42 | (.parseVersion ^GenericVersionScheme version-scheme ^String version))
43 |
44 | (defmethod ext/compare-versions [:fkn :fkn]
45 | [_lib coord-x coord-y _config]
46 | (apply compare (map parse-version [coord-x coord-y])))
47 |
48 | (defmethod ext/coord-deps :fkn
49 | [lib coord _manifest config]
50 | (remove
51 | (fn [[lib {:keys [optional]}]] optional)
52 | (get-in repo [lib (ext/dep-id lib coord config)])))
53 |
54 | (defn make-path
55 | [lib {:keys [fkn/version]}]
56 | (let [[n c] (str/split (name lib) #"\$")]
57 | (str "REPO/" (namespace lib)
58 | "/" n
59 | "/" version
60 | "/" n (if c (str "-" c) "") "-" version ".jar")))
61 |
62 | (defmethod ext/coord-paths :fkn
63 | [lib coord _manifest _config]
64 | [(make-path lib coord)])
65 |
66 | (defmethod ext/find-versions :fkn
67 | [_lib _coord _type _config]
68 | nil)
69 |
70 | (comment
71 | (with-libs
72 | {'a/a {{:fkn/version "0.1.2"} [['b/b {:fkn/version "1.2.3"}]]}
73 | 'b/b {{:fkn/version "1.2.3"} nil}}
74 | (ext/coord-deps 'a/a {:fkn/version "0.1.2"} :fkn nil))
75 | )
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/extensions/deps.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 ^{:skip-wiki true}
10 | clojure.tools.deps.extensions.deps
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.tools.deps :as deps]
14 | [clojure.tools.deps.extensions :as ext]
15 | [clojure.tools.deps.util.dir :as dir]
16 | [clojure.tools.deps.util.io :as io]
17 | [clojure.tools.deps.util.session :as session])
18 | (:import
19 | [java.io File]))
20 |
21 | (set! *warn-on-reflection* true)
22 |
23 | (defn- deps-map
24 | [config dir]
25 | (let [f (jio/file dir "deps.edn")]
26 | (session/retrieve
27 | {:deps :map :file (.getAbsolutePath f)} ;; session key
28 | #(if (.exists f)
29 | (deps/merge-edns [(deps/root-deps) (deps/slurp-deps f)])
30 | (deps/root-deps)))))
31 |
32 | (defmethod ext/coord-deps :deps
33 | [_lib {:keys [deps/root] :as _coord} _mf config]
34 | (dir/with-dir (jio/file root)
35 | (seq (:deps (deps-map config root)))))
36 |
37 | (defmethod ext/coord-paths :deps
38 | [_lib {:keys [deps/root] :as _coord} _mf config]
39 | (dir/with-dir (jio/file root)
40 | (->> (:paths (deps-map config root))
41 | (map #(dir/canonicalize (jio/file %)))
42 | (map #(do
43 | (when (not (dir/sub-path? %))
44 | (io/printerrln "WARNING: Deprecated use of path" % "external to project" root))
45 | %))
46 | (map #(.getCanonicalPath ^File %))
47 | vec)))
48 |
49 | (defmethod ext/manifest-file :deps
50 | [_lib {:keys [deps/root] :as _coord} _mf _config]
51 | (let [manifest (jio/file root "deps.edn")]
52 | (when (.exists manifest)
53 | (.getAbsolutePath manifest))))
54 |
55 | (defmethod ext/coord-usage :deps [lib {:keys [deps/root] :as _coord} manifest-type config]
56 | (dir/with-dir (jio/file root)
57 | (:tools/usage (deps-map config root))))
58 |
59 | (defmethod ext/prep-command :deps [lib {:keys [deps/root] :as _coord} manifest-type config]
60 | (dir/with-dir (jio/file root)
61 | (let [external-deps (deps-map config root)]
62 | (when-let [prep-info (:deps/prep-lib external-deps)]
63 | (let [exec-args (-> external-deps :aliases (get (:alias prep-info)) :exec-args)]
64 | (cond-> prep-info
65 | exec-args (assoc :exec-args exec-args)))))))
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/script/generate_manifest2.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 ^{:skip-wiki true}
10 | clojure.tools.deps.script.generate-manifest2
11 | (:require [clojure.java.io :as jio]
12 | [clojure.tools.cli :as cli]
13 | [clojure.tools.deps.gen.pom :as pom]
14 | [clojure.tools.deps.script.parse :as parse]
15 | [clojure.tools.deps.script.make-classpath2 :as makecp]
16 | [clojure.tools.deps.util.io :refer [printerrln]])
17 | (:import
18 | [clojure.lang IExceptionInfo]))
19 |
20 | (def ^:private opts
21 | [[nil "--config-user PATH" "User deps.edn location" :parse-fn makecp/blank-to-nil]
22 | [nil "--config-project PATH" "Project deps.edn location"]
23 | [nil "--config-data EDN" "Final deps.edn data to treat as the last deps.edn file" :parse-fn parse/parse-config]
24 | [nil "--gen TYPE" "manifest type to generate" :parse-fn keyword]
25 | ["-R" "--resolve-aliases ALIASES" "Concatenated resolve-deps alias names" :parse-fn parse/parse-kws]
26 | ["-C" "--makecp-aliases ALIASES" "Concatenated make-classpath alias names" :parse-fn parse/parse-kws]
27 | ["-A" "--repl-aliases ALIASES" "Concatenated repl alias names" :parse-fn parse/parse-kws]])
28 |
29 | (defn -main
30 | "Main entry point for generating a manifest file.
31 |
32 | Required:
33 | --config-user PATH - User deps.edn location
34 | --config-project PATH - Project deps.edn location
35 | --config-data={...} - deps.edn as data
36 | --gen TYPE - manifest type to generate (currently only pom)
37 |
38 | Options (see make-classpath2)"
39 | [& args]
40 | (let [{:keys [options errors]} (cli/parse-opts args opts)]
41 | (when (seq errors)
42 | (run! println errors)
43 | (System/exit 1))
44 | (let [{:keys [gen config-user config-project]} options]
45 | (try
46 | (let [basis-map (makecp/run-core options)
47 | mod-map (:basis basis-map)
48 | ;; treat all transitive deps as top-level deps
49 | updated-deps (reduce-kv (fn [m lib {:keys [dependents] :as coord}]
50 | (if (seq dependents) m (assoc m lib coord)))
51 | {} (:libs mod-map))]
52 | (pom/sync-pom (merge mod-map {:deps updated-deps}) (jio/file ".")))
53 | (catch Throwable t
54 | (printerrln "Error generating" (name gen) "manifest:" (.getMessage t))
55 | (when-not (instance? IExceptionInfo t)
56 | (.printStackTrace t))
57 | (System/exit 1))))))
58 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/tool.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.deps.tool
10 | (:require
11 | [clojure.java.io :as jio]
12 | [clojure.pprint :as pprint]
13 | [clojure.string :as str]
14 | [clojure.tools.deps :as deps]
15 | [clojure.tools.deps.extensions :as ext]
16 | [clojure.tools.deps.util.io :as io])
17 | (:import
18 | [java.io File]))
19 |
20 | (defn- tool-dir
21 | ^File []
22 | (jio/file (.getParentFile (jio/file (deps/user-deps-path))) "tools"))
23 |
24 | (defn- tool-file
25 | "Create File location for tool name"
26 | ^File [tool]
27 | (jio/file (tool-dir) (str tool ".edn")))
28 |
29 | (defn install-tool
30 | "Procure the lib+coord, install the tool to the user tools dir (with lib, coord)"
31 | [lib coord as]
32 | (let [{:keys [root-edn user-edn]} (deps/find-edn-maps)
33 | master-edn (deps/merge-edns [root-edn user-edn])
34 | deps-info (ext/manifest-type lib coord master-edn)
35 | f (tool-file as)]
36 | ;; procure
37 | (ext/coord-paths lib (merge coord deps-info) (:deps/manifest deps-info) master-edn)
38 | ;; ensure tool dir
39 | (.mkdirs (.getParentFile f))
40 | ;; write tool file
41 | (spit f
42 | (with-out-str
43 | (binding [*print-namespace-maps* false
44 | pprint/*print-right-margin* 100]
45 | (pprint/pprint {:lib lib :coord coord}))))))
46 |
47 | (defn resolve-tool
48 | "Resolve a tool by name, look up and return:
49 | {:lib lib
50 | :coord coord}
51 | Or nil if unknown."
52 | [tool]
53 | (let [f (tool-file tool)]
54 | (when (.exists f)
55 | (io/slurp-edn f))))
56 |
57 | (defn usage
58 | "Resolve a tool and return it's usage data, which may be nil.
59 | Throws ex-info if tool is unknown."
60 | [tool]
61 | (if-let [{:keys [lib coord]} (resolve-tool tool)]
62 | (let [{:keys [root-edn user-edn]} (deps/find-edn-maps)
63 | config (deps/merge-edns [root-edn user-edn])
64 | [lib coord] (ext/canonicalize lib coord config)
65 | manifest-type (ext/manifest-type lib coord config)]
66 | (ext/coord-usage lib (merge coord manifest-type) (:deps/manifest manifest-type) config))
67 | (throw (ex-info (str "Unknown tool: " tool) {:tool tool}))))
68 |
69 | (defn list-tools
70 | "Return seq of available tool names"
71 | []
72 | (->> (.listFiles (tool-dir))
73 | (filter #(.isFile ^File %))
74 | (map #(.getName ^File %))
75 | (filter #(str/ends-with? % ".edn"))
76 | (map #(subs % 0 (- (count %) 4)))
77 | sort))
78 |
79 | (defn remove-tool
80 | "Removes tool installation, if it exists. Returns true if it exists and was deleted."
81 | [tool]
82 | (let [f (tool-file tool)]
83 | (when (.exists f)
84 | (.delete f))))
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/script/resolve_tags.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 ^{:skip-wiki true}
10 | clojure.tools.deps.script.resolve-tags
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.pprint :as pp]
14 | [clojure.walk :as walk]
15 | [clojure.tools.deps :as deps]
16 | [clojure.tools.deps.extensions.git :as git]
17 | [clojure.tools.deps.util.io :refer [printerrln]]
18 | [clojure.tools.gitlibs :as gitlibs]
19 | [clojure.tools.cli :as cli])
20 | (:import
21 | [clojure.lang IExceptionInfo]))
22 |
23 | (def ^:private opts
24 | [[nil "--deps-file PATH" "deps.edn file to update"]])
25 |
26 | (defn- resolve-git-dep
27 | [counter lib {untag :tag, unsha :sha, :git/keys [url tag sha] :as git-coord}]
28 | (let [the-url (or url (git/auto-git-url lib))
29 | the-tag (or tag untag)
30 | the-sha (or sha unsha)]
31 | (if the-tag
32 | (let [new-sha (gitlibs/resolve the-url the-tag)]
33 | (if (= the-sha new-sha)
34 | git-coord ;; no change
35 | (do
36 | (printerrln "Resolved" the-tag "=>" new-sha "in" the-url)
37 | (swap! counter inc)
38 | (cond-> {:git/tag the-tag
39 | :git/sha (subs new-sha 0 7)}
40 | url (assoc :git/url url)))))
41 | git-coord)))
42 |
43 | (defn- resolve-git-deps
44 | [counter deps-map]
45 | (let [f (partial resolve-git-dep counter)]
46 | (walk/postwalk
47 | (fn [node]
48 | (if (map? node)
49 | (reduce-kv
50 | (fn [new-deps k v]
51 | (if (and (symbol? k) (map? v)
52 | (contains? (->> v keys (map namespace) set) "git"))
53 | (assoc new-deps k (resolve-git-dep counter k v))
54 | new-deps))
55 | node node)
56 | node))
57 | deps-map)))
58 |
59 | (defn exec
60 | [{:keys [deps-file]}]
61 | (try
62 | (let [deps-map (deps/slurp-deps (jio/file deps-file))
63 | counter (atom 0)]
64 | (printerrln "Resolving git tags in" deps-file "...")
65 | (let [resolved-map (resolve-git-deps counter deps-map)]
66 | (if (zero? @counter)
67 | (printerrln "No unresolved tags found.")
68 | (spit deps-file
69 | (with-out-str
70 | (with-bindings {#'pp/*print-right-margin* 100
71 | #'pp/*print-miser-width* 80
72 | #'*print-namespace-maps* false}
73 | (pp/pprint resolved-map)))))))
74 | (catch Throwable t
75 | (printerrln "Error resolving tags." (.getMessage t))
76 | (when-not (instance? IExceptionInfo t)
77 | (.printStackTrace t))
78 | (System/exit 1))))
79 |
80 | (defn -main
81 | "Main entry point for resolve-tags script.
82 |
83 | Required:
84 | --deps-file deps.edn - deps.edn files in which to resolve git tags
85 |
86 | Read deps.edn, find git coordinates with :tag but without :sha, resolve those
87 | tags to shas, and over-write the deps.edn."
88 | [& args]
89 | (let [{:keys [options]} (cli/parse-opts args opts)]
90 | (exec options)))
91 |
92 | (comment
93 | (-main "--deps-file" "deps.edn")
94 | )
95 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | tools.deps
2 | ========================================
3 |
4 | A functional API for transitive dependency graph expansion and the creation of classpaths.
5 |
6 | # Rationale
7 |
8 | Clojure "endeavors to be a general-purpose language suitable in those areas where Java is suitable" (from [Rationale](https://clojure.org/about/rationale)). To effectively target the JVM platform, Clojure needs to provide ready access to Java libraries, ideally in a way suited for dynamic development. In practice, this means meeting the JVM platform in two places:
9 |
10 | * the classpath used when invoking JVM processes (and/or URLClassLoaders)
11 | * transitive dependency download and resolution from Maven repositories
12 |
13 | tools.deps aims to provide a functional API to access these capabilities.
14 |
15 | tools.deps makes it simple and easy to interactively consume JVM libraries, without dragging in unrelated concerns of building programs or project management. (It should also be a useful shared resource for future work on project and build tools.)
16 |
17 | tools.deps will support package installers for Clojure (e.g. brew, apt-get, etc) to provide a path for Clojure installation and ongoing Clojure development.
18 |
19 | The Clojure 1.9 release for the first time required multiple artifacts to run Clojure (clojure, spec.alpha, and core.specs.alpha) and thus the issues of transitive dependency are now immediately in front of a Clojure user in the first minute.
20 |
21 | Maven-artifacts-first orientation of current tooling has created great rigidity and friction for dev processes - making it hard to e.g. work with libs not yet building/publishing artifacts (new users!), working on speculative changes w/o artifacts, working on mutual changes across multiple libs, give control to a 3rd party tool to manage shared dependencies, and to directly leverage git which is now widely used as a source of truth for code lifecycles.
22 |
23 | Also see:
24 |
25 | * [Getting Started](https://clojure.org/guides/getting_started)
26 | * [Deps and CLI Guide](https://clojure.org/guides/deps_and_cli)
27 | * [Deps and CLI Reference](https://clojure.org/reference/deps_and_cli)
28 | * [Dependency Heaven](http://cdn.cognitect.com/presentations/2017/dependency_heaven.pdf) from EuroClojure 2017 - [video](https://youtube.com/watch?v=sStlTye-Kjk)
29 | * [Projects that use or work with tools.deps and clj](https://github.com/clojure/tools.deps.alpha/wiki/Tools)
30 | * [Tool implementation and installers](https://github.com/clojure/brew-install)
31 |
32 | # Release Information
33 |
34 | Latest release: 0.27.1564
35 |
36 | * [All released versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22tools.deps%22)
37 |
38 | [deps.edn](https://clojure.org/guides/deps_and_cli) dependency information:
39 |
40 | ```
41 | org.clojure/tools.deps {:mvn/version "0.27.1564"}
42 | ```
43 |
44 | [Leiningen](https://github.com/technomancy/leiningen/) dependency information:
45 |
46 | ```
47 | [org.clojure/tools.deps "0.27.1564"]
48 | ```
49 |
50 | [Maven](https://maven.apache.org) dependency information:
51 |
52 | ```
53 |
54 | org.clojure
55 | tools.deps
56 | 0.27.1564
57 |
58 | ```
59 |
60 | # API
61 |
62 | For info on using tools.deps as a library, see:
63 |
64 | * [API Docs](https://clojure.github.io/tools.deps)
65 |
66 | # Developer Information
67 |
68 | * [GitHub project](https://github.com/clojure/tools.deps)
69 | * [How to contribute](https://clojure.org/community/contributing)
70 | * [Bug Tracker](https://clojure.atlassian.net/browse/TDEPS)
71 | * [Continuous Integration](https://github.com/clojure/tools.deps/actions/workflows/test.yml)
72 |
73 | # Copyright and License
74 |
75 | Copyright © 2017-2025 Rich Hickey, Alex Miller, and contributors
76 |
77 | All rights reserved. The use and
78 | distribution terms for this software are covered by the
79 | [Eclipse Public License 1.0] which can be found in the file
80 | LICENSE at the root of this distribution. By using this software
81 | in any fashion, you are agreeing to be bound by the terms of this
82 | license. You must not remove this notice, or any other, from this
83 | software.
84 |
85 | [Eclipse Public License 1.0]: https://opensource.org/license/epl-1-0/
86 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/extensions/test_git.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.deps.extensions.test-git
10 | (:require
11 | [clojure.test :refer [deftest is are]]
12 | [clojure.tools.deps.extensions :as ext]
13 | [clojure.tools.deps.extensions.git :as git])
14 | (:import
15 | [clojure.lang ExceptionInfo]))
16 |
17 | (deftest auto-git-url
18 | (are [url lib] (= url (git/auto-git-url lib))
19 | "https://github.com/clojure/tools.deps.git" 'io.github.clojure/tools.deps
20 | "https://github.com/clojure/tools.deps.git" 'com.github.clojure/tools.deps
21 | "https://gitlab.com/clojure/tools.deps.git" 'io.gitlab.clojure/tools.deps
22 | "https://gitlab.com/clojure/tools.deps.git" 'com.gitlab.clojure/tools.deps
23 | "https://bitbucket.org/clojure/tools.deps.git" 'io.bitbucket.clojure/tools.deps
24 | "https://bitbucket.org/clojure/tools.deps.git" 'org.bitbucket.clojure/tools.deps
25 | "https://git.sr.ht/~foo/bar" 'ht.sr.foo/bar))
26 |
27 | (deftest full-sha
28 | (is (true? (git/full-sha? "f7443aa3ad854d5ab351f7ea327d6b161c5f3850")))
29 | (is (false? (git/full-sha? "f7443aa"))))
30 |
31 | (deftest canonicalize
32 | ;; infer url
33 | (is (= ['io.github.clojure/tools.deps {:git/url "https://github.com/clojure/tools.deps.git"
34 | :git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"}]
35 | (ext/canonicalize 'io.github.clojure/tools.deps {:git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"} {})))
36 |
37 | ;; standardize sha
38 | (is (= ['io.github.clojure/tools.deps {:git/url "https://github.com/clojure/tools.deps.git"
39 | :git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"}]
40 | (ext/canonicalize 'io.github.clojure/tools.deps {:sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"} {})))
41 |
42 | ;; standardize sha/tag
43 | (is (= ['io.github.clojure/tools.deps {:git/url "https://github.com/clojure/tools.deps.git"
44 | :git/sha "c2a3bbe9df5f574c8af17f07d18b75198194d26e"
45 | :git/tag "tools.deps.alpha-0.5.317"}]
46 | (ext/canonicalize 'io.github.clojure/tools.deps {:sha "c2a3bbe"
47 | :tag "tools.deps.alpha-0.5.317"} {}))))
48 |
49 | (deftest canonicalize-errors
50 | ;; unknown coord type
51 | (is (thrown-with-msg? ExceptionInfo #"Coord of unknown type"
52 | (ext/canonicalize 'io.github.clojure/tools.deps {} {})))
53 |
54 | ;; can't infer :git/url and none specified
55 | (is (thrown-with-msg? ExceptionInfo #"Failed to infer git url for: org.clojure/tools.deps"
56 | (ext/canonicalize 'org.clojure/tools.deps {:git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"} {})))
57 |
58 | ;; both :sha and :git/sha
59 | (is (thrown-with-msg? ExceptionInfo #"git coord has both :sha and :git/sha for io.github.clojure/tools.deps"
60 | (ext/canonicalize 'io.github.clojure/tools.deps {:sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"
61 | :git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"} {})))
62 |
63 | ;; lib must be qualified
64 | (is (thrown-with-msg? ExceptionInfo #"Invalid lib name: tools.deps"
65 | (ext/canonicalize 'tools.deps {:git/url "https://github.com/clojure/tools.deps.git"} {})))
66 |
67 | ;; require full sha if no tag
68 | (is (thrown-with-msg? ExceptionInfo #"Library io.github.clojure/tools.deps has prefix sha, use full sha or add tag"
69 | (ext/canonicalize 'io.github.clojure/tools.deps {:git/sha "9bf5778"} {})))
70 |
71 | ;; require at least prefix sha with tag
72 | (is (thrown-with-msg? ExceptionInfo #"Library io.github.clojure/tools.deps has coord with missing sha"
73 | (ext/canonicalize 'io.github.clojure/tools.deps {:git/tag "tools.deps.alpha-0.5.317"} {})))
74 |
75 | ;; sha/tag must point to same commit
76 | (is (thrown-with-msg? ExceptionInfo #"Library io.github.clojure/tools.deps has sha and tag that point to different commits"
77 | (ext/canonicalize 'io.github.clojure/tools.deps {:git/tag "tools.deps.alpha-0.5.317" :git/sha "9bf5778"} {})))
78 |
79 | ;; tag must be a tag
80 | (is (thrown-with-msg? ExceptionInfo #"Library io.github.clojure/tools.deps has invalid tag"
81 | (ext/canonicalize 'io.github.clojure/tools.deps {:git/tag "9bf5778" :git/sha "9bf5778"} {})))
82 | )
83 |
84 | (comment
85 | (canonicalize)
86 | )
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/extensions/local.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 ^{:skip-wiki true}
10 | clojure.tools.deps.extensions.local
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.string :as str]
14 | [clojure.tools.deps.extensions :as ext]
15 | [clojure.tools.deps.extensions.pom :as pom]
16 | [clojure.tools.deps.util.dir :as dir]
17 | [clojure.tools.deps.util.maven :as maven]
18 | [clojure.tools.deps.util.session :as session])
19 | (:import
20 | [java.io File IOException]
21 | [java.net URL]
22 | [java.util.jar JarFile JarEntry]
23 | ;; maven-builder-support
24 | [org.apache.maven.model.building UrlModelSource]
25 | [org.apache.maven.model License]))
26 |
27 | (defmethod ext/coord-type-keys :local
28 | [_type]
29 | #{:local/root})
30 |
31 | (defmethod ext/dep-id :local
32 | [lib {:keys [local/root] :as _coord} _config]
33 | {:lib lib
34 | :root root})
35 |
36 | (defn- ensure-file
37 | ^File [lib root]
38 | (let [f (jio/file root)]
39 | (if (.exists f)
40 | f
41 | (throw (ex-info (format "Local lib %s not found: %s" lib root) {:lib lib :root root})))))
42 |
43 | (defmethod ext/canonicalize :local
44 | [lib {:keys [local/root] :as coord} _config]
45 | (let [canonical-root (.getCanonicalPath (dir/canonicalize (jio/file root)))]
46 | (ensure-file lib canonical-root) ;; throw if missing
47 | [lib (assoc coord :local/root canonical-root)]))
48 |
49 | (defmethod ext/lib-location :local
50 | [_lib {:keys [local/root]} _config]
51 | {:base root
52 | :path ""
53 | :type :local})
54 |
55 | (defmethod ext/find-versions :local
56 | [_lib _coord _type _config]
57 | nil)
58 |
59 | (defmethod ext/manifest-type :local
60 | [lib {:keys [local/root deps/manifest] :as _coord} _config]
61 | (cond
62 | manifest {:deps/manifest manifest :deps/root root}
63 | (.isFile (ensure-file lib root)) {:deps/manifest :jar, :deps/root root}
64 | :else (ext/detect-manifest root)))
65 |
66 | (defmethod ext/coord-summary :local [lib {:keys [local/root]}]
67 | (str lib " " root))
68 |
69 | (defmethod ext/license-info :local
70 | [lib coord config]
71 | (let [coord (merge coord (ext/manifest-type lib coord config))]
72 | (ext/license-info-mf lib coord (:deps/manifest coord) config)))
73 |
74 | (defn find-pom
75 | "Find path of pom file in jar file, or nil if it doesn't exist"
76 | [^JarFile jar-file]
77 | (try
78 | (loop [[^JarEntry entry & entries] (enumeration-seq (.entries jar-file))]
79 | (when entry
80 | (let [name (.getName entry)]
81 | (if (and (str/starts-with? name "META-INF/")
82 | (str/ends-with? name "pom.xml"))
83 | name
84 | (recur entries)))))
85 | (catch IOException _t nil)))
86 |
87 | (defmethod ext/coord-deps :jar
88 | [lib {:keys [local/root] :as _coord} _manifest config]
89 | (let [jar (JarFile. (ensure-file lib root))]
90 | (if-let [path (find-pom jar)]
91 | (let [url (URL. (str "jar:file:" root "!/" path))
92 | src (UrlModelSource. url)
93 | settings (session/retrieve :mvn/settings #(maven/get-settings))
94 | model (pom/read-model src config settings)]
95 | (pom/model-deps model))
96 | [])))
97 |
98 | (defmethod ext/coord-paths :jar
99 | [_lib coord _manifest _config]
100 | [(:local/root coord)])
101 |
102 | ;; 0 if x and y are the same jar or dir
103 | (defmethod ext/compare-versions [:local :local]
104 | [lib {x-root :local/root :as x} {y-root :local/root :as y} _config]
105 | (if (= x-root y-root)
106 | 0
107 | (throw (ex-info (str "No known ancestor relationship between local versions for " lib ": " x-root " and " y-root)
108 | {:lib lib :x x :y y}))))
109 |
110 | (defmethod ext/manifest-file :jar
111 | [_lib {:keys [deps/root] :as _coord} _mf _config]
112 | nil)
113 |
114 | (defmethod ext/license-info-mf :jar
115 | [lib {:keys [local/root] :as _coord} _mf config]
116 | (let [jar (JarFile. (ensure-file lib root))]
117 | (when-let [path (find-pom jar)]
118 | (let [url (URL. (str "jar:file:" root "!/" path))
119 | src (UrlModelSource. url)
120 | settings (session/retrieve :mvn/settings #(maven/get-settings))
121 | model (pom/read-model src config settings)
122 | licenses (.getLicenses model)
123 | ^License license (when (and licenses (pos? (count licenses))) (first licenses))]
124 | (when license
125 | (let [name (.getName license)
126 | url (.getUrl license)]
127 | (when (or name url)
128 | (cond-> {}
129 | name (assoc :name name)
130 | url (assoc :url url)))))))))
131 |
132 | (defmethod ext/coord-usage :jar
133 | [_lib _coord _manifest-type _config]
134 | ;; TBD
135 | nil)
136 |
137 | (defmethod ext/prep-command :jar
138 | [_lib _coord _manifest-type _config]
139 | ;; TBD - could look in jar
140 | nil)
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/tree.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.deps.tree
10 | (:require
11 | [clojure.walk :as walk]
12 | [clojure.tools.deps :as deps]
13 | [clojure.tools.deps.extensions :as ext]))
14 |
15 | ;; Data manipulation
16 |
17 | (defn trace->tree
18 | "Convert a deps trace data structure into a deps tree.
19 |
20 | A deps tree has the structure of the full dependency expansion.
21 | Each node of the tree is a map from lib to coord-info with at least these keys:
22 | :lib - library symbol
23 | :coord - the coord map that was used (may not be the original coord if replaced
24 | due to default-deps or override-deps)
25 | :include - boolean of whether this node is included in the returned deps
26 | :reason - why the node was or was not included
27 | :children - vector of child nodes"
28 | [trace]
29 | (let [{:keys [log]} trace]
30 | (loop [[{:keys [lib path reason] :as step} & steps] log
31 | i 0
32 | tree {}]
33 | (if step
34 | (let [nstep (assoc step :step i)
35 | full-path (conj path lib)
36 | tree-path (interleave (repeat :children) full-path)
37 | tree' (if (= reason :newer-version)
38 | (walk/postwalk
39 | (fn [v]
40 | (if (and (map? v) (contains? v lib) (-> v lib :include))
41 | (assoc v lib (merge (get v lib) {:include false, :reason :superseded}))
42 | v))
43 | tree)
44 | tree)]
45 | (recur steps (inc i) (assoc-in tree' tree-path nstep)))
46 | tree))))
47 |
48 | (defn calc-trace
49 | "Like calc-basis, but create and return the dep expansion trace. The trace
50 | can be passed to trace->tree to get tree data.
51 |
52 | The opts map includes the same opts accepted by clojure.tools.deps/create-basis.
53 | By default, uses the root, user, and project deps and no argmaps (essentially the same
54 | classpath you get by default from the Clojure CLI).
55 |
56 | Each dep source value can be :standard, a string path, a deps edn map, or nil.
57 | Sources are merged in the order - :root, :user, :project, :extra.
58 |
59 | Aliases refer to argmaps in the merged deps that will be supplied to the basis
60 | subprocesses (tool, resolve-deps, make-classpath-map).
61 |
62 | Options:
63 | :dir - directory root path, defaults to current directory
64 | :root - dep source, default = :standard
65 | :user - dep source, default = :standard
66 | :project - dep source, default = :standard (\"./deps.edn\")
67 | :extra - dep source, default = nil
68 | :aliases - coll of aliases of argmaps to apply to subprocesses"
69 | ([] (calc-trace nil))
70 | ([opts]
71 | (let [{:keys [extra aliases]} opts
72 | trace-opts (merge opts
73 | {:extra (assoc-in extra [:aliases :__TRACE__ :trace] true)}
74 | {:aliases (conj (or aliases []) :__TRACE__)})]
75 |
76 | (-> trace-opts deps/create-basis :libs meta :trace))))
77 |
78 | ;; Printing
79 |
80 | (defn- space
81 | [n]
82 | (apply str (repeat n \space)))
83 |
84 | (defn- print-node
85 | [{:keys [lib coord include reason]} indented {:keys [hide-libs]}]
86 | (when (and lib (or (= reason :new-top-dep) (not (contains? hide-libs lib))))
87 | (let [pre (space indented)
88 | summary (ext/coord-summary lib coord)]
89 | (println
90 | (case reason
91 | :new-top-dep
92 | (str pre summary)
93 |
94 | (:new-dep :same-version)
95 | (str pre ". " summary)
96 |
97 | :newer-version
98 | (str pre ". " summary " " reason)
99 |
100 | (:use-top :older-version :excluded :parent-omitted :superseded) ;; :superseded is internal here
101 | (str pre "X " summary " " reason)
102 |
103 | ;; fallthrough, unknown reason
104 | (str pre "? " summary include reason))))))
105 |
106 | (defn print-tree
107 | "Print the tree to the console.
108 | Options:
109 | :indent Indent spacing (default = 2)
110 | :hide-libs Set of libs to ignore as deps under top deps, default = #{org.clojure/clojure}"
111 | ([tree {:keys [indent] :or {indent 2} :as opts}]
112 | (print-tree tree (- 0 indent) opts))
113 | ([{:keys [children] :as tree} indented opts]
114 | (let [opts' (merge {:indent 2, :hide-libs '#{org.clojure/clojure}} opts)]
115 | (print-node tree indented opts')
116 | (doseq [child (sort-by :step (vals children))]
117 | (print-tree child (+ indented (:indent opts')) opts')))))
118 |
119 | (comment
120 | (require '[clojure.tools.deps.util.io :as io])
121 | (-> "/Users/alex.miller/tmp/20201124/trace.edn" io/slurp-edn trace->tree (print-tree {}))
122 | (-> "/Users/alex.miller/code/tools.deps/trace.edn" io/slurp-edn trace->tree (print-tree {}))
123 | (-> "/Users/alex.miller/tmp/20201022/trace.edn" io/slurp-edn trace->tree (print-tree {}))
124 | (let [log (:log (io/slurp-edn "/Users/alex.miller/tmp/20201124/trace.edn"))]
125 | (distinct (map :reason log)))
126 |
127 | )
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/extensions/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 ^{:skip-wiki true}
10 | clojure.tools.deps.extensions.pom
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.string :as str]
14 | [clojure.tools.deps.extensions :as ext]
15 | [clojure.tools.deps.util.maven :as maven]
16 | [clojure.tools.deps.util.session :as session])
17 | (:import
18 | [java.io File]
19 | [java.util Properties]
20 | ;; maven-model
21 | [org.apache.maven.model Model Dependency Exclusion]
22 | ;; maven-model-builder
23 | [org.apache.maven.model.building DefaultModelBuildingRequest DefaultModelBuilderFactory ModelSource FileModelSource]
24 | [org.apache.maven.model.resolution ModelResolver]
25 | ;; maven-resolver-impl
26 | [org.eclipse.aether.internal.impl DefaultRemoteRepositoryManager]
27 | ;; maven-model
28 | [org.apache.maven.model Resource License]
29 | ;; maven-core
30 | [org.apache.maven.project ProjectModelResolver ProjectBuildingRequest$RepositoryMerging]
31 | ))
32 |
33 | (set! *warn-on-reflection* true)
34 |
35 | (defn- model-resolver
36 | ^ModelResolver [{:keys [mvn/repos mvn/local-repo]} settings]
37 | (let [local-repo (or local-repo @maven/cached-local-repo)
38 | locator (maven/make-locator)
39 | system (maven/make-system locator)
40 | session (maven/make-session system settings local-repo)
41 | repo-mgr (doto (DefaultRemoteRepositoryManager.) (.initService locator))
42 | repos (maven/remote-repos repos settings)]
43 | (ProjectModelResolver. session nil system repo-mgr repos ProjectBuildingRequest$RepositoryMerging/REQUEST_DOMINANT nil)))
44 |
45 | (defn read-model
46 | ^Model [^ModelSource source config settings]
47 | (let [props (Properties.)
48 | _ (.putAll props (System/getProperties))
49 | _ (.setProperty props "project.basedir" ".")
50 | req (doto (DefaultModelBuildingRequest.)
51 | (.setModelSource source)
52 | (.setModelResolver (model-resolver config settings))
53 | (.setSystemProperties props))
54 | builder (.newInstance (DefaultModelBuilderFactory.))
55 | result (.build builder req)]
56 | (.getEffectiveModel result)))
57 |
58 | (defn read-model-file
59 | ^Model [^File file config]
60 | (let [settings (session/retrieve :mvn/settings #(maven/get-settings))]
61 | (session/retrieve
62 | {:pom :model :file (.getAbsolutePath file)} ;; session key
63 | #(read-model (FileModelSource. file) config settings))))
64 |
65 | (defn- model-exclusions->data
66 | [exclusions]
67 | (when (and exclusions (pos? (count exclusions)))
68 | (into #{}
69 | (map (fn [^Exclusion exclusion]
70 | (symbol (.getGroupId exclusion) (.getArtifactId exclusion))))
71 | exclusions)))
72 |
73 | (defn- is-compile
74 | [^Dependency dep]
75 | (contains? #{"compile" "runtime"} (.getScope dep)))
76 |
77 | (defn- model-dep->data
78 | [^Dependency dep]
79 | (let [scope (.getScope dep)
80 | optional (.isOptional dep)
81 | exclusions (model-exclusions->data (.getExclusions dep))
82 | artifact-id (.getArtifactId dep)
83 | classifier (.getClassifier dep)]
84 | [(symbol (.getGroupId dep) (if (str/blank? classifier) artifact-id (str artifact-id "$" classifier)))
85 | (cond-> {:mvn/version (.getVersion dep)}
86 | scope (assoc :scope scope)
87 | optional (assoc :optional true)
88 | (seq exclusions) (assoc :exclusions exclusions))]))
89 |
90 | (defn model-deps
91 | [^Model model]
92 | (->> (.getDependencies model)
93 | (filter is-compile)
94 | (map model-dep->data)))
95 |
96 | (defmethod ext/coord-deps :pom
97 | [_lib {:keys [deps/root] :as _coord} _mf config]
98 | (let [pom (jio/file root "pom.xml")
99 | model (read-model-file pom config)]
100 | (model-deps model)))
101 |
102 | (defmethod ext/coord-paths :pom
103 | [lib {:keys [deps/root] :as _coord} _mf config]
104 | (let [pom (jio/file root "pom.xml")
105 | model (read-model-file pom config)
106 |
107 | ;; Maven core 3.8.2 returns an absolute directory here, which is a breaking regression
108 | ;; from previous versions (see https://issues.apache.org/jira/browse/MNG-7218).
109 | ;; Working around this with conditional code that deals with either absolute or relative.
110 | ;; When MNG-7218 is fixed and deps bumped, might be able to revert the absolute path here.
111 | src-dir (jio/file (.. model getBuild getSourceDirectory))
112 | src-path (if (.isAbsolute src-dir)
113 | (.getCanonicalPath src-dir)
114 | (.getCanonicalPath (jio/file root src-dir)))
115 |
116 | srcs (into [src-path
117 | (.getCanonicalPath (jio/file root "src/main/clojure"))]
118 | (for [^Resource resource (.. model getBuild getResources)]
119 | (let [dir (jio/file (.getDirectory resource))]
120 | (when dir
121 | (if (.isAbsolute dir)
122 | (.getCanonicalPath dir)
123 | (.getCanonicalPath (jio/file root dir)))))))]
124 | (->> srcs (remove nil?) distinct)))
125 |
126 | (defmethod ext/manifest-file :pom
127 | [_lib {:keys [deps/root] :as _coord} _mf _config]
128 | (.getAbsolutePath (jio/file root "pom.xml")))
129 |
130 | (defmethod ext/license-info-mf :pom
131 | [lib {:keys [deps/root] :as _coord} _mf config]
132 | (let [pom (jio/file root "pom.xml")
133 | model (read-model-file pom config)
134 | licenses (.getLicenses model)
135 | ^License license (when (and licenses (pos? (count licenses))) (first licenses))]
136 | (when license
137 | (let [name (.getName license)
138 | url (.getUrl license)]
139 | (when (or name url)
140 | (cond-> {}
141 | name (assoc :name name)
142 | url (assoc :url url)))))))
143 |
144 | (defmethod ext/coord-usage :pom
145 | [lib {:keys [deps/root]} manifest-type config]
146 | ;; TBD
147 | nil)
148 |
149 | (defmethod ext/prep-command :pom
150 | [lib {:keys [deps/root]} manifest-type config]
151 | ;; TBD
152 | nil)
153 |
154 | (comment
155 | (ext/coord-deps 'org.clojure/core.async {:deps/root "../core.async" :deps/manifest :pom}
156 | :pom {:mvn/repos maven/standard-repos})
157 |
158 | (ext/coord-paths 'org.clojure/core.async {:deps/root "../core.async" :deps/manifest :pom}
159 | :pom {:mvn/repos maven/standard-repos})
160 | )
161 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/util/s3_transporter.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 ^{:skip-wiki true}
10 | clojure.tools.deps.util.s3-transporter
11 | (:refer-clojure :exclude [peek get])
12 | (:require
13 | [clojure.string :as str])
14 | (:import
15 | [java.io InputStream OutputStream IOException]
16 | [java.net URI]
17 | [java.nio ByteBuffer]
18 | [org.eclipse.aether RepositorySystemSession]
19 | [org.eclipse.aether.repository RemoteRepository AuthenticationContext]
20 | [org.eclipse.aether.spi.connector.transport Transporter PeekTask GetTask]))
21 |
22 | (set! *warn-on-reflection* true)
23 |
24 | (defn s3-peek
25 | "Returns nil if path exists, anomaly category otherwise"
26 | [s3-client bucket path]
27 | (let [s3-response ((requiring-resolve 'cognitect.aws.client.api/invoke) s3-client
28 | {:op :HeadObject,
29 | :request {:Bucket bucket, :Key path}})]
30 | (:cognitect.anomalies/category s3-response)))
31 |
32 | (defn stream-copy
33 | [^OutputStream os ^InputStream is ^long offset on-read]
34 | (let [bb (ByteBuffer/allocate 32768)
35 | ba (.array bb)]
36 | (when (pos? offset)
37 | (let [skipped (.skip is offset)]
38 | (when-not (= skipped offset)
39 | (throw (IOException. (str "Failed skipping " offset ", only skipped " skipped))))))
40 | (try
41 | (loop []
42 | (let [read (.read is ba)]
43 | (when (<= 0 read)
44 | (.write os ba 0 read)
45 | (.rewind bb)
46 | (.limit bb read)
47 | (on-read bb)
48 | (recur))))
49 | (finally
50 | (.close is)
51 | (.close os)))))
52 |
53 | (defn s3-get-object
54 | [s3-client bucket path ^OutputStream output-stream offset on-read]
55 | (let [s3-response ((requiring-resolve 'cognitect.aws.client.api/invoke) s3-client {:op :GetObject, :request {:Bucket bucket, :Key path}})
56 | is ^InputStream (:Body s3-response)]
57 | (if is
58 | (stream-copy output-stream is offset on-read)
59 | (let [{:keys [cognitect.anomalies/category cognitect.http-client/throwable cognitect.anomalies/message]} s3-response]
60 | (if (#{:cognitect.anomalies/forbidden :cognitect.anomalies/not-found} category)
61 | (throw (ex-info "Artifact not found" {:bucket bucket, :path path, :reason category}))
62 | (throw (ex-info (format "Unexpected error downloading artifact from %s" bucket)
63 | {:bucket bucket, :path path, :reason category} throwable)))))))
64 |
65 | ;; s3://BUCKET/PATH?region=us-east-1
66 | (defn parse-url
67 | [^RemoteRepository repo]
68 | (let [u (URI/create (.getUrl repo))
69 | host (.getHost u)
70 | path (str/join "/" (remove str/blank? (str/split (.getPath u) #"/")))
71 | query (.getQuery u)
72 | kvs (when query (str/split query #"&"))
73 | {:strs [region]} (reduce (fn [m kv] (let [[k v] (str/split kv #"=")] (assoc m k v))) {} kvs)]
74 | {:bucket host, :region region, :repo-path path}))
75 |
76 | (let [lock (Object.)]
77 | (defn- dynaload-s3-client
78 | [client-atom user pw region bucket]
79 | (locking lock (require 'clojure.tools.deps.util.s3-aws-client))
80 | (let [f (requiring-resolve 'clojure.tools.deps.util.s3-aws-client/new-s3-client)]
81 | (swap! client-atom #(if % % (f user pw region bucket)))
82 | @client-atom)))
83 |
84 | (defn new-transporter
85 | [^RepositorySystemSession session ^RemoteRepository repository]
86 | (let [auth-context (AuthenticationContext/forRepository session repository)
87 | user (when auth-context (.get auth-context AuthenticationContext/USERNAME))
88 | pw (when auth-context (.get auth-context AuthenticationContext/PASSWORD))
89 | on-close #(when auth-context (.close auth-context))
90 | {:keys [bucket region repo-path]} (parse-url repository)
91 | s3-client-holder (atom nil)] ;; defer creation till needed
92 | (reify Transporter
93 | (^void peek [_ ^PeekTask peek-task]
94 | (let [path (.. peek-task getLocation toString)
95 | full-path (str repo-path "/" path)
96 | s3-client (dynaload-s3-client s3-client-holder user pw region bucket)
97 | res (s3-peek s3-client bucket full-path)]
98 | (when res
99 | (throw (ex-info "Artifact not found" {:bucket bucket, :path path, :reason res})))))
100 | (^void get [_ ^GetTask get-task]
101 | (let [path (.. get-task getLocation toString)
102 | full-path (str repo-path "/" path)
103 | offset (.getResumeOffset get-task)
104 | os (.newOutputStream get-task (> offset 0))
105 | listener (.getListener get-task)
106 | s3-client (dynaload-s3-client s3-client-holder user pw region bucket)]
107 | (.transportStarted listener offset -1)
108 | (s3-get-object s3-client bucket full-path os offset #(.transportProgressed listener %))))
109 | (classify [_ throwable]
110 | (if (#{:cognitect.anomalies/forbidden :cognitect.anomalies/not-found} (-> throwable ex-data :reason))
111 | Transporter/ERROR_NOT_FOUND
112 | Transporter/ERROR_OTHER))
113 | ;;(put [_ ^PutTask put-task]) ;; not supported
114 | (close [_]
115 | (when on-close (on-close))))))
116 |
117 | (comment
118 | (require '[cognitect.aws.client.api :as aws] 'clojure.repl)
119 | ;; use ambient creds
120 | (def s3-client (aws/client {:api :s3 :region :us-east-1}))
121 |
122 |
123 | (def resp (aws/invoke s3-client {:op :GetObject
124 | :request {:Bucket "datomic-releases-1fc2183a"
125 | :Key "maven/releases/com/datomic/ion/0.9.35/ion-0.9.35.pom"}}))
126 |
127 | (aws/invoke s3-client {:op :GetBucketLocation
128 | :request {:Bucket "datomic-releases-1fc2183a"}})
129 |
130 | (aws/invoke s3-client {:op :GetObject
131 | :request {:Bucket "datomic-releases-1fc2183a"
132 | :Key "/maven/releases/com/datomic/ion/0.9.35/ion-0.9.35.pom"}})
133 |
134 | (s3-peek s3-client "datomic-releases-1fc2183a" "/maven/releases/com/datomic/ion/0.9.35/ion-0.9.35.pom")
135 |
136 | (with-open [os (java.io.FileOutputStream. "download.pom")]
137 | (s3-get-object s3-client "datomic-releases-1fc2183a" "/maven/releases/com/datomic/ion/0.9.35/ion-0.9.35.pom"
138 | os 0 #(println "read data" %)))
139 |
140 | (def ann (s3-peek s3-client "datomic-releases-1fc2183a" "/maven/releases/com/datomic/ion/0.9.35/ion-0.9.35.foo"))
141 |
142 | (aws/ops s3-client)
143 | (aws/invoke s3-client {:op :GetBucketAcl, :request {:Bucket "datomic-releases-1fc2183a"}})
144 | (aws/validate-requests s3-client true)
145 | (aws/request-spec-key s3-client :GetObject)
146 | (clojure.repl/doc :cognitect.aws.s3/GetObjectRequest)
147 | (aws/doc s3-client :cognitect.aws.s3/GetBucketLocation)
148 | (clojure.repl/doc aws/request-spec-key)
149 | )
150 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/specs.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.deps.specs
10 | (:require [clojure.spec.alpha :as s]))
11 |
12 | ;;;; generic types
13 |
14 | (s/def ::lib symbol?)
15 | (s/def ::path string?)
16 | (s/def ::alias keyword?)
17 |
18 | ;;;; coordinates
19 |
20 | ;; generic coord attributes
21 | (s/def :deps/root ::path)
22 | (s/def :deps/manifest keyword?)
23 | (s/def :deps/exclusions (s/coll-of ::lib))
24 | (s/def :deps/coord (s/keys :opt [:deps/root :deps/manifest] :opt-un [:deps/exclusions]))
25 |
26 | ;; maven coords
27 | (s/def :mvn/version string?)
28 | (s/def :mvn/coord (s/merge :deps/coord
29 | (s/keys :req [:mvn/version])))
30 |
31 | ;; local coords
32 | (s/def :local/root string?)
33 | (s/def :local/coord (s/merge :deps/coord
34 | (s/keys :req [:local/root])))
35 |
36 | ;; git coords
37 | (s/def :git/url string?)
38 | (s/def :git/sha string?)
39 | (s/def :git/tag string?)
40 | (s/def :git/coord (s/merge :deps/coord
41 | (s/keys :opt [:git/url :git/sha :git/tag])))
42 |
43 | ;; should this become a multispec?
44 | (s/def ::coord (s/nilable
45 | (s/or :mvn :mvn/coord
46 | :local :local/coord
47 | :git :git/coord)))
48 |
49 | (s/def ::path-ref (s/or :path ::path :alias ::alias))
50 | (s/def :aliased/paths (s/coll-of ::path-ref :kind vector? :into []))
51 |
52 | ;; tool args
53 | ;; ::replace-deps - map of lib to coordinate to replace the
54 | (s/def ::tool-args (s/keys :opt-un [::replace-deps ::replace-paths ::deps ::paths]))
55 | (s/def ::replace-deps (s/map-of ::lib ::coord))
56 | (s/def ::replace-paths :aliased/paths)
57 |
58 | ;; resolve-deps args - used to modify the expanded deps tree
59 | ;; ::extra-deps - map of lib to coordinate added to the initial deps collection
60 | ;; ::override-deps - map of lib to coordinate to use instead of the coord found during expansion
61 | ;; ::default-deps - map of lib to coordinate to use if no coord is specified in extension
62 | (s/def ::resolve-args (s/keys :opt-un [::extra-deps ::override-deps ::default-deps]))
63 | (s/def ::extra-deps (s/map-of ::lib ::coord))
64 | (s/def ::override-deps (s/map-of ::lib ::coord))
65 | (s/def ::default-deps (s/map-of ::lib ::coord))
66 | (s/def ::threads pos-int?)
67 | (s/def ::trace boolean?)
68 |
69 | ;; make-classpath args - used when constructing the classpath
70 | ;; ::classpath-overrides - map of lib to path to use instead of the artifact found during resolution
71 | ;; ::extra-paths - collection of extra paths to add to the classpath in addition to ::paths
72 | (s/def ::classpath-args (s/keys :opt-un [::classpath-overrides ::extra-paths]))
73 | (s/def ::classpath-overrides (s/map-of ::lib ::path))
74 | (s/def ::extra-paths :aliased/paths)
75 |
76 | ;; exec args - used when executing a function with -X or -T
77 | ;; ::exec-args - map of default function args
78 | ;; ::exec-fn - default function symbol
79 | ;; ::ns-default - default namespace to use when resolving functions
80 | ;; ::ns-aliases - map of alias to namespace to use when resolving functions
81 | (s/def ::exec-args (s/keys :opt-un [::exec-args ::exec-fn ::ns-default ::ns-aliases]))
82 | (s/def ::exec-args (s/nilable map?))
83 | (s/def ::ns-default simple-symbol?)
84 | (s/def ::ns-aliases (s/map-of simple-symbol? simple-symbol?))
85 |
86 | ;; deps map (format of the deps.edn file)
87 | (s/def ::paths :aliased/paths)
88 | (s/def ::deps (s/map-of ::lib ::coord))
89 | (s/def ::aliases (s/map-of ::alias any?))
90 | (s/def ::deps-map (s/nilable (s/keys
91 | :opt-un [::paths ::deps ::aliases]
92 | :opt [:mvn/repos :mvn/local-repo :tools/usage :deps/prep-lib])))
93 |
94 | ;; lib map
95 | ;; a map of lib to resolved coordinate (a coord with a ::path) and dependent info
96 | (s/def ::dependents (s/coll-of ::lib))
97 | (s/def ::resolved-coord (s/merge ::coord (s/keys :opt-un [:aliased/paths ::dependents])))
98 | (s/def ::lib-map (s/map-of ::lib ::resolved-coord))
99 |
100 | ;; classpath
101 | (s/def ::classpath string?)
102 |
103 | ;; Procurers
104 |
105 | ;; Maven
106 | (s/def :mvn/repos (s/map-of ::repo-id ::repo))
107 | (s/def ::repo-id string?)
108 | (s/def ::repo (s/nilable (s/keys :opt-un [::url :mvn/releases :mvn/snapshots])))
109 | (s/def ::url string?)
110 | (s/def :mvn-repo/enabled boolean?)
111 | (s/def :mvn-repo/update (s/or :policy #{:daily :always :never} :interval int?))
112 | (s/def :mvn-repo/checksum #{:warn :fail :ignore})
113 | (s/def :mvn/repo-policy (s/keys :opt-un [:mvn-repo/enabled :mvn-repo/update :mvn-repo/checksum]))
114 | (s/def :mvn/releases :mvn/repo-policy)
115 | (s/def :mvn/snapshots :mvn/repo-policy)
116 | (s/def :mvn/local-repo string?)
117 |
118 | ;; Tool usage
119 | (s/def :tools/usage (s/keys :opt-un [::ns-default ::ns-aliases]))
120 |
121 | ;; Prep lib
122 | (s/def :deps/prep-lib (s/keys :req-un [:prep/ensure ::alias :prep/fn]))
123 | (s/def :prep/ensure ::path)
124 | (s/def :prep/fn symbol?)
125 |
126 | (defn valid-deps?
127 | "Determine whether the deps map is valid according to the specs"
128 | [deps-map]
129 | (s/valid? ::deps-map deps-map))
130 |
131 | (defn explain-deps
132 | "If a spec is invalid, return a message explaining why, suitable
133 | for an error message"
134 | [deps-map]
135 | (let [err-data (s/explain-data ::deps-map deps-map)]
136 | (if (nil? err-data)
137 | "Failed spec, reason unknown"
138 | (let [problems (->> (::s/problems err-data)
139 | (sort-by #(- (count (:in %))))
140 | (sort-by #(- (count (:path %)))))
141 | {:keys [path pred val reason via in]} (first problems)]
142 | (str "Found: " (pr-str val) ", expected: " (if reason reason (s/abbrev pred)) ", in: " (pr-str in))))))
143 |
144 | ;; API
145 |
146 | (s/fdef clojure.tools.deps/resolve-deps
147 | :args (s/cat :deps ::deps-map :options ::resolve-args)
148 | :ret ::lib-map)
149 |
150 | (s/fdef clojure.tools.deps/make-classpath-map
151 | :args (s/cat :deps ::deps-map, :libs ::lib-map, :classpath-args ::classpath-args)
152 | :ret map?)
153 |
154 | (s/fdef clojure.tools.deps/make-classpath
155 | :args (s/cat :libs ::lib-map, :paths ::paths, :classpath-args ::classpath-args)
156 | :ret string?)
157 |
158 |
159 | (comment
160 | ;; some scratch code to recursively check every deps.edn under
161 | ;; a root directory whether it's valid against the specs
162 | (require
163 | '[clojure.spec.test.alpha :as stest]
164 | '[clojure.tools.deps :as deps])
165 | (import '[java.nio.file Files Paths FileVisitor FileVisitResult])
166 | (stest/instrument (stest/enumerate-namespace 'clojure.tools.deps))
167 |
168 | (Files/walkFileTree
169 | (Paths/get "../" (into-array String []))
170 | (reify FileVisitor
171 | (postVisitDirectory [_ dir ex] FileVisitResult/CONTINUE)
172 | (preVisitDirectory [_ dir attrs] FileVisitResult/CONTINUE)
173 | (visitFileFailed [_ f ex] FileVisitResult/CONTINUE)
174 | (visitFile [_ f attrs]
175 | (when (.endsWith (str f) "/deps.edn")
176 | (print "Checking" (str f))
177 | (let [v (s/valid? ::deps-map (#'deps/slurp-edn-map (.toFile f)))]
178 | (println ":" v)
179 | (when-not v
180 | (s/explain ::deps-map (#'deps/slurp-edn-map (.toFile f))))))
181 | FileVisitResult/CONTINUE)))
182 | )
183 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | tools.deps
4 | 0.27.1565-SNAPSHOT
5 | tools.deps
6 | Clojure dependencies as data and classpath generation tools.
7 |
8 |
9 | org.clojure
10 | pom.contrib
11 | 1.3.0
12 |
13 |
14 |
15 |
16 | puredanger
17 | Alex Miller
18 |
19 |
20 |
21 |
22 |
23 | true
24 | 1.12.2
25 | 1.8.2
26 | 3.8.8
27 |
28 |
29 | 1.12.2
30 |
31 |
32 |
33 |
34 | org.clojure
35 | clojure
36 | ${clojure.version}
37 |
38 |
39 | org.apache.maven.resolver
40 | maven-resolver-api
41 | ${resolverVersion}
42 |
43 |
44 | org.apache.commons
45 | commons-lang3
46 |
47 |
48 |
49 |
50 |
51 | org.apache.commons
52 | commons-lang3
53 | 3.18.0
54 |
55 |
56 | org.apache.maven.resolver
57 | maven-resolver-spi
58 | ${resolverVersion}
59 |
60 |
61 | org.apache.maven.resolver
62 | maven-resolver-impl
63 | ${resolverVersion}
64 |
65 |
66 | org.apache.maven.resolver
67 | maven-resolver-util
68 | ${resolverVersion}
69 |
70 |
71 | org.apache.maven.resolver
72 | maven-resolver-connector-basic
73 | ${resolverVersion}
74 |
75 |
76 | org.apache.maven.resolver
77 | maven-resolver-transport-file
78 | ${resolverVersion}
79 |
80 |
81 | org.apache.maven.resolver
82 | maven-resolver-transport-http
83 | ${resolverVersion}
84 |
85 |
86 | org.apache.maven
87 | maven-resolver-provider
88 | ${mavenVersion}
89 |
90 |
91 | org.apache.maven
92 | maven-core
93 | ${mavenVersion}
94 |
95 |
96 | com.google.guava
97 | guava
98 |
99 |
100 |
101 |
102 | org.slf4j
103 | slf4j-nop
104 | 1.7.36
105 | test
106 |
107 |
108 | org.clojure
109 | data.xml
110 | 0.2.0-alpha9
111 |
112 |
113 | org.clojure
114 | tools.gitlibs
115 | 2.6.206
116 |
117 |
118 | org.clojure
119 | tools.cli
120 | 1.1.230
121 |
122 |
123 | com.cognitect.aws
124 | api
125 | 0.8.762
126 |
127 |
128 | com.cognitect
129 | http-client
130 | 1.0.127
131 |
132 |
133 | org.eclipse.jetty
134 | jetty-http
135 |
136 |
137 | org.eclipse.jetty
138 | jetty-client
139 |
140 |
141 | org.eclipse.jetty
142 | jetty-util
143 |
144 |
145 |
146 |
147 | org.eclipse.jetty
148 | jetty-http
149 | 9.4.58.v20250814
150 |
151 |
152 | org.eclipse.jetty
153 | jetty-client
154 | 9.4.58.v20250814
155 |
156 |
157 | org.eclipse.jetty
158 | jetty-client
159 | 9.4.58.v20250814
160 |
161 |
162 | com.cognitect.aws
163 | endpoints
164 | 871.2.32.21
165 |
166 |
167 | com.cognitect.aws
168 | s3
169 | 871.2.32.2
170 |
171 |
172 | javax.inject
173 | javax.inject
174 | 1
175 |
176 |
177 |
178 |
179 |
180 |
181 | src/main/resources
182 | true
183 |
184 |
185 |
186 |
187 | org.apache.maven.plugins
188 | maven-resources-plugin
189 | 3.1.0
190 |
191 |
192 |
195 | com.theoryinpractise
196 | clojure-maven-plugin
197 | 1.7.1
198 | true
199 |
200 | ${clojure.warnOnReflection}
201 | true
202 |
203 |
204 |
205 | clojure-compile
206 | none
207 |
208 |
209 | clojure-test
210 | test
211 |
212 | test
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 | scm:git:git@github.com:clojure/tools.deps.git
222 | scm:git:git@github.com:clojure/tools.deps.git
223 | git@github.com:clojure/tools.deps.git
224 | HEAD
225 |
226 |
227 |
228 |
229 | clojars
230 | https://clojars.org/repo/
231 |
232 |
233 |
234 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/extensions/git.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 ^{:skip-wiki true}
10 | clojure.tools.deps.extensions.git
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.tools.deps.extensions :as ext]
14 | [clojure.tools.gitlibs :as gitlibs]))
15 |
16 | (def ^:private git-services
17 | {:github {:service #"^(?:com|io).github.([^.]+)$" :url "https://github.com/%s/%s.git"}
18 | :gitlab {:service #"^(?:com|io).gitlab.([^.]+)$" :url "https://gitlab.com/%s/%s.git"}
19 | :bitbucket {:service #"^(?:org|io).bitbucket.([^.]+)$" :url "https://bitbucket.org/%s/%s.git"}
20 | :beanstalk {:service #"^(?:com|io).beanstalkapp.([^.]+)$" :url "https://%s.git.beanstalkapp.com/%s.git"}
21 | :sourcehut {:service #"^ht.sr.([^.]+)$" :url "https://git.sr.ht/~%s/%s"}})
22 |
23 | (defn auto-git-url
24 | "Create url from lib name, ie:
25 | io.github.foo/bar => https://github.com/foo/bar.git
26 | or return nil if not a name that can be auto converted to a url."
27 | [lib]
28 | (let [group (namespace lib)
29 | project (name lib)]
30 | (when group
31 | (some (fn [{:keys [service url]}]
32 | (when-let [matches (re-matches service group)]
33 | (format url (second matches) project)))
34 | (vals git-services)))))
35 |
36 | (defn full-sha?
37 | [sha]
38 | (boolean (and sha (= 40 (count sha)))))
39 |
40 | (defmethod ext/coord-type-keys :git
41 | [_type]
42 | #{:git/url :git/sha :git/tag :sha})
43 |
44 | (defn- coord-err
45 | ^Throwable [msg lib coord]
46 | (ex-info msg {:lib lib :coord coord}))
47 |
48 | (defn- make-standard
49 | [coord url sha tag]
50 | (->
51 | (cond-> coord
52 | url (assoc :git/url url)
53 | sha (assoc :git/sha sha)
54 | tag (assoc :git/tag tag))
55 | (dissoc :sha :tag)))
56 |
57 | (defmethod ext/canonicalize :git
58 | [lib {unsha :sha untag :tag :git/keys [url sha tag] :as coord} _config]
59 | (when (nil? (namespace lib)) (throw (coord-err (format "Invalid lib name: %s" lib) lib coord)))
60 | (when (and unsha sha) (throw (coord-err (format "git coord has both :sha and :git/sha for %s" lib) lib coord)))
61 | (when (and untag tag) (throw (coord-err (format "git coord has both :tag and :git/tag for %s" tag) lib coord)))
62 |
63 | (let [canon-sha (or sha unsha)
64 | canon-tag (or tag untag)
65 | canon-url (or url (auto-git-url lib))]
66 | (when (nil? canon-url)
67 | (throw (coord-err (format "Failed to infer git url for: %s" lib) lib coord)))
68 | (when (and canon-tag (not (some #{canon-tag} (gitlibs/tags canon-url))))
69 | (throw (coord-err (format "Library %s has invalid tag: %s" lib canon-tag) lib coord)))
70 | (if canon-sha
71 | (if canon-tag
72 | (let [full-sha (if (full-sha? canon-sha) canon-sha (gitlibs/resolve canon-url canon-sha))]
73 | (when-not (= (gitlibs/resolve canon-url canon-sha) (gitlibs/resolve canon-url canon-tag))
74 | (throw (coord-err (format "Library %s has sha and tag that point to different commits" lib) lib coord)))
75 | [lib (make-standard coord canon-url full-sha canon-tag)])
76 | (if (full-sha? canon-sha)
77 | [lib (make-standard coord canon-url canon-sha canon-tag)]
78 | (throw (ex-info (format "Library %s has prefix sha, use full sha or add tag" lib) {:lib lib :coord coord}))))
79 | (throw (ex-info (format "Library %s has coord with missing sha" lib) {:lib lib :coord coord})))))
80 |
81 | (defn- to-canonical
82 | [lib {:git/keys [url sha] :as coord} config]
83 | (if (and url (full-sha? sha))
84 | [lib coord]
85 | (ext/canonicalize lib coord config)))
86 |
87 | (defmethod ext/lib-location :git
88 | [lib coord config]
89 | (let [[lib {:git/keys [sha]}] (to-canonical lib coord config)]
90 | {:base (str (gitlibs/cache-dir) "/libs") ;; gitlibs repo location is not in a public API...
91 | :path (str lib "/" sha)
92 | :type :git}))
93 |
94 | (defmethod ext/dep-id :git
95 | [lib coord config]
96 | (let [[_lib {:git/keys [url sha]}] (to-canonical lib coord config)]
97 | {:git/url url, :git/sha sha}))
98 |
99 | (defmethod ext/manifest-type :git
100 | [lib coord config]
101 | (let [[lib {:git/keys [url sha] :deps/keys [manifest root]}] (to-canonical lib coord config)]
102 | (when-not url
103 | (throw (ex-info (format ":git/url not found or inferred for %s" lib) {:lib lib :coord coord})))
104 | (let [sha-dir (gitlibs/procure url lib sha)]
105 | (if sha-dir
106 | (let [root-dir (if root
107 | (let [root-file (jio/file root)]
108 | (if (.isAbsolute root-file) ;; should be only after coordinate resolution
109 | (.getCanonicalPath root-file)
110 | (.getCanonicalPath (jio/file sha-dir root-file))))
111 | sha-dir)]
112 | (if manifest
113 | {:deps/manifest manifest, :deps/root root-dir}
114 | (ext/detect-manifest root-dir)))
115 | (throw (ex-info (format "Commit not found for %s in repo %s at %s" lib url sha)
116 | {:lib lib :coord coord}))))))
117 |
118 | (defmethod ext/coord-summary :git [lib coord]
119 | (let [[lib {:git/keys [url tag sha]}] (to-canonical lib coord nil)]
120 | (str lib " " (if tag tag (subs sha 0 7)))))
121 |
122 | (defmethod ext/license-info :git
123 | [lib coord config]
124 | (let [coord (merge coord (ext/manifest-type lib coord config))]
125 | (ext/license-info-mf lib coord (:deps/manifest coord) config)))
126 |
127 | ;; 0 if x and y are the same commit
128 | ;; negative if x is parent of y (y derives from x)
129 | ;; positive if y is parent of x (x derives from y)
130 | (defmethod ext/compare-versions [:git :git]
131 | [lib x-coord y-coord config]
132 | (let [[_ {x-url :git/url, x-sha :git/sha, :as x}] (to-canonical lib x-coord config)
133 | [_ {y-url :git/url, y-sha :git/sha, :as y}] (to-canonical lib y-coord config)]
134 | (if (= x-sha y-sha)
135 | 0
136 | (let [desc (if (= x-url y-url)
137 | (or
138 | (gitlibs/descendant x-url [x-sha y-sha])
139 | (gitlibs/descendant y-url [x-sha y-sha]))
140 | (and
141 | (gitlibs/descendant x-url [x-sha y-sha])
142 | (gitlibs/descendant y-url [x-sha y-sha])))]
143 | (cond
144 | (nil? desc) (throw (ex-info (str "No known ancestor relationship between git versions for " lib "\n"
145 | " " x-url " at " x-sha "\n"
146 | " " y-url " at " y-sha)
147 | {:x x :y y}))
148 | (= desc x-sha) 1
149 | (= desc y-sha) -1)))))
150 |
151 | (defmethod ext/find-versions :git
152 | [lib coord _coord-type config]
153 | (let [url (or (:git/url coord) (auto-git-url lib))]
154 | (when url
155 | (try
156 | (map
157 | (fn [tag] {:git/tag tag :git/sha (subs (gitlibs/commit-sha url tag) 0 7)})
158 | (gitlibs/tags url))
159 | (catch Throwable _ nil)))))
160 |
161 | (comment
162 | (ext/find-versions 'io.github.cognitect-labs/test-runner {} :git nil)
163 |
164 | (ext/find-versions 'org.clojure/spec.alpha
165 | {:git/url "https://github.com/clojure/spec.alpha.git"} :git nil)
166 |
167 | (ext/lib-location 'foo/foo
168 | {:git/url "https://github.com/clojure/core.async.git"
169 | :sha "ecea2539a724a415b15e50f12815b4ab115cfd35"} {})
170 |
171 | (binding [*print-namespace-maps* false]
172 | (run! prn
173 | (ext/find-versions 'io.github.clojure/tools.deps.alpha nil :git nil)))
174 |
175 | ;; error - prefix sha
176 | (ext/canonicalize 'org.clojure/spec.alpha
177 | {:git/url "https://github.com/clojure/spec.alpha.git" :sha "739c1af5"}
178 | nil)
179 |
180 | (ext/dep-id 'org.clojure/spec.alpha
181 | {:git/url "https://github.com/clojure/spec.alpha.git" :sha "739c1af56dae621aedf1bb282025a0d676eff713"}
182 | nil)
183 |
184 | (ext/manifest-type 'org.clojure/spec.alpha
185 | {:git/url "https://github.com/clojure/spec.alpha.git" :git/sha "739c1af56dae621aedf1bb282025a0d676eff713"}
186 | nil)
187 |
188 | (ext/compare-versions
189 | 'org.clojure/spec.alpha
190 | {:git/url "https://github.com/clojure/spec.alpha.git" :git/sha "739c1af56dae621aedf1bb282025a0d676eff713"}
191 | {:git/url "git@github.com:clojure/spec.alpha.git" :git/sha "a65fb3aceec67d1096105cab707e6ad7e5f063af"}
192 | nil)
193 |
194 | )
195 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/script/make_classpath2.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 ^{:skip-wiki true}
10 | clojure.tools.deps.script.make-classpath2
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.pprint :as pprint]
14 | [clojure.tools.cli :as cli]
15 | [clojure.tools.deps :as deps]
16 | [clojure.tools.deps.extensions :as ext]
17 | [clojure.tools.deps.tool :as tool]
18 | [clojure.tools.deps.util.io :as io :refer [printerrln]]
19 | [clojure.tools.deps.script.parse :as parse]
20 | [clojure.tools.deps.tree :as tree])
21 | (:import
22 | [clojure.lang IExceptionInfo]))
23 |
24 | (defn blank-to-nil [s]
25 | (if (zero? (count s)) nil s))
26 |
27 | (def ^:private opts
28 | [;; deps.edn inputs
29 | [nil "--config-user PATH" "User deps.edn location" :parse-fn blank-to-nil]
30 | [nil "--config-project PATH" "Project deps.edn location"]
31 | [nil "--config-data EDN" "Final deps.edn data to treat as the last deps.edn file" :parse-fn parse/parse-config]
32 | ;; tool args to resolve
33 | [nil "--tool-mode" "Tool mode (-T), may optionally supply tool-name or tool-aliases"]
34 | [nil "--tool-name NAME" "Tool name"]
35 | ;; output files
36 | [nil "--cp-file PATH" "Classpatch cache file to write"]
37 | [nil "--jvm-file PATH" "JVM options file"]
38 | [nil "--main-file PATH" "Main options file"]
39 | [nil "--manifest-file PATH" "Manifest list file"]
40 | [nil "--basis-file PATH" "Basis file"]
41 | [nil "--skip-cp" "Skip writing .cp files"]
42 | ;; aliases
43 | ["-A" "--repl-aliases ALIASES" "Concatenated repl alias names" :parse-fn parse/parse-kws]
44 | ["-M" "--main-aliases ALIASES" "Concatenated main option alias names" :parse-fn parse/parse-kws]
45 | ["-X" "--exec-aliases ALIASES" "Concatenated exec alias names" :parse-fn parse/parse-kws]
46 | ["-T" "--tool-aliases ALIASES" "Concatenated tool alias names" :parse-fn parse/parse-kws]
47 | ;; options
48 | [nil "--trace" "Emit trace log to trace.edn"]
49 | [nil "--threads THREADS" "Threads for concurrent downloads"]
50 | [nil "--tree" "Print deps tree to console"]])
51 |
52 | (defn parse-opts
53 | "Parse the command line opts to make-classpath"
54 | [args]
55 | (cli/parse-opts args opts))
56 |
57 | (defn check-aliases
58 | "Check that all aliases are known and warn if aliases are undeclared"
59 | [deps aliases]
60 | (when-let [unknown (seq (remove #(contains? (:aliases deps) %) (distinct aliases)))]
61 | (printerrln "WARNING: Specified aliases are undeclared and are not being used:" (vec unknown))))
62 |
63 | (defn resolve-tool-args
64 | "Resolves the tool by name to the coord + usage data.
65 | Returns the proper alias args as if the tool was specified as an alias."
66 | [tool-name]
67 | (if-let [{:keys [lib coord]} (tool/resolve-tool tool-name)]
68 | (let [config nil ;; for now, tools are only git or local which have none
69 | manifest-type (ext/manifest-type lib coord config)
70 | coord' (merge coord manifest-type)
71 | {:keys [ns-default ns-aliases]} (ext/coord-usage lib coord' (:deps/manifest coord') config)]
72 | {:replace-deps {lib coord'}
73 | :ns-default ns-default
74 | :ns-aliases ns-aliases})
75 | (throw (ex-info (str "Unknown tool: " tool-name) {:tool tool-name}))))
76 |
77 | (defn run-core
78 | "Run make-classpath script from/to data. Returns map w/keys:
79 | :basis ;; the basis, including classpath roots
80 | :trace ;; if requested, trace.edn file
81 | :manifests ;; manifest files used in making classpath"
82 | [{:keys [config-user config-project config-data tool-data
83 | main-aliases exec-aliases repl-aliases tool-aliases
84 | skip-cp threads trace tree] :as _opts}]
85 | (when (and main-aliases exec-aliases)
86 | (throw (ex-info "-M and -X cannot be used at the same time" {})))
87 | (when (and (string? config-data) (not (.exists (jio/file config-data))))
88 | (throw (ex-info (str "Extra deps file not found: " config-data) {})))
89 | (let [combined-exec-aliases (concat main-aliases exec-aliases repl-aliases tool-aliases)
90 | args (cond-> nil
91 | threads (assoc :threads (Long/parseLong threads))
92 | trace (assoc :trace trace)
93 | tree (assoc :trace true)
94 | skip-cp (assoc :skip-cp true)
95 | (or tool-data tool-aliases) (assoc :replace-paths ["."] :replace-deps {})
96 | tool-data (merge tool-data))
97 | basis (deps/create-basis
98 | {:user config-user
99 | :project config-project
100 | :extra config-data
101 | :aliases combined-exec-aliases
102 | :args args})
103 |
104 | ;; check for unused aliases and warn
105 | _ (check-aliases basis combined-exec-aliases)
106 |
107 | libs (:libs basis)
108 | trace (-> libs meta :trace)
109 |
110 | ;; check for unprepped libs
111 | _ (deps/prep-libs! libs {:action :error} basis)
112 |
113 | ;; determine manifest files to add to cache check
114 | manifests (->>
115 | (for [[lib coord] libs]
116 | (let [mf (ext/manifest-type lib coord basis)]
117 | (ext/manifest-file lib coord (:deps/manifest mf) basis)))
118 | (remove nil?)
119 | seq)]
120 | (when (and (-> (:argmap basis) :main-opts seq) repl-aliases)
121 | (io/printerrln "WARNING: Use of :main-opts with -A is deprecated. Use -M instead."))
122 | (cond-> {:basis basis}
123 | trace (assoc :trace trace)
124 | manifests (assoc :manifests manifests))))
125 |
126 | (defn write-lines
127 | [lines file]
128 | (if lines
129 | (io/write-file file (apply str (interleave lines (repeat "\n"))))
130 | (let [jf (jio/file file)]
131 | (when (.exists jf)
132 | (.delete jf)))))
133 |
134 | (defn run
135 | "Run make-classpath script. See -main for details."
136 | [{:keys [cp-file jvm-file main-file basis-file manifest-file skip-cp tool-mode tool-name tool-aliases trace tree] :as opts}]
137 | (let [opts' (cond-> opts
138 | (and tool-mode (not tool-aliases))
139 | (assoc :tool-data (when tool-name (resolve-tool-args tool-name))))
140 | {:keys [basis manifests], trace-log :trace} (run-core opts')
141 | {:keys [argmap libs classpath-roots]} basis
142 | {:keys [jvm-opts main-opts]} argmap]
143 | (when trace
144 | (spit "trace.edn" (binding [*print-namespace-maps* false] (with-out-str (pprint/pprint trace-log)))))
145 | (when tree
146 | (-> trace-log tree/trace->tree (tree/print-tree nil)))
147 | (when-not skip-cp
148 | (io/write-file cp-file (-> classpath-roots deps/join-classpath)))
149 | (io/write-file basis-file (binding [*print-namespace-maps* false] (pr-str basis)))
150 | (write-lines (seq jvm-opts) jvm-file)
151 | (write-lines (seq main-opts) main-file) ;; FUTURE: add check to only do this if main-aliases were passed
152 | (write-lines manifests manifest-file)))
153 |
154 | (defn -main
155 | "Main entry point for make-classpath script.
156 |
157 | Options:
158 | --config-user=path - user deps.edn file (usually ~/.clojure/deps.edn)
159 | --config-project=path - project deps.edn file (usually ./deps.edn)
160 | --config-data={...} - deps.edn as data, or file name (from -Sdeps)
161 | --tool-mode - flag for tool mode
162 | --tool-name - name of tool to run
163 | --cp-file=path - cp cache file to write
164 | --jvm-file=path - jvm opts file to write
165 | --main-file=path - main opts file to write
166 | --manifest-file=path - manifest list file to write
167 | --basis-file=path - basis file to write
168 | -Mmain-aliases - concatenated main-opt alias names
169 | -Aaliases - concatenated repl alias names
170 | -Xaliases - concatenated exec alias names
171 | -Taliases - concatenated tool alias names
172 |
173 | Resolves the dependencies and updates the lib, classpath, etc files.
174 | The cp file is at /.cp
175 | The main opts file is at /.main (if needed)
176 | The jvm opts file is at /.jvm (if needed)
177 | The manifest file is at /.manifest (if needed)"
178 | [& args]
179 | (try
180 | (let [{:keys [options errors]} (parse-opts args)]
181 | (when (seq errors)
182 | (run! println errors)
183 | (System/exit 1))
184 | (run options))
185 | (catch Throwable t
186 | (printerrln "Error building classpath." (.getMessage t))
187 | (when-not (instance? IExceptionInfo t)
188 | (.printStackTrace t))
189 | (System/exit 1))))
190 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/extensions.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 ^{:skip-wiki true}
10 | clojure.tools.deps.extensions
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.set :as set]))
14 |
15 | ;; Helper for autodetect of manifest type
16 |
17 | ;; vector to control ordering
18 | (def manifest-types
19 | ["deps.edn" :deps,
20 | "pom.xml" :pom
21 | ;; "project.clj" :lein
22 | ])
23 |
24 | (defn detect-manifest
25 | "Given a directory, detect the manifest type and return the manifest info."
26 | [dir]
27 | (loop [[file-name manifest-type & others] manifest-types]
28 | (when file-name
29 | (let [f (jio/file dir file-name)]
30 | (if (and (.exists f) (.isFile f))
31 | {:deps/manifest manifest-type, :deps/root dir}
32 | (recur others))))))
33 |
34 | ;; Methods switching on coordinate type
35 |
36 | (defmulti coord-type-keys
37 | "Takes a coordinate type and returns valid set of keys indicating that coord type"
38 | (fn [type] type))
39 |
40 | (defmethod coord-type-keys :default [type] #{})
41 |
42 | (defn procurer-types
43 | "Returns set of registerd procurer types (results may change if procurer methods are registered)."
44 | []
45 | (disj (-> (.getMethodTable ^clojure.lang.MultiFn coord-type-keys) keys set) :default))
46 |
47 | (defn coord-type
48 | "Determine the coordinate type of the coordinate, based on the self-published procurer
49 | keys from coord-type-keys."
50 | [coord]
51 | (when (map? coord)
52 | (let [exts (procurer-types)
53 | coord-keys (-> coord keys set)
54 | matches (reduce (fn [ms type]
55 | (cond-> ms
56 | (seq (set/intersection (coord-type-keys type) coord-keys))
57 | (conj type)))
58 | [] exts)]
59 | (case (count matches)
60 | 0 (throw (ex-info (format "Coord of unknown type: %s" (pr-str coord)) {:coord coord}))
61 | 1 (first matches)
62 | (throw (ex-info (format "Coord type is ambiguous: %s" (pr-str coord)) {:coord coord}))))))
63 |
64 | (defmulti lib-location
65 | "Takes a coordinate and returns the location where the lib would be
66 | installed locally. Location keys:
67 |
68 | :base local repo base directory path
69 | :path path within base dir
70 | :type coordinate type"
71 | (fn [lib coord config] (coord-type coord)))
72 |
73 | (defmulti canonicalize
74 | "Takes a lib and coordinate and returns a canonical form.
75 | For example, a Maven coordinate might resolve LATEST to a version or a Git
76 | coordinate might resolve a partial sha to a full sha. Returns [lib coord]."
77 | (fn [lib coord config] (coord-type coord)))
78 |
79 | (defmethod canonicalize :default [lib coord config]
80 | [lib coord])
81 |
82 | (defmulti dep-id
83 | "Returns an identifier value that can be used to detect a lib/coord cycle while
84 | expanding deps. This will only be called after canonicalization so it can rely
85 | on the canonical form."
86 | (fn [lib coord config] (coord-type coord)))
87 |
88 | (defn- throw-bad-coord
89 | [lib coord]
90 | (if (map? coord)
91 | (let [type (coord-type coord)]
92 | (if (nil? type)
93 | (throw (ex-info (str "No coordinate type found for library " lib " in coordinate " (pr-str coord)) {:lib lib :coord coord}))
94 | (throw (ex-info (str "Coordinate type " type " not loaded for library " lib " in coordinate " (pr-str coord))
95 | {:lib lib :coord coord}))))
96 | (throw (ex-info (str "Bad coordinate for library " lib ", expected map: " (pr-str coord)) {:lib lib :coord coord}))))
97 |
98 | (defmethod dep-id :default [lib coord config]
99 | (throw-bad-coord lib coord))
100 |
101 | (defmulti manifest-type
102 | "Takes a lib, a coord, and the root config. Dispatch based on the
103 | coordinate type. Detect and return the manifest type and location
104 | for this coordinate."
105 | (fn [lib coord config] (coord-type coord)))
106 |
107 | (defmethod manifest-type :default [lib coord config]
108 | (throw-bad-coord lib coord))
109 |
110 | (defmulti coord-summary
111 | "Takes a coord, and returns a concise description, used when printing tree"
112 | (fn [lib coord] (coord-type coord)))
113 |
114 | (defmethod coord-summary :default [lib coord]
115 | (str lib " " (coord-type coord)))
116 |
117 | (defmulti license-info
118 | "Return map of license info (:name and :url) or nil if unknown"
119 | (fn [lib coord config] (coord-type coord)))
120 |
121 | (defmethod license-info :default [lib coord config] nil)
122 |
123 | ;; Version comparison, either within or across coordinate types
124 |
125 | (defmulti compare-versions
126 | "Given two coordinates, use this as a comparator returning a negative number, zero,
127 | or positive number when coord-x is logically 'less than', 'equal to', or 'greater than'
128 | coord-y. The dispatch occurs on the type of x and y."
129 | (fn [lib coord-x coord-y config] [(coord-type coord-x) (coord-type coord-y)]))
130 |
131 | (defmethod compare-versions :default
132 | [lib coord-x coord-y config]
133 | (throw (ex-info (str "Unable to compare versions for " lib ": " (pr-str coord-x) " and " (pr-str coord-y))
134 | {:lib lib :coord-x coord-x :coord-y coord-y})))
135 |
136 | ;; Find coords
137 |
138 | (defmulti find-versions
139 | "Return a coll of coords based on a lib and a partial coord"
140 | (fn [lib coord coord-type config] coord-type))
141 |
142 | (defmethod find-versions :default [lib coord coord-type config]
143 | (throw-bad-coord lib coord))
144 |
145 | (defn find-all-versions
146 | "Find versions across all registered procurer types.
147 | Returns coll of coordinates for this lib (based on lib and partial coordinate).
148 | For each procurer type, coordinates are returned in chronological order."
149 | [lib coord config]
150 | (mapcat #(find-versions lib coord % config) (procurer-types)))
151 |
152 | ;; Methods switching on manifest type
153 |
154 | (defn- throw-bad-manifest
155 | [lib coord manifest-type]
156 | (if manifest-type
157 | (throw (ex-info (str "Manifest type " manifest-type " not loaded for " lib " in coordinate " (pr-str coord))
158 | {:lib lib :coord coord}))
159 | (throw (ex-info (str "Manifest file not found for " lib " in coordinate " (pr-str coord))
160 | {:lib lib :coord coord}))))
161 |
162 | (defmulti coord-deps
163 | "Return coll of immediate [lib coord] external deps for this library."
164 | (fn [lib coord manifest-type config] manifest-type))
165 |
166 | (defmethod coord-deps :default [lib coord manifest-type config]
167 | (throw-bad-manifest lib coord manifest-type))
168 |
169 | (defmulti coord-paths
170 | "Return coll of classpath roots for this library on disk."
171 | (fn [lib coord manifest-type config] manifest-type))
172 |
173 | (defmethod coord-paths :default [lib coord manifest-type config]
174 | (throw-bad-manifest lib coord manifest-type))
175 |
176 | (defmulti manifest-file
177 | "Return path to manifest file (if any). If this file is updated,
178 | causes the cache to be recomputed."
179 | (fn [lib coord manifest-type config] manifest-type))
180 |
181 | (defmethod manifest-file :default [lib coord manifest-type config]
182 | (throw-bad-manifest lib coord manifest-type))
183 |
184 | (defmulti license-info-mf
185 | "Return map of license info (:name and :url) or nil if unknown
186 | based on the manifest."
187 | (fn [lib coord manifest-type config] manifest-type))
188 |
189 | (defmethod license-info-mf :default [lib coord manifest-type config] nil)
190 |
191 | (defmulti coord-usage
192 | "Return usage info map for this library with the following optional keys:
193 | :ns-default - default namespace symbol
194 | :ns-aliases - map of alias to namespace symbol"
195 | (fn [lib coord manifest-type config] manifest-type))
196 |
197 | (defmethod coord-usage :default [lib coord manifest-type config]
198 | (throw-bad-manifest lib coord manifest-type))
199 |
200 | (defmulti prep-command
201 | "Return prep command for this library with the following keys:
202 | :alias - alias to use when invoking (keyword)
203 | :fn - function to invoke in alias (symbol)
204 | :ensure - relative path in repo to ensure exists after prep"
205 | (fn [lib coord manifest-type config] manifest-type))
206 |
207 | (defmethod prep-command :default [lib coord manifest-type config]
208 | (throw-bad-manifest lib coord manifest-type))
209 |
210 | (comment
211 | (require '[clojure.tools.deps.util.maven :as maven])
212 |
213 | (binding [*print-namespace-maps* false]
214 | (run! prn
215 | (find-all-versions 'io.github.clojure/tools.deps.alpha nil {:mvn/repos maven/standard-repos})))
216 |
217 | (binding [*print-namespace-maps* false]
218 | (run! prn
219 | (find-all-versions 'org.clojure/tools.deps.alpha nil {:mvn/repos maven/standard-repos})))
220 |
221 | )
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/gen/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 ^{:skip-wiki true}
10 | clojure.tools.deps.gen.pom
11 | (:require [clojure.java.io :as jio]
12 | [clojure.data.xml :as xml]
13 | [clojure.data.xml.tree :as tree]
14 | [clojure.data.xml.event :as event]
15 | [clojure.zip :as zip]
16 | [clojure.tools.deps.util.maven :as maven]
17 | [clojure.tools.deps.util.io :refer [printerrln]])
18 | (:import [java.io File Reader]
19 | [clojure.data.xml.node Element]))
20 |
21 | (set! *warn-on-reflection* true)
22 |
23 | (xml/alias-uri 'pom "http://maven.apache.org/POM/4.0.0")
24 |
25 | (defn- to-dep
26 | [[lib {:keys [mvn/version exclusions optional] :as coord}]]
27 | (let [[group-id artifact-id classifier] (maven/lib->names lib)]
28 | (if version
29 | (cond->
30 | [::pom/dependency
31 | [::pom/groupId group-id]
32 | [::pom/artifactId artifact-id]
33 | [::pom/version version]]
34 |
35 | classifier
36 | (conj [::pom/classifier classifier])
37 |
38 | (seq exclusions)
39 | (conj [::pom/exclusions
40 | (map (fn [excl]
41 | [::pom/exclusion
42 | [::pom/groupId (or (namespace excl) (name excl))]
43 | [::pom/artifactId (name excl)]])
44 | exclusions)])
45 |
46 | optional
47 | (conj [::pom/optional "true"]))
48 | (printerrln "Skipping coordinate:" coord))))
49 |
50 | (defn- gen-deps
51 | [deps]
52 | [::pom/dependencies
53 | (map to-dep deps)])
54 |
55 | (defn- gen-source-dir
56 | [path]
57 | [::pom/sourceDirectory path])
58 |
59 | (defn- to-repo-policy
60 | [parent-tag {:keys [enabled update checksum]}]
61 | [parent-tag
62 | (when (some? enabled) [::pom/enabled (str enabled)])
63 | (when update [::pom/updatePolicy (if (keyword? update) (name update) (str "interval:" update))])
64 | (when checksum [::pom/checksumPolicy (name checksum)])])
65 |
66 | (defn- to-repo
67 | [[name {:keys [url snapshots releases]}]]
68 | [::pom/repository
69 | [::pom/id name]
70 | [::pom/url url]
71 | (when releases (to-repo-policy ::pom/releases releases))
72 | (when snapshots (to-repo-policy ::pom/snapshots snapshots))])
73 |
74 | (defn- gen-repos
75 | [repos]
76 | [::pom/repositories
77 | (map to-repo repos)])
78 |
79 | (defn- gen-pom
80 | [{:keys [deps src-paths resource-paths repos group artifact version]
81 | :or {version "0.1.0"}}]
82 | (let [[path & paths] src-paths]
83 | (xml/sexp-as-element
84 | [::pom/project
85 | {:xmlns "http://maven.apache.org/POM/4.0.0"
86 | (keyword "xmlns:xsi") "http://www.w3.org/2001/XMLSchema-instance"
87 | (keyword "xsi:schemaLocation") "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"}
88 | [::pom/modelVersion "4.0.0"]
89 | [::pom/packaging "jar"]
90 | [::pom/groupId group]
91 | [::pom/artifactId artifact]
92 | [::pom/version version]
93 | [::pom/name artifact]
94 | (gen-deps deps)
95 | (when path
96 | (when (seq paths) (apply printerrln "Skipping paths:" paths))
97 | [::pom/build (gen-source-dir path)])
98 | (gen-repos repos)])))
99 |
100 | (defn- make-xml-element
101 | [{:keys [tag attrs] :as node} children]
102 | (with-meta
103 | (apply xml/element tag attrs children)
104 | (meta node)))
105 |
106 | (defn- xml-update
107 | [root tag-path replace-node]
108 | (let [z (zip/zipper xml/element? :content make-xml-element root)]
109 | (zip/root
110 | (loop [[tag & more-tags :as tags] tag-path, parent z, child (zip/down z)]
111 | (if child
112 | (if (= tag (:tag (zip/node child)))
113 | (if (seq more-tags)
114 | (recur more-tags child (zip/down child))
115 | (zip/edit child (constantly replace-node)))
116 | (if-let [next-sibling (zip/right child)]
117 | (recur tags parent next-sibling)
118 | (if (seq more-tags)
119 | (let [new-parent (zip/append-child parent (xml/sexp-as-element tag))
120 | new-child (zip/rightmost (zip/down new-parent))]
121 | (recur more-tags new-child (zip/down new-child)))
122 | (zip/append-child parent replace-node))))
123 | (if (seq more-tags)
124 | (let [new-parent (zip/append-child parent (xml/sexp-as-element tag))
125 | new-child (zip/rightmost (zip/down new-parent))]
126 | (recur more-tags new-child (zip/down new-child)))
127 | (zip/append-child parent replace-node)))))))
128 |
129 | (defn- replace-deps
130 | [pom deps]
131 | (xml-update pom [::pom/dependencies] (xml/sexp-as-element (gen-deps deps))))
132 |
133 | (defn- replace-paths
134 | [pom [path & paths]]
135 | (if path
136 | (do
137 | (when (seq paths) (apply printerrln "Skipping paths:" paths))
138 | (xml-update pom [::pom/build ::pom/sourceDirectory] (xml/sexp-as-element (gen-source-dir path))))
139 | pom))
140 |
141 | (defn- replace-repos
142 | [pom repos]
143 | (if (seq repos)
144 | (xml-update pom [::pom/repositories] (xml/sexp-as-element (gen-repos repos)))
145 | pom))
146 |
147 | (defn- replace-lib
148 | [pom lib]
149 | (if lib
150 | (-> pom
151 | (xml-update [::pom/groupId] (xml/sexp-as-element [::pom/groupId (namespace lib)]))
152 | (xml-update [::pom/artifactId] (xml/sexp-as-element [::pom/artifactId (name lib)]))
153 | (xml-update [::pom/name] (xml/sexp-as-element [::pom/name (name lib)])))
154 | pom))
155 |
156 | (defn- replace-version
157 | [pom version]
158 | (if version
159 | (xml-update pom [::pom/version] (xml/sexp-as-element [::pom/version version]))
160 | pom))
161 |
162 | (defn- parse-xml
163 | [^Reader rdr]
164 | (let [roots (tree/seq-tree event/event-element event/event-exit? event/event-node
165 | (xml/event-seq rdr {:include-node? #{:element :characters :comment}
166 | :skip-whitespace true}))]
167 | (first (filter #(instance? Element %) (first roots)))))
168 |
169 | (defn- resolve-path-ref
170 | "Recursively resolve path refs to a coll of paths. Path refs may be:
171 | string - a path
172 | keyword - a path alias or the special alias, :paths
173 | coll of the above"
174 | [path-ref {:keys [paths aliases] :as edn-map}]
175 | (let [alias-map (merge aliases {:paths paths})]
176 | (loop [acc []
177 | [fpath & rpaths] [path-ref]]
178 | (cond
179 | (nil? fpath) acc
180 | (string? fpath) (recur (conj acc fpath) rpaths)
181 | (keyword? fpath) (let [res (get alias-map fpath)]
182 | (if (coll? res)
183 | (recur acc (concat res rpaths))
184 | (recur acc (conj res rpaths))))
185 | (coll? fpath) (recur acc (concat rpaths fpath))))))
186 |
187 | (defn- libs->deps
188 | "Convert libmap to root deps"
189 | [libs]
190 | (reduce-kv
191 | (fn [ret lib {:keys [dependents] :as coord}]
192 | (if (seq dependents)
193 | ret
194 | (assoc ret lib coord)))
195 | {} libs))
196 |
197 | (defn sync-pom
198 | "Creates or synchronizes a pom given a map of :basis and :params.
199 |
200 | From basis, uses:
201 | :deps to build
202 | :paths to build
203 | :mvn/repos to build (omits maven central, included by default)
204 |
205 | Params:
206 | :target-dir Path to target output directory (required)
207 | :src-pom Path to source pom file (optional, default = \"pom.xml\")
208 | :lib Symbol of groupId/artifactId (required for new, optional for existing)
209 | :version String of project version (optional)"
210 | ([{:keys [basis params]}]
211 | (let [{:keys [libs paths :mvn/repos]} basis
212 | root-deps (libs->deps libs)
213 | {:keys [target-dir src-pom lib version] :or {src-pom "pom.xml"}} params
214 | resolved-paths (resolve-path-ref paths basis)
215 | repos (remove #(= "https://repo1.maven.org/maven2/" (-> % val :url)) repos)
216 | pom-file (jio/file src-pom)
217 | pom (if (.exists pom-file)
218 | (with-open [rdr (jio/reader pom-file)]
219 | (-> rdr
220 | parse-xml
221 | (replace-deps root-deps)
222 | (replace-paths resolved-paths)
223 | (replace-repos repos)
224 | (replace-lib lib)
225 | (replace-version version)))
226 | (gen-pom
227 | (cond->
228 | {:deps root-deps
229 | :src-paths resolved-paths
230 | :repos repos
231 | :group (namespace lib)
232 | :artifact (name lib)}
233 | version (assoc :version version))))
234 | target-pom (jio/file target-dir "pom.xml")]
235 | (spit target-pom (xml/indent-str pom))))
236 |
237 | ;; deprecated arity
238 | ([{:keys [deps paths :mvn/repos] :as deps-edn} ^File dir]
239 | (let [artifact-name (.. dir getCanonicalFile getName)
240 | resolved-paths (resolve-path-ref paths deps-edn)
241 | src-pom (jio/file dir "pom.xml")]
242 | (sync-pom {:basis {:deps deps
243 | :libs deps
244 | :paths resolved-paths
245 | :mvn/repos repos}
246 | :params (merge
247 | {:target-dir (.getCanonicalPath dir)}
248 | (if (.exists src-pom)
249 | {:src-pom (.getCanonicalPath src-pom)}
250 | {:lib (symbol artifact-name artifact-name)}))}))))
251 |
252 | (comment
253 | (require '[clojure.tools.deps.alpha :as deps])
254 | (let [{:keys [install-edn user-edn project-edn]} (deps/find-edn-maps)
255 | edn (deps/merge-edns [install-edn user-edn project-edn])
256 | basis (deps/calc-basis edn)]
257 | (sync-pom basis (jio/file ".")))
258 |
259 | (let [{:keys [install-edn user-edn project-edn]} (deps/find-edn-maps)
260 | edn (deps/merge-edns [install-edn user-edn project-edn])
261 | basis (deps/calc-basis edn)]
262 | (sync-pom
263 | {:basis basis
264 | :params {:src-pom "../../tmp/pom2.xml"
265 | :target-dir "../../tmp"
266 | :lib 'foo/bar
267 | :version "1.2.3"}}))
268 | )
269 |
--------------------------------------------------------------------------------
/src/main/clojure/clojure/tools/deps/extensions/maven.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 ^{:skip-wiki true}
10 | clojure.tools.deps.extensions.maven
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.string :as str]
14 | [clojure.tools.deps.extensions :as ext]
15 | [clojure.tools.deps.util.maven :as maven]
16 | [clojure.tools.deps.util.session :as session])
17 | (:import
18 | [java.io File]
19 |
20 | ;; maven-resolver-api
21 | [org.eclipse.aether RepositorySystem RepositorySystemSession]
22 | [org.eclipse.aether.resolution ArtifactRequest ArtifactDescriptorRequest VersionRangeRequest
23 | VersionRequest ArtifactResolutionException ArtifactDescriptorResult]
24 | [org.eclipse.aether.version Version]
25 |
26 | ;; maven-resolver-util
27 | [org.eclipse.aether.util.version GenericVersionScheme]
28 | [org.apache.maven.settings Settings]))
29 |
30 | (set! *warn-on-reflection* true)
31 |
32 | ;; Main extension points for using Maven deps
33 |
34 | (defmethod ext/coord-type-keys :mvn
35 | [_type]
36 | #{:mvn/version})
37 |
38 | (defn- specific-version
39 | [version]
40 | (second (re-matches #"^\[([^,]*)]$" version)))
41 |
42 | (defn- resolve-version-range
43 | ;; only call when version is a range
44 | [lib {:keys [mvn/version] :as coord} {:keys [mvn/local-repo mvn/repos] :as config}]
45 | (let [local-repo (or local-repo @maven/cached-local-repo)
46 | system ^RepositorySystem (session/retrieve-local :mvn/system #(maven/make-system))
47 | settings ^Settings (session/retrieve :mvn/settings #(maven/get-settings))
48 | session ^RepositorySystemSession (session/retrieve-local :mvn/session #(maven/make-session system settings local-repo))]
49 | (or
50 | (session/retrieve {:type :mvn/highest-version lib version}
51 | (fn []
52 | (let [artifact (maven/coord->artifact lib coord)
53 | req (VersionRangeRequest. artifact (maven/remote-repos repos settings) nil)
54 | result (.resolveVersionRange system session req)
55 | high-version (and result (.getHighestVersion result))]
56 | (when high-version (.toString ^Version high-version)))))
57 | (throw (ex-info (str "Unable to resolve " lib " version: " version) {:lib lib :coord coord})))))
58 |
59 | (defmethod ext/canonicalize :mvn
60 | [lib {:keys [mvn/version] :as coord} {:keys [mvn/repos mvn/local-repo] :as config}]
61 | (let [specific (specific-version version)]
62 | (cond
63 | (contains? #{"RELEASE" "LATEST"} version)
64 | (let [local-repo (or local-repo @maven/cached-local-repo)
65 | system ^RepositorySystem (session/retrieve-local :mvn/system #(maven/make-system))
66 | settings ^Settings (session/retrieve :mvn/settings #(maven/get-settings))
67 | session ^RepositorySystemSession (session/retrieve-local :mvn/session #(maven/make-session system settings local-repo))
68 | artifact (maven/coord->artifact lib coord)
69 | req (VersionRequest. artifact (maven/remote-repos repos settings) nil)
70 | result (.resolveVersion system session req)]
71 | (if result
72 | [lib (assoc coord :mvn/version (.getVersion result))]
73 | (throw (ex-info (str "Unable to resolve " lib " version: " version) {:lib lib :coord coord}))))
74 |
75 | ;; cuts down on version range requests when we're not going to honor it anyways
76 | specific
77 | [lib (assoc coord :mvn/version specific)]
78 |
79 | (maven/version-range? version)
80 | [lib (assoc coord :mvn/version (resolve-version-range lib coord config))]
81 |
82 | :else
83 | [lib coord])))
84 |
85 | (defmethod ext/lib-location :mvn
86 | [lib {:keys [mvn/version]} {:keys [mvn/local-repo]}]
87 | (let [[group-id artifact-id classifier] (maven/lib->names lib)]
88 | {:base (or local-repo @maven/cached-local-repo)
89 | :path (.getPath ^File
90 | (apply jio/file
91 | (concat (str/split group-id #"\.") [artifact-id version])))
92 | :classifier classifier
93 | :type :mvn}))
94 |
95 | (defmethod ext/dep-id :mvn
96 | [_lib coord _config]
97 | (select-keys coord [:mvn/version]))
98 |
99 | (defmethod ext/manifest-type :mvn
100 | [_lib _coord _config]
101 | {:deps/manifest :mvn})
102 |
103 | (defmethod ext/coord-summary :mvn [lib {:keys [mvn/version]}]
104 | (str lib " " version))
105 |
106 | (defn- read-descriptor
107 | ^ArtifactDescriptorResult [lib coord {:keys [mvn/repos mvn/local-repo]}]
108 | (let [local-repo (or local-repo @maven/cached-local-repo)
109 | system ^RepositorySystem (session/retrieve-local :mvn/system #(maven/make-system))
110 | settings ^Settings (session/retrieve :mvn/settings #(maven/get-settings))
111 | session ^RepositorySystemSession (session/retrieve-local :mvn/session #(maven/make-session system settings local-repo))
112 | artifact (maven/coord->artifact lib coord)
113 | repos (maven/remote-repos repos settings)
114 | req (ArtifactDescriptorRequest. artifact repos nil)]
115 | (.readArtifactDescriptor system session req)))
116 |
117 | (defn- check-version
118 | [lib {:keys [mvn/version]}]
119 | (when (nil? version)
120 | (throw (ex-info (str "No :mvn/version specified for " lib) {}))))
121 |
122 | (defmethod ext/license-info :mvn
123 | [lib coord config]
124 | (check-version lib coord)
125 | (let [descriptor (read-descriptor lib coord config)
126 | props (.getProperties descriptor)
127 | name (some-> props (.get "license.0.name"))
128 | url (some-> props (.get "license.0.url"))]
129 | (when (or name url)
130 | (cond-> {} name (assoc :name name) url (assoc :url url)))))
131 |
132 | (defonce ^:private version-scheme (GenericVersionScheme.))
133 |
134 | (defn- parse-version [{version :mvn/version :as _coord}]
135 | (.parseVersion ^GenericVersionScheme version-scheme ^String version))
136 |
137 | (defmethod ext/compare-versions [:mvn :mvn]
138 | [lib coord-x coord-y _config]
139 | (check-version lib coord-x)
140 | (check-version lib coord-y)
141 | (apply compare (map parse-version [coord-x coord-y])))
142 |
143 | (defmethod ext/coord-deps :mvn
144 | [lib coord _manifest config]
145 | (check-version lib coord)
146 | (let [descriptor (read-descriptor lib coord config)]
147 | (into []
148 | (comp
149 | (map maven/dep->data)
150 | (filter #(contains? #{"compile" "runtime"} (:scope (second %))))
151 | (remove (comp :optional second))
152 | (map #(update-in % [1] dissoc :scope :optional)))
153 | (.getDependencies descriptor))))
154 |
155 | (defn- get-artifact
156 | [lib coord ^RepositorySystem system ^RepositorySystemSession session mvn-repos]
157 | (check-version lib coord)
158 | (try
159 | (let [artifact (maven/coord->artifact lib coord)
160 | req (ArtifactRequest. artifact mvn-repos nil)
161 | result (.resolveArtifact system session req)]
162 | (cond
163 | (.isResolved result) (.. result getArtifact getFile getAbsolutePath)
164 | (.isMissing result) (throw (ex-info (str "Unable to download: [" lib (pr-str (:mvn/version coord)) "]") {:lib lib :coord coord}))
165 | :else (throw (first (.getExceptions result)))))
166 | (catch ArtifactResolutionException e
167 | (throw (ex-info (.getMessage e) {:lib lib, :coord coord})))))
168 |
169 | (defmethod ext/coord-paths :mvn
170 | [lib {:keys [extension] :or {extension "jar"} :as coord} _manifest {:keys [mvn/repos mvn/local-repo]}]
171 | (check-version lib coord)
172 | (when (contains? #{"jar"} extension)
173 | (let [local-repo (or local-repo @maven/cached-local-repo)
174 | system ^RepositorySystem (session/retrieve-local :mvn/system #(maven/make-system))
175 | settings ^Settings (session/retrieve :mvn/settings #(maven/get-settings))
176 | session ^RepositorySystemSession (session/retrieve-local :mvn/session #(maven/make-session system settings local-repo))
177 | mvn-repos (maven/remote-repos repos settings)]
178 | [(get-artifact lib coord system session mvn-repos)])))
179 |
180 | (defmethod ext/manifest-file :mvn
181 | [_lib {:keys [deps/root] :as _coord} _mf _config]
182 | nil)
183 |
184 | (defmethod ext/find-versions :mvn
185 | [lib _coord _coord-type {:keys [mvn/repos mvn/local-repo]}]
186 | (let [local-repo (or local-repo maven/default-local-repo)
187 | system ^RepositorySystem (session/retrieve-local :mvn/system #(maven/make-system))
188 | settings ^Settings (session/retrieve :mvn/settings #(maven/get-settings))
189 | session ^RepositorySystemSession (session/retrieve-local :mvn/session #(maven/make-session system settings local-repo))
190 | artifact (maven/coord->artifact lib {:mvn/version "(,]"})
191 | req (VersionRangeRequest. artifact (maven/remote-repos repos settings) nil)
192 | result (.resolveVersionRange system session req)
193 | versions (.getVersions result)]
194 | (when (seq versions)
195 | (->> versions
196 | (map str)
197 | (remove #(str/ends-with? % "-SNAPSHOT"))
198 | (map #(hash-map :mvn/version %))
199 | (into [])))))
200 |
201 | (defmethod ext/coord-usage :mvn
202 | [lib {:keys [deps/root]} manifest-type config]
203 | ;; TBD - could look in jar, could download well-known classifier
204 | nil)
205 |
206 | (defmethod ext/prep-command :mvn
207 | [lib {:keys [deps/root]} manifest-type config]
208 | ;; TBD - could look in jar, could download well-known classifier
209 | nil)
210 |
211 | (comment
212 | (ext/lib-location 'org.clojure/clojure {:mvn/version "1.8.0"} {})
213 |
214 | (binding [*print-namespace-maps* false]
215 | (run! prn
216 | (ext/find-versions 'org.clojure/clojure nil :mvn {:mvn/repos maven/standard-repos})))
217 |
218 | ;; given a dep, find the child deps
219 | (ext/coord-deps 'org.clojure/clojure {:mvn/version "1.9.0-alpha17"} :mvn {:mvn/repos maven/standard-repos})
220 |
221 | (ext/coord-deps 'cider/cider-nrepl {:mvn/version "0.17.0-SNAPSHOT"} :mvn {:mvn/repos maven/standard-repos})
222 | (ext/canonicalize 'joda-time/joda-time {:mvn/version "[2.2,)"} {:mvn/repos maven/standard-repos})
223 |
224 | ;; give a dep, download just that dep (not transitive - that's handled by the core algorithm)
225 | (ext/coord-paths 'org.clojure/clojure {:mvn/version "1.9.0-alpha17"} :mvn {:mvn/repos maven/standard-repos})
226 |
227 | ;; get specific classifier
228 | (ext/coord-paths 'org.jogamp.gluegen/gluegen-rt$natives-linux-amd64 {:mvn/version "2.3.2"}
229 | :mvn {:mvn/repos maven/standard-repos})
230 |
231 | (parse-version {:mvn/version "1.1.0"})
232 |
233 | (ext/compare-versions 'org.clojure/clojure {:mvn/version "1.1.0-alpha10"} {:mvn/version "1.1.0-beta1"} {})
234 |
235 | (ext/coord-deps 'org.clojure/clojure {:mvn/version "1.10.0-master-SNAPSHOT"} :mvn
236 | {:mvn/repos (merge maven/standard-repos
237 | {"sonatype-oss-public" {:url "https://oss.sonatype.org/content/groups/public/"}})})
238 |
239 | (def rr (maven/remote-repo ["sonatype-oss-public" {:url "https://oss.sonatype.org/content/groups/public/"}])))
240 |
241 |
242 |
--------------------------------------------------------------------------------
/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/main/clojure/clojure/tools/deps/util/maven.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 ^{:skip-wiki true}
10 | clojure.tools.deps.util.maven
11 | (:require
12 | [clojure.java.io :as jio]
13 | [clojure.string :as str]
14 | [clojure.tools.deps.util.io :refer [printerrln]]
15 | clojure.tools.deps.util.s3-transporter)
16 | (:import
17 | ;; maven-resolver-api
18 | [org.eclipse.aether RepositorySystem RepositorySystemSession DefaultRepositoryCache DefaultRepositorySystemSession ConfigurationProperties]
19 | [org.eclipse.aether.artifact Artifact DefaultArtifact]
20 | [org.eclipse.aether.repository LocalRepository Proxy RemoteRepository RemoteRepository$Builder RepositoryPolicy]
21 | [org.eclipse.aether.graph Dependency Exclusion]
22 | [org.eclipse.aether.transfer TransferListener TransferEvent]
23 |
24 | ;; maven-resolver-spi
25 | [org.eclipse.aether.spi.connector RepositoryConnectorFactory]
26 | [org.eclipse.aether.spi.connector.transport TransporterFactory]
27 | [org.eclipse.aether.spi.locator ServiceLocator]
28 |
29 | ;; maven-resolver-impl
30 | [org.eclipse.aether.impl DefaultServiceLocator]
31 |
32 | ;; maven-resolver-connector-basic
33 | [org.eclipse.aether.connector.basic BasicRepositoryConnectorFactory]
34 |
35 | ;; maven-resolver-transport-file
36 | [org.eclipse.aether.transport.file FileTransporterFactory]
37 |
38 | ;; maven-resolver-transport-http
39 | [org.eclipse.aether.transport.http HttpTransporterFactory]
40 |
41 | ;; maven-aether-provider
42 | [org.apache.maven.repository.internal MavenRepositorySystemUtils]
43 |
44 | ;; maven-resolver-util
45 | [org.eclipse.aether.util.repository AuthenticationBuilder DefaultProxySelector DefaultMirrorSelector]
46 |
47 | ;; maven-core
48 | [org.apache.maven.settings DefaultMavenSettingsBuilder Settings Server Mirror]
49 |
50 | ;; maven-settings-builder
51 | [org.apache.maven.settings.building DefaultSettingsBuilderFactory]
52 |
53 | ;; plexus-utils
54 | [org.codehaus.plexus.util.xml Xpp3Dom]))
55 |
56 |
57 | (set! *warn-on-reflection* true)
58 |
59 | ;; Remote repositories
60 |
61 | (def standard-repos {"central" {:url "https://repo1.maven.org/maven2/"}
62 | "clojars" {:url "https://repo.clojars.org/"}})
63 |
64 | (defn- set-settings-builder
65 | [^DefaultMavenSettingsBuilder default-builder settings-builder]
66 | (doto (.. default-builder getClass (getDeclaredField "settingsBuilder"))
67 | (.setAccessible true)
68 | (.set default-builder settings-builder)))
69 |
70 | (defn get-settings
71 | ^Settings []
72 | (.buildSettings
73 | (doto (DefaultMavenSettingsBuilder.)
74 | (set-settings-builder (.newInstance (DefaultSettingsBuilderFactory.))))))
75 |
76 | (defn- select-mirror
77 | ^RemoteRepository [^Settings settings ^RemoteRepository repo]
78 | (let [mirrors (.getMirrors settings)
79 | selector (DefaultMirrorSelector.)]
80 | (run! (fn [^Mirror mirror] (.add selector
81 | (.getId mirror)
82 | (.getUrl mirror)
83 | (.getLayout mirror)
84 | false
85 | (.getMirrorOf mirror)
86 | (.getMirrorOfLayouts mirror)))
87 | mirrors)
88 | (.getMirror selector repo)))
89 |
90 | (defn- select-proxy
91 | ^Proxy [^Settings settings ^RemoteRepository repo]
92 | (->> (.getProxies settings)
93 | (keep (fn [^org.apache.maven.settings.Proxy proxy-setting]
94 | (when (.isActive proxy-setting)
95 | (.. (DefaultProxySelector.)
96 | (add (Proxy. (.getProtocol proxy-setting)
97 | (.getHost proxy-setting)
98 | (.getPort proxy-setting)
99 | (.. (AuthenticationBuilder.)
100 | (addUsername (.getUsername proxy-setting))
101 | (addPassword (.getPassword proxy-setting))
102 | build))
103 | (.getNonProxyHosts proxy-setting))
104 | (getProxy repo)))))
105 | first))
106 |
107 | (defn- repo-policy
108 | "Converts repo policy map to RepositoryPolicy.
109 | :enabled - is enabled (default = true)
110 | :update - one of :daily (default), :always, :never, or an interval in minutes
111 | :checksum - one of :warn (default), :fail, :ignore"
112 | [name {:keys [enabled update checksum]
113 | :or {enabled true, update :daily, checksum :warn}}]
114 | (RepositoryPolicy. enabled
115 | (case update
116 | :daily RepositoryPolicy/UPDATE_POLICY_DAILY
117 | :always RepositoryPolicy/UPDATE_POLICY_ALWAYS
118 | :never RepositoryPolicy/UPDATE_POLICY_NEVER
119 | (str update))
120 | (case checksum
121 | :warn RepositoryPolicy/CHECKSUM_POLICY_WARN
122 | :fail RepositoryPolicy/CHECKSUM_POLICY_FAIL
123 | :ignore RepositoryPolicy/CHECKSUM_POLICY_IGNORE
124 | (throw (ex-info (format "Invalid checksum policy: %s on repository: %s" checksum name)
125 | {:name name
126 | :enabled enabled
127 | :update update
128 | :checksum checksum})))))
129 |
130 | (defn remote-repo
131 | (^RemoteRepository [repo-entry]
132 | (remote-repo repo-entry (get-settings)))
133 | (^RemoteRepository [[^String name {:keys [url snapshots releases] :as repo-config}] ^Settings settings]
134 | (when (and (str/starts-with? url "http:") (nil? (System/getenv "CLOJURE_CLI_ALLOW_HTTP_REPO")))
135 | (throw (ex-info (str "Invalid repo url (http not supported): " url) (or repo-config {}))))
136 | (let [builder (RemoteRepository$Builder. name "default" url)
137 | maybe-repo (.build builder)
138 | mirror (select-mirror settings maybe-repo)
139 | ^RemoteRepository remote-repo (or mirror maybe-repo)
140 | proxy (select-proxy settings remote-repo)
141 | server-id (.getId remote-repo)
142 | ^Server server-setting (->> (.getServers settings) (filter #(= server-id (.getId ^Server %))) first)]
143 | (->
144 | (cond-> builder
145 | snapshots (.setSnapshotPolicy (repo-policy name snapshots))
146 | releases (.setReleasePolicy (repo-policy name releases))
147 | mirror (.setUrl (.getUrl mirror))
148 | server-setting (.setAuthentication
149 | (-> (AuthenticationBuilder.)
150 | (.addUsername (.getUsername server-setting))
151 | (.addPassword (.getPassword server-setting))
152 | (.addPrivateKey (.getPrivateKey server-setting) (.getPassphrase server-setting))
153 | (.build)))
154 | proxy (.setProxy proxy))
155 | (.build)))))
156 |
157 |
158 | (defn remote-repos
159 | ([repos]
160 | (remote-repos repos (get-settings)))
161 | ([{:strs [central clojars] :as repos} ^Settings settings]
162 | ;; always return central, then clojars, then other repos
163 | (->> (concat [["central" central] ["clojars" clojars]] (dissoc repos "central" "clojars"))
164 | (remove (fn [[_name config]] (nil? config)))
165 | (mapv #(remote-repo % settings)))))
166 |
167 | ;; Local repository
168 |
169 | (defn ^:private local-repo-path
170 | "Helper to form the path to the default local repo - use `@cached-local-repo` for
171 | caching delayed value"
172 | []
173 | (.getAbsolutePath (jio/file (System/getProperty "user.home") ".m2" "repository")))
174 |
175 | (def default-local-repo
176 | "DEPRECATED - use `@cached-local-repo`"
177 | (local-repo-path))
178 |
179 | (def cached-local-repo
180 | "Delayed default local repo lookup for ~/.m2/repository, access with `@cached-local-repo`"
181 | (delay (local-repo-path)))
182 |
183 | (defn make-local-repo
184 | ^LocalRepository [^String dir]
185 | (LocalRepository. dir))
186 |
187 | ;; Maven system and session
188 |
189 | (defn make-locator
190 | ^ServiceLocator []
191 | (let [^DefaultServiceLocator loc
192 | (doto (MavenRepositorySystemUtils/newServiceLocator)
193 | (.addService RepositoryConnectorFactory BasicRepositoryConnectorFactory)
194 | (.addService TransporterFactory FileTransporterFactory)
195 | (.addService TransporterFactory HttpTransporterFactory))]
196 | (try
197 | (let [c (Class/forName "clojure.tools.deps.util.S3TransporterFactory")]
198 | (.addService loc TransporterFactory c))
199 | (catch ClassNotFoundException _
200 | (printerrln "Warning: failed to load the S3TransporterFactory class")
201 | loc))))
202 |
203 | (def the-locator
204 | (delay (make-locator)))
205 |
206 | (defn make-system
207 | (^RepositorySystem []
208 | (make-system (make-locator)))
209 | (^RepositorySystem [^ServiceLocator locator]
210 | (.getService locator RepositorySystem)))
211 |
212 | (def ^TransferListener console-listener
213 | (reify TransferListener
214 | (transferStarted [_ event]
215 | (let [event ^TransferEvent event
216 | resource (.getResource event)
217 | name (.getResourceName resource)
218 | repo (.getRepositoryId resource)]
219 | (printerrln "Downloading:" name "from" repo)))
220 | (transferCorrupted [_ event]
221 | (printerrln "Download corrupted:" (.. ^TransferEvent event getException getMessage)))
222 | (transferFailed [_ event]
223 | ;; This happens when Maven can't find an artifact in a particular repo
224 | ;; (but still may find it in a different repo), ie this is a common event
225 | #_(printerrln "Download failed:" (.. ^TransferEvent event getException getMessage)))
226 | (transferInitiated [_ _event])
227 | (transferProgressed [_ _event])
228 | (transferSucceeded [_ _event])))
229 |
230 | (defn add-server-config [^DefaultRepositorySystemSession session ^Server server]
231 | (when-let [^Xpp3Dom configuration (.getConfiguration server)]
232 | (when-let [^Xpp3Dom headers (.getChild configuration "httpHeaders")]
233 | (.setConfigProperty session
234 | (str ConfigurationProperties/HTTP_HEADERS "." (.getId server))
235 | (into {}
236 | (keep (fn [^Xpp3Dom header]
237 | (let [name (.getChild header "name")
238 | value (.getChild header "value")]
239 | (when (and name value)
240 | [(.getValue name) (.getValue value)]))))
241 | (.getChildren headers "property"))))))
242 |
243 | (defn make-session
244 | (^RepositorySystemSession [^RepositorySystem system local-repo] ;; DEPRECATED
245 | (make-session system (get-settings) local-repo))
246 | (^RepositorySystemSession [^RepositorySystem system ^Settings settings local-repo]
247 | (let [session (MavenRepositorySystemUtils/newSession)
248 | local-repo-mgr (.newLocalRepositoryManager system session (make-local-repo local-repo))]
249 | (.setLocalRepositoryManager session local-repo-mgr)
250 | (.setTransferListener session console-listener)
251 | (.setCache session (DefaultRepositoryCache.))
252 | (doseq [^Server server (.getServers settings)]
253 | (add-server-config session server))
254 | session)))
255 |
256 | (defn exclusions->data
257 | [exclusions]
258 | (when (and exclusions (pos? (count exclusions)))
259 | (into #{}
260 | (map (fn [^Exclusion exclusion]
261 | (symbol (.getGroupId exclusion) (.getArtifactId exclusion))))
262 | exclusions)))
263 |
264 | (defn dep->data
265 | [^Dependency dep]
266 | (let [scope (.getScope dep)
267 | optional (.isOptional dep)
268 | exclusions (exclusions->data (.getExclusions dep))
269 | ^Artifact artifact (.getArtifact dep)
270 | artifact-id (.getArtifactId artifact)
271 | classifier (.getClassifier artifact)
272 | ext (.getExtension artifact)]
273 | [(symbol (.getGroupId artifact) (if (str/blank? classifier) artifact-id (str artifact-id "$" classifier)))
274 | (cond-> {:mvn/version (.getVersion artifact)}
275 | (not= "jar" ext) (assoc :extension ext)
276 | scope (assoc :scope scope)
277 | optional (assoc :optional true)
278 | (seq exclusions) (assoc :exclusions exclusions))]))
279 |
280 | (defn lib->names
281 | "Split lib symbol into [group-id artifact-id classifier]"
282 | [lib]
283 | (let [[artifact-id classifier] (str/split (name lib) #"\$")]
284 | [(or (namespace lib) artifact-id) artifact-id classifier]))
285 |
286 | (defn coord->artifact
287 | ^Artifact [lib {:keys [mvn/version classifier extension] :or {extension "jar"} :as coord}]
288 | (when classifier
289 | (throw (ex-info (str "Invalid library spec:\n"
290 | (format " %s %s\n" lib (dissoc coord :deps/manifest))
291 | ":classifier in Maven coordinates is no longer supported.\n"
292 | "Use groupId/artifactId$classifier in lib names instead.")
293 | {:lib lib, :coord coord})))
294 | (let [[group-id artifact-id classifier] (lib->names lib)
295 | version (or version "LATEST")
296 | artifact (DefaultArtifact. group-id artifact-id classifier extension version)]
297 | artifact))
298 |
299 | (defn version-range?
300 | [version]
301 | (boolean (when version (re-find #"\[|\(" version))))
302 |
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/script/test_make_classpath2.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.deps.script.test-make-classpath2
2 | (:require
3 | [clojure.test :refer [deftest is]]
4 | [clojure.tools.deps.script.make-classpath2 :as mc]
5 | [clojure.java.io :as jio])
6 | (:import
7 | [java.io File]))
8 |
9 | (defn submap?
10 | "Is m1 a subset of m2?"
11 | [m1 m2]
12 | (if (and (map? m1) (map? m2))
13 | (every? (fn [[k v]] (and (contains? m2 k)
14 | (submap? v (get m2 k))))
15 | m1)
16 | (= m1 m2)))
17 |
18 | (deftest outside-project
19 | (let [{:keys [basis]} (mc/run-core {:config-user nil})]
20 | (is (submap?
21 | {:libs {'org.clojure/clojure {}
22 | 'org.clojure/spec.alpha {}
23 | 'org.clojure/core.specs.alpha {}}}
24 | basis))))
25 |
26 | (deftest in-project
27 | (let [{:keys [basis]} (mc/run-core {:config-user nil
28 | :config-project {:deps {'org.clojure/clojure {:mvn/version "1.9.0"}}}})]
29 | (is (submap?
30 | {:libs {'org.clojure/clojure {:mvn/version "1.9.0"}
31 | 'org.clojure/spec.alpha {}
32 | 'org.clojure/core.specs.alpha {}}}
33 | basis))))
34 |
35 | ;; alias :e with :extra-deps extends the project deps
36 | (deftest extra-deps
37 | (let [{:keys [basis]} (mc/run-core {:config-project {:deps {'org.clojure/clojure {:mvn/version "1.9.0"}}
38 | :aliases {:e {:extra-deps {'org.clojure/test.check {:mvn/version "0.9.0"}}}}}
39 | :repl-aliases [:e]})]
40 | (is (submap?
41 | {:libs {'org.clojure/clojure {:mvn/version "1.9.0"}
42 | 'org.clojure/spec.alpha {}
43 | 'org.clojure/core.specs.alpha {}
44 | 'org.clojure/test.check {:mvn/version "0.9.0"}}}
45 | basis))))
46 |
47 | ;; alias :t with :replace-deps replaces the project deps and paths
48 | (deftest tool-deps
49 | (doseq [k [:deps :replace-deps]] ;; synonyms
50 | (let [{:keys [basis]} (mc/run-core {:config-project {:deps {'org.clojure/clojure {:mvn/version "1.9.0"}}
51 | :aliases {:t {k {'org.clojure/test.check {:mvn/version "0.9.0"}}}}}
52 | :tool-aliases [:t]})]
53 | (is (submap?
54 | {:paths ["."]
55 | :deps {'org.clojure/test.check {:mvn/version "0.9.0"}}
56 | :libs {'org.clojure/test.check {:mvn/version "0.9.0"}}}
57 | basis)))))
58 |
59 | ;; simulate named tool
60 | (deftest tool-deps-named
61 | (doseq [k [:deps :replace-deps]] ;; synonyms
62 | (let [{:keys [basis]} (mc/run-core {:config-project {:deps {'org.clojure/clojure {:mvn/version "1.9.0"}}}
63 | :tool-data {k {'org.clojure/test.check {:mvn/version "0.9.0"}}
64 | :replace-paths ["."]}})]
65 | (is (submap?
66 | {:paths ["."]
67 | :deps {'org.clojure/test.check {:mvn/version "0.9.0"}}
68 | :libs {'org.clojure/test.check {:mvn/version "0.9.0"}}}
69 | basis)))))
70 |
71 | ;; alias :o with :override-deps overrides the version to use
72 | (deftest override-deps
73 | (let [{:keys [basis]} (mc/run-core {:config-project {:aliases {:o {:override-deps {'org.clojure/clojure {:mvn/version "1.6.0"}}}}}
74 | :repl-aliases [:o]})]
75 | (is (submap?
76 | {:libs {'org.clojure/clojure {:mvn/version "1.6.0"}}}
77 | basis))))
78 |
79 | (defn select-cp
80 | [classpath key]
81 | (->> classpath (filter #(contains? (val %) key)) (apply conj {})))
82 |
83 | ;; paths and deps in alias replace
84 | (deftest alias-paths-and-deps
85 | (doseq [p [:paths :replace-paths]
86 | d [:deps :replace-deps]]
87 | (let [{:keys [basis]} (mc/run-core {:config-project {:paths ["a" "b"]
88 | :aliases {:q {p ["a" "c"]
89 | d {'org.clojure/clojure {:mvn/version "1.6.0"}}}}}
90 | :repl-aliases [:q]})]
91 | (is (submap?
92 | {:paths ["a" "c"]
93 | :deps {'org.clojure/clojure {:mvn/version "1.6.0"}}}
94 | basis))
95 | (is (= #{"a" "c"} (-> basis :classpath (select-cp :path-key) keys set))))))
96 |
97 | ;; paths replace in chain
98 | (deftest paths-replace
99 | (let [{:keys [basis]} (mc/run-core {:config-user {:paths ["x"]}
100 | :config-project {:paths ["y"]}
101 | :config-data {:paths ["z"]}})]
102 | (is (submap? {:paths ["z"]} basis))
103 | (is (= #{"z"} (-> basis :classpath (select-cp :path-key) keys set)))))
104 |
105 | ;; :paths in alias replaces, multiple alias :paths will be combined
106 | (deftest alias-paths-replace
107 | (doseq [p [:paths :replace-paths]] ;; FUTURE - remove :paths here (will warn for now)
108 | (let [{:keys [basis]} (mc/run-core {:config-user {:aliases {:p {p ["x" "y"]}}}
109 | :config-project {:aliases {:q {p ["z"]}}}
110 | :repl-aliases [:p :q]})]
111 | (is (submap? {:paths ["x" "y" "z"]} basis))
112 | (is (= #{"x" "y" "z"} (-> basis :classpath (select-cp :path-key) keys set))))))
113 |
114 | ;; :extra-paths add
115 | (deftest extra-paths-add
116 | (let [{:keys [basis]} (mc/run-core {:config-user {:aliases {:p {:extra-paths ["x" "y"]}}}
117 | :config-project {:aliases {:q {:extra-paths ["z"]}}}
118 | :repl-aliases [:p :q]})]
119 | (is (submap?
120 | {:paths ["src"]}
121 | basis))
122 | (is (= #{"src" "x" "y" "z"} (-> basis :classpath (select-cp :path-key) keys set)))))
123 |
124 | ;; java opts in aliases are additive
125 | (deftest jvm-opts-add
126 | (let [core-ret (mc/run-core {:config-user {:aliases {:j1 {:jvm-opts ["-server" "-Xms100m"]}}}
127 | :config-project {:aliases {:j2 {:jvm-opts ["-Xmx200m"]}}}
128 | :repl-aliases [:j1 :j2]})]
129 | (is (= ["-server" "-Xms100m" "-Xmx200m"] (-> core-ret :basis :argmap :jvm-opts)))))
130 |
131 | ;; main opts replace
132 | (deftest main-opts-replace
133 | (doseq [a [:repl-aliases :main-aliases]] ;; will WARN for -A, but not for -M
134 | (let [core-ret (mc/run-core {:config-user {:aliases {:m1 {:main-opts ["a" "b"]}}}
135 | :config-project {:aliases {:m2 {:main-opts ["c"]}}}
136 | a [:m1 :m2]})]
137 | (is (= ["c"] (-> core-ret :basis :argmap :main-opts))))))
138 |
139 | ;; local manifests returned
140 | (deftest manifest-local
141 | (let [{:keys [manifests]} (mc/run-core {:config-project {:deps {'io.github.clojure/data.json {:git/sha "f367490" :git/tag "v2.4.0"}
142 | 'io.github.clojure/data.codec {:git/sha "8ef09db", :git/tag "data.codec-0.1.1", :deps/manifest :pom}}}})]
143 | ;; returns a manifest for both projects (deps.edn and pom.xml respectively)
144 | (is (= 2 (count manifests)))))
145 |
146 | ;; repositories should be retained for generate-manifest2's use
147 | (deftest repo-config-retained
148 | (let [{:keys [basis]} (mc/run-core {})] ;; root deps has central and clojars
149 | (is (= #{"central" "clojars"} (-> basis :mvn/repos keys set)))))
150 |
151 | ;; skip-cp flag prevents resolve-deps/make-classpath
152 | (deftest skip-cp-flag
153 | (let [{:keys [basis]} (mc/run-core {:config-project {:deps {'org.clojure/clojure {:mvn/version "1.10.0"}}}
154 | :skip-cp true})]
155 | (is (= {} (select-keys basis [:libs :classpath :classpath-roots])))))
156 |
157 | ;; skip-cp flag still passes exec-args for -X or -T
158 | (deftest skip-cp-exec
159 | (let [core-ret (mc/run-core {:config-project {:deps {'org.clojure/clojure {:mvn/version "1.10.0"}}
160 | :aliases {:x {:exec-fn 'clojure.core/prn :exec-args {:a 1}}}}
161 | :exec-aliases [:x]
162 | :skip-cp true})]
163 | (is (submap? {:exec-fn 'clojure.core/prn, :exec-args {:a 1}}
164 | (-> core-ret :basis :argmap)))))
165 |
166 | (deftest removing-deps
167 | (let [{:keys [basis]} (mc/run-core {:config-user {:aliases
168 | {:remove-clojure
169 | {:classpath-overrides
170 | '{org.clojure/clojure nil
171 | org.clojure/spec.alpha nil
172 | org.clojure/core.specs.alpha nil}}}}
173 | :repl-aliases [:remove-clojure]})
174 | {:keys [libs classpath-roots classpath]} basis]
175 | (is (= 3 (count libs))) ;; lib set is not changed by classpath-overrides
176 | (is (= ["src"] classpath-roots))
177 | (is (= {"src" {:path-key :paths}} classpath))))
178 |
179 | (deftest tool-alias
180 | (let [{:keys [basis]}
181 | (mc/run-core {:config-user {:aliases {:t {:extra-deps {'org.clojure/data.json {:mvn/version "2.0.1"}}}}}
182 | :config-project {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}}}
183 | :tool-aliases [:t]})
184 | {:keys [libs classpath-roots classpath]} basis
185 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)]
186 | ;; includes tool dep and not project deps
187 | (is (contains? libs 'org.clojure/data.json))
188 | (is (not (contains? libs 'cheshire/cheshire)))
189 | ;; paths only contains project root dir
190 | (is (= 1 (count paths)))
191 | (is (= (.getCanonicalPath (jio/file (first paths))) (.getCanonicalPath (jio/file "."))))))
192 |
193 | ;; clj -T a/fn
194 | (deftest tool-bare
195 | (let [{:keys [basis]}
196 | (mc/run-core {:config-project {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}}}
197 | :tool-data {:replace-paths ["."]}})
198 | {:keys [libs classpath-roots classpath]} basis
199 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)]
200 | (is (not (contains? libs 'cheshire/cheshire)))
201 | (is (= 1 (count paths)))
202 | (is (= (.getCanonicalPath (jio/file (first paths))) (.getCanonicalPath (jio/file "."))))))
203 |
204 | ;; clj -Tfoo
205 | (deftest tool-by-name
206 | (let [{:keys [basis]}
207 | (mc/run-core {:config-project {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}}}
208 | :tool-data {:replace-deps {'org.clojure/data.json {:mvn/version "2.0.1"}}
209 | :replace-paths ["."]
210 | :ns-default 'a.b}})
211 | {:keys [libs classpath-roots classpath argmap]} basis
212 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)]
213 | ;; execute-args in basis argmap
214 | (is (= (:ns-default argmap) 'a.b))
215 | ;; tool deps, not project deps
216 | (is (not (contains? libs 'cheshire/cheshire)))
217 | (is (contains? libs 'org.clojure/data.json))
218 | ;; ., not project paths
219 | (is (= (map #(.getCanonicalPath (jio/file %)) ["."])
220 | (map #(.getCanonicalPath (jio/file %)) paths)))))
221 |
222 | (deftest tool-by-name-newer-clojure
223 | (let [{:keys [basis]}
224 | (mc/run-core {:config-root {:deps {'org.clojure/clojure {:mvn/version "1.9.0"}} :path ["src"]}
225 | :config-project {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}}}
226 | :tool-data {:replace-deps {'org.clojure/clojure {:mvn/version "1.12.0"}}
227 | :replace-paths ["."]
228 | :ns-default 'a.b}})
229 | {:keys [libs classpath-roots classpath argmap]} basis
230 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)]
231 | ;; execute-args in basis argmap
232 | (is (= (:ns-default argmap) 'a.b))
233 | ;; tool deps, not project deps
234 | (is (not (contains? libs 'cheshire/cheshire)))
235 | (is (submap? {:deps {'org.clojure/clojure {:mvn/version "1.12.0"}}} basis))))
236 |
237 | ;; clj -T:a:b
238 | (deftest tool-with-aliases
239 | (let [{:keys [basis]}
240 | (mc/run-core {:config-project {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}}
241 | :aliases {:a {:replace-paths ["x"]}
242 | :b {:replace-paths ["y"]}}}
243 | :tool-aliases [:a :b]})
244 | {:keys [libs classpath-roots classpath]} basis
245 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)]
246 | ;; tool deps, not project deps
247 | (is (not (contains? libs 'cheshire/cheshire)))
248 | (is (= (map #(.getCanonicalPath (jio/file %)) ["x" "y" "."])
249 | (map #(.getCanonicalPath (jio/file %)) paths)))))
250 |
251 | (deftest config-data
252 | (let [{:keys [basis]} (mc/run-core {:config-project {:deps {'org.clojure/clojure {:mvn/version "1.12.0"}}}
253 | :config-data {:deps {'org.clojure/data.json {:mvn/version "2.5.0"}}}})]
254 | (is (contains? (:libs basis) 'org.clojure/data.json))))
255 |
256 | (deftest config-data-file
257 | (let [temp-file (File/createTempFile "deps" ".edn")
258 | _ (spit temp-file "{:deps {org.clojure/data.json {:mvn/version \"2.5.0\"}}}")
259 | {:keys [basis]} (mc/run-core {:config-project {:deps {'org.clojure/clojure {:mvn/version "1.12.0"}}}
260 | :config-data (.getAbsolutePath temp-file)})]
261 | (is (contains? (:libs basis) 'org.clojure/data.json))))
262 |
263 | (comment
264 | (clojure.test/run-tests)
265 | )
--------------------------------------------------------------------------------
/src/test/clojure/clojure/tools/deps/test_deps.clj:
--------------------------------------------------------------------------------
1 | (ns clojure.tools.deps.test-deps
2 | (:require
3 | [clojure.java.io :as jio]
4 | [clojure.test :refer [deftest is are testing]]
5 | [clojure.tools.deps :as deps]
6 | [clojure.tools.deps.util :as util]
7 | [clojure.tools.deps.extensions :as ext]
8 | [clojure.tools.deps.extensions.faken :as fkn]
9 | [clojure.tools.deps.specs :as specs]
10 | [clojure.tools.deps.util.dir :as dir]
11 | [clojure.tools.deps.util.maven :as mvn])
12 | (:import
13 | [java.io File]))
14 |
15 | (deftest test-slurp-deps-on-nonexistent-file
16 | (is (nil? (deps/slurp-deps (File. "NONEXISTENT_FILE")))))
17 |
18 | (deftest test-merge-or-replace
19 | (are [vals ret]
20 | (= ret (apply #'deps/merge-or-replace vals))
21 |
22 | [nil nil] nil
23 | [nil {:a 1}] {:a 1}
24 | [{:a 1} nil] {:a 1}
25 | [{:a 1 :b 1} {:a 2 :c 3} {:c 4 :d 5}] {:a 2 :b 1 :c 4 :d 5}
26 | [nil 1] 1
27 | [1 nil] 1
28 | [1 2] 2))
29 |
30 | (deftest test-merge-edns
31 | (is (= (deps/merge-edns
32 | [{:deps {'a {:v 1}, 'b {:v 1}}
33 | :a/x {:a 1}
34 | :a/y "abc"}
35 | {:deps {'b {:v 2}, 'c {:v 3}}
36 | :a/x {:b 1}
37 | :a/y "def"}
38 | nil])
39 | {:deps {'a {:v 1}, 'b {:v 2}, 'c {:v 3}}
40 | :a/x {:a 1, :b 1}
41 | :a/y "def"})))
42 |
43 | (deftest merge-alias-maps
44 | (are [m1 m2 out]
45 | (= out (#'deps/merge-alias-maps m1 m2))
46 |
47 | {} {} {}
48 | {} {:extra-deps {:a 1}} {:extra-deps {:a 1}}
49 | {:extra-deps {:a 1 :b 1}} {:extra-deps {:b 2}} {:extra-deps {:a 1 :b 2}}
50 | {} {:default-deps {:a 1}} {:default-deps {:a 1}}
51 | {:default-deps {:a 1 :b 1}} {:default-deps {:b 2}} {:default-deps {:a 1 :b 2}}
52 | {} {:override-deps {:a 1}} {:override-deps {:a 1}}
53 | {:override-deps {:a 1 :b 1}} {:override-deps {:b 2}} {:override-deps {:a 1 :b 2}}
54 | {} {:extra-paths ["a" "b"]} {:extra-paths ["a" "b"]}
55 | {:extra-paths ["a" "b"]} {:extra-paths ["c" "d"]} {:extra-paths ["a" "b" "c" "d"]}
56 | {} {:jvm-opts ["-Xms100m" "-Xmx200m"]} {:jvm-opts ["-Xms100m" "-Xmx200m"]}
57 | {:jvm-opts ["-Xms100m" "-Xmx200m"]} {:jvm-opts ["-Dfoo=bar"]} {:jvm-opts ["-Xms100m" "-Xmx200m" "-Dfoo=bar"]}
58 | {} {:main-opts ["foo.bar" "1"]} {:main-opts ["foo.bar" "1"]}
59 | {:main-opts ["foo.bar" "1"]} {:main-opts ["foo.baz" "2"]} {:main-opts ["foo.baz" "2"]}))
60 |
61 | (def repo
62 | ;; "real"
63 | {'org.clojure/clojure {{:fkn/version "1.9.0"}
64 | [['org.clojure/spec.alpha {:fkn/version "0.1.124"}]
65 | ['org.clojure/core.specs.alpha {:fkn/version "0.1.10"}]]}
66 | 'org.clojure/spec.alpha {{:fkn/version "0.1.124"} nil
67 | {:fkn/version "0.1.1"} nil}
68 | 'org.clojure/core.specs.alpha {{:fkn/version "0.1.10"} nil}
69 |
70 | ;; testing various scenarios
71 | 'e1/a {{:fkn/version "1"} [['e1/b {:fkn/version "1"}]
72 | ['e1/c {:fkn/version "2"}]]}
73 | 'e1/b {{:fkn/version "1"} [['e1/c {:fkn/version "1"}]]}
74 | 'e1/c {{:fkn/version "1"} nil
75 | {:fkn/version "2"} nil}
76 | 'opt/a {{:fkn/version "1"} [['opt/b {:fkn/version "1" :optional true}]
77 | ['opt/c {:fkn/version "1"}]]}
78 | 'opt/b {{:fkn/version "1"} nil}
79 | 'opt/c {{:fkn/version "1"} nil}})
80 |
81 | (deftest test-top-optional-included
82 | (fkn/with-libs repo
83 | (is (= (set (keys (deps/resolve-deps {:deps {'opt/b {:fkn/version "1"}}} nil)))
84 | #{'opt/b}))))
85 |
86 | (deftest test-transitive-optional-not-included
87 | (fkn/with-libs repo
88 | (is (= (set (keys (deps/resolve-deps {:deps {'opt/a {:fkn/version "1"}}} nil)))
89 | #{'opt/a 'opt/c}))))
90 |
91 | (deftest test-basic-expand
92 | (fkn/with-libs repo
93 | (is (= (set (keys (deps/resolve-deps {:deps {'org.clojure/clojure {:fkn/version "1.9.0"}}} nil)))
94 | #{'org.clojure/clojure 'org.clojure/spec.alpha 'org.clojure/core.specs.alpha}))))
95 |
96 | (deftest test-top-dominates
97 | (fkn/with-libs repo
98 | ;; dependent dep decides version
99 | (is (= (-> {:deps {'org.clojure/clojure {:fkn/version "1.9.0"}}}
100 | (deps/resolve-deps nil)
101 | (get 'org.clojure/spec.alpha)
102 | :fkn/version)
103 | "0.1.124"))
104 |
105 | ;; top dep wins
106 | (is (= (-> {:deps {'org.clojure/clojure {:fkn/version "1.9.0"}
107 | 'org.clojure/spec.alpha {:fkn/version "0.1.1"}}}
108 | (deps/resolve-deps nil)
109 | (get 'org.clojure/spec.alpha)
110 | :fkn/version)
111 | "0.1.1"))))
112 |
113 | (deftest test-override-deps
114 | (fkn/with-libs repo
115 | ;; override dep wins
116 | (is (= (-> {:deps {'org.clojure/clojure {:fkn/version "1.9.0"}}}
117 | (deps/resolve-deps
118 | {:override-deps {'org.clojure/spec.alpha {:fkn/version "0.1.1"}}})
119 | (get 'org.clojure/spec.alpha)
120 | :fkn/version)
121 | "0.1.1"))))
122 |
123 | (deftest test-default-deps
124 | (fkn/with-libs repo
125 | ;; default dep wins if none provided
126 | (is (= (-> {:deps {'org.clojure/clojure nil}}
127 | (deps/resolve-deps
128 | {:default-deps
129 | {'org.clojure/clojure {:fkn/version "2.0.0"}}})
130 | (get 'org.clojure/clojure)
131 | :fkn/version)
132 | "2.0.0"))))
133 |
134 | (defn libs->lib-ver
135 | [libmap]
136 | (reduce-kv
137 | (fn [lib-ver lib coord] (assoc lib-ver (-> lib name keyword) (:fkn/version coord)))
138 | {} libmap))
139 |
140 | ;; +a1 -> +b1 -> -c1
141 | ;; -> +c2
142 | (deftest test-dep-choice
143 | (fkn/with-libs repo
144 | (is (= (->> (deps/resolve-deps {:deps {'e1/a {:fkn/version "1"}}} nil) libs->lib-ver)
145 | {:a "1", :b "1", :c "2"}))))
146 |
147 | ;; -> +a1 -> +d1
148 | ;; -> +b1 -> -e1 -> -d2
149 | ;; -> +c1 -> +e2
150 | (deftest test-dep-parent-missing
151 | (fkn/with-libs
152 | {'ex/a {{:fkn/version "1"} [['ex/d {:fkn/version "1"}]]}
153 | 'ex/b {{:fkn/version "1"} [['ex/e {:fkn/version "1"}]]}
154 | 'ex/c {{:fkn/version "1"} [['ex/e {:fkn/version "2"}]]}
155 | 'ex/d {{:fkn/version "1"} nil
156 | {:fkn/version "2"} nil}
157 | 'ex/e {{:fkn/version "1"} [['ex/d {:fkn/version "2"}]]
158 | {:fkn/version "2"} nil}}
159 | (let [r (->> (deps/resolve-deps {:deps {'ex/a {:fkn/version "1"}
160 | 'ex/b {:fkn/version "1"}
161 | 'ex/c {:fkn/version "1"}}} nil)
162 | libs->lib-ver)]
163 | (is (= r {:a "1", :b "1", :c "1", :d "1", :e "2"})))))
164 |
165 | ;; +a1 -> +b1 -> +x2 -> +y1
166 | ;; +c1 -> -x1 -> -z1
167 | (deftest test-dep-choice2
168 | (fkn/with-libs
169 | {'ex/a {{:fkn/version "1"} [['ex/b {:fkn/version "1"}]]}
170 | 'ex/b {{:fkn/version "1"} [['ex/x {:fkn/version "2"}]]}
171 | 'ex/c {{:fkn/version "1"} [['ex/x {:fkn/version "1"}]]}
172 | 'ex/x {{:fkn/version "2"} [['ex/y {:fkn/version "1"}]]
173 | {:fkn/version "1"} [['ex/z {:fkn/version "1"}]]}
174 | 'ex/y {{:fkn/version "1"} nil}
175 | 'ex/z {{:fkn/version "1"} nil}}
176 | (is (= {:a "1", :b "1", :c "1", :x "2", :y "1"}
177 | (let [res (deps/resolve-deps {:deps {'ex/a {:fkn/version "1"}, 'ex/c {:fkn/version "1"}}} nil)]
178 | (libs->lib-ver res))))))
179 |
180 | ;; c1 included via both a and b, with exclusions in one branch and without in the other
181 | ;; should always include d1
182 | ;; +a1 -> +c1 (excl d) -> d1
183 | ;; +b1 -> +c1 -> +d1
184 | (deftest test-dep-same-version-different-exclusions
185 | (fkn/with-libs
186 | {'ex/a {{:fkn/version "1"} [['ex/c {:fkn/version "1" :exclusions ['ex/d]}]]}
187 | 'ex/b {{:fkn/version "1"} [['ex/c {:fkn/version "1"}]]}
188 | 'ex/c {{:fkn/version "1"} [['ex/d {:fkn/version "1"}]]}
189 | 'ex/d {{:fkn/version "1"} nil}}
190 | (is (= {:a "1", :b "1", :c "1", :d "1"}
191 | (libs->lib-ver (deps/resolve-deps {:deps {'ex/a {:fkn/version "1"}, 'ex/b {:fkn/version "1"}}} nil))
192 | (libs->lib-ver (deps/resolve-deps {:deps {'ex/b {:fkn/version "1"}, 'ex/a {:fkn/version "1"}}} nil))))))
193 |
194 | ;; +a1 -> +b1 -> -c1 -> a1
195 | ;; -> +c2 -> a1
196 | (deftest test-circular-deps
197 | (fkn/with-libs {'ex/a {{:fkn/version "1"} [['ex/b {:fkn/version "1"}]
198 | ['ex/c {:fkn/version "2"}]]}
199 | 'ex/b {{:fkn/version "1"} [['ex/c {:fkn/version "1"}]]}
200 | 'ex/c {{:fkn/version "1"} [['ex/a {:fkn/version "1"}]]
201 | {:fkn/version "2"} [['ex/a {:fkn/version "1"}]]}}
202 | (is (= {:a "1", :b "1", :c "2"}
203 | (let [res (deps/resolve-deps {:deps {'ex/a {:fkn/version "1"}}} nil)]
204 | (libs->lib-ver res))))))
205 |
206 | ;; +a1 -> -d1 -> -e1
207 | ;; +b1 -> +c1 -> +d2
208 | ;; e1 is found and selected due to d1, then cut when d2 is found
209 | (deftest test-cut-previously-selected-child
210 | (fkn/with-libs {'ex/a {{:fkn/version "1"} [['ex/d {:fkn/version "1"}]]}
211 | 'ex/b {{:fkn/version "1"} [['ex/c {:fkn/version "1"}]]}
212 | 'ex/c {{:fkn/version "1"} [['ex/d {:fkn/version "2"}]]}
213 | 'ex/d {{:fkn/version "1"} [['ex/e {:fkn/version "1"}]]
214 | {:fkn/version "2"} nil}
215 | 'ex/e {{:fkn.version "1"} nil}}
216 | (is (= {:a "1", :b "1", :c "1", :d "2"}
217 | (let [res (deps/resolve-deps {:deps {'ex/a {:fkn/version "1"}
218 | 'ex/b {:fkn/version "1"}}} nil)]
219 | (libs->lib-ver res))))))
220 |
221 | ;; +a1 -> -d1 -> -e1 -> -f1
222 | ;; +b1 -> +c1 -> +g1 -> +d2 -> +e2
223 | ;; e1 is found and selected due to d1, then cut when d2 is found
224 | (deftest test-cut-previously-selected-child-2
225 | (fkn/with-libs {'ex/a {{:fkn/version "1"} [['ex/d {:fkn/version "1"}]]}
226 | 'ex/b {{:fkn/version "1"} [['ex/c {:fkn/version "1"}]]}
227 | 'ex/c {{:fkn/version "1"} [['ex/g {:fkn/version "1"}]]}
228 | 'ex/d {{:fkn/version "1"} [['ex/e {:fkn/version "1"}]]
229 | {:fkn/version "2"} [['ex/e {:fkn/version "2"}]]}
230 | 'ex/e {{:fkn/version "1"} [['ex/f {:fkn/version "1"}]]
231 | {:fkn/version "2"} nil}
232 | 'ex/f {{:fkn/version "1"} nil}
233 | 'ex/g {{:fkn/version "1"} [['ex/d {:fkn/version "2"}]]}}
234 | (is (= {:a "1", :b "1", :c "1", :d "2", :e "2", :g "1"}
235 | (let [res (deps/resolve-deps {:deps {'ex/a {:fkn/version "1"}
236 | 'ex/b {:fkn/version "1"}}} nil)]
237 | (libs->lib-ver res))))))
238 |
239 | ;; +a1 -> -h1 -> -c1 -> -d1
240 | ;; +b1 -> +e1 -> +c1 -> +d1
241 | ;; -> +h2 -> c1 -> d1
242 | ;; h2 supersedes previous h1, but need to ensure d1 is included via c1 somewhere
243 | (deftest test-cut-previously-selected-child-3
244 | (fkn/with-libs {'ex/a {{:fkn/version "1"} [['ex/h {:fkn/version "1"}]]}
245 | 'ex/b {{:fkn/version "1"} [['ex/e {:fkn/version "1"}]]}
246 | 'ex/c {{:fkn/version "1"} [['ex/d {:fkn/version "1"}]]}
247 | 'ex/d {{:fkn/version "1"} nil}
248 | 'ex/e {{:fkn/version "1"} [['ex/c {:fkn/version "1"}] ['ex/h {:fkn/version "2"}]]}
249 | 'ex/h {{:fkn/version "1"} [['ex/c {:fkn/version "1"}]]
250 | {:fkn/version "2"} [['ex/c {:fkn/version "1"}]]}}
251 | (is (= {:a "1", :b "1", :c "1", :d "1", :e "1", :h "2"}
252 | (let [res (deps/resolve-deps {:deps {'ex/a {:fkn/version "1"}
253 | 'ex/b {:fkn/version "1"}}} nil)]
254 | (libs->lib-ver res))))))
255 |
256 | ;; +a -> +b -> -x2 -> -y2 -> -z2
257 | ;; -> +c -> +d -> +x3 -> +y2 -> +z2
258 | ;; -> -x1 -> -y1 -> -z1
259 | ;; include all of x3/y3/z3
260 | (deftest test-multi-version-discovery
261 | (fkn/with-libs {'ex/a {{:fkn/version "1"} [['ex/b {:fkn/version "1"}]
262 | ['ex/c {:fkn/version "1"}]
263 | ['ex/x {:fkn/version "1"}]]}
264 | 'ex/b {{:fkn/version "1"} [['ex/x {:fkn/version "2"}]]}
265 | 'ex/c {{:fkn/version "1"} [['ex/d {:fkn/version "1"}]]}
266 | 'ex/d {{:fkn/version "1"} [['ex/x {:fkn/version "3"}]]}
267 | 'ex/x {{:fkn/version "1"} [['ex/y {:fkn/version "1"}]]
268 | {:fkn/version "2"} [['ex/y {:fkn/version "2"}]]
269 | {:fkn/version "3"} [['ex/y {:fkn/version "2"}]]}
270 | 'ex/y {{:fkn/version "1"} [['ex/z {:fkn/version "1"}]]
271 | {:fkn/version "2"} [['ex/z {:fkn/version "2"}]]}
272 | 'ex/z {{:fkn/version "1"} nil
273 | {:fkn/version "2"} nil}}
274 | (is (= {:a "1", :b "1", :c "1", :d "1", :x "3", :y "2", :z "2"}
275 | (let [res (deps/resolve-deps {:deps {'ex/a {:fkn/version "1"}}} nil)]
276 | (libs->lib-ver res))))))
277 |
278 | (def ^:dynamic ^File *test-dir* nil)
279 |
280 | (defmacro with-test-dir
281 | [& body]
282 | `(let [name# (-> clojure.test/*testing-vars* last symbol str)
283 | dir# (jio/file "test-out" name#)]
284 | (.delete dir#)
285 | (.mkdirs dir#)
286 | (binding [*test-dir* dir#]
287 | ~@body)))
288 |
289 | (deftest test-local-root
290 | (with-test-dir
291 | (let [base (.getCanonicalFile *test-dir*)]
292 | (jio/make-parents *test-dir* "b/deps.edn")
293 | (testing "a relative local root canonicalizes relative to parent dep"
294 | (binding [dir/*the-dir* base]
295 | (is (= ['ex/b {:local/root (.getPath (.getCanonicalFile (File. base "b")))}]
296 | (ext/canonicalize 'ex/b {:local/root "b"} {})))))
297 | (testing "an absolute local root canonicalizes to itself"
298 | (binding [dir/*the-dir* base]
299 | (let [abs-b (.getAbsolutePath (jio/file *test-dir* "b"))]
300 | (is (= ['ex/b {:local/root abs-b}]
301 | (ext/canonicalize 'ex/b {:local/root abs-b} {})))))))))
302 |
303 | (deftest test-local-root-relative-to-project-deps
304 | (with-test-dir
305 | (let [base (.getCanonicalFile *test-dir*)
306 | adeps (.getPath (jio/file base "a/deps.edn"))
307 | bdeps (.getPath (jio/file base "b/deps.edn"))
308 | adir (.getAbsolutePath (jio/file base "a"))
309 | bdir (.getAbsolutePath (jio/file base "b"))]
310 | (jio/make-parents *test-dir* "a/deps.edn")
311 | (jio/make-parents *test-dir* "b/deps.edn")
312 | (spit adeps "{:deps {b/b {:local/root \"../b\"}}}")
313 | (spit bdeps "{:paths [\"src\"]}")
314 | (let [abasis (deps/create-basis {:user nil :dir adir})]
315 | (is (contains? (:classpath abasis) (.getAbsolutePath (jio/file bdir "src"))))))))
316 |
317 | (deftest empty-nil-deps-is-valid
318 | (testing "file exists but is empty (nil)"
319 | (is (specs/valid-deps? nil))))
320 |
321 | (deftest TDEPS-238
322 | (testing "deps are invalid with extra nested vector in :exclusions"
323 | (let [invalid {:deps
324 | {'org.clojure/core.memoize
325 | {:mvn/version "1.0.257"
326 | :exclusions [['org.clojure/data.priority-map]]}}}]
327 | (is (not (specs/valid-deps? invalid))))))
328 |
329 | (comment
330 | (test-local-root-relative-to-project-deps)
331 | )
332 |
333 | ;; simple check that pom resolution is working - load tda itself as pom dep
334 | (deftest test-local-pom
335 | (is (seq (deps/resolve-deps
336 | {:deps {'c/tda {:local/root "." :deps/manifest :pom}}
337 | :mvn/repos mvn/standard-repos}
338 | nil))))
339 |
340 | (def install-data
341 | {:paths ["src"]
342 | :deps {'org.clojure/clojure {:mvn/version "1.10.1"}}
343 | :aliases {:test {:extra-paths ["test"]}}
344 | :mvn/repos {"central" {:url "https://repo1.maven.org/maven2/"}
345 | "clojars" {:url "https://repo.clojars.org/"}}})
346 |
347 | (deftest calc-basis
348 | (let [{:keys [libs classpath] :as basis} (deps/calc-basis install-data)]
349 | ;; basis is superset of merged deps
350 | (is (= install-data (select-keys basis (keys install-data))))
351 |
352 | ;; lib map contains transitive deps
353 | (is (= (set (keys libs)) #{'org.clojure/clojure 'org.clojure/spec.alpha 'org.clojure/core.specs.alpha}))
354 |
355 | ;; classpath has all deps and src path
356 | (is (= (set (vals classpath)) #{{:lib-name 'org.clojure/clojure}
357 | {:lib-name 'org.clojure/spec.alpha}
358 | {:lib-name 'org.clojure/core.specs.alpha}
359 | {:path-key :paths}}))
360 | (is (contains? (set (keys classpath)) "src"))))
361 |
362 | (defn select-cp
363 | [classpath key]
364 | (->> classpath (filter #(contains? (val %) key)) (apply conj {})))
365 |
366 | (deftest calc-basis-extra-deps
367 | (let [ra {:extra-deps {'org.clojure/tools.deps.alpha {:mvn/version "0.8.677"}}}
368 | {:keys [libs]} (deps/calc-basis install-data {:resolve-args ra})
369 | expanded-deps (-> libs keys set)]
370 | ;; libs has extra deps and transitive deps
371 | (is (< 4 (count expanded-deps)))
372 | (is (contains? expanded-deps 'org.clojure/tools.deps.alpha))))
373 |
374 | (deftest calc-basis-override-deps
375 | (let [ra {:extra-deps {'org.clojure/clojure {:mvn/version "1.6.0"}}}
376 | {:keys [libs]} (deps/calc-basis install-data {:resolve-args ra})]
377 | ;; libs has extra deps and transitive deps
378 | (is (= (get-in libs ['org.clojure/clojure :mvn/version]) "1.6.0"))))
379 |
380 | (deftest calc-basis-extra-paths
381 | (let [cpa {:extra-paths ["x" "y"]}
382 | {:keys [classpath]} (deps/calc-basis install-data {:classpath-args cpa})]
383 | ;; classpath has extra paths
384 | (is (= {"src" {:path-key :paths}, "x" {:path-key :extra-paths}, "y" {:path-key :extra-paths}}
385 | (select-cp classpath :path-key)))))
386 |
387 | (deftest calc-basis-classpath-overrides
388 | (let [cpa {:classpath-overrides {'org.clojure/clojure "foo"}}
389 | {:keys [classpath]} (deps/calc-basis install-data {:classpath-args cpa})]
390 | ;; classpath has replaced path
391 | (is (= (get classpath "foo") {:lib-name 'org.clojure/clojure}))))
392 |
393 | (deftest optional-deps-included
394 | (let [master-edn (merge install-data
395 | '{:deps {org.clojure/clojure {:mvn/version "1.10.1"}
396 | org.clojure/core.async {:mvn/version "1.1.587" :optional true}}})
397 | {:keys [libs]} (deps/calc-basis master-edn)]
398 |
399 | ;; libs contains optional dep
400 | (is (= (get-in libs ['org.clojure/core.async :mvn/version]) "1.1.587"))))
401 |
402 | ;(update-excl [lib use-coord coord-id use-path include reason exclusions cut])
403 | (deftest test-update-excl
404 | ;; new lib/version, no exclusions
405 | (let [ret (#'deps/update-excl 'a {:mvn/version "1"} {:mvn/version "1"} '[b a] true :new-version nil nil)]
406 | (is (= {:exclusions' nil, :cut' nil}
407 | (select-keys ret [:exclusions' :cut'])))
408 | (is (not (nil? (:child-pred ret)))))
409 |
410 | ;; new lib/version, with exclusions
411 | (let [ret (#'deps/update-excl 'a {:mvn/version "1" :exclusions ['c]} {:mvn/version "1"} '[b a] true :new-version nil nil)]
412 | (is (= {:exclusions' '{[b a] #{c}}, :cut' '{[a {:mvn/version "1"}] #{c}}}
413 | (select-keys ret [:exclusions' :cut'])))
414 | (is (not (nil? (:child-pred ret)))))
415 |
416 | ;; same lib/version, fewer excludes
417 | ;; a (excl c)
418 | ;; b -> a -> c1
419 | (let [excl '{[a] #{c}}
420 | ret (#'deps/update-excl 'a {:mvn/version "1"} {:mvn/version "1"} '[b a] false :same-version
421 | excl '{[a {:mvn/version "1"}] #{c}})]
422 | (is (= {:exclusions' excl, :cut' '{[a #:mvn{:version "1"}] nil}} (select-keys ret [:exclusions' :cut']))) ;; remove cut
423 | (let [pred (:child-pred ret)]
424 | (is (true? (boolean (pred 'c))))))
425 |
426 | ;; same lib/version, subset excludes
427 | ;; a (excl c d)
428 | ;; b -> a (excl c)
429 | (let [excl '{[a] #{c d}}
430 | cut '{[a {:mvn/version "1"}] #{c d}}
431 | ret (#'deps/update-excl 'a '{:mvn/version "1" :exclusions [c]} {:mvn/version "1"} '[b a] false
432 | :same-version excl cut)]
433 | (is (= {:exclusions' '{[a] #{c d}, [b a] #{c}}, :cut' '{[a {:mvn/version "1"}] #{c}}}
434 | (select-keys ret [:exclusions' :cut'])))
435 | (let [pred (:child-pred ret)]
436 | (is (false? (boolean (pred 'c)))) ;; already enqueued
437 | (is (true? (boolean (pred 'd)))))) ;; newly enqueueable due to smaller exclusion set
438 |
439 | ;; same lib/version, same excludes
440 | ;; a (excl c)
441 | ;; b -> a (excl c)
442 | (let [excl '{[a] #{c}}
443 | cut '{[a {:mvn/version "1"}] #{c}}
444 | ret (#'deps/update-excl 'a {:mvn/version "1" :exclusions ['c]} {:mvn/version "1"} '[b a] false :same-version excl cut)]
445 | (is (= {:exclusions' (assoc excl '[b a] '#{c}), :cut' cut} (select-keys ret [:exclusions' :cut']))) ;; no change in cut
446 | (let [pred (:child-pred ret)]
447 | (is (false? (boolean (pred 'c))))))
448 |
449 | ;; same lib/version, more excludes
450 | ;; a (excl c)
451 | ;; b -> a (excl c, d)
452 | (let [excl '{[a] #{c}}
453 | cut '{[a {:mvn/version "1"}] #{c}}
454 | ret (#'deps/update-excl 'a '{:mvn/version "1" :exclusions [c d]} {:mvn/version "1"} '[b a] false :same-version excl cut)]
455 | (is (= {:exclusions' '{[a] #{c}, [b a] #{c d}}, :cut' cut} (select-keys ret [:exclusions' :cut']))) ;; no change in cut
456 | (let [pred (:child-pred ret)] ;; c excluded in both, but re-enqueue d - always intersection
457 | (is (false? (boolean (pred 'c))))
458 | ;;(is (true? (boolean (pred 'd))))
459 | )))
460 |
461 | ;; +x1 -> -a1 -> +b2
462 | ;; +z1 -> +y1 -> +a2 -> -b1 (or +b1, but at least a consistent result)
463 | ;; TDEPS-58
464 | (deftest test-dep-ordering
465 | (fkn/with-libs
466 | {'ex/a {{:fkn/version "1"} [['ex/b {:fkn/version "2"}]]
467 | {:fkn/version "2"} [['ex/b {:fkn/version "1"}]]}
468 | 'ex/b {{:fkn/version "1"} nil
469 | {:fkn/version "2"} nil}
470 | 'ex/x {{:fkn/version "1"} [['ex/a {:fkn/version "1"}]]}
471 | 'ex/y {{:fkn/version "1"} [['ex/a {:fkn/version "2"}]]}
472 | 'ex/z {{:fkn/version "1"} [['ex/y {:fkn/version "1"}]]}}
473 | (is (= (let [res (deps/resolve-deps {:deps {'ex/x {:fkn/version "1"}, 'ex/z {:fkn/version "1"}}} nil)]
474 | (reduce-kv #(assoc %1 (-> %2 name keyword) (:fkn/version %3)) {} res))
475 | (let [res (deps/resolve-deps {:deps {'ex/z {:fkn/version "1"}, 'ex/x {:fkn/version "1"}}} nil)]
476 | (reduce-kv #(assoc %1 (-> %2 name keyword) (:fkn/version %3)) {} res))))))
477 |
478 | (deftest test-create-basis
479 | (fkn/with-libs
480 | {'ex/a {{:fkn/version "1"} nil}
481 | 'ex/b {{:fkn/version "1"} nil}
482 | 'ex/c {{:fkn/version "1"} nil}}
483 | (are [libs params] (= (set libs) (set (keys (:libs (deps/create-basis params)))))
484 | ;; check pulling project sources
485 | [] {:root nil :user nil :project nil}
486 | ['ex/a] {:root {:deps {'ex/a {:fkn/version "1"}}} :user nil :project nil}
487 | ['ex/a] {:root nil :user {:deps {'ex/a {:fkn/version "1"}}} :project nil}
488 | ['ex/a] {:root nil :user nil :project {:deps {'ex/a {:fkn/version "1"}}}}
489 | ['ex/a] {:root nil :user nil :project nil :extra {:deps {'ex/a {:fkn/version "1"}}}}
490 |
491 | ;; check aliases
492 | ['ex/a 'ex/b] {:root nil
493 | :project {:deps {'ex/a {:fkn/version "1"}}
494 | :aliases {:b {:extra-deps {'ex/b {:fkn/version "1"}}}}}
495 | :aliases [:b]})))
496 |
497 | ;; focus not on merging but on verifying output from single deps.edn + aliases
498 | (deftest test-create-basis-full
499 | (fkn/with-libs
500 | {'ex/a {{:fkn/version "1"} nil}
501 | 'ex/b {{:fkn/version "1"} nil}
502 | 'ex/c {{:fkn/version "1"} nil}}
503 |
504 | (are [project aliases expected]
505 | (let [basis (deps/create-basis {:root nil :user nil :project project :aliases aliases})]
506 | ;;(println "\nbasis:")
507 | ;;(clojure.pprint/pprint basis)
508 | (and (util/submap? project basis) ;; basis is superset of deps.edn
509 | (util/submap? expected basis)))
510 |
511 | ;; no aliases, basis deps case
512 | {:deps {'ex/a {:fkn/version "1"}}}
513 | []
514 | {:libs {'ex/a {:fkn/version "1", :deps/manifest :fkn, :paths ["REPO/ex/a/1/a-1.jar"]}}
515 | :classpath {"REPO/ex/a/1/a-1.jar" {:lib-name 'ex/a}}
516 | :classpath-roots ["REPO/ex/a/1/a-1.jar"]}
517 |
518 | ;; aliases to add both resolve-args and classpath-args
519 | {:deps {'ex/a {:fkn/version "1"}}
520 | :aliases {:a1 {:extra-deps {'ex/b {:fkn/version "2"}} ;; mixture of resolve-deps/make-cp args
521 | :extra-paths ["p"]}
522 | :a2 {:override-deps {'ex/b {:fkn/version "1"}}
523 | :default-deps {'ex/c {:fkn/version "1"}}
524 | :classpath-overrides {'ex/a "override.jar"}}}}
525 | [:a1 :a2]
526 | {:libs {'ex/a {:fkn/version "1", :deps/manifest :fkn, :paths ["REPO/ex/a/1/a-1.jar"]}
527 | 'ex/b {:fkn/version "1", :deps/manifest :fkn, :paths ["REPO/ex/b/1/b-1.jar"]}}
528 | :classpath {"override.jar" {:lib-name 'ex/a} ;; see override
529 | "REPO/ex/b/1/b-1.jar" {:lib-name 'ex/b}
530 | "p" {:path-key :extra-paths}} ;; see extra path
531 | :classpath-roots ["p" "override.jar" "REPO/ex/b/1/b-1.jar"] ;; ditto above
532 | :basis-config {:root nil, :user nil, :aliases [:a1 :a2]} ;; aliases remembered
533 | })))
534 |
535 | (deftest test-project-deps-doesnt-exist
536 | (is (= "BOGUS.edn" (-> (deps/create-basis {:user nil :project "BOGUS.edn"})
537 | :basis-config :project)))
538 | (is (= "BOGUS.edn" (-> (deps/create-basis {:user nil :dir "foo" :project "BOGUS.edn"})
539 | :basis-config :project)))
540 | (is (= "foo/BOGUS.edn" (-> (deps/create-basis {:user nil :project "foo/BOGUS.edn"})
541 | :basis-config :project))))
542 |
543 | (deftest test-resolved-added-libs
544 | (let [basis (deps/create-basis {:user nil :project nil})
545 | libs (:libs basis)]
546 | (are [add] (contains? (:added (deps/resolve-added-libs {:existing libs, :procurer basis, :add add}))
547 | (key (first add)))
548 | '{org.clojure/tools.cli {:mvn/version "1.0.214"}}
549 | '{io.github.clojure/tools.deps.graph {:git/tag "v1.1.76" :git/sha "6c58e98"}}
550 | '{org.clojure/tools.deps {:local/root "."}})))
551 |
552 | (deftest test-find-all-versions
553 | (is (seq (ext/find-all-versions 'ragtime nil {:mvn/repos mvn/standard-repos}))))
554 |
555 | (comment
556 | (test-resolved-added-libs)
557 | (test-find-all-versions)
558 | )
559 |
--------------------------------------------------------------------------------