├── VERSION_TEMPLATE ├── CHANGELOG.md ├── .gitignore ├── .github ├── workflows │ ├── snapshot.yml │ ├── doc-build.yml │ ├── release.yml │ └── test.yml └── PULL_REQUEST_TEMPLATE ├── script └── version ├── CONTRIBUTING.md ├── src ├── test │ └── clojure │ │ └── clojure │ │ └── tools │ │ └── deps │ │ ├── util.clj │ │ └── test_edn.clj └── main │ └── clojure │ └── clojure │ └── tools │ └── deps │ ├── specs.clj │ └── edn.clj ├── README.md ├── pom.xml └── LICENSE /VERSION_TEMPLATE: -------------------------------------------------------------------------------- 1 | 0.1.GENERATED_VERSION 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | =========== 3 | 4 | * next 5 | * 6 | -------------------------------------------------------------------------------- /.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 | on: [workflow_dispatch] 4 | 5 | jobs: 6 | call-snapshot: 7 | uses: clojure/build.ci/.github/workflows/snapshot.yml@master 8 | secrets: inherit 9 | -------------------------------------------------------------------------------- /.github/workflows/doc-build.yml: -------------------------------------------------------------------------------- 1 | name: Build API Docs 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | call-doc-build-workflow: 8 | uses: clojure/build.ci/.github/workflows/doc-build.yml@master 9 | with: 10 | project: clojure/tools.deps.edn 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release on demand 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | releaseVersion: 7 | description: "Version to release" 8 | required: true 9 | snapshotVersion: 10 | description: "Snapshot version after release" 11 | required: true 12 | 13 | jobs: 14 | call-release: 15 | uses: clojure/build.ci/.github/workflows/release.yml@master 16 | with: 17 | releaseVersion: ${{ github.event.inputs.releaseVersion }} 18 | snapshotVersion: ${{ github.event.inputs.snapshotVersion }} 19 | secrets: inherit -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest] # macOS-latest, windows-latest] 10 | java-version: ["8", "11", "17", "21"] 11 | clojure-version: ["1.10.3", "1.11.1"] 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Java 16 | uses: actions/setup-java@v4 17 | with: 18 | java-version: ${{ matrix.java-version }} 19 | distribution: 'temurin' 20 | cache: 'maven' 21 | - name: Build with Maven 22 | run: mvn -ntp -B -Dclojure.version=${{ matrix.clojure-version }} clean test 23 | -------------------------------------------------------------------------------- /.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/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/test_edn.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.tools.deps.test-edn 2 | (:require 3 | [clojure.test :refer [deftest is are testing]] 4 | [clojure.tools.deps.edn :as deps-edn]) 5 | (:import 6 | [java.io File])) 7 | 8 | (deftest test-slurp-deps-on-nonexistent-file 9 | (is (nil? (deps-edn/read-deps (File. "NONEXISTENT_FILE"))))) 10 | 11 | (deftest test-merge-or-replace 12 | (are [vals ret] 13 | (= ret (apply #'deps-edn/merge-or-replace vals)) 14 | 15 | [nil nil] nil 16 | [nil {:a 1}] {:a 1} 17 | [{:a 1} nil] {:a 1} 18 | [{:a 1 :b 1} {:a 2 :c 3} {:c 4 :d 5}] {:a 2 :b 1 :c 4 :d 5} 19 | [nil 1] 1 20 | [1 nil] 1 21 | [1 2] 2)) 22 | 23 | (deftest test-merge-edns 24 | (is (= (deps-edn/merge-edns 25 | [{:deps {'a {:v 1}, 'b {:v 1}} 26 | :a/x {:a 1} 27 | :a/y "abc"} 28 | {:deps {'b {:v 2}, 'c {:v 3}} 29 | :a/x {:b 1} 30 | :a/y "def"} 31 | nil]) 32 | {:deps {'a {:v 1}, 'b {:v 2}, 'c {:v 3}} 33 | :a/x {:a 1, :b 1} 34 | :a/y "def"}))) 35 | 36 | (deftest merge-alias-maps 37 | (are [m1 m2 out] 38 | (= out (#'deps-edn/merge-alias-maps m1 m2)) 39 | 40 | {} {} {} 41 | {} {:extra-deps {:a 1}} {:extra-deps {:a 1}} 42 | {:extra-deps {:a 1 :b 1}} {:extra-deps {:b 2}} {:extra-deps {:a 1 :b 2}} 43 | {} {:default-deps {:a 1}} {:default-deps {:a 1}} 44 | {:default-deps {:a 1 :b 1}} {:default-deps {:b 2}} {:default-deps {:a 1 :b 2}} 45 | {} {:override-deps {:a 1}} {:override-deps {:a 1}} 46 | {:override-deps {:a 1 :b 1}} {:override-deps {:b 2}} {:override-deps {:a 1 :b 2}} 47 | {} {:extra-paths ["a" "b"]} {:extra-paths ["a" "b"]} 48 | {:extra-paths ["a" "b"]} {:extra-paths ["c" "d"]} {:extra-paths ["a" "b" "c" "d"]} 49 | {} {:jvm-opts ["-Xms100m" "-Xmx200m"]} {:jvm-opts ["-Xms100m" "-Xmx200m"]} 50 | {:jvm-opts ["-Xms100m" "-Xmx200m"]} {:jvm-opts ["-Dfoo=bar"]} {:jvm-opts ["-Xms100m" "-Xmx200m" "-Dfoo=bar"]} 51 | {} {:main-opts ["foo.bar" "1"]} {:main-opts ["foo.bar" "1"]} 52 | {:main-opts ["foo.bar" "1"]} {:main-opts ["foo.baz" "2"]} {:main-opts ["foo.baz" "2"]})) 53 | 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tools.deps.edn 2 | ======================================== 3 | 4 | Reader for deps.edn 5 | 6 | # Rationale 7 | 8 | This library is a small library for reading and understanding deps.edn files. It can 9 | be used in scenarios where the full [tools.deps](https://github.com/clojure/tools.deps) 10 | library is not needed. 11 | 12 | * [deps.edn Reference](https://clojure.org/reference/deps_edn) 13 | 14 | # Release Information 15 | 16 | Latest release: TBD 17 | 18 | * [All released versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22tools.deps.edn%22) 19 | 20 | [deps.edn](https://clojure.org/reference/deps_edn) dependency information: 21 | 22 | ``` 23 | org.clojure/tools.deps.edn {:mvn/version "TBD"} 24 | ``` 25 | 26 | [Leiningen](https://github.com/technomancy/leiningen/) dependency information: 27 | 28 | ``` 29 | [org.clojure/tools.deps.edn "TBD"] 30 | ``` 31 | 32 | [Maven](https://maven.apache.org) dependency information: 33 | 34 | ``` 35 | 36 | org.clojure 37 | tools.deps.edn 38 | TBD 39 | 40 | ``` 41 | 42 | # API 43 | 44 | For info on using tools.deps.edn as a library, see: 45 | 46 | * [API Docs](https://clojure.github.io/tools.deps.edn) 47 | 48 | # Developer Information 49 | 50 | * [GitHub project](https://github.com/clojure/tools.deps.edn) 51 | * [How to contribute](https://clojure.org/community/contributing) 52 | * [Bug Tracker](https://clojure.atlassian.net/browse/TDEPS) 53 | * [Continuous Integration](https://github.com/clojure/tools.deps.edn/actions/workflows/test.yml) 54 | 55 | # Copyright and License 56 | 57 | Copyright © Rich Hickey, Alex Miller, and contributors 58 | 59 | All rights reserved. The use and 60 | distribution terms for this software are covered by the 61 | [Eclipse Public License 1.0] which can be found in the file 62 | LICENSE at the root of this distribution. By using this software 63 | in any fashion, you are agreeing to be bound by the terms of this 64 | license. You must not remove this notice, or any other, from this 65 | software. 66 | 67 | [Eclipse Public License 1.0]: https://opensource.org/license/epl-1-0/ 68 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | tools.deps.edn 4 | 0.1.0-SNAPSHOT 5 | tools.deps.edn 6 | 7 | 8 | org.clojure 9 | pom.contrib 10 | 1.3.0 11 | 12 | 13 | 14 | 15 | puredanger 16 | Alex Miller 17 | 18 | 19 | 20 | 21 | 22 | true 23 | 1.12.0 24 | 25 | 26 | 27 | 28 | org.clojure 29 | clojure 30 | ${clojure.version} 31 | 32 | 33 | 34 | 35 | 36 | 37 | src/main/resources 38 | true 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-resources-plugin 45 | 3.1.0 46 | 47 | 48 | 51 | com.theoryinpractise 52 | clojure-maven-plugin 53 | 1.7.1 54 | true 55 | 56 | ${clojure.warnOnReflection} 57 | true 58 | 59 | 60 | 61 | clojure-compile 62 | none 63 | 64 | 65 | clojure-test 66 | test 67 | 68 | test 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | scm:git:git@github.com:clojure/tools.deps.edn.git 78 | scm:git:git@github.com:clojure/tools.deps.edn.git 79 | git@github.com:clojure/tools.deps.edn.git 80 | HEAD 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /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))))))) 143 | 144 | (comment 145 | ;; some scratch code to recursively check every deps.edn under 146 | ;; a root directory whether it's valid against the specs 147 | (require 148 | '[clojure.spec.test.alpha :as stest] 149 | '[clojure.tools.deps :as deps]) 150 | (import '[java.nio.file Files Paths FileVisitor FileVisitResult]) 151 | (stest/instrument (stest/enumerate-namespace 'clojure.tools.deps)) 152 | 153 | (Files/walkFileTree 154 | (Paths/get "../" (into-array String [])) 155 | (reify FileVisitor 156 | (postVisitDirectory [_ dir ex] FileVisitResult/CONTINUE) 157 | (preVisitDirectory [_ dir attrs] FileVisitResult/CONTINUE) 158 | (visitFileFailed [_ f ex] FileVisitResult/CONTINUE) 159 | (visitFile [_ f attrs] 160 | (when (.endsWith (str f) "/deps.edn") 161 | (print "Checking" (str f)) 162 | (let [v (s/valid? ::deps-map (#'deps/slurp-edn-map (.toFile f)))] 163 | (println ":" v) 164 | (when-not v 165 | (s/explain ::deps-map (#'deps/slurp-edn-map (.toFile f)))))) 166 | FileVisitResult/CONTINUE))) 167 | ) 168 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/edn.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.edn 10 | (:require 11 | [clojure.edn :as edn] 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.tools.deps.specs :as specs] 15 | [clojure.walk :as walk]) 16 | (:import 17 | [java.io File PushbackReader] 18 | [clojure.lang EdnReader$ReaderException] 19 | )) 20 | 21 | (set! *warn-on-reflection* true) 22 | 23 | ;;;; Read 24 | 25 | (defonce ^:private nl (System/getProperty "line.separator")) 26 | 27 | (defn- printerrln 28 | "println to *err*" 29 | [& msgs] 30 | (binding [*out* *err* 31 | *print-readably* nil] 32 | (pr (str (str/join " " msgs) nl)) 33 | (flush))) 34 | 35 | (defn- io-err 36 | "Helper function to construct an ex-info for an exception reading 37 | file at path with the message format fmt (which should have one 38 | variable for the path)." 39 | ^Throwable [fmt & {:keys [path]}] 40 | (let [abs-path (.getAbsolutePath (jio/file path))] 41 | (ex-info (format fmt abs-path) {:path abs-path}))) 42 | 43 | (defn read-edn 44 | "Read edn from file f, which should contain exactly one edn value. 45 | If f exists but is blank, nil is returned. 46 | Throws if file is unreadable or contains multiple values. 47 | Opts: 48 | :path String path to file being read" 49 | [^File f & opts] 50 | (with-open [rdr (PushbackReader. (jio/reader f))] 51 | (let [EOF (Object.) 52 | val (try 53 | (let [val (edn/read {:default tagged-literal :eof EOF} rdr)] 54 | (if (identical? EOF val) 55 | nil ;; empty file 56 | (if (not (identical? EOF (edn/read {:eof EOF} rdr))) 57 | (throw (ex-info "Expected edn to contain a single value." {})) 58 | val))) 59 | (catch EdnReader$ReaderException e 60 | (throw (io-err (str (.getMessage e) " (%s)") opts))) 61 | (catch RuntimeException t 62 | (if (str/starts-with? (.getMessage t) "EOF while reading") 63 | (throw (io-err "Error reading edn, delimiter unmatched (%s)" opts)) 64 | (throw (io-err (str "Error reading edn. " (.getMessage t) " (%s)") opts)))))]))) 65 | 66 | (defn validate 67 | "Validate a deps-edn map according to the specs, throw if invalid. 68 | Opts: 69 | :path String path to file being read" 70 | [deps-edn & opts] 71 | (if (specs/valid-deps? deps-edn) 72 | deps-edn 73 | (throw (io-err (str "Error reading deps %s. " (specs/explain-deps deps-edn)) opts)))) 74 | 75 | ;;;; Canonicalize 76 | 77 | (defn- canonicalize-sym 78 | [s & opts] 79 | (if (simple-symbol? s) 80 | (let [cs (as-> (name s) n (symbol n n))] 81 | (printerrln "DEPRECATED: Libs must be qualified, change" s "=>" cs 82 | (if-let [path (:path opts)] (str "(" path ")" ""))) 83 | cs) 84 | s)) 85 | 86 | (defn- canonicalize-exclusions 87 | [{:keys [exclusions] :as coord} & opts] 88 | (if (seq (filter simple-symbol? exclusions)) 89 | (assoc coord :exclusions (mapv #(canonicalize-sym % opts) exclusions)) 90 | coord)) 91 | 92 | (defn- canonicalize-dep-map 93 | [deps-map & opts] 94 | (when deps-map 95 | (reduce-kv (fn [acc lib coord] 96 | (let [new-lib (if (simple-symbol? lib) (canonicalize-sym lib opts) lib) 97 | new-coord (canonicalize-exclusions coord opts)] 98 | (assoc acc new-lib new-coord))) 99 | {} deps-map))) 100 | 101 | (defn canonicalize 102 | "Canonicalize a deps.edn map (convert simple lib symbols to qualified lib symbols). 103 | Opts: 104 | :path String path to file being read" 105 | [deps-edn & opts] 106 | (walk/postwalk 107 | (fn [x] 108 | (if (map? x) 109 | (reduce (fn [xr k] 110 | (if-let [xm (get xr k)] 111 | (assoc xr k (canonicalize-dep-map xm opts)) 112 | xr)) 113 | x #{:deps :default-deps :override-deps :extra-deps :classpath-overrides}) 114 | x)) 115 | deps-edn)) 116 | 117 | ;;;; Read deps with validation and canonicalization 118 | 119 | (defn read-deps 120 | "Corece f to a file with jio/file, then read, validate, and canonicalize 121 | the deps.edn. This is the primary entry point for reading. 122 | 123 | Opts: none" 124 | [f & opts] 125 | (let [file (jio/file f) 126 | opts {:path (.getPath file)}] 127 | (when (.exists file) 128 | (-> file (read-edn opts) (validate opts) (canonicalize opts))))) 129 | 130 | ;;;; deps edn manipulation 131 | 132 | (defn- merge-or-replace 133 | "If maps, merge, otherwise replace" 134 | [& vals] 135 | (when (some identity vals) 136 | (reduce (fn [ret val] 137 | (if (and (map? ret) (map? val)) 138 | (merge ret val) 139 | (or val ret))) 140 | nil vals))) 141 | 142 | (defn merge-edns 143 | "Merge multiple deps edn maps from left to right into a single deps edn map." 144 | [deps-edn-maps] 145 | (apply merge-with merge-or-replace (remove nil? deps-edn-maps))) 146 | 147 | ;;;; Aliases 148 | 149 | ;; per-key binary merge-with rules 150 | 151 | (def ^:private last-wins (comp last #(remove nil? %) vector)) 152 | (def ^:private append (comp vec concat)) 153 | (def ^:private append-unique (comp vec distinct concat)) 154 | 155 | (def ^:private merge-alias-rules 156 | {:deps merge ;; FUTURE: remove 157 | :replace-deps merge ;; formerly :deps 158 | :extra-deps merge 159 | :override-deps merge 160 | :default-deps merge 161 | :classpath-overrides merge 162 | :paths append-unique ;; FUTURE: remove 163 | :replace-paths append-unique ;; formerly :paths 164 | :extra-paths append-unique 165 | :jvm-opts append 166 | :main-opts last-wins 167 | :exec-fn last-wins 168 | :exec-args merge-or-replace 169 | :ns-aliases merge 170 | :ns-default last-wins}) 171 | 172 | (defn- choose-rule [alias-key val] 173 | (or (merge-alias-rules alias-key) 174 | (if (map? val) 175 | merge 176 | (fn [_v1 v2] v2)))) 177 | 178 | (defn- merge-alias-maps 179 | "Like merge-with, but using custom per-alias-key merge function" 180 | [& ms] 181 | (reduce 182 | #(reduce 183 | (fn [m [k v]] (update m k (choose-rule k v) v)) 184 | %1 %2) 185 | {} ms)) 186 | 187 | (defn combine-aliases 188 | "Find, read, and combine alias maps identified by alias keywords from 189 | a deps edn map into a single args map." 190 | [edn-map alias-kws] 191 | (->> alias-kws 192 | (map #(get-in edn-map [:aliases %])) 193 | (apply merge-alias-maps))) 194 | 195 | 196 | 197 | 198 | (defn- chase-key 199 | "Given an aliases set and a keyword k, return a flattened vector of path 200 | entries for that k, resolving recursively if needed, or nil." 201 | [aliases k] 202 | (let [path-coll (get aliases k)] 203 | (when (seq path-coll) 204 | (into [] (mapcat #(if (string? %) [[% {:path-key k}]] (chase-key aliases %))) path-coll)))) 205 | 206 | (defn- flatten-paths 207 | [{:keys [paths aliases] :as deps-edn-map} {:keys [extra-paths] :as classpath-args}] 208 | (let [aliases' (assoc aliases :paths paths :extra-paths extra-paths)] 209 | (into [] (comp (mapcat #(chase-key aliases' %)) (remove nil?)) [:extra-paths :paths]))) 210 | 211 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------