├── VERSION_TEMPLATE ├── .clj-kondo └── config.edn ├── script └── build │ ├── update_version │ ├── git_revision │ ├── branch_revision │ ├── trunk_revision │ └── revision ├── .gitignore ├── .github ├── workflows │ └── maven.yml └── PULL_REQUEST_TEMPLATE ├── CONTRIBUTING.md ├── src ├── main │ ├── resources │ │ └── clojure │ │ │ └── tools │ │ │ └── deps │ │ │ ├── deps.edn │ │ │ └── license-abbrev.edn │ ├── clojure │ │ └── clojure │ │ │ └── tools │ │ │ ├── deps │ │ │ └── alpha │ │ │ │ ├── script │ │ │ │ ├── parse.clj │ │ │ │ ├── print_tree.clj │ │ │ │ ├── generate_manifest2.clj │ │ │ │ ├── resolve_tags.clj │ │ │ │ └── make_classpath2.clj │ │ │ │ ├── util │ │ │ │ ├── concurrent.clj │ │ │ │ ├── s3_aws_client.clj │ │ │ │ ├── session.clj │ │ │ │ ├── io.clj │ │ │ │ ├── dir.clj │ │ │ │ ├── s3_transporter.clj │ │ │ │ └── maven.clj │ │ │ │ ├── extensions │ │ │ │ ├── deps.clj │ │ │ │ ├── local.clj │ │ │ │ ├── pom.clj │ │ │ │ ├── git.clj │ │ │ │ └── maven.clj │ │ │ │ ├── tool.clj │ │ │ │ ├── tree.clj │ │ │ │ ├── specs.clj │ │ │ │ ├── extensions.clj │ │ │ │ └── gen │ │ │ │ └── pom.clj │ │ │ └── cli │ │ │ ├── help.clj │ │ │ └── api.clj │ └── java │ │ └── clojure │ │ └── tools │ │ └── deps │ │ └── alpha │ │ └── util │ │ └── S3TransporterFactory.java └── test │ └── clojure │ └── clojure │ └── tools │ ├── deps │ └── alpha │ │ ├── util │ │ ├── dir_test.clj │ │ └── test_s3_transporter.clj │ │ ├── gen │ │ └── test_pom.clj │ │ ├── extensions │ │ ├── faken.clj │ │ └── test_git.clj │ │ └── script │ │ └── test_make_classpath2.clj │ └── cli │ └── test_api.clj ├── deps.edn ├── README.md ├── pom.xml └── LICENSE /VERSION_TEMPLATE: -------------------------------------------------------------------------------- 1 | 0.15.GENERATED_VERSION 2 | -------------------------------------------------------------------------------- /.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:skip-comments true 2 | :linters 3 | {:unused-binding {:level :off}}} 4 | -------------------------------------------------------------------------------- /script/build/update_version: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | mvn versions:set -DgenerateBackupPoms=false -DnewVersion=`script/build/revision`-SNAPSHOT 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 | -------------------------------------------------------------------------------- /script/build/git_revision: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Return the portion of the version number generated from git 4 | # 5 | 6 | set -e 7 | 8 | trunk_basis=`script/build/trunk_revision` 9 | sha=`git rev-parse HEAD` 10 | 11 | sha=${sha:0:${#sha}-34} # drop the last 34 characters, keep 6 12 | 13 | echo $trunk_basis 14 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3.1.0 12 | - name: Set up JDK 1.8 13 | uses: actions/setup-java@v3.6.0 14 | with: 15 | java-version: 8 16 | distribution: 'temurin' 17 | cache: 'maven' 18 | - name: Build with Maven 19 | run: mvn package --file pom.xml 20 | -------------------------------------------------------------------------------- /script/build/branch_revision: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # If on a branch other than master, returns the number of commits made off of master 4 | # If on master, returns 0 5 | 6 | set -e 7 | 8 | master_tag=`git rev-parse --abbrev-ref HEAD` 9 | 10 | if [ "$master_tag" == "master" ]; then 11 | echo "0" 12 | else 13 | last_commit=`git rev-parse HEAD` 14 | revision=`git rev-list master..$last_commit | wc -l` 15 | echo $revision 16 | fi 17 | -------------------------------------------------------------------------------- /script/build/trunk_revision: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Returns the number of commits made since the v0.0 tag 4 | 5 | set -e 6 | 7 | REVISION=`git --no-replace-objects describe --match v0.0` 8 | 9 | # Extract the version number from the string. Do this in two steps so 10 | # it is a little easier to understand. 11 | REVISION=${REVISION:5} # drop the first 5 characters 12 | REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters 13 | 14 | echo $REVISION 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /script/build/revision: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Return the complete revision number 4 | # ...-[-qualifier] 5 | 6 | set -e 7 | 8 | version_template=`cat VERSION_TEMPLATE` 9 | 10 | if [[ "$version_template" =~ ^[0-9]+\.[0-9]+\.GENERATED_VERSION(-[a-zA-Z0-9]+)?$ ]]; then 11 | 12 | git_revision=`script/build/git_revision` 13 | echo ${version_template/GENERATED_VERSION/$git_revision} 14 | 15 | else 16 | echo "Invalid version template string: $version_template" >&2 17 | exit -1 18 | fi 19 | 20 | -------------------------------------------------------------------------------- /src/main/resources/clojure/tools/deps/deps.edn: -------------------------------------------------------------------------------- 1 | { 2 | :paths ["src"] 3 | 4 | :deps { 5 | org.clojure/clojure {:mvn/version "1.11.1"} 6 | } 7 | 8 | :aliases { 9 | :deps {:replace-paths [] 10 | :replace-deps {org.clojure/tools.deps.alpha {:mvn/version "${project.version}"} 11 | org.slf4j/slf4j-nop {:mvn/version "1.7.25"}} 12 | :ns-default clojure.tools.cli.api 13 | :ns-aliases {help clojure.tools.cli.help}} 14 | :test {:extra-paths ["test"]} 15 | } 16 | 17 | :mvn/repos { 18 | "central" {:url "https://repo1.maven.org/maven2/"} 19 | "clojars" {:url "https://repo.clojars.org/"} 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.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/alpha/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.alpha.util.dir-test 10 | (:require 11 | [clojure.tools.deps.alpha.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/alpha/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.alpha.script.parse 11 | (:require 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.edn :as edn] 15 | [clojure.tools.deps.alpha :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 | (edn/read-string {:default tagged-literal} s))) 43 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.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 #(do 34 | (push-thread-bindings bindings) 35 | (f))] 36 | (.submit executor ^Callable task))) 37 | 38 | (defn shutdown-on-error 39 | [^ExecutorService executor] 40 | (.shutdownNow executor) 41 | (.awaitTermination executor 1 TimeUnit/SECONDS)) 42 | 43 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/script/print_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 ^{:skip-wiki true} 10 | clojure.tools.deps.alpha.script.print-tree 11 | (:require 12 | [clojure.tools.deps.alpha :as deps] 13 | [clojure.tools.deps.alpha.util.io :as io :refer [printerrln]] 14 | [clojure.tools.cli :as cli]) 15 | (:import 16 | [clojure.lang IExceptionInfo])) 17 | 18 | (def ^:private opts 19 | [[nil "--libs-file PATH" "Libs cache file to write"]]) 20 | 21 | (defn run 22 | "Run print-tree script. See -main for details." 23 | [{:keys [libs-file] :as _options}] 24 | (let [lib-map (io/slurp-edn libs-file)] 25 | (deps/print-tree lib-map))) 26 | 27 | (defn -main 28 | "Main entry point for print-tree script. 29 | 30 | Required: 31 | --libs-file=path - libs file 32 | 33 | Prints the tree from libs file" 34 | [& args] 35 | (try 36 | (let [{:keys [options errors]} (cli/parse-opts args opts)] 37 | (when (seq errors) 38 | (run! println errors) 39 | (System/exit 1)) 40 | (run options)) 41 | (catch Throwable t 42 | (printerrln "Error printing tree." (.getMessage t)) 43 | (when-not (instance? IExceptionInfo t) 44 | (.printStackTrace t)) 45 | (System/exit 1)))) 46 | 47 | (comment 48 | (run {:libs-file "foo.libs"}) 49 | ) 50 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.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/alpha/util/test_s3_transporter.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.tools.deps.alpha.util.test-s3-transporter 2 | (:require 3 | [clojure.test :refer [are deftest]] 4 | [clojure.tools.deps.alpha.util.s3-transporter :as s3t] 5 | [clojure.tools.deps.alpha.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/alpha/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.alpha.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 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src/main/clojure" "src/main/resources"] 2 | :deps { 3 | org.clojure/clojure {:mvn/version "1.10.3"} 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"} 6 | org.apache.maven.resolver/maven-resolver-impl {:mvn/version "1.8.2"} 7 | org.apache.maven.resolver/maven-resolver-util {:mvn/version "1.8.2"} 8 | org.apache.maven.resolver/maven-resolver-connector-basic {:mvn/version "1.8.2"} 9 | org.apache.maven.resolver/maven-resolver-transport-file {:mvn/version "1.8.2"} 10 | org.apache.maven.resolver/maven-resolver-transport-http {:mvn/version "1.8.2"} 11 | org.apache.maven/maven-resolver-provider {:mvn/version "3.8.6"} 12 | org.apache.maven/maven-core {:mvn/version "3.8.6" exclusions [commons-io/commons-io com.google.guava/guava]} 13 | commons-io/commons-io {:mvn/version "2.11.0"} ;; update transitive dep due to CVE-2021-29425 14 | com.google.guava/guava {:mvn/version "31.1-android"} ;; update transitive dep due to CVE-2020-8908 15 | org.clojure/data.xml {:mvn/version "0.2.0-alpha8"} 16 | org.clojure/tools.gitlibs {:mvn/version "2.4.181"} 17 | org.clojure/tools.cli {:mvn/version "1.0.214"} 18 | com.cognitect.aws/api {:mvn/version "0.8.612"} 19 | com.cognitect.aws/endpoints {:mvn/version "1.1.12.321"} 20 | com.cognitect.aws/s3 {:mvn/version "822.2.1145.0"} 21 | javax.inject/javax.inject {:mvn/version "1"} 22 | } 23 | :aliases { 24 | ;; clj -M:lint 25 | :lint {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2022.10.14"}} 26 | :main-opts ["-m" "clj-kondo.main" "--lint" "src/main/clojure" "--lint" "src/test/clojure"]} 27 | 28 | ;; clj -M:cve 29 | :cve {:extra-deps {io.github.clj-holmes/clj-watson {:git/tag "v4.1.0" :git/sha "e3da1b6"}} 30 | :jvm-opts ["--illegal-access=deny"] 31 | :main-opts ["-m" "clj-watson.cli" "scan" "-p" "deps.edn"]} 32 | 33 | ;; clj -M:outdated 34 | :outdated {:extra-deps {com.github.liquidz/antq {:mvn/version "RELEASE"}} 35 | :main-opts ["-m" "antq.core"]} 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/deps/alpha/gen/test_pom.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.tools.deps.alpha.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.alpha.gen.pom :as gen-pom] 7 | [clojure.tools.deps.alpha :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/alpha/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.alpha.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/alpha/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.alpha.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.alpha.util.s3-transporter")); 35 | NEW_TRANSPORTER = Clojure.var("clojure.tools.deps.alpha.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/alpha/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.alpha.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)) -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/deps/alpha/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.alpha.extensions.faken 10 | (:require 11 | [clojure.string :as str] 12 | [clojure.tools.deps.alpha.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/alpha/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.alpha.extensions.deps 11 | (:require 12 | [clojure.java.io :as jio] 13 | [clojure.tools.deps.alpha :as deps] 14 | [clojure.tools.deps.alpha.extensions :as ext] 15 | [clojure.tools.deps.alpha.util.dir :as dir] 16 | [clojure.tools.deps.alpha.util.io :as io] 17 | [clojure.tools.deps.alpha.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/alpha/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.alpha.tool 10 | (:require 11 | [clojure.java.io :as jio] 12 | [clojure.pprint :as pprint] 13 | [clojure.string :as str] 14 | [clojure.tools.deps.alpha :as deps] 15 | [clojure.tools.deps.alpha.extensions :as ext] 16 | [clojure.tools.deps.alpha.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/alpha/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.alpha.script.generate-manifest2 11 | (:require [clojure.java.io :as jio] 12 | [clojure.tools.cli :as cli] 13 | [clojure.tools.deps.alpha :as deps] 14 | [clojure.tools.deps.alpha.gen.pom :as pom] 15 | [clojure.tools.deps.alpha.script.parse :as parse] 16 | [clojure.tools.deps.alpha.script.make-classpath2 :as makecp] 17 | [clojure.tools.deps.alpha.util.io :refer [printerrln]]) 18 | (:import 19 | [clojure.lang IExceptionInfo])) 20 | 21 | (def ^:private opts 22 | [[nil "--config-user PATH" "User deps.edn location"] 23 | [nil "--config-project PATH" "Project deps.edn location"] 24 | [nil "--config-data EDN" "Final deps.edn data to treat as the last deps.edn file" :parse-fn parse/parse-config] 25 | [nil "--gen TYPE" "manifest type to generate" :parse-fn keyword] 26 | ["-R" "--resolve-aliases ALIASES" "Concatenated resolve-deps alias names" :parse-fn parse/parse-kws] 27 | ["-C" "--makecp-aliases ALIASES" "Concatenated make-classpath alias names" :parse-fn parse/parse-kws] 28 | ["-A" "--repl-aliases ALIASES" "Concatenated repl alias names" :parse-fn parse/parse-kws]]) 29 | 30 | (defn -main 31 | "Main entry point for generating a manifest file. 32 | 33 | Required: 34 | --config-user PATH - User deps.edn location 35 | --config-project PATH - Project deps.edn location 36 | --config-data={...} - deps.edn as data 37 | --gen TYPE - manifest type to generate (currently only pom) 38 | 39 | Options: 40 | -Raliases - concated resolve-deps alias names, applied to the :deps 41 | -Aaliases - concatenated generic alias names" 42 | [& args] 43 | (let [{:keys [options errors]} (cli/parse-opts args opts)] 44 | (when (seq errors) 45 | (run! println errors) 46 | (System/exit 1)) 47 | (let [{:keys [gen config-user config-project]} options] 48 | (try 49 | (let [mod-map (makecp/run-core (merge options 50 | {:install-deps (deps/root-deps) 51 | :user-deps (makecp/read-deps config-user) 52 | :project-deps (makecp/read-deps config-project)})) 53 | ;; treat all transitive deps as top-level deps 54 | updated-deps (reduce-kv (fn [m lib {:keys [dependents] :as coord}] 55 | (if (seq dependents) m (assoc m lib coord))) 56 | {} (:libs mod-map))] 57 | (pom/sync-pom (merge mod-map {:deps updated-deps}) (jio/file "."))) 58 | (catch Throwable t 59 | (printerrln "Error generating" (name gen) "manifest:" (.getMessage t)) 60 | (when-not (instance? IExceptionInfo t) 61 | (.printStackTrace t)) 62 | (System/exit 1)))))) 63 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/cli/help.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.cli.help 10 | (:require 11 | [clojure.string :as str] 12 | [clojure.repl :as repl])) 13 | 14 | (defn- garner-ns-defaults [] 15 | (try 16 | (require 'clojure.run.exec) 17 | (let [nsd (some-> "clojure.run.exec/*ns-default*" symbol resolve deref) 18 | nsa (some-> "clojure.run.exec/*ns-aliases*" symbol resolve deref)] 19 | (cond-> {} 20 | nsd (assoc :ns-default nsd) 21 | nsa (assoc :ns-aliases nsa))) 22 | (catch Exception e 23 | (throw (ex-info (.getMessage e) {} e))))) 24 | 25 | (defn- qualify-fn 26 | "Compute function symbol based on exec-fn, ns-aliases, and ns-default" 27 | [fsym ns-aliases ns-default] 28 | ;; validation - make specs? 29 | (when (and fsym (not (symbol? fsym))) 30 | (throw (ex-info (str "Expected function symbol:" fsym) {}))) 31 | 32 | (when fsym 33 | (if (qualified-ident? fsym) 34 | (let [nsym (get ns-aliases (symbol (namespace fsym)))] 35 | (if nsym 36 | (symbol (str nsym) (name fsym)) 37 | fsym)) 38 | (if ns-default 39 | (symbol (str ns-default) (str fsym)) 40 | (throw (ex-info (str "Unqualified function can't be resolved:" fsym) {})))))) 41 | 42 | (defn doc 43 | "Print doc for the specified namespace or function. If neither is specified, print docs 44 | for :ns-default. 45 | 46 | Options: 47 | :ns Print docs for namespace 48 | :fn Print docs for function" 49 | [{:keys [ns fn] :as args}] 50 | (let [{:keys [ns-default ns-aliases]} (merge args (garner-ns-defaults))] 51 | (if fn 52 | (#'repl/print-doc (meta (requiring-resolve (qualify-fn fn ns-aliases ns-default)))) 53 | (let [ns-maybe (or ns ns-default) 54 | ns (if ns-aliases (get ns-aliases ns-maybe ns-maybe) ns-maybe)] 55 | (when (nil? ns) 56 | (throw (ex-info "No namespace or function specified" {}))) 57 | (require ns) 58 | (let [my-ns (the-ns ns) 59 | ns-doc (:doc (meta my-ns))] 60 | ;; Print namespace docs 61 | (when (not (str/blank? ns-doc)) 62 | (println ns-doc) 63 | (println)) 64 | ;; Print function docs 65 | (doseq [[k v] (->> my-ns ns-publics (sort-by key))] 66 | (when (instance? clojure.lang.Fn @v) 67 | (#'repl/print-doc (meta v))))))))) 68 | 69 | (defn dir 70 | "Prints a sorted directory of public vars in a namespace. If a namespace is not 71 | specified :ns-default is used instead." 72 | [{:keys [ns] :as args}] 73 | (let [{:keys [ns-default ns-aliases]} (merge args (garner-ns-defaults)) 74 | ns-maybe (or ns ns-default) 75 | ns (if ns-aliases (get ns-aliases ns-maybe ns-maybe) ns-maybe) 76 | _ (require ns) 77 | my-ns (the-ns ns)] 78 | (doseq [[s v] (->> my-ns ns-publics (sort-by key))] 79 | (when (instance? clojure.lang.Fn @v) 80 | (println s))))) 81 | 82 | (comment 83 | (doc {:ns 'clojure.tools.cli.help}) 84 | (doc {:fn 'clojure.tools.cli.help/doc}) 85 | (dir {:ns 'clojure.tools.cli.help}) 86 | ) 87 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.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.alpha :as deps] 16 | [clojure.tools.deps.alpha.extensions.git :as git] 17 | [clojure.tools.deps.alpha.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 | # NOTE: This project has been superseded by [tools.deps](https://github.com/clojure/tools.deps) 2 | 3 | tools.deps.alpha 4 | ======================================== 5 | 6 | A functional API for transitive dependency graph expansion and the creation of classpaths. 7 | 8 | # Rationale 9 | 10 | 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: 11 | 12 | * the classpath used when invoking JVM processes (and/or URLClassLoaders) 13 | * transitive dependency download and resolution from Maven repositories 14 | 15 | tools.deps.alpha aims to provide a functional API to access these capabilities. 16 | 17 | tools.deps.alpha 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.) 18 | 19 | tools.deps.alpha will support package installers for Clojure (e.g. brew, apt-get, etc) to provide a path for Clojure installation and ongoing Clojure development. 20 | 21 | The Clojure 1.9 release for the first time requires 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. 22 | 23 | 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. 24 | 25 | Also see: 26 | 27 | * [Getting Started](https://clojure.org/guides/getting_started) 28 | * [Deps and CLI Guide](https://clojure.org/guides/deps_and_cli) 29 | * [Deps and CLI Reference](https://clojure.org/reference/deps_and_cli) 30 | * [Dependency Heaven](http://cdn.cognitect.com/presentations/2017/dependency_heaven.pdf) from EuroClojure 2017 - [video](https://youtube.com/watch?v=sStlTye-Kjk) 31 | * [Projects that use or work with tools.deps and clj](https://github.com/clojure/tools.deps.alpha/wiki/Tools) 32 | * [Tool implementation and installers](https://github.com/clojure/brew-install) 33 | 34 | # Release Information 35 | 36 | Latest release: 0.15.1254 37 | 38 | * [All released versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22tools.deps.alpha%22) 39 | 40 | [deps.edn](https://clojure.org/guides/deps_and_cli) dependency information: 41 | 42 | ``` 43 | org.clojure/tools.deps.alpha {:mvn/version "0.15.1254"} 44 | ``` 45 | 46 | [Leiningen](https://github.com/technomancy/leiningen/) dependency information: 47 | 48 | ``` 49 | [org.clojure/tools.deps.alpha "0.15.1254"] 50 | ``` 51 | 52 | [Maven](https://maven.apache.org) dependency information: 53 | 54 | ``` 55 | 56 | org.clojure 57 | tools.deps.alpha 58 | 0.15.1254 59 | 60 | ``` 61 | 62 | # API 63 | 64 | For info on using tools.deps as a library, see: 65 | 66 | * [API Docs](https://clojure.github.io/tools.deps.alpha) 67 | 68 | # Developer Information 69 | 70 | * [GitHub project](https://github.com/clojure/tools.deps.alpha) 71 | * [How to contribute](https://clojure.org/community/contributing) 72 | * [Bug Tracker](https://clojure.atlassian.net/browse/TDEPS) 73 | * [Continuous Integration](https://build.clojure.org/job/tools.deps.alpha/) 74 | * [Compatibility Test Matrix](https://build.clojure.org/job/tools.deps.alpha-test-matrix/) 75 | 76 | # Copyright and License 77 | 78 | Copyright © 2017-2022 Rich Hickey, Alex Miller, and contributors 79 | 80 | All rights reserved. The use and 81 | distribution terms for this software are covered by the 82 | [Eclipse Public License 1.0] which can be found in the file 83 | LICENSE at the root of this distribution. By using this software 84 | in any fashion, you are agreeing to be bound by the terms of this 85 | license. You must not remove this notice, or any other, from this 86 | software. 87 | 88 | [Eclipse Public License 1.0]: https://opensource.org/licenses/eclipse-1.0.php 89 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.extensions.local 11 | (:require 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.tools.deps.alpha.extensions :as ext] 15 | [clojure.tools.deps.alpha.extensions.pom :as pom] 16 | [clojure.tools.deps.alpha.util.dir :as dir] 17 | [clojure.tools.deps.alpha.util.maven :as maven] 18 | [clojure.tools.deps.alpha.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 | (defmethod ext/manifest-file :jar 103 | [_lib {:keys [deps/root] :as _coord} _mf _config] 104 | nil) 105 | 106 | (defmethod ext/license-info-mf :jar 107 | [lib {:keys [local/root] :as _coord} _mf config] 108 | (let [jar (JarFile. (ensure-file lib root))] 109 | (when-let [path (find-pom jar)] 110 | (let [url (URL. (str "jar:file:" root "!/" path)) 111 | src (UrlModelSource. url) 112 | settings (session/retrieve :mvn/settings #(maven/get-settings)) 113 | model (pom/read-model src config settings) 114 | licenses (.getLicenses model) 115 | ^License license (when (and licenses (pos? (count licenses))) (first licenses))] 116 | (when license 117 | (let [name (.getName license) 118 | url (.getUrl license)] 119 | (when (or name url) 120 | (cond-> {} 121 | name (assoc :name name) 122 | url (assoc :url url))))))))) 123 | 124 | (defmethod ext/coord-usage :jar 125 | [_lib _coord _manifest-type _config] 126 | ;; TBD 127 | nil) 128 | 129 | (defmethod ext/prep-command :jar 130 | [_lib _coord _manifest-type _config] 131 | ;; TBD - could look in jar 132 | nil) -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/deps/alpha/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.alpha.extensions.test-git 10 | (:require 11 | [clojure.test :refer [deftest is are]] 12 | [clojure.tools.deps.alpha.extensions :as ext] 13 | [clojure.tools.deps.alpha.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.alpha.git" 'io.github.clojure/tools.deps.alpha 20 | "https://github.com/clojure/tools.deps.alpha.git" 'com.github.clojure/tools.deps.alpha 21 | "https://gitlab.com/clojure/tools.deps.alpha.git" 'io.gitlab.clojure/tools.deps.alpha 22 | "https://gitlab.com/clojure/tools.deps.alpha.git" 'com.gitlab.clojure/tools.deps.alpha 23 | "https://bitbucket.org/clojure/tools.deps.alpha.git" 'io.bitbucket.clojure/tools.deps.alpha 24 | "https://bitbucket.org/clojure/tools.deps.alpha.git" 'org.bitbucket.clojure/tools.deps.alpha 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.alpha {:git/url "https://github.com/clojure/tools.deps.alpha.git" 34 | :git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"}] 35 | (ext/canonicalize 'io.github.clojure/tools.deps.alpha {:git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"} {}))) 36 | 37 | ;; standardize sha 38 | (is (= ['io.github.clojure/tools.deps.alpha {:git/url "https://github.com/clojure/tools.deps.alpha.git" 39 | :git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"}] 40 | (ext/canonicalize 'io.github.clojure/tools.deps.alpha {:sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"} {}))) 41 | 42 | ;; standardize sha/tag 43 | (is (= ['io.github.clojure/tools.deps.alpha {:git/url "https://github.com/clojure/tools.deps.alpha.git" 44 | :git/sha "c2a3bbe9df5f574c8af17f07d18b75198194d26e" 45 | :git/tag "tools.deps.alpha-0.5.317"}] 46 | (ext/canonicalize 'io.github.clojure/tools.deps.alpha {: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.alpha {} {}))) 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.alpha" 56 | (ext/canonicalize 'org.clojure/tools.deps.alpha {: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.alpha" 60 | (ext/canonicalize 'io.github.clojure/tools.deps.alpha {:sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c" 61 | :git/sha "9bf5778dc26dd5018dbf04fc8e7dbb32ddc4036c"} {}))) 62 | 63 | ;; lib must be qualified 64 | (is (thrown-with-msg? ExceptionInfo #"Invalid lib name: tools.deps.alpha" 65 | (ext/canonicalize 'tools.deps.alpha {:git/url "https://github.com/clojure/tools.deps.alpha.git"} {}))) 66 | 67 | ;; require full sha if no tag 68 | (is (thrown-with-msg? ExceptionInfo #"Library io.github.clojure/tools.deps.alpha has prefix sha, use full sha or add tag" 69 | (ext/canonicalize 'io.github.clojure/tools.deps.alpha {:git/sha "9bf5778"} {}))) 70 | 71 | ;; require at least prefix sha with tag 72 | (is (thrown-with-msg? ExceptionInfo #"Library io.github.clojure/tools.deps.alpha has coord with missing sha" 73 | (ext/canonicalize 'io.github.clojure/tools.deps.alpha {: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.alpha has sha and tag that point to different commits" 77 | (ext/canonicalize 'io.github.clojure/tools.deps.alpha {: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.alpha has invalid tag" 81 | (ext/canonicalize 'io.github.clojure/tools.deps.alpha {:git/tag "9bf5778" :git/sha "9bf5778"} {}))) 82 | ) 83 | 84 | (comment 85 | (canonicalize) 86 | ) -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.tree 10 | (:require 11 | [clojure.walk :as walk] 12 | [clojure.tools.deps.alpha :as deps] 13 | [clojure.tools.deps.alpha.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.alpha/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 | :root - dep source, default = :standard 64 | :user - dep source, default = :standard 65 | :project - dep source, default = :standard (\"./deps.edn\") 66 | :extra - dep source, default = nil 67 | :aliases - coll of aliases of argmaps to apply to subprocesses" 68 | ([] (calc-trace nil)) 69 | ([opts] 70 | (let [{:keys [extra aliases]} opts 71 | trace-opts (merge opts 72 | {:extra (assoc-in extra [:aliases :__TRACE__ :trace] true)} 73 | {:aliases (conj (or aliases []) :__TRACE__)})] 74 | 75 | (-> trace-opts deps/create-basis :libs meta :trace)))) 76 | 77 | ;; Printing 78 | 79 | (defn- space 80 | [n] 81 | (apply str (repeat n \space))) 82 | 83 | (defn- print-node 84 | [{:keys [lib coord include reason]} indented {:keys [hide-libs]}] 85 | (when (and lib (or (= reason :new-top-dep) (not (contains? hide-libs lib)))) 86 | (let [pre (space indented) 87 | summary (ext/coord-summary lib coord)] 88 | (println 89 | (case reason 90 | :new-top-dep 91 | (str pre summary) 92 | 93 | (:new-dep :same-version) 94 | (str pre ". " summary) 95 | 96 | :newer-version 97 | (str pre ". " summary " " reason) 98 | 99 | (:use-top :older-version :excluded :parent-omitted :superseded) ;; :superseded is internal here 100 | (str pre "X " summary " " reason) 101 | 102 | ;; fallthrough, unknown reason 103 | (str pre "? " summary include reason)))))) 104 | 105 | (defn print-tree 106 | "Print the tree to the console. 107 | Options: 108 | :indent Indent spacing (default = 2) 109 | :hide-libs Set of libs to ignore as deps under top deps, default = #{org.clojure/clojure}" 110 | ([tree {:keys [indent] :or {indent 2} :as opts}] 111 | (print-tree tree (- 0 indent) opts)) 112 | ([{:keys [children] :as tree} indented opts] 113 | (let [opts' (merge {:indent 2, :hide-libs '#{org.clojure/clojure}} opts)] 114 | (print-node tree indented opts') 115 | (doseq [child (sort-by :step (vals children))] 116 | (print-tree child (+ indented (:indent opts')) opts'))))) 117 | 118 | (comment 119 | (require '[clojure.tools.deps.alpha.util.io :as io]) 120 | (-> "/Users/alex.miller/tmp/20201124/trace.edn" io/slurp-edn trace->tree (print-tree {})) 121 | (-> "/Users/alex.miller/code/tools.deps.alpha2/trace.edn" io/slurp-edn trace->tree (print-tree {})) 122 | (-> "/Users/alex.miller/tmp/20201022/trace.edn" io/slurp-edn trace->tree (print-tree {})) 123 | (let [log (:log (io/slurp-edn "/Users/alex.miller/tmp/20201124/trace.edn"))] 124 | (distinct (map :reason log))) 125 | 126 | ) -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.extensions.pom 11 | (:require 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.tools.deps.alpha.extensions :as ext] 15 | [clojure.tools.deps.alpha.util.maven :as maven] 16 | [clojure.tools.deps.alpha.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/alpha/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.alpha.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/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 | ;; API 127 | 128 | (s/fdef clojure.tools.deps.alpha/resolve-deps 129 | :args (s/cat :deps ::deps-map :options ::resolve-args) 130 | :ret ::lib-map) 131 | 132 | (s/fdef clojure.tools.deps.alpha/make-classpath-map 133 | :args (s/cat :deps ::deps-map, :libs ::lib-map, :classpath-args ::claspath-args) 134 | :ret map?) 135 | 136 | (s/fdef clojure.tools.deps.alpha/make-classpath 137 | :args (s/cat :libs ::lib-map, :paths ::paths, :classpath-args ::classpath-args) 138 | :ret string?) 139 | 140 | (comment 141 | ;; some scratch code to recursively check every deps.edn under 142 | ;; a root directory whether it's valid against the specs 143 | (require 144 | '[clojure.spec.test.alpha :as stest] 145 | '[clojure.tools.deps.alpha :as deps]) 146 | (import '[java.nio.file Files Paths FileVisitor FileVisitResult]) 147 | (stest/instrument (stest/enumerate-namespace 'clojure.tools.deps.alpha)) 148 | 149 | (Files/walkFileTree 150 | (Paths/get "../" (into-array String [])) 151 | (reify FileVisitor 152 | (postVisitDirectory [_ dir ex] FileVisitResult/CONTINUE) 153 | (preVisitDirectory [_ dir attrs] FileVisitResult/CONTINUE) 154 | (visitFileFailed [_ f ex] FileVisitResult/CONTINUE) 155 | (visitFile [_ f attrs] 156 | (when (.endsWith (str f) "/deps.edn") 157 | (print "Checking" (str f)) 158 | (let [v (s/valid? ::deps-map (#'deps/slurp-edn-map (.toFile f)))] 159 | (println ":" v) 160 | (when-not v 161 | (s/explain ::deps-map (#'deps/slurp-edn-map (.toFile f)))))) 162 | FileVisitResult/CONTINUE))) 163 | ) 164 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | tools.deps.alpha 4 | 0.15.1255-SNAPSHOT 5 | tools.deps.alpha 6 | 7 | 8 | org.clojure 9 | pom.contrib 10 | 1.1.0 11 | 12 | 13 | 14 | 15 | puredanger 16 | Alex Miller 17 | 18 | 19 | 20 | 21 | 22 | true 23 | 1.10.3 24 | 1.8.2 25 | 3.8.6 26 | 27 | 28 | 1.10.3 29 | 30 | 31 | 32 | 33 | org.clojure 34 | clojure 35 | ${clojure.version} 36 | 37 | 38 | org.apache.maven.resolver 39 | maven-resolver-api 40 | ${resolverVersion} 41 | 42 | 43 | org.apache.maven.resolver 44 | maven-resolver-spi 45 | ${resolverVersion} 46 | 47 | 48 | org.apache.maven.resolver 49 | maven-resolver-impl 50 | ${resolverVersion} 51 | 52 | 53 | org.apache.maven.resolver 54 | maven-resolver-util 55 | ${resolverVersion} 56 | 57 | 58 | org.apache.maven.resolver 59 | maven-resolver-connector-basic 60 | ${resolverVersion} 61 | 62 | 63 | org.apache.maven.resolver 64 | maven-resolver-transport-file 65 | ${resolverVersion} 66 | 67 | 68 | org.apache.maven.resolver 69 | maven-resolver-transport-http 70 | ${resolverVersion} 71 | 72 | 73 | org.apache.maven 74 | maven-resolver-provider 75 | ${mavenVersion} 76 | 77 | 78 | org.apache.maven 79 | maven-core 80 | ${mavenVersion} 81 | 82 | 83 | commons-io 84 | commons-io 85 | 86 | 87 | com.google.guava 88 | guava 89 | 90 | 91 | 92 | 93 | commons-io 94 | commons-io 95 | 2.11.0 96 | 97 | 98 | com.google.guava 99 | guava 100 | 31.1-android 101 | 102 | 103 | org.slf4j 104 | slf4j-nop 105 | 1.7.36 106 | test 107 | 108 | 109 | org.clojure 110 | data.xml 111 | 0.2.0-alpha8 112 | 113 | 114 | org.clojure 115 | tools.gitlibs 116 | 2.4.181 117 | 118 | 119 | org.clojure 120 | tools.cli 121 | 1.0.214 122 | 123 | 124 | com.cognitect.aws 125 | api 126 | 0.8.612 127 | 128 | 129 | com.cognitect.aws 130 | endpoints 131 | 1.1.12.321 132 | 133 | 134 | com.cognitect.aws 135 | s3 136 | 822.2.1145.0 137 | 138 | 139 | javax.inject 140 | javax.inject 141 | 1 142 | 143 | 144 | 145 | 146 | 147 | 148 | src/main/resources 149 | true 150 | 151 | 152 | 153 | 154 | org.apache.maven.plugins 155 | maven-resources-plugin 156 | 3.1.0 157 | 158 | 159 | 162 | com.theoryinpractise 163 | clojure-maven-plugin 164 | 1.7.1 165 | true 166 | 167 | ${clojure.warnOnReflection} 168 | true 169 | 170 | 171 | 172 | clojure-compile 173 | none 174 | 175 | 176 | clojure-test 177 | test 178 | 179 | test 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | scm:git:git@github.com:clojure/tools.deps.alpha.git 189 | scm:git:git@github.com:clojure/tools.deps.alpha.git 190 | git@github.com:clojure/tools.deps.alpha.git 191 | HEAD 192 | 193 | 194 | 195 | 196 | clojars 197 | https://clojars.org/repo/ 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.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.alpha.util.s3-aws-client)) 80 | (let [f (requiring-resolve 'clojure.tools.deps.alpha.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/test/clojure/clojure/tools/cli/test_api.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.tools.cli.test-api 2 | (:require 3 | [clojure.java.io :as jio] 4 | [clojure.string :as str] 5 | [clojure.test :refer [deftest is] :as test] 6 | [clojure.tools.cli.api :as api] 7 | [clojure.tools.deps.alpha] ;; ensure extensions loaded 8 | [clojure.tools.deps.alpha.util.maven :as mvn] 9 | [clojure.tools.deps.alpha.util.dir :as dir]) 10 | (:import 11 | [java.io File])) 12 | 13 | (def ^:dynamic ^File *test-dir*) 14 | 15 | (defmacro with-test-dir 16 | [& body] 17 | `(let [name# (-> test/*testing-vars* last symbol str) 18 | dir# (jio/file "test-out" name#)] 19 | (.delete dir#) 20 | (.mkdirs dir#) 21 | (binding [*test-dir* dir#] 22 | ~@body))) 23 | 24 | (deftest test-prep-with-aliases 25 | (with-test-dir 26 | (let [p1deps (jio/file *test-dir* "p1/deps.edn") 27 | p2deps (jio/file *test-dir* "p2/deps.edn")] 28 | 29 | ;; set up p1 with an alias that, if used, pulls p2 30 | (jio/make-parents p1deps) 31 | (spit p1deps 32 | (pr-str {:aliases {:x {:extra-deps {'foo/bar {:local/root "../p2"}}}}})) 33 | 34 | ;; set up p2 to prep 35 | (jio/make-parents p2deps) 36 | (spit (jio/file *test-dir* "p2/build.clj") 37 | "(ns build 38 | (:require [clojure.java.io :as jio])) 39 | (defn prep [_] 40 | (jio/make-parents \"prepped/out\"))") 41 | (spit p2deps 42 | (pr-str {:deps/prep-lib {:ensure "prepped" 43 | :alias :build 44 | :fn 'prep} 45 | :aliases {:build {:ns-default 'build}}})) 46 | 47 | ;; prep p1 with aliases 48 | (dir/with-dir (jio/file *test-dir* "p1") 49 | (api/prep 50 | {:root {:mvn/repos mvn/standard-repos} 51 | :user nil 52 | :project :standard 53 | :aliases [:x] 54 | :force true})) 55 | 56 | ;; check that it prepped p2 57 | (is (true? (.exists (jio/file *test-dir* "p2/prepped"))))))) 58 | 59 | 60 | (deftest test-prep-across-modules 61 | (with-test-dir 62 | (spit (jio/file *test-dir* "deps.edn") 63 | (pr-str {:deps {'mono/moda {:local/root "mod/a"} 64 | 'mono/modb {:local/root "mod/b"}}})) 65 | (let [adeps (jio/file *test-dir* "mod/a/deps.edn")] 66 | (jio/make-parents adeps) 67 | (spit adeps "{}")) 68 | (let [bdeps (jio/file *test-dir* "mod/b/deps.edn")] 69 | (jio/make-parents bdeps) 70 | (spit bdeps 71 | (pr-str {:paths ["src"] 72 | :deps/prep-lib {:alias :resources 73 | :fn 'bcore/generate 74 | :ensure "target/resources"} 75 | :aliases {:resources {:deps {'mono/moda {:local/root "../a"}} :paths ["src"]}}}))) 76 | (let [bgen (jio/file *test-dir* "mod/b/src/bcore.clj") 77 | cp-path (.getCanonicalPath (jio/file *test-dir* "mod/b/target/resources/cp"))] 78 | (jio/make-parents bgen) 79 | (spit bgen 80 | (str 81 | "(ns bcore)" 82 | (format "(defn generate [_] (.mkdirs (.getParentFile (java.io.File. \"%s\"))) (spit \"%s\" (System/getProperty \"java.class.path\")))" 83 | cp-path cp-path))) 84 | (api/prep 85 | {:root {:mvn/repos mvn/standard-repos} 86 | :user nil 87 | :project {:deps {'org.clojure/clojure {:mvn/version "1.11.1"} 88 | 'mono/root {:local/root (.getPath *test-dir*)}}} 89 | ;; :log :debug 90 | :force true}) 91 | (let [cp-out (slurp cp-path)] 92 | (is (true? (str/includes? cp-out (.getCanonicalPath (jio/file *test-dir* "mod/a/src"))))))))) 93 | 94 | (deftest test-prep-exec-args 95 | (with-test-dir 96 | (let [p1deps (jio/file *test-dir* "p1/deps.edn") 97 | p2deps (jio/file *test-dir* "p2/deps.edn")] 98 | 99 | ;; set up p1 to depend on p2 100 | (jio/make-parents p1deps) 101 | (spit p1deps (pr-str {:deps {'foo/p2 {:local/root "../p2"}}})) 102 | 103 | ;; set up p2 to prep 104 | (jio/make-parents p2deps) 105 | (spit (jio/file *test-dir* "p2/build.clj") 106 | "(ns build 107 | (:require [clojure.java.io :as jio])) 108 | (defn prep [args] 109 | (jio/make-parents \"prepped/out\") 110 | (spit \"prepped/out\" args))") 111 | (spit p2deps 112 | (pr-str {:deps/prep-lib {:ensure "prepped" 113 | :alias :build 114 | :fn 'prep} 115 | :aliases {:build {:ns-default 'build 116 | :exec-args {:hi :there}}}})) 117 | 118 | ;; prep p1 with aliases 119 | (dir/with-dir (jio/file *test-dir* "p1") 120 | (api/prep 121 | {:root {:mvn/repos mvn/standard-repos} 122 | :user nil 123 | :project :standard 124 | :force true})) 125 | 126 | ;; check that it prepped p2 and received the args 127 | (is (= "{:hi :there}" (slurp (jio/file *test-dir* "p2/prepped/out"))))))) 128 | 129 | (deftest test-self-prep 130 | (with-test-dir 131 | (let [p1deps (jio/file *test-dir* "p1/deps.edn") 132 | p2deps (jio/file *test-dir* "p2/deps.edn")] 133 | 134 | ;; set up p1 to depend on p2 but also need self prep 135 | (jio/make-parents p1deps) 136 | (spit (jio/file *test-dir* "p1/build.clj") 137 | "(ns build 138 | (:require [clojure.java.io :as jio])) 139 | (defn prep [args] 140 | (jio/make-parents \"prepped/out\"))") 141 | (spit p1deps 142 | (pr-str {:deps {'foo/p2 {:local/root "../p2"}} 143 | :deps/prep-lib {:ensure "prepped" 144 | :alias :build 145 | :fn 'prep} 146 | :aliases {:build {:ns-default 'build}}})) 147 | 148 | ;; set up p2 to prep 149 | (jio/make-parents p2deps) 150 | (spit (jio/file *test-dir* "p2/build.clj") 151 | "(ns build 152 | (:require [clojure.java.io :as jio])) 153 | (defn prep [args] 154 | (jio/make-parents \"prepped/out\"))") 155 | (spit p2deps 156 | (pr-str {:deps/prep-lib {:ensure "prepped" 157 | :alias :build 158 | :fn 'prep} 159 | :aliases {:build {:ns-default 'build}}})) 160 | 161 | ;; prep p1 162 | (dir/with-dir (jio/file *test-dir* "p1") 163 | (api/prep 164 | {:root {:mvn/repos mvn/standard-repos} 165 | :user nil 166 | :project :standard 167 | ;; :log :debug 168 | :force true 169 | :current true})) 170 | 171 | ;; check that it prepped p1 and p2 172 | (is (true? (.exists (jio/file *test-dir* "p1/prepped")))) 173 | (is (true? (.exists (jio/file *test-dir* "p2/prepped"))))))) 174 | 175 | (deftest test-find-maven-version 176 | (let [s (with-out-str (api/find-versions {:lib 'org.clojure/clojure :n :all}))] 177 | (is (str/includes? s "1.10.3"))) 178 | 179 | (is (= "" (with-out-str (api/find-versions {:lib 'bogus.taco/slurpee}))))) 180 | 181 | (deftest test-find-git-version 182 | (let [s (with-out-str (api/find-versions {:lib 'io.github.clojure/tools.build :n :all}))] 183 | (is (str/includes? s "v0.8.2"))) 184 | 185 | (is (= "" (with-out-str (api/find-versions {:lib 'io.github.clojure/bogus-taco-slurpee}))))) 186 | 187 | (comment 188 | (test-find-maven-version) 189 | (test-find-git-version) 190 | ) 191 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.extensions.git 11 | (:require 12 | [clojure.java.io :as jio] 13 | [clojure.tools.deps.alpha.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 | (some (fn [{:keys [service url]}] 31 | (when-let [matches (re-matches service group)] 32 | (format url (second matches) project))) 33 | (vals git-services)))) 34 | 35 | (defn full-sha? 36 | [sha] 37 | (boolean (and sha (= 40 (count sha))))) 38 | 39 | (defmethod ext/coord-type-keys :git 40 | [_type] 41 | #{:git/url :git/sha :git/tag :sha}) 42 | 43 | (defn- coord-err 44 | ^Throwable [msg lib coord] 45 | (ex-info msg {:lib lib :coord coord})) 46 | 47 | (defn- make-standard 48 | [coord url sha tag] 49 | (-> 50 | (cond-> coord 51 | url (assoc :git/url url) 52 | sha (assoc :git/sha sha) 53 | tag (assoc :git/tag tag)) 54 | (dissoc :sha :tag))) 55 | 56 | (defmethod ext/canonicalize :git 57 | [lib {unsha :sha untag :tag :git/keys [url sha tag] :as coord} _config] 58 | (when (nil? (namespace lib)) (throw (coord-err (format "Invalid lib name: %s" lib) lib coord))) 59 | (when (and unsha sha) (throw (coord-err (format "git coord has both :sha and :git/sha for %s" lib) lib coord))) 60 | (when (and untag tag) (throw (coord-err (format "git coord has both :tag and :git/tag for %s" tag) lib coord))) 61 | 62 | (let [canon-sha (or sha unsha) 63 | canon-tag (or tag untag) 64 | canon-url (or url (auto-git-url lib))] 65 | (when (nil? canon-url) 66 | (throw (coord-err (format "Failed to infer git url for: %s" lib) lib coord))) 67 | (when (and canon-tag (not (some #{canon-tag} (gitlibs/tags canon-url)))) 68 | (throw (coord-err (format "Library %s has invalid tag: %s" lib canon-tag) lib coord))) 69 | (if canon-sha 70 | (if canon-tag 71 | (let [full-sha (if (full-sha? canon-sha) canon-sha (gitlibs/resolve canon-url canon-sha))] 72 | (when-not (= (gitlibs/resolve canon-url canon-sha) (gitlibs/resolve canon-url canon-tag)) 73 | (throw (coord-err (format "Library %s has sha and tag that point to different commits" lib) lib coord))) 74 | [lib (make-standard coord canon-url full-sha canon-tag)]) 75 | (if (full-sha? canon-sha) 76 | [lib (make-standard coord canon-url canon-sha canon-tag)] 77 | (throw (ex-info (format "Library %s has prefix sha, use full sha or add tag" lib) {:lib lib :coord coord})))) 78 | (throw (ex-info (format "Library %s has coord with missing sha" lib) {:lib lib :coord coord}))))) 79 | 80 | (defn- to-canonical 81 | [lib {:git/keys [url sha] :as coord} config] 82 | (if (and url (full-sha? sha)) 83 | [lib coord] 84 | (ext/canonicalize lib coord config))) 85 | 86 | (defmethod ext/lib-location :git 87 | [lib coord config] 88 | (let [[lib {:git/keys [sha]}] (to-canonical lib coord config)] 89 | {:base (str (gitlibs/cache-dir) "/libs") ;; gitlibs repo location is not in a public API... 90 | :path (str lib "/" sha) 91 | :type :git})) 92 | 93 | (defmethod ext/dep-id :git 94 | [lib coord config] 95 | (let [[_lib {:git/keys [url sha]}] (to-canonical lib coord config)] 96 | {:git/url url, :git/sha sha})) 97 | 98 | (defmethod ext/manifest-type :git 99 | [lib coord config] 100 | (let [[lib {:git/keys [url sha] :deps/keys [manifest root]}] (to-canonical lib coord config)] 101 | (when-not url 102 | (throw (ex-info (format ":git/url not found or inferred for %s" lib) {:lib lib :coord coord}))) 103 | (let [sha-dir (gitlibs/procure url lib sha)] 104 | (if sha-dir 105 | (let [root-dir (if root 106 | (let [root-file (jio/file root)] 107 | (if (.isAbsolute root-file) ;; should be only after coordinate resolution 108 | (.getCanonicalPath root-file) 109 | (.getCanonicalPath (jio/file sha-dir root-file)))) 110 | sha-dir)] 111 | (if manifest 112 | {:deps/manifest manifest, :deps/root root-dir} 113 | (ext/detect-manifest root-dir))) 114 | (throw (ex-info (format "Commit not found for %s in repo %s at %s" lib url sha) 115 | {:lib lib :coord coord})))))) 116 | 117 | (defmethod ext/coord-summary :git [lib coord] 118 | (let [[lib {:git/keys [url tag sha]}] (to-canonical lib coord nil)] 119 | (str lib " " (if tag tag (subs sha 0 7))))) 120 | 121 | (defmethod ext/license-info :git 122 | [lib coord config] 123 | (let [coord (merge coord (ext/manifest-type lib coord config))] 124 | (ext/license-info-mf lib coord (:deps/manifest coord) config))) 125 | 126 | ;; 0 if x and y are the same commit 127 | ;; negative if x is parent of y (y derives from x) 128 | ;; positive if y is parent of x (x derives from y) 129 | (defmethod ext/compare-versions [:git :git] 130 | [lib x-coord y-coord config] 131 | (let [[_ {x-url :git/url, x-sha :git/sha, :as x}] (to-canonical lib x-coord config) 132 | [_ {y-url :git/url, y-sha :git/sha, :as y}] (to-canonical lib y-coord config)] 133 | (if (= x-sha y-sha) 134 | 0 135 | (let [desc (if (= x-url y-url) 136 | (or 137 | (gitlibs/descendant x-url [x-sha y-sha]) 138 | (gitlibs/descendant y-url [x-sha y-sha])) 139 | (and 140 | (gitlibs/descendant x-url [x-sha y-sha]) 141 | (gitlibs/descendant y-url [x-sha y-sha])))] 142 | (cond 143 | (nil? desc) (throw (ex-info (str "No known ancestor relationship between git versions for " lib "\n" 144 | " " x-url " at " x-sha "\n" 145 | " " y-url " at " y-sha) 146 | {:x x :y y})) 147 | (= desc x-sha) 1 148 | (= desc y-sha) -1))))) 149 | 150 | (defmethod ext/find-versions :git 151 | [lib coord _coord-type config] 152 | (let [url (or (:git/url coord) (auto-git-url lib))] 153 | (when url 154 | (try 155 | (map 156 | (fn [tag] {:git/tag tag :git/sha (subs (gitlibs/commit-sha url tag) 0 7)}) 157 | (gitlibs/tags url)) 158 | (catch Throwable _ nil))))) 159 | 160 | (comment 161 | (ext/find-versions 'io.github.cognitect-labs/test-runner {} :git nil) 162 | 163 | (ext/find-versions 'org.clojure/spec.alpha 164 | {:git/url "https://github.com/clojure/spec.alpha.git"} :git nil) 165 | 166 | (ext/lib-location 'foo/foo 167 | {:git/url "https://github.com/clojure/core.async.git" 168 | :sha "ecea2539a724a415b15e50f12815b4ab115cfd35"} {}) 169 | 170 | (binding [*print-namespace-maps* false] 171 | (run! prn 172 | (ext/find-versions 'io.github.clojure/tools.deps.alpha nil :git nil))) 173 | 174 | ;; error - prefix sha 175 | (ext/canonicalize 'org.clojure/spec.alpha 176 | {:git/url "https://github.com/clojure/spec.alpha.git" :sha "739c1af5"} 177 | nil) 178 | 179 | (ext/dep-id 'org.clojure/spec.alpha 180 | {:git/url "https://github.com/clojure/spec.alpha.git" :sha "739c1af56dae621aedf1bb282025a0d676eff713"} 181 | nil) 182 | 183 | (ext/manifest-type 'org.clojure/spec.alpha 184 | {:git/url "https://github.com/clojure/spec.alpha.git" :git/sha "739c1af56dae621aedf1bb282025a0d676eff713"} 185 | nil) 186 | 187 | (ext/compare-versions 188 | 'org.clojure/spec.alpha 189 | {:git/url "https://github.com/clojure/spec.alpha.git" :git/sha "739c1af56dae621aedf1bb282025a0d676eff713"} 190 | {:git/url "git@github.com:clojure/spec.alpha.git" :git/sha "a65fb3aceec67d1096105cab707e6ad7e5f063af"} 191 | nil) 192 | 193 | ) 194 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.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 and return first that finds some. 147 | Returns coll of coordinates for this lib (based on lib and partial coordinate)." 148 | [lib coord config] 149 | (some #(find-versions lib coord % config) (procurer-types))) 150 | 151 | ;; Methods switching on manifest type 152 | 153 | (defn- throw-bad-manifest 154 | [lib coord manifest-type] 155 | (if manifest-type 156 | (throw (ex-info (str "Manifest type " manifest-type " not loaded for " lib " in coordinate " (pr-str coord)) 157 | {:lib lib :coord coord})) 158 | (throw (ex-info (str "Manifest file not found for " lib " in coordinate " (pr-str coord)) 159 | {:lib lib :coord coord})))) 160 | 161 | (defmulti coord-deps 162 | "Return coll of immediate [lib coord] external deps for this library." 163 | (fn [lib coord manifest-type config] manifest-type)) 164 | 165 | (defmethod coord-deps :default [lib coord manifest-type config] 166 | (throw-bad-manifest lib coord manifest-type)) 167 | 168 | (defmulti coord-paths 169 | "Return coll of classpath roots for this library on disk." 170 | (fn [lib coord manifest-type config] manifest-type)) 171 | 172 | (defmethod coord-paths :default [lib coord manifest-type config] 173 | (throw-bad-manifest lib coord manifest-type)) 174 | 175 | (defmulti manifest-file 176 | "Return path to manifest file (if any). If this file is updated, 177 | causes the cache to be recomputed." 178 | (fn [lib coord manifest-type config] manifest-type)) 179 | 180 | (defmethod manifest-file :default [lib coord manifest-type config] 181 | (throw-bad-manifest lib coord manifest-type)) 182 | 183 | (defmulti license-info-mf 184 | "Return map of license info (:name and :url) or nil if unknown 185 | based on the manifest." 186 | (fn [lib coord manifest-type config] manifest-type)) 187 | 188 | (defmethod license-info-mf :default [lib coord manifest-type config] nil) 189 | 190 | (defmulti coord-usage 191 | "Return usage info map for this library with the following optional keys: 192 | :ns-default - default namespace symbol 193 | :ns-aliases - map of alias to namespace symbol" 194 | (fn [lib coord manifest-type config] manifest-type)) 195 | 196 | (defmethod coord-usage :default [lib coord manifest-type config] 197 | (throw-bad-manifest lib coord manifest-type)) 198 | 199 | (defmulti prep-command 200 | "Return prep command for this library with the following keys: 201 | :alias - alias to use when invoking (keyword) 202 | :fn - function to invoke in alias (symbol) 203 | :ensure - relative path in repo to ensure exists after prep" 204 | (fn [lib coord manifest-type config] manifest-type)) 205 | 206 | (defmethod prep-command :default [lib coord manifest-type config] 207 | (throw-bad-manifest lib coord manifest-type)) 208 | 209 | (comment 210 | (require '[clojure.tools.deps.alpha.util.maven :as maven]) 211 | 212 | (binding [*print-namespace-maps* false] 213 | (run! prn 214 | (find-all-versions 'io.github.clojure/tools.deps.alpha nil {:mvn/repos maven/standard-repos}))) 215 | 216 | (binding [*print-namespace-maps* false] 217 | (run! prn 218 | (find-all-versions 'org.clojure/tools.deps.alpha nil {:mvn/repos maven/standard-repos}))) 219 | 220 | ) -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.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.alpha.util.maven :as maven] 17 | [clojure.tools.deps.alpha.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/alpha/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.alpha.extensions.maven 11 | (:require 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.tools.deps.alpha.extensions :as ext] 15 | [clojure.tools.deps.alpha.util.maven :as maven] 16 | [clojure.tools.deps.alpha.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 | (into [] (map (fn [v] {:mvn/version (.toString ^Version v)}) versions))))) 196 | 197 | (defmethod ext/coord-usage :mvn 198 | [lib {:keys [deps/root]} manifest-type config] 199 | ;; TBD - could look in jar, could download well-known classifier 200 | nil) 201 | 202 | (defmethod ext/prep-command :mvn 203 | [lib {:keys [deps/root]} manifest-type config] 204 | ;; TBD - could look in jar, could download well-known classifier 205 | nil) 206 | 207 | (comment 208 | (ext/lib-location 'org.clojure/clojure {:mvn/version "1.8.0"} {}) 209 | 210 | (binding [*print-namespace-maps* false] 211 | (run! prn 212 | (ext/find-versions 'org.clojure/clojure nil :mvn {:mvn/repos maven/standard-repos}))) 213 | 214 | ;; given a dep, find the child deps 215 | (ext/coord-deps 'org.clojure/clojure {:mvn/version "1.9.0-alpha17"} :mvn {:mvn/repos maven/standard-repos}) 216 | 217 | (ext/coord-deps 'cider/cider-nrepl {:mvn/version "0.17.0-SNAPSHOT"} :mvn {:mvn/repos maven/standard-repos}) 218 | (ext/canonicalize 'joda-time/joda-time {:mvn/version "[2.2,)"} {:mvn/repos maven/standard-repos}) 219 | 220 | ;; give a dep, download just that dep (not transitive - that's handled by the core algorithm) 221 | (ext/coord-paths 'org.clojure/clojure {:mvn/version "1.9.0-alpha17"} :mvn {:mvn/repos maven/standard-repos}) 222 | 223 | ;; get specific classifier 224 | (ext/coord-paths 'org.jogamp.gluegen/gluegen-rt$natives-linux-amd64 {:mvn/version "2.3.2"} 225 | :mvn {:mvn/repos maven/standard-repos}) 226 | 227 | (parse-version {:mvn/version "1.1.0"}) 228 | 229 | (ext/compare-versions 'org.clojure/clojure {:mvn/version "1.1.0-alpha10"} {:mvn/version "1.1.0-beta1"} {}) 230 | 231 | (ext/coord-deps 'org.clojure/clojure {:mvn/version "1.10.0-master-SNAPSHOT"} :mvn 232 | {:mvn/repos (merge maven/standard-repos 233 | {"sonatype-oss-public" {:url "https://oss.sonatype.org/content/groups/public/"}})}) 234 | 235 | (def rr (maven/remote-repo ["sonatype-oss-public" {:url "https://oss.sonatype.org/content/groups/public/"}]))) 236 | 237 | 238 | -------------------------------------------------------------------------------- /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/alpha/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.alpha.script.make-classpath2 11 | (:require 12 | [clojure.java.io :as jio] 13 | [clojure.pprint :as pprint] 14 | [clojure.string :as str] 15 | [clojure.tools.cli :as cli] 16 | [clojure.tools.deps.alpha :as deps] 17 | [clojure.tools.deps.alpha.extensions :as ext] 18 | [clojure.tools.deps.alpha.tool :as tool] 19 | [clojure.tools.deps.alpha.util.io :as io :refer [printerrln]] 20 | [clojure.tools.deps.alpha.script.parse :as parse] 21 | [clojure.tools.deps.alpha.tree :as tree]) 22 | (:import 23 | [clojure.lang IExceptionInfo])) 24 | 25 | (def ^:private opts 26 | [;; deps.edn inputs 27 | [nil "--config-user PATH" "User deps.edn location"] 28 | [nil "--config-project PATH" "Project deps.edn location"] 29 | [nil "--config-data EDN" "Final deps.edn data to treat as the last deps.edn file" :parse-fn parse/parse-config] 30 | ;; tool args to resolve 31 | [nil "--tool-mode" "Tool mode (-T), may optionally supply tool-name or tool-aliases"] 32 | [nil "--tool-name NAME" "Tool name"] 33 | ;; output files 34 | [nil "--libs-file PATH" "Libs cache file to write"] 35 | [nil "--cp-file PATH" "Classpatch cache file to write"] 36 | [nil "--jvm-file PATH" "JVM options file"] 37 | [nil "--main-file PATH" "Main options file"] 38 | [nil "--manifest-file PATH" "Manifest list file"] 39 | [nil "--basis-file PATH" "Basis file"] 40 | [nil "--skip-cp" "Skip writing .cp and .libs files"] 41 | ;; aliases 42 | ["-R" "--resolve-aliases ALIASES" "Concatenated resolve-deps alias names" :parse-fn parse/parse-kws] 43 | ["-C" "--makecp-aliases ALIASES" "Concatenated make-classpath alias names" :parse-fn parse/parse-kws] 44 | ["-A" "--repl-aliases ALIASES" "Concatenated repl alias names" :parse-fn parse/parse-kws] 45 | ["-M" "--main-aliases ALIASES" "Concatenated main option alias names" :parse-fn parse/parse-kws] 46 | ["-X" "--exec-aliases ALIASES" "Concatenated exec alias names" :parse-fn parse/parse-kws] 47 | ["-T" "--tool-aliases ALIASES" "Concatenated tool alias names" :parse-fn parse/parse-kws] 48 | ;; options 49 | [nil "--trace" "Emit trace log to trace.edn"] 50 | [nil "--threads THREADS" "Threads for concurrent downloads"] 51 | [nil "--tree" "Print deps tree to console"]]) 52 | 53 | (defn parse-opts 54 | "Parse the command line opts to make-classpath" 55 | [args] 56 | (cli/parse-opts args opts)) 57 | 58 | (defn check-aliases 59 | "Check that all aliases are known and warn if aliases are undeclared" 60 | [deps aliases] 61 | (when-let [unknown (seq (remove #(contains? (:aliases deps) %) (distinct aliases)))] 62 | (printerrln "WARNING: Specified aliases are undeclared and are not being used:" (vec unknown)))) 63 | 64 | (defn resolve-tool-args 65 | "Resolves the tool by name to the coord + usage data. 66 | Returns the proper alias args as if the tool was specified as an alias." 67 | [tool-name config] 68 | (if-let [{:keys [lib coord]} (tool/resolve-tool tool-name)] 69 | (let [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 | :replace-paths ["."] 74 | :ns-default ns-default 75 | :ns-aliases ns-aliases}) 76 | (throw (ex-info (str "Unknown tool: " tool-name) {:tool tool-name})))) 77 | 78 | (defn run-core 79 | "Run make-classpath script from/to data (no file stuff). Returns: 80 | {;; Main outputs: 81 | :libs lib-map ;; from resolve-deps, .libs file 82 | :cp classpath ;; from make-classpath, .cp file 83 | :main main-opts ;; effective main opts, .main file 84 | :jvm jvm-opts ;; effective jvm opts, .jvm file 85 | :trace trace-log ;; from resolve-deps, if requested, trace.edn file 86 | 87 | ;; Intermediate/source data: 88 | :deps merged-deps ;; effective merged :deps 89 | :paths local-paths ;; from make-classpath, just effective local paths 90 | ;; and any other qualified keys from top level merged deps 91 | }" 92 | [{:keys [install-deps user-deps project-deps config-data ;; all deps.edn maps 93 | tool-mode tool-name tool-resolver ;; -T options 94 | resolve-aliases makecp-aliases main-aliases exec-aliases repl-aliases tool-aliases 95 | skip-cp threads trace tree] :as _opts}] 96 | (when (and main-aliases exec-aliases) 97 | (throw (ex-info "-M and -X cannot be used at the same time" {}))) 98 | (let [pretool-edn (deps/merge-edns [install-deps user-deps project-deps config-data]) 99 | ;; tool use - :deps/:paths/:replace-deps/:replace-paths in project if needed 100 | tool-args (cond 101 | tool-name (tool-resolver tool-name pretool-edn) 102 | tool-mode {:replace-deps {} :replace-paths ["."]}) 103 | tool-edn (when tool-args {:aliases {:deps/TOOL tool-args}}) 104 | ;; :deps/TOOL is a synthetic deps.edn combining the tool definition and usage 105 | ;; it is injected at the end of the deps chain and added as a pseudo alias 106 | ;; the effects are seen in the basis but this pseduo alias should not escape 107 | combined-tool-args (deps/combine-aliases 108 | (deps/merge-edns [pretool-edn tool-edn]) 109 | (concat main-aliases exec-aliases repl-aliases tool-aliases (when tool-edn [:deps/TOOL]))) 110 | project-deps (deps/tool project-deps combined-tool-args) 111 | 112 | ;; calc basis 113 | merge-edn (deps/merge-edns [install-deps user-deps project-deps config-data (when tool-edn tool-edn)]) ;; recalc to get updated project-deps 114 | combined-exec-aliases (concat main-aliases exec-aliases repl-aliases tool-aliases (when tool-edn [:deps/TOOL])) 115 | _ (check-aliases merge-edn (concat resolve-aliases makecp-aliases combined-exec-aliases)) 116 | resolve-argmap (deps/combine-aliases merge-edn (concat resolve-aliases combined-exec-aliases)) 117 | resolve-args (cond-> resolve-argmap 118 | threads (assoc :threads (Long/parseLong threads)) 119 | trace (assoc :trace trace) 120 | tree (assoc :trace true)) 121 | cp-args (deps/combine-aliases merge-edn (concat makecp-aliases combined-exec-aliases)) 122 | exec-argmap (deps/combine-aliases merge-edn combined-exec-aliases) 123 | execute-args (select-keys exec-argmap [:ns-default :ns-aliases :exec-fn :exec-args]) 124 | execute-args (let [arg-kw (:exec-args execute-args)] 125 | (if (keyword? arg-kw) 126 | (assoc execute-args :exec-args (get-in merge-edn [:aliases arg-kw])) 127 | execute-args)) 128 | basis (if skip-cp 129 | (when (pos? (count execute-args)) 130 | {:execute-args execute-args}) 131 | (deps/calc-basis merge-edn 132 | (cond-> {} 133 | resolve-args (assoc :resolve-args resolve-args) 134 | cp-args (assoc :classpath-args cp-args) 135 | execute-args (assoc :execute-args execute-args)))) 136 | 137 | ;; check for unprepped libs 138 | _ (deps/prep-libs! (:libs basis) {:action :error} basis) 139 | 140 | ;; determine manifest files to add to cache check 141 | manifests (->> 142 | (for [[lib coord] (:libs basis)] 143 | (let [mf (ext/manifest-type lib coord basis)] 144 | (ext/manifest-file lib coord (:deps/manifest mf) basis))) 145 | (remove nil?) 146 | seq) 147 | 148 | ;; handle jvm and main opts 149 | jvm (seq (get exec-argmap :jvm-opts)) 150 | main (seq (get exec-argmap :main-opts))] 151 | (when (and main repl-aliases) 152 | (io/printerrln "WARNING: Use of :main-opts with -A is deprecated. Use -M instead.")) 153 | (cond-> basis 154 | jvm (assoc :jvm jvm) 155 | ;; FUTURE: narrow this to (and main main-aliases) 156 | main (assoc :main main) 157 | manifests (assoc :manifests manifests)))) 158 | 159 | (defn read-deps 160 | [name] 161 | (when (not (str/blank? name)) 162 | (let [f (jio/file name)] 163 | (when (.exists f) 164 | (deps/slurp-deps f))))) 165 | 166 | (defn write-lines 167 | [lines file] 168 | (if lines 169 | (io/write-file file (apply str (interleave lines (repeat "\n")))) 170 | (let [jf (jio/file file)] 171 | (when (.exists jf) 172 | (.delete jf))))) 173 | 174 | (defn run 175 | "Run make-classpath script. See -main for details." 176 | [{:keys [config-user config-project libs-file cp-file jvm-file main-file basis-file manifest-file skip-cp trace tree] :as opts}] 177 | (let [opts' (merge opts {:install-deps (deps/root-deps) 178 | :user-deps (read-deps config-user) 179 | :project-deps (read-deps config-project) 180 | :tool-resolver resolve-tool-args}) 181 | {:keys [libs classpath-roots jvm main manifests] :as basis} (run-core opts') 182 | trace-log (-> libs meta :trace)] 183 | (when trace 184 | (spit "trace.edn" (binding [*print-namespace-maps* false] (with-out-str (pprint/pprint trace-log))))) 185 | (when tree 186 | (-> trace-log tree/trace->tree (tree/print-tree nil))) 187 | (when-not skip-cp 188 | (io/write-file libs-file (binding [*print-namespace-maps* false] (pr-str libs))) 189 | (io/write-file cp-file (-> classpath-roots deps/join-classpath))) 190 | (io/write-file basis-file (binding [*print-namespace-maps* false] (pr-str basis))) 191 | (write-lines jvm jvm-file) 192 | (write-lines main main-file) 193 | (write-lines manifests manifest-file))) 194 | 195 | (defn -main 196 | "Main entry point for make-classpath script. 197 | 198 | Options: 199 | --config-user=path - user deps.edn file (usually ~/.clojure/deps.edn) 200 | --config-project=path - project deps.edn file (usually ./deps.edn) 201 | --config-data={...} - deps.edn as data (from -Sdeps) 202 | --tool-mode - flag for tool mode 203 | --tool-name - name of tool to run 204 | --libs-file=path - libs cache file to write 205 | --cp-file=path - cp cache file to write 206 | --jvm-file=path - jvm opts file to write 207 | --main-file=path - main opts file to write 208 | --manifest-file=path - manifest list file to write 209 | --basis-file=path - basis file to write 210 | -Rresolve-aliases - concatenated resolve-deps alias names 211 | -Cmakecp-aliases - concatenated make-classpath alias names 212 | -Mmain-aliases - concatenated main-opt alias names 213 | -Aaliases - concatenated repl alias names 214 | -Xaliases - concatenated exec alias names 215 | -Taliases - concatenated tool alias names 216 | 217 | Resolves the dependencies and updates the lib, classpath, etc files. 218 | The libs file is at /.libs 219 | The cp file is at /.cp 220 | The main opts file is at /.main (if needed) 221 | The jvm opts file is at /.jvm (if needed) 222 | The manifest file is at /.manifest (if needed)" 223 | [& args] 224 | (try 225 | (let [{:keys [options errors]} (parse-opts args)] 226 | (when (seq errors) 227 | (run! println errors) 228 | (System/exit 1)) 229 | (run options)) 230 | (catch Throwable t 231 | (printerrln "Error building classpath." (.getMessage t)) 232 | (when-not (instance? IExceptionInfo t) 233 | (.printStackTrace t)) 234 | (System/exit 1)))) 235 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/tools/deps/alpha/script/test_make_classpath2.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.tools.deps.alpha.script.test-make-classpath2 2 | (:require 3 | [clojure.test :refer [deftest is]] 4 | [clojure.tools.deps.alpha.script.make-classpath2 :as mc] 5 | [clojure.java.io :as jio])) 6 | 7 | (def install-data 8 | {:paths ["src"] 9 | :deps {'org.clojure/clojure {:mvn/version "1.10.1"}} 10 | :aliases {:test {:extra-paths ["test"]}} 11 | :mvn/repos {"central" {:url "https://repo1.maven.org/maven2/"} 12 | "clojars" {:url "https://repo.clojars.org/"}}}) 13 | 14 | (defn submap? 15 | "Is m1 a subset of m2?" 16 | [m1 m2] 17 | (if (and (map? m1) (map? m2)) 18 | (every? (fn [[k v]] (and (contains? m2 k) 19 | (submap? v (get m2 k)))) 20 | m1) 21 | (= m1 m2))) 22 | 23 | (deftest outside-project 24 | (let [basis (mc/run-core {:install-deps install-data})] 25 | (is (submap? 26 | {:libs {'org.clojure/clojure {:mvn/version "1.10.1"} 27 | 'org.clojure/spec.alpha {:mvn/version "0.2.176"} 28 | 'org.clojure/core.specs.alpha {:mvn/version "0.2.44"}}} 29 | basis)))) 30 | 31 | (deftest in-project 32 | (let [basis (mc/run-core {:install-deps install-data 33 | :project-deps {:deps {'org.clojure/clojure {:mvn/version "1.9.0"}}}})] 34 | (is (submap? 35 | {:libs {'org.clojure/clojure {:mvn/version "1.9.0"} 36 | 'org.clojure/spec.alpha {:mvn/version "0.1.143"} 37 | 'org.clojure/core.specs.alpha {:mvn/version "0.1.24"}}} 38 | basis)))) 39 | 40 | ;; alias :e with :extra-deps extends the project deps 41 | (deftest extra-deps 42 | (let [basis (mc/run-core {:install-deps install-data 43 | :project-deps {:deps {'org.clojure/clojure {:mvn/version "1.9.0"}} 44 | :aliases {:e {:extra-deps {'org.clojure/test.check {:mvn/version "0.9.0"}}}}} 45 | :repl-aliases [:e]})] 46 | (is (submap? 47 | {:resolve-args {:extra-deps {'org.clojure/test.check {:mvn/version "0.9.0"}}} 48 | :libs {'org.clojure/clojure {:mvn/version "1.9.0"} 49 | 'org.clojure/spec.alpha {:mvn/version "0.1.143"} 50 | 'org.clojure/core.specs.alpha {:mvn/version "0.1.24"} 51 | 'org.clojure/test.check {:mvn/version "0.9.0"}}} 52 | basis)))) 53 | 54 | ;; alias :t with :replace-deps replaces the project deps 55 | (deftest tool-deps 56 | (doseq [k [:deps :replace-deps]] ;; FUTURE - remove :deps here (will warn for now) 57 | (let [basis (mc/run-core {:install-deps install-data 58 | :project-deps {:deps {'org.clojure/clojure {:mvn/version "1.9.0"}} 59 | :aliases {:t {k {'org.clojure/test.check {:mvn/version "0.9.0"}}}}} 60 | :repl-aliases [:t]})] 61 | (is (submap? 62 | {:paths ["src"] 63 | :deps {'org.clojure/test.check {:mvn/version "0.9.0"}} 64 | :libs {'org.clojure/clojure {:mvn/version "1.10.1"} 65 | 'org.clojure/spec.alpha {:mvn/version "0.2.176"} 66 | 'org.clojure/core.specs.alpha {:mvn/version "0.2.44"} 67 | 'org.clojure/test.check {:mvn/version "0.9.0"}}} 68 | basis))))) 69 | 70 | ;; alias :o with :override-deps overrides the version to use 71 | (deftest override-deps 72 | (let [basis (mc/run-core {:install-deps install-data 73 | :project-deps {:aliases {:o {:override-deps {'org.clojure/clojure {:mvn/version "1.6.0"}}}}} 74 | :repl-aliases [:o]})] 75 | (is (submap? 76 | {:resolve-args {:override-deps {'org.clojure/clojure {:mvn/version "1.6.0"}}} 77 | :libs {'org.clojure/clojure {:mvn/version "1.6.0"}}} 78 | basis)))) 79 | 80 | (defn select-cp 81 | [classpath key] 82 | (->> classpath (filter #(contains? (val %) key)) (apply conj {}))) 83 | 84 | ;; paths and deps in alias replace 85 | (deftest alias-paths-and-deps 86 | (doseq [p [:paths :replace-paths] ;; FUTURE - remove :paths, :deps here (will warn for now) 87 | d [:deps :replace-deps]] 88 | (let [basis (mc/run-core {:install-deps install-data 89 | :project-deps {:paths ["a" "b"] 90 | :aliases {:q {p ["a" "c"] 91 | d {'org.clojure/clojure {:mvn/version "1.6.0"}}}}} 92 | :repl-aliases [:q]})] 93 | (is (submap? 94 | {:paths ["a" "c"] 95 | :deps {'org.clojure/clojure {:mvn/version "1.6.0"}}} 96 | basis)) 97 | (is (= #{"a" "c"} (-> basis :classpath (select-cp :path-key) keys set)))))) 98 | 99 | ;; paths replace in chain 100 | (deftest paths-replace 101 | (let [basis (mc/run-core {:install-deps install-data 102 | :user-deps {:paths ["x"]} 103 | :project-deps {:paths ["y"]} 104 | :config-data {:paths ["z"]}})] 105 | (is (submap? {:paths ["z"]} basis)) 106 | (is (= #{"z"} (-> basis :classpath (select-cp :path-key) keys set))))) 107 | 108 | ;; :paths in alias replaces, multiple alias :paths will be combined 109 | (deftest alias-paths-replace 110 | (doseq [p [:paths :replace-paths]] ;; FUTURE - remove :paths here (will warn for now) 111 | (let [basis (mc/run-core {:install-deps install-data 112 | :user-deps {:aliases {:p {p ["x" "y"]}}} 113 | :project-deps {:aliases {:q {p ["z"]}}} 114 | :repl-aliases [:p :q]})] 115 | (is (submap? {:paths ["x" "y" "z"]} basis)) 116 | (is (= #{"x" "y" "z"} (-> basis :classpath (select-cp :path-key) keys set)))))) 117 | 118 | ;; :extra-paths add 119 | (deftest extra-paths-add 120 | (let [basis (mc/run-core {:install-deps install-data 121 | :user-deps {:aliases {:p {:extra-paths ["x" "y"]}}} 122 | :project-deps {:aliases {:q {:extra-paths ["z"]}}} 123 | :repl-aliases [:p :q]})] 124 | (is (submap? 125 | {:paths ["src"] 126 | :classpath-args {:extra-paths ["x" "y" "z"]}} 127 | basis)) 128 | (is (= #{"src" "x" "y" "z"} (-> basis :classpath (select-cp :path-key) keys set))))) 129 | 130 | ;; java opts in aliases are additive 131 | (deftest jvm-opts-add 132 | (let [basis (mc/run-core {:install-deps install-data 133 | :user-deps {:aliases {:j1 {:jvm-opts ["-server" "-Xms100m"]}}} 134 | :project-deps {:aliases {:j2 {:jvm-opts ["-Xmx200m"]}}} 135 | :repl-aliases [:j1 :j2]})] 136 | (is (= ["-server" "-Xms100m" "-Xmx200m"] (:jvm basis))))) 137 | 138 | ;; main opts replace 139 | (deftest main-opts-replace 140 | (let [basis (mc/run-core {:install-deps install-data 141 | :user-deps {:aliases {:m1 {:main-opts ["a" "b"]}}} 142 | :project-deps {:aliases {:m2 {:main-opts ["c"]}}} 143 | :repl-aliases [:m1 :m2]})] 144 | (is (= ["c"] (:main basis))))) 145 | 146 | ;; local manifests returned 147 | (deftest manifest-local 148 | (let [basis (mc/run-core {:install-deps install-data 149 | :project-deps {:deps {'io.github.clojure/data.json {:git/sha "f367490" :git/tag "v2.4.0"} 150 | 'io.github.clojure/data.codec {:git/sha "8ef09db", :git/tag "data.codec-0.1.1", :deps/manifest :pom}}}})] 151 | ;; returns a manifest for both projects (deps.edn and pom.xml respectively) 152 | (is (= 2 (count (:manifests basis)))))) 153 | 154 | ;; repositories should be retained for generate-manifest2's use 155 | (deftest repo-config-retained 156 | (let [basis (mc/run-core {:install-deps install-data})] ;; install-data has central and clojars 157 | (is (= #{"central" "clojars"} (-> basis :mvn/repos keys set))))) 158 | 159 | ;; skip-cp flag prevents resolve-deps/make-classpath 160 | (deftest skip-cp-flag 161 | (let [basis (mc/run-core {:install-deps install-data 162 | :project-deps {:deps {'org.clojure/clojure {:mvn/version "1.10.0"}}} 163 | :skip-cp true})] 164 | (is (nil? basis)))) 165 | 166 | ;; skip-cp flag still passes exec-args for -X or -T 167 | (deftest skip-cp-exec 168 | (let [basis (mc/run-core {:install-deps install-data 169 | :project-deps {:deps {'org.clojure/clojure {:mvn/version "1.10.0"}} 170 | :aliases {:x {:exec-fn 'clojure.core/prn :exec-args {:a 1}}}} 171 | :exec-aliases [:x] 172 | :skip-cp true})] 173 | (is (= {:execute-args {:exec-fn 'clojure.core/prn :exec-args {:a 1}}} basis)))) 174 | 175 | (deftest removing-deps 176 | (let [basis (mc/run-core {:install-deps install-data 177 | :user-deps {:aliases 178 | {:remove-clojure 179 | {:classpath-overrides 180 | '{org.clojure/clojure nil 181 | org.clojure/spec.alpha nil 182 | org.clojure/core.specs.alpha nil}}}} 183 | :repl-aliases [:remove-clojure]})] 184 | (is (= 3 (count (:libs basis)))) ;; lib set is not changed by classpath-overrides 185 | (is (= ["src"] (:classpath-roots basis))) 186 | (is (= {"src" {:path-key :paths}} (:classpath basis))))) 187 | 188 | (deftest tool-alias 189 | (let [{:keys [libs classpath-roots classpath]} 190 | (mc/run-core {:install-deps install-data 191 | :user-deps {:aliases {:t {:extra-deps {'org.clojure/data.json {:mvn/version "2.0.1"}}}}} 192 | :project-deps {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}}} 193 | :tool-aliases [:t] 194 | :tool-mode true}) 195 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)] 196 | ;; includes tool dep and not project deps 197 | (is (contains? libs 'org.clojure/data.json)) 198 | (is (not (contains? libs 'cheshire/cheshire))) 199 | ;; paths only contains project root dir 200 | (is (= 1 (count paths))) 201 | (is (= (.getCanonicalPath (jio/file (first paths))) (.getCanonicalPath (jio/file ".")))))) 202 | 203 | ;; clj -T a/fn 204 | (deftest tool-bare 205 | (let [{:keys [libs classpath-roots classpath resolved-function]} 206 | (mc/run-core {:install-deps install-data 207 | :user-deps {} 208 | :project-deps {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}}} 209 | :tool-mode true}) 210 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)] 211 | (is (not (contains? libs 'cheshire/cheshire))) 212 | (is (= 1 (count paths))) 213 | (is (= (.getCanonicalPath (jio/file (first paths))) (.getCanonicalPath (jio/file ".")))))) 214 | 215 | ;; clj -Tfoo 216 | (deftest tool-by-name 217 | (let [{:keys [libs classpath-roots classpath execute-args]} 218 | (mc/run-core {:install-deps install-data 219 | :user-deps {} 220 | :project-deps {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}}} 221 | :tool-mode true 222 | :tool-name "foo" 223 | :tool-resolver {"foo" {:replace-deps {'org.clojure/data.json {:mvn/version "2.0.1"}} 224 | :replace-paths ["."] 225 | :ns-default 'a.b}}}) 226 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)] 227 | ;; execute-args in basis 228 | (is (= {:ns-default 'a.b} execute-args)) 229 | ;; tool deps, not project deps 230 | (is (not (contains? libs 'cheshire/cheshire))) 231 | (is (contains? libs 'org.clojure/data.json)) 232 | ;; ., not project paths 233 | (is (= (map #(.getCanonicalPath (jio/file %)) ["."]) 234 | (map #(.getCanonicalPath (jio/file %)) paths))))) 235 | 236 | ;; clj -T:a:b 237 | (deftest tool-with-aliases 238 | (let [{:keys [libs classpath-roots classpath]} 239 | (mc/run-core {:install-deps install-data 240 | :user-deps {} 241 | :project-deps {:deps {'cheshire/cheshire {:mvn/version "5.10.0"}} 242 | :aliases {:a {:replace-paths ["x"]} 243 | :b {:replace-paths ["y"]}}} 244 | :tool-mode true 245 | :tool-aliases [:a :b]}) 246 | paths (filter #(get-in classpath [% :path-key]) classpath-roots)] 247 | ;; tool deps, not project deps 248 | (is (not (contains? libs 'cheshire/cheshire))) 249 | (is (= (map #(.getCanonicalPath (jio/file %)) ["x" "y" "."]) 250 | (map #(.getCanonicalPath (jio/file %)) paths))))) 251 | 252 | (comment 253 | (clojure.test/run-tests) 254 | ) 255 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/deps/alpha/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.alpha.util.maven 11 | (:require 12 | [clojure.java.io :as jio] 13 | [clojure.string :as str] 14 | [clojure.tools.deps.alpha.util.io :refer [printerrln]] 15 | clojure.tools.deps.alpha.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 | (let [builder (RemoteRepository$Builder. name "default" url) 135 | maybe-repo (.build builder) 136 | mirror (select-mirror settings maybe-repo) 137 | ^RemoteRepository remote-repo (or mirror maybe-repo) 138 | proxy (select-proxy settings remote-repo) 139 | server-id (.getId remote-repo) 140 | ^Server server-setting (->> (.getServers settings) (filter #(= server-id (.getId ^Server %))) first)] 141 | (-> 142 | (cond-> builder 143 | snapshots (.setSnapshotPolicy (repo-policy name snapshots)) 144 | releases (.setReleasePolicy (repo-policy name releases)) 145 | mirror (.setUrl (.getUrl mirror)) 146 | server-setting (.setAuthentication 147 | (-> (AuthenticationBuilder.) 148 | (.addUsername (.getUsername server-setting)) 149 | (.addPassword (.getPassword server-setting)) 150 | (.addPrivateKey (.getPrivateKey server-setting) (.getPassphrase server-setting)) 151 | (.build))) 152 | proxy (.setProxy proxy)) 153 | (.build)))) 154 | ) 155 | 156 | (defn remote-repos 157 | ([repos] 158 | (remote-repos repos (get-settings))) 159 | ([{:strs [central clojars] :as repos} ^Settings settings] 160 | ;; always return central, then clojars, then other repos 161 | (->> (concat [["central" central] ["clojars" clojars]] (dissoc repos "central" "clojars")) 162 | (remove (fn [[_name config]] (nil? config))) 163 | (mapv #(remote-repo % settings))))) 164 | 165 | ;; Local repository 166 | 167 | (defn ^:private local-repo-path 168 | "Helper to form the path to the default local repo - use `@cached-local-repo` for 169 | caching delayed value" 170 | [] 171 | (.getAbsolutePath (jio/file (System/getProperty "user.home") ".m2" "repository"))) 172 | 173 | (def default-local-repo 174 | "DEPRECATED - use `@cached-local-repo`" 175 | (local-repo-path)) 176 | 177 | (def cached-local-repo 178 | "Delayed default local repo lookup for ~/.m2/repository, access with `@cached-local-repo`" 179 | (delay (local-repo-path))) 180 | 181 | (defn make-local-repo 182 | ^LocalRepository [^String dir] 183 | (LocalRepository. dir)) 184 | 185 | ;; Maven system and session 186 | 187 | (defn make-locator 188 | ^ServiceLocator [] 189 | (let [^DefaultServiceLocator loc 190 | (doto (MavenRepositorySystemUtils/newServiceLocator) 191 | (.addService RepositoryConnectorFactory BasicRepositoryConnectorFactory) 192 | (.addService TransporterFactory FileTransporterFactory) 193 | (.addService TransporterFactory HttpTransporterFactory))] 194 | (try 195 | (let [c (Class/forName "clojure.tools.deps.alpha.util.S3TransporterFactory")] 196 | (.addService loc TransporterFactory c)) 197 | (catch ClassNotFoundException _ 198 | (printerrln "Warning: failed to load the S3TransporterFactory class") 199 | loc)))) 200 | 201 | (def the-locator 202 | (delay (make-locator))) 203 | 204 | (defn make-system 205 | (^RepositorySystem [] 206 | (make-system (make-locator))) 207 | (^RepositorySystem [^ServiceLocator locator] 208 | (.getService locator RepositorySystem))) 209 | 210 | (def ^TransferListener console-listener 211 | (reify TransferListener 212 | (transferStarted [_ event] 213 | (let [event ^TransferEvent event 214 | resource (.getResource event) 215 | name (.getResourceName resource) 216 | repo (.getRepositoryId resource)] 217 | (printerrln "Downloading:" name "from" repo))) 218 | (transferCorrupted [_ event] 219 | (printerrln "Download corrupted:" (.. ^TransferEvent event getException getMessage))) 220 | (transferFailed [_ event] 221 | ;; This happens when Maven can't find an artifact in a particular repo 222 | ;; (but still may find it in a different repo), ie this is a common event 223 | #_(printerrln "Download failed:" (.. ^TransferEvent event getException getMessage))) 224 | (transferInitiated [_ _event]) 225 | (transferProgressed [_ _event]) 226 | (transferSucceeded [_ _event]))) 227 | 228 | (defn add-server-config [^DefaultRepositorySystemSession session ^Server server] 229 | (when-let [^Xpp3Dom configuration (.getConfiguration server)] 230 | (when-let [^Xpp3Dom headers (.getChild configuration "httpHeaders")] 231 | (.setConfigProperty session 232 | (str ConfigurationProperties/HTTP_HEADERS "." (.getId server)) 233 | (into {} 234 | (keep (fn [^Xpp3Dom header] 235 | (let [name (.getChild header "name") 236 | value (.getChild header "value")] 237 | (when (and name value) 238 | [(.getValue name) (.getValue value)])))) 239 | (.getChildren headers "property")))))) 240 | 241 | (defn make-session 242 | (^RepositorySystemSession [^RepositorySystem system local-repo] ;; DEPRECATED 243 | (make-session system (get-settings) local-repo)) 244 | (^RepositorySystemSession [^RepositorySystem system ^Settings settings local-repo] 245 | (let [session (MavenRepositorySystemUtils/newSession) 246 | local-repo-mgr (.newLocalRepositoryManager system session (make-local-repo local-repo))] 247 | (.setLocalRepositoryManager session local-repo-mgr) 248 | (.setTransferListener session console-listener) 249 | (.setCache session (DefaultRepositoryCache.)) 250 | (doseq [^Server server (.getServers settings)] 251 | (add-server-config session server)) 252 | session))) 253 | 254 | (defn exclusions->data 255 | [exclusions] 256 | (when (and exclusions (pos? (count exclusions))) 257 | (into #{} 258 | (map (fn [^Exclusion exclusion] 259 | (symbol (.getGroupId exclusion) (.getArtifactId exclusion)))) 260 | exclusions))) 261 | 262 | (defn dep->data 263 | [^Dependency dep] 264 | (let [scope (.getScope dep) 265 | optional (.isOptional dep) 266 | exclusions (exclusions->data (.getExclusions dep)) 267 | ^Artifact artifact (.getArtifact dep) 268 | artifact-id (.getArtifactId artifact) 269 | classifier (.getClassifier artifact) 270 | ext (.getExtension artifact)] 271 | [(symbol (.getGroupId artifact) (if (str/blank? classifier) artifact-id (str artifact-id "$" classifier))) 272 | (cond-> {:mvn/version (.getVersion artifact)} 273 | (not= "jar" ext) (assoc :extension ext) 274 | scope (assoc :scope scope) 275 | optional (assoc :optional true) 276 | (seq exclusions) (assoc :exclusions exclusions))])) 277 | 278 | (defn lib->names 279 | "Split lib symbol into [group-id artifact-id classifier]" 280 | [lib] 281 | (let [[artifact-id classifier] (str/split (name lib) #"\$")] 282 | [(or (namespace lib) artifact-id) artifact-id classifier])) 283 | 284 | (defn coord->artifact 285 | ^Artifact [lib {:keys [mvn/version classifier extension] :or {extension "jar"} :as coord}] 286 | (when classifier 287 | (throw (ex-info (str "Invalid library spec:\n" 288 | (format " %s %s\n" lib (dissoc coord :deps/manifest)) 289 | ":classifier in Maven coordinates is no longer supported.\n" 290 | "Use groupId/artifactId$classifier in lib names instead.") 291 | {:lib lib, :coord coord}))) 292 | (let [[group-id artifact-id classifier] (lib->names lib) 293 | version (or version "LATEST") 294 | artifact (DefaultArtifact. group-id artifact-id classifier extension version)] 295 | artifact)) 296 | 297 | (defn version-range? 298 | [version] 299 | (boolean (when version (re-find #"\[|\(" version)))) 300 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/tools/cli/api.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.cli.api 10 | "This api provides functions that can be executed from the Clojure tools using -X:deps." 11 | (:refer-clojure :exclude [list]) 12 | (:require 13 | [clojure.edn :as edn] 14 | [clojure.java.io :as jio] 15 | [clojure.pprint :as pprint] 16 | [clojure.string :as str] 17 | [clojure.tools.deps.alpha :as deps] 18 | [clojure.tools.deps.alpha.tool :as tool] 19 | [clojure.tools.deps.alpha.tree :as tree] 20 | [clojure.tools.deps.alpha.script.resolve-tags :as resolve-tags] 21 | [clojure.tools.deps.alpha.extensions :as ext] 22 | [clojure.tools.deps.alpha.extensions.pom :as pom] 23 | [clojure.tools.deps.alpha.extensions.local :as local] 24 | [clojure.tools.deps.alpha.gen.pom :as gen-pom] 25 | [clojure.tools.deps.alpha.util.maven :as mvn] 26 | [clojure.tools.deps.alpha.util.io :as io :refer [printerrln]] 27 | [clojure.set :as set]) 28 | (:import 29 | [java.io File FileNotFoundException IOException] 30 | [java.nio.file Files] 31 | [java.nio.file.attribute FileAttribute] 32 | [java.util.jar JarFile] 33 | [org.apache.maven.model Model] 34 | [org.eclipse.aether.artifact DefaultArtifact] 35 | [org.eclipse.aether.installation InstallRequest] 36 | [clojure.lang IExceptionInfo])) 37 | 38 | (set! *warn-on-reflection* true) 39 | 40 | (defn basis 41 | "Create a basis from a set of deps sources and a set of aliases. By default, use 42 | root, user, and project deps and no argmaps (essentially the same classpath you get by 43 | default from the Clojure CLI). 44 | 45 | Each dep source value can be :standard, a string path, a deps edn map, or nil. 46 | Sources are merged in the order - :root, :user, :project, :extra. 47 | 48 | Aliases refer to argmaps in the merged deps that will be supplied to the basis 49 | subprocesses (tool, resolve-deps, make-classpath-map). 50 | 51 | The following subprocess argmap args can be provided: 52 | Key Subproc Description 53 | :replace-deps tool Replace project deps 54 | :replace-paths tool Replace project paths 55 | :extra-deps resolve-deps Add additional deps 56 | :override-deps resolve-deps Override coord of dep 57 | :default-deps resolve-deps Provide coord if missing 58 | :extra-paths make-classpath-map Add additional paths 59 | :classpath-overrides make-classpath-map Replace lib path in cp 60 | 61 | Options: 62 | :root - dep source, default = :standard 63 | :user - dep source, default = :standard 64 | :project - dep source, default = :standard (\"./deps.edn\") 65 | :extra - dep source, default = nil 66 | :aliases - coll of aliases of argmaps to apply to subprocesses 67 | 68 | Returns {:basis basis}, which basis is initial deps edn map plus these keys: 69 | :resolve-args - the resolve args passed in, if any 70 | :classpath-args - the classpath args passed in, if any 71 | :libs - lib map, per resolve-deps 72 | :classpath - classpath map per make-classpath-map 73 | :classpath-roots - vector of paths in classpath order" 74 | [params] 75 | (merge params 76 | {:basis (deps/create-basis params)})) 77 | 78 | (defn prep 79 | "Prep the unprepped libs found in the transitive lib set of basis. 80 | 81 | This program accepts the same basis-modifying arguments from the `basis` program. 82 | Each dep source value can be :standard, a string path, a deps edn map, or nil. 83 | Sources are merged in the order - :root, :user, :project, :extra. 84 | 85 | Options: 86 | :force - flag on whether to force prepped libs to re-prep (default = false) 87 | :current - flag on whether to prep current project too (default = false) 88 | :log - :none, :info (default), or :debug 89 | 90 | Basis options: 91 | :root - dep source, default = :standard 92 | :user - dep source, default = :standard 93 | :project - dep source, default = :standard (\"./deps.edn\") 94 | :extra - dep source, default = nil 95 | :aliases - coll of kw aliases of argmaps to apply to subprocesses 96 | 97 | Returns params used." 98 | [{:keys [force log current] :or {log :info, current false} :as params}] 99 | (let [basis (deps/create-basis params) 100 | opts {:action (if force :force :prep) 101 | :log log 102 | :current current}] 103 | (deps/prep-libs! (:libs basis) opts basis) 104 | params)) 105 | 106 | (comment 107 | (do 108 | (prep 109 | {:root {:mvn/repos mvn/standard-repos, :deps nil} 110 | :project {:deps '{org.clojure/clojure {:mvn/version "1.10.3"} 111 | io.github.puredanger/cool-lib 112 | {:git/sha "657d5ce88be340ab2a6c0befeae998366105be84"}}} 113 | :log :debug 114 | :force true}) 115 | nil) 116 | ) 117 | 118 | (defn tree 119 | "Print deps tree for the current project's deps.edn built from either the 120 | a basis, or if provided, the trace file. 121 | 122 | This program accepts the same basis-modifying arguments from the `basis` program. 123 | Each dep source value can be :standard, a string path, a deps edn map, or nil. 124 | Sources are merged in the order - :root, :user, :project, :extra. 125 | 126 | By default, :format will :print to the console in a human friendly tree. Use 127 | :edn mode to print the tree to edn. 128 | 129 | In print mode, deps are printed with prefix of either . (included) or X (excluded). 130 | A reason code for inclusion/exclusion may be added at the end of the line. 131 | 132 | Basis options: 133 | :root - dep source, default = :standard 134 | :user - dep source, default = :standard 135 | :project - dep source, default = :standard (\"./deps.edn\") 136 | :extra - dep source, default = nil 137 | :aliases - coll of kw aliases of argmaps to apply to subprocesses 138 | 139 | Input options (if provided, basis options ignored): 140 | :file Path to trace.edn file (from clj -Strace) to use in computing the tree 141 | 142 | Output mode: 143 | :format :print (default) or :edn 144 | 145 | Print output mode modifiers: 146 | :indent Indent spacing (default = 2) 147 | :hide-libs Set of libs to hide as deps (if not top dep), default = #{org.clojure/clojure}" 148 | [opts] 149 | (try 150 | (let [{:keys [file format] :or {format :print}} opts 151 | trace (if file 152 | (io/slurp-edn file) 153 | (tree/calc-trace opts)) 154 | tree (tree/trace->tree trace)] 155 | (case format 156 | :print (tree/print-tree tree opts) 157 | :edn (pprint/pprint tree) 158 | (throw (ex-info (str "Unknown format " format) {})))) 159 | (catch Throwable t 160 | (printerrln "Error generating tree:" (.getMessage t)) 161 | (when-not (instance? IExceptionInfo t) 162 | (.printStackTrace t)) 163 | (System/exit 1)))) 164 | 165 | (comment 166 | (tree nil) 167 | (tree {:extra {:aliases {:foo {:extra-deps {'criterium/criterium {:mvn/version "0.4.0"}}}}} 168 | :aliases [:foo]}) 169 | ) 170 | 171 | (def ^:private cli-alias-keys 172 | #{:deps :replace-deps :extra-deps :override-deps :default-deps 173 | :paths :replace-paths :extra-paths :classpath-overrides 174 | :exec-fn :exec-args :ns-default :ns-aliases 175 | :main-opts :jvm-opts}) 176 | 177 | (defn aliases 178 | "List all aliases available for use with the CLI using -M, -X or -T execution 179 | (note that some aliases may be usable with more than one of these). Also, the 180 | deps.edn sources of the alias are specified. 181 | 182 | This program accepts the same basis-modifying arguments from the `basis` program. 183 | Each dep source value can be :standard, a string path, a deps edn map, or nil. 184 | Sources are merged in the order - :root, :user, :project, :extra. 185 | 186 | For example, to print only aliases defined in this project: 187 | clj -X:deps aliases :root nil :user nil 188 | 189 | Basis options: 190 | :root - dep source, default = :standard 191 | :user - dep source, default = :standard 192 | :project - dep source, default = :standard (\"./deps.edn\") 193 | :extra - dep source, default = nil 194 | 195 | The aliases are printed to the console." 196 | [params] 197 | (let [edn-srcs (deps/create-edn-maps params) 198 | src-aliases (reduce-kv #(assoc %1 %2 (:aliases %3)) {} edn-srcs) 199 | cli-aliases (reduce-kv 200 | (fn [m src aliases] 201 | (assoc m 202 | src 203 | (reduce-kv 204 | (fn [a alias alias-defn] 205 | (cond-> a 206 | (pos? (count (set/intersection cli-alias-keys (set (keys alias-defn))))) 207 | (assoc alias alias-defn))) 208 | {} aliases))) 209 | {} src-aliases) 210 | all-aliases (->> cli-aliases (map val) (mapcat #(-> % keys sort)) distinct)] 211 | (doseq [alias all-aliases] 212 | (let [srcs (reduce-kv (fn [srcs src deps-edn] 213 | (if (contains? (:aliases deps-edn) alias) 214 | (conj srcs src) 215 | srcs)) 216 | [] edn-srcs)] 217 | (println alias (str "(" (str/join ", " (map name srcs)) ")")))))) 218 | 219 | (def ^:private license-abbrev 220 | (delay 221 | (-> "clojure/tools/deps/license-abbrev.edn" jio/resource slurp edn/read-string))) 222 | 223 | (defn- license-string 224 | [info license-mode] 225 | (let [abbrevs @license-abbrev 226 | license-name (when (#{:full :short} license-mode) (:name info))] 227 | (if (and license-name (= license-mode :short)) 228 | (get abbrevs license-name license-name) 229 | license-name))) 230 | 231 | (defn list 232 | "List all deps on the classpath, optimized for knowing the final set of included 233 | libs. The `tree` program can provide more info on why or why not a particular 234 | lib is included. 235 | 236 | Licenses will be printed in short form by default but can also be listed as 237 | in :full or :none for none at all using the :license key. 238 | 239 | By default, :format will :print to the console in a human friendly tree. Use 240 | :edn mode to print the tree to edn. 241 | 242 | This program accepts the same basis-modifying arguments from the `basis` program. 243 | Each dep source value can be :standard, a string path, a deps edn map, or nil. 244 | Sources are merged in the order - :root, :user, :project, :extra. 245 | 246 | Options: 247 | :license - :full, :short (default), :none 248 | 249 | Output mode options: 250 | :format :print (default) or :edn 251 | 252 | Basis options: 253 | :root - dep source, default = :standard 254 | :user - dep source, default = :standard 255 | :project - dep source, default = :standard (\"./deps.edn\") 256 | :extra - dep source, default = nil 257 | :aliases - coll of kw aliases of argmaps to apply to subprocesses 258 | 259 | The libs are printed to the console." 260 | [params] 261 | (let [{license-mode :license, format :format 262 | :or {license-mode :short, format :print}} params 263 | basis (deps/create-basis params) 264 | libs (:libs basis) 265 | data (into (sorted-map) 266 | (map (fn [lib] 267 | (let [coord (get libs lib) 268 | info (ext/license-info lib coord basis)] 269 | [lib (cond-> coord info (assoc :license info))])) 270 | (-> libs keys sort)))] 271 | (if (= format :edn) 272 | (binding [*print-namespace-maps* false] 273 | (pprint/pprint data)) 274 | (doseq [[lib coord] data] 275 | (let [summary (ext/coord-summary lib coord) 276 | info (:license coord) 277 | license-string (license-string info license-mode)] 278 | (println summary (if license-string (str " (" license-string ")") ""))))))) 279 | 280 | (comment 281 | (list nil) 282 | @license-abbrev 283 | ) 284 | 285 | ;;;; git resolve-tags 286 | 287 | (defn git-resolve-tags 288 | "Resolve git tags in deps.edn git deps to full shas." 289 | [_] 290 | (resolve-tags/exec {:deps-file "deps.edn"})) 291 | 292 | ;;;; Generate pom 293 | 294 | (defn mvn-pom 295 | "Sync or create pom.xml from deps.edn. 296 | 297 | This program accepts the same basis-modifying arguments from the `basis` program. 298 | Each dep source value can be :standard, a string path, a deps edn map, or nil. 299 | Sources are merged in the order - :root, :user, :project, :extra. 300 | 301 | Basis options: 302 | :root - dep source, default = :standard 303 | :user - dep source, default = :standard 304 | :project - dep source, default = :standard (\"./deps.edn\") 305 | :extra - dep source, default = nil 306 | :aliases - coll of kw aliases of argmaps to apply to subprocesses 307 | 308 | Deprecated options (use the basis :aliases above instead): 309 | :argmaps - vector of aliases to combine into argmaps to resolve-deps and make-classpath" 310 | [{:keys [argmaps] :as opts}] 311 | (try 312 | (let [opts' (if argmaps (assoc opts :aliases (vec (concat argmaps (:aliases opts)))) opts) 313 | basis (deps/create-basis opts') 314 | ;; treat all transitive deps as top-level deps 315 | updated-deps (reduce-kv (fn [m lib {:keys [dependents] :as coord}] 316 | (if (seq dependents) m (assoc m lib coord))) 317 | {} (:libs basis))] 318 | (gen-pom/sync-pom (merge basis {:deps updated-deps}) (jio/file "."))) 319 | (catch Throwable t 320 | (printerrln "Error generating pom manifest:" (.getMessage t)) 321 | (when-not (instance? IExceptionInfo t) 322 | (.printStackTrace t)) 323 | (System/exit 1)))) 324 | 325 | ;;;; Install jar into local repository 326 | 327 | (defn- read-pom-file 328 | [pom] 329 | (let [pom-file (jio/file pom)] 330 | (if (.exists pom-file) 331 | (let [^Model model (pom/read-model-file pom-file (deps/root-deps))] 332 | {:group-id (.getGroupId model) 333 | :artifact-id (.getArtifactId model) 334 | :version (.getVersion model) 335 | :pom-file pom}) 336 | (throw (FileNotFoundException. (str "Pom file not found: " (str pom))))))) 337 | 338 | (defn- gen-pom-file 339 | [lib version classifier] 340 | (let [group-id (namespace lib) 341 | artifact-id (name lib) 342 | temp-dir (.toString (Files/createTempDirectory "pom" (make-array FileAttribute 0))) 343 | pom-file (str temp-dir "/pom.xml")] 344 | (gen-pom/sync-pom {:params {:target-dir temp-dir 345 | :src-pom pom-file 346 | :lib lib 347 | :version version}}) 348 | {:group-id group-id 349 | :artifact-id artifact-id 350 | :version version 351 | :classifier classifier 352 | :pom-file pom-file})) 353 | 354 | (defn- read-pom-in-jar 355 | [jar-name] 356 | (let [jar-file (jio/file jar-name)] 357 | (when (or (nil? jar-name) (not (.exists jar-file))) 358 | (throw (FileNotFoundException. (str "Jar file not found: " jar-name)))) 359 | (let [jar (JarFile. jar-file)] 360 | (if-let [path (local/find-pom jar)] 361 | (let [entry (.getJarEntry jar path) 362 | jis (.getInputStream jar entry) 363 | tmp (File/createTempFile "pom" ".xml")] 364 | (jio/copy jis tmp) 365 | (read-pom-file tmp)) 366 | (throw (IOException. (str "Jar file does not contain pom: " jar-name))))))) 367 | 368 | (defn- output-path 369 | [local-repo group-id artifact-id version] 370 | (let [path-parts (concat 371 | [(or local-repo @mvn/cached-local-repo)] 372 | (str/split group-id #"\.") 373 | [artifact-id version])] 374 | (.getAbsolutePath ^File (apply jio/file path-parts)))) 375 | 376 | (defn mvn-install 377 | "Install a jar and pom to the Maven local cache. 378 | The pom file must either be supplied, or generated based 379 | on provided lib/version/classifier, or provided inside the jar. 380 | The group/artifact/version coordinate will be pulled from the 381 | pom source as above. 382 | 383 | Required: 384 | :jar (reqired) - path to jar file (embedded pom used by default) 385 | 386 | Explicit pom options: 387 | :pom - path to pom file (pom in jar ignored) 388 | 389 | Generated pom options: 390 | :lib - qualified symbol like my.org/lib 391 | :version - string 392 | :classifier - string 393 | 394 | Other options: 395 | :local-repo (optional) - path to local repo (default = ~/.m2/repository) 396 | 397 | Execute ad-hoc: 398 | clj -X:deps mvn/install :jar '\"foo-1.2.3.jar\"'" 399 | [{:keys [jar pom lib version classifier local-repo]}] 400 | (println "Installing" jar (if pom (str "and " pom) "")) 401 | (let [{:keys [pom-file group-id artifact-id version classifier]} 402 | (cond 403 | pom (read-pom-file pom) 404 | lib (gen-pom-file lib version classifier) 405 | :else (read-pom-in-jar jar)) 406 | jar-file (jio/file jar) 407 | pom-file (jio/file pom-file) 408 | system (mvn/make-system) 409 | settings (mvn/get-settings) 410 | session (mvn/make-session system settings (or local-repo @mvn/cached-local-repo)) 411 | artifacts [(.setFile (DefaultArtifact. group-id artifact-id classifier "jar" version) jar-file) 412 | (.setFile (DefaultArtifact. group-id artifact-id classifier "pom" version) pom-file)] 413 | install-request (.setArtifacts (InstallRequest.) artifacts)] 414 | (.install system session install-request) 415 | (println "Installed to" (output-path local-repo group-id artifact-id version)))) 416 | 417 | (defn find-versions 418 | "Find available tool versions given either a lib (with :lib) or 419 | existing installed tool (with :tool). If lib, check all registered 420 | procurers and print one coordinate per line when found. 421 | 422 | Options: 423 | :lib Qualified lib symbol 424 | :tool Tool name for installed tool 425 | :n Number of coordinates to return, default = 8, :all for all" 426 | [{:keys [lib tool n] :or {n 8} :as args}] 427 | (let [{:keys [root-edn user-edn]} (deps/find-edn-maps) 428 | master-edn (deps/merge-edns [root-edn user-edn]) 429 | trunc-fn (if (= n :all) (fn [n x] x) #(apply take-last %&)) 430 | coords (cond 431 | tool 432 | (if-let [{:keys [lib coord]} (tool/resolve-tool (name tool))] 433 | (ext/find-versions lib coord (ext/coord-type coord) master-edn) 434 | (throw (ex-info (str "Unknown tool: " tool) {:tool tool}))) 435 | 436 | lib 437 | (ext/find-all-versions lib {} master-edn) 438 | 439 | :else 440 | (throw (ex-info "Either :lib or :tool must be provided to find versions" (or args {}))))] 441 | (run! #(binding [*print-namespace-maps* false] (prn %)) (trunc-fn n coords)))) 442 | 443 | (comment 444 | (find-versions '{:lib org.clojure/tools.gitlibs :n 4}) 445 | (find-versions '{:lib io.github.clojure/tools.gitlibs :n 10}) 446 | (find-versions '{:tool tools :n 3}) 447 | (find-versions nil) 448 | ) 449 | --------------------------------------------------------------------------------