├── test_projects ├── sample │ ├── .nrepl-port │ ├── checkouts │ │ └── sample2 │ │ │ ├── src │ │ │ └── sample2 │ │ │ │ ├── alt.clj │ │ │ │ └── core.clj │ │ │ ├── .gitignore │ │ │ ├── test │ │ │ └── sample2 │ │ │ │ └── core_test.clj │ │ │ ├── project.clj │ │ │ └── README │ ├── src │ │ └── nom │ │ │ └── nom │ │ │ ├── check.clj │ │ │ └── nom.clj │ ├── test │ │ └── test_nom_nom_nom.clj │ └── project.clj ├── overlapped-sourcepaths │ ├── src │ │ └── foo │ └── project.clj ├── sample-reader-cond │ ├── src │ │ └── nom │ │ │ └── nom │ │ │ ├── clj.clj │ │ │ └── cljc.cljc │ ├── test │ │ ├── clj_test.clj │ │ ├── cljc_test.cljc │ │ └── selectors.clj │ └── project.clj ├── tricky-name │ ├── .gitignore │ ├── src │ │ └── org │ │ │ └── domain │ │ │ └── tricky_name │ │ │ ├── brunch.clj │ │ │ ├── munch.clj │ │ │ └── core.clj │ └── project.clj ├── with-resources │ ├── resources │ │ └── nested │ │ │ └── dir │ │ │ └── sample.txt │ └── project.clj ├── sample-failing │ ├── test │ │ └── sample │ │ │ └── unreadable.clj │ ├── src │ │ └── nom │ │ │ └── nom │ │ │ └── nom.clj │ └── project.clj ├── more-gen-classes │ ├── src │ │ └── more_gen_classes │ │ │ ├── bar.clj │ │ │ ├── baz.clj │ │ │ └── foo.clj │ ├── doc │ │ └── intro.md │ ├── .gitignore │ ├── README.md │ └── project.clj ├── sample-no-aot │ ├── src │ │ └── nom │ │ │ └── nom │ │ │ └── nom.clj │ ├── test │ │ ├── namespace.clj │ │ └── selectors.clj │ └── project.clj ├── native │ ├── .gitignore │ └── project.clj ├── uberjar-merging │ ├── resources │ │ └── data_readers.clj │ └── project.clj ├── bad-require │ ├── src │ │ └── bad_require │ │ │ └── core.clj │ ├── .gitignore │ └── project.clj ├── java-main │ ├── .gitignore │ ├── src │ │ └── java │ │ │ └── my │ │ │ └── java │ │ │ └── Main.java │ └── project.clj ├── file-not-found-thrower │ ├── .gitignore │ ├── src │ │ └── file_not_found_thrower │ │ │ └── core.clj │ └── project.clj ├── with-classifiers │ └── project.clj ├── README.txt ├── provided │ ├── project.clj │ └── src │ │ └── provided │ │ └── core │ │ └── Example.java ├── with-aliases2 │ ├── profiles.clj │ └── project.clj ├── jvm-opts │ └── project.clj ├── sample-profile-meta │ └── project.clj ├── with-aliases │ └── project.clj └── uberjar-components-merging │ └── components1.xml ├── test ├── sample-connect-string ├── sample-connect-string-http ├── sample-index.zip └── leiningen │ ├── echo.clj │ ├── project.clj │ └── test │ ├── vcs.clj │ ├── keys.clj │ ├── install.clj │ ├── jvm_opts.clj │ ├── search.clj │ ├── update_in.clj │ ├── do.clj │ ├── javac.clj │ ├── help.clj │ ├── release.clj │ ├── deploy.clj │ ├── uberjar.clj │ ├── new │ └── templates.clj │ ├── run.clj │ ├── test.clj │ ├── helper.clj │ ├── with_profile.clj │ ├── compile.clj │ ├── jar.clj │ └── new.clj ├── resources ├── leiningen │ ├── help │ │ ├── copying │ │ ├── faq │ │ ├── gpg │ │ ├── news │ │ ├── readme │ │ ├── deploying │ │ ├── profiles │ │ ├── sample │ │ ├── tutorial │ │ ├── project.clj │ │ ├── templates │ │ └── mixed-source │ └── new │ │ ├── template │ │ ├── foo.clj │ │ ├── gitignore │ │ ├── hgignore │ │ ├── README.md │ │ ├── project.clj │ │ └── temp.clj │ │ ├── app │ │ ├── intro.md │ │ ├── core.clj │ │ ├── gitignore │ │ ├── test.clj │ │ ├── hgignore │ │ ├── project.clj │ │ └── README.md │ │ ├── default │ │ ├── intro.md │ │ ├── core.clj │ │ ├── gitignore │ │ ├── hgignore │ │ ├── test.clj │ │ ├── project.clj │ │ └── README.md │ │ └── plugin │ │ ├── name.clj │ │ ├── gitignore │ │ ├── hgignore │ │ ├── project.clj │ │ └── README.md ├── leiningen.png └── repl-welcome ├── TUTORIAL.md ├── .gitattributes ├── leiningen-core ├── test │ └── leiningen │ │ ├── sirius.clj │ │ ├── zero.clj │ │ ├── var_args.clj │ │ ├── bluuugh.clj │ │ ├── one_or_two.clj │ │ ├── fixed_and_var_args.clj │ │ └── core │ │ └── test │ │ ├── helper.clj │ │ ├── mirrors.clj │ │ ├── user.clj │ │ ├── eval.clj │ │ └── classpath.clj ├── dev-resources │ ├── checkouts │ │ ├── lib1 │ │ │ └── project.clj │ │ └── lib2 │ │ │ └── project.clj │ ├── p2.clj │ ├── p3.clj │ ├── replace-repositories.clj │ ├── p1.clj │ ├── profile-metadata.clj │ └── leiningen │ │ └── downloads.clj ├── project.clj ├── resources │ └── clojars.pem ├── README.md ├── pom.xml └── src │ └── leiningen │ └── core │ └── ssl.clj ├── lein-pprint ├── project.clj ├── src │ └── leiningen │ │ └── pprint.clj └── README.md ├── .travis.yml ├── src └── leiningen │ ├── version.clj │ ├── plugin.clj │ ├── upgrade.clj │ ├── retest.clj │ ├── show_profiles.clj │ ├── classpath.clj │ ├── new │ ├── template.clj │ ├── plugin.clj │ ├── app.clj │ └── default.clj │ ├── install.clj │ ├── do.clj │ ├── check.clj │ ├── update_in.clj │ ├── trampoline.clj │ ├── with_profile.clj │ ├── vcs.clj │ ├── clean.clj │ ├── deps.clj │ └── release.clj ├── .gitignore ├── bin ├── issues.clj ├── release └── lein-pkg ├── bash_completion.bash ├── doc ├── lein.1 └── TEMPLATES.md ├── project.clj ├── zsh_completion.zsh ├── pcmpl-lein.el └── CONTRIBUTING.md /test_projects/sample/.nrepl-port: -------------------------------------------------------------------------------- 1 | 4242 -------------------------------------------------------------------------------- /test/sample-connect-string: -------------------------------------------------------------------------------- 1 | myhost:23 2 | -------------------------------------------------------------------------------- /resources/leiningen/help/copying: -------------------------------------------------------------------------------- 1 | ../../../COPYING -------------------------------------------------------------------------------- /resources/leiningen/help/faq: -------------------------------------------------------------------------------- 1 | ../../../doc/FAQ.md -------------------------------------------------------------------------------- /resources/leiningen/help/gpg: -------------------------------------------------------------------------------- 1 | ../../../doc/GPG.md -------------------------------------------------------------------------------- /resources/leiningen/help/news: -------------------------------------------------------------------------------- 1 | ../../../NEWS.md -------------------------------------------------------------------------------- /test_projects/overlapped-sourcepaths/src/foo: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/leiningen/help/readme: -------------------------------------------------------------------------------- 1 | ../../../README.md -------------------------------------------------------------------------------- /resources/leiningen/help/deploying: -------------------------------------------------------------------------------- 1 | ../../../doc/DEPLOY.md -------------------------------------------------------------------------------- /resources/leiningen/help/profiles: -------------------------------------------------------------------------------- 1 | ../../../doc/PROFILES.md -------------------------------------------------------------------------------- /resources/leiningen/help/sample: -------------------------------------------------------------------------------- 1 | ../../../sample.project.clj -------------------------------------------------------------------------------- /resources/leiningen/help/tutorial: -------------------------------------------------------------------------------- 1 | ../../../doc/TUTORIAL.md -------------------------------------------------------------------------------- /resources/leiningen/help/project.clj: -------------------------------------------------------------------------------- 1 | ../../../sample.project.clj -------------------------------------------------------------------------------- /resources/leiningen/help/templates: -------------------------------------------------------------------------------- 1 | ../../../doc/TEMPLATES.md -------------------------------------------------------------------------------- /test/sample-connect-string-http: -------------------------------------------------------------------------------- 1 | http://localhost:23/repl 2 | -------------------------------------------------------------------------------- /resources/leiningen/help/mixed-source: -------------------------------------------------------------------------------- 1 | ../../../doc/MIXED_PROJECTS.md -------------------------------------------------------------------------------- /resources/leiningen/new/template/foo.clj: -------------------------------------------------------------------------------- 1 | (def {{name}} :foo) 2 | -------------------------------------------------------------------------------- /test_projects/sample-reader-cond/src/nom/nom/clj.clj: -------------------------------------------------------------------------------- 1 | (ns nom.nom.clj) 2 | -------------------------------------------------------------------------------- /test_projects/sample-reader-cond/src/nom/nom/cljc.cljc: -------------------------------------------------------------------------------- 1 | (ns nom.nom.cljc) 2 | -------------------------------------------------------------------------------- /test_projects/tricky-name/.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | lib 4 | classes 5 | -------------------------------------------------------------------------------- /test_projects/sample/checkouts/sample2/src/sample2/alt.clj: -------------------------------------------------------------------------------- 1 | (ns sample2.alt) 2 | -------------------------------------------------------------------------------- /test_projects/with-resources/resources/nested/dir/sample.txt: -------------------------------------------------------------------------------- 1 | Do not remove me! 2 | -------------------------------------------------------------------------------- /test_projects/sample-failing/test/sample/unreadable.clj: -------------------------------------------------------------------------------- 1 | (ns sample.unreadable 2 | 3 | -------------------------------------------------------------------------------- /test_projects/sample/checkouts/sample2/.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | lib 4 | classes -------------------------------------------------------------------------------- /test/sample-index.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatut/leiningen/master/test/sample-index.zip -------------------------------------------------------------------------------- /resources/leiningen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatut/leiningen/master/resources/leiningen.png -------------------------------------------------------------------------------- /test_projects/more-gen-classes/src/more_gen_classes/bar.clj: -------------------------------------------------------------------------------- 1 | (ns more-gen-classes.bar 2 | (:gen-class)) 3 | -------------------------------------------------------------------------------- /test_projects/more-gen-classes/src/more_gen_classes/baz.clj: -------------------------------------------------------------------------------- 1 | (ns more-gen-classes.baz 2 | (:gen-class)) 3 | -------------------------------------------------------------------------------- /test_projects/more-gen-classes/src/more_gen_classes/foo.clj: -------------------------------------------------------------------------------- 1 | (ns more-gen-classes.foo 2 | (:gen-class)) 3 | -------------------------------------------------------------------------------- /test_projects/sample-no-aot/src/nom/nom/nom.clj: -------------------------------------------------------------------------------- 1 | (ns nom.nom.nom) 2 | ;; This file is not AOT compiled! 3 | -------------------------------------------------------------------------------- /TUTORIAL.md: -------------------------------------------------------------------------------- 1 | The [tutorial has moved](https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md)! 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | #disable autocrlf for all .bat files since they already have CRLF in raw format 2 | *.bat -crlf 3 | -------------------------------------------------------------------------------- /test_projects/native/.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | /lib 4 | /nnnative 5 | /classes 6 | .lein-failures 7 | .lein-deps-sum 8 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/sirius.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.sirius) 2 | 3 | (defn ^:pass-through-help sirius [project & args] args) 4 | -------------------------------------------------------------------------------- /test/leiningen/echo.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.echo) 2 | 3 | (defn ^:no-project-needed echo [project & args] 4 | (apply println args)) 5 | -------------------------------------------------------------------------------- /test_projects/sample/checkouts/sample2/src/sample2/core.clj: -------------------------------------------------------------------------------- 1 | (ns sample2.core 2 | (:require sample2.alt) 3 | (:gen-class)) 4 | -------------------------------------------------------------------------------- /leiningen-core/dev-resources/checkouts/lib1/project.clj: -------------------------------------------------------------------------------- 1 | (defproject checkout-lib1 "0.0.1" 2 | :description "Test some checkouts.") 3 | -------------------------------------------------------------------------------- /leiningen-core/dev-resources/checkouts/lib2/project.clj: -------------------------------------------------------------------------------- 1 | (defproject checkout-lib2 "0.0.1" 2 | :description "Test some checkouts.") 3 | -------------------------------------------------------------------------------- /test_projects/sample/src/nom/nom/check.clj: -------------------------------------------------------------------------------- 1 | (ns nom.nom.check 2 | (:require [sample2.core]) 3 | (:gen-class)) 4 | 5 | (defn -main []) 6 | -------------------------------------------------------------------------------- /test_projects/uberjar-merging/resources/data_readers.clj: -------------------------------------------------------------------------------- 1 | {nomnomnom/identity clojure.core/identity, 2 | mf/i nomnomnom/override, 3 | } 4 | -------------------------------------------------------------------------------- /test_projects/bad-require/src/bad_require/core.clj: -------------------------------------------------------------------------------- 1 | (ns bad-require.core 2 | (:require [this.namespace.does.not.exist])) 3 | 4 | (defn -main []) 5 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/zero.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.zero "Dummy task for tests.") 2 | 3 | (defn zero [project] 4 | (println "a dummy task for tests")) 5 | -------------------------------------------------------------------------------- /resources/leiningen/new/app/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to {{name}} 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /resources/leiningen/new/default/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to {{name}} 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /resources/leiningen/new/default/core.clj: -------------------------------------------------------------------------------- 1 | (ns {{namespace}}) 2 | 3 | (defn foo 4 | "I don't do a whole lot." 5 | [x] 6 | (println x "Hello, World!")) 7 | -------------------------------------------------------------------------------- /test_projects/java-main/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /leiningen-core/dev-resources/p2.clj: -------------------------------------------------------------------------------- 1 | (defproject middler "0.0.1" 2 | :description "Test some middleware." 3 | :middleware [leiningen.core.test.project/add-seven]) 4 | -------------------------------------------------------------------------------- /test_projects/more-gen-classes/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to more-gen-classes 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/var_args.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.var-args "Dummy task for tests.") 2 | 3 | (defn var-args [project & args] 4 | (println "a dummy task for tests.")) 5 | -------------------------------------------------------------------------------- /test_projects/file-not-found-thrower/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /test_projects/overlapped-sourcepaths/project.clj: -------------------------------------------------------------------------------- 1 | (defproject overlapped-sourcepaths "0.1.0" 2 | :dependencies [[org.clojure/clojure "1.3.0"]] 3 | :java-source-paths ["src"]) 4 | -------------------------------------------------------------------------------- /resources/leiningen/new/app/core.clj: -------------------------------------------------------------------------------- 1 | (ns {{namespace}} 2 | (:gen-class)) 3 | 4 | (defn -main 5 | "I don't do a whole lot ... yet." 6 | [& args] 7 | (println "Hello, World!")) 8 | -------------------------------------------------------------------------------- /resources/leiningen/new/app/gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ -------------------------------------------------------------------------------- /resources/leiningen/new/plugin/name.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.{{unprefixed-name}}) 2 | 3 | (defn {{unprefixed-name}} 4 | "I don't do a lot." 5 | [project & args] 6 | (println "Hi!")) 7 | -------------------------------------------------------------------------------- /resources/leiningen/new/default/gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ -------------------------------------------------------------------------------- /resources/leiningen/new/plugin/gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ -------------------------------------------------------------------------------- /resources/leiningen/new/template/gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ -------------------------------------------------------------------------------- /test/leiningen/project.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.project) 2 | 3 | (defn ^:no-project-needed project [project & args] 4 | (if (seq args) 5 | (get-in project (mapv keyword args)) 6 | project)) 7 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/bluuugh.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.bluuugh 2 | "Dummy task for tests.") 3 | 4 | (defn ^:no-project-needed bluuugh 5 | [project] 6 | (println "This is a dummy task for tests.")) -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/one_or_two.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.one-or-two "Dummy task for tests") 2 | 3 | (defn one-or-two 4 | "Dummy task for tests" 5 | ([project one]) 6 | ([project one two])) 7 | -------------------------------------------------------------------------------- /test_projects/with-classifiers/project.clj: -------------------------------------------------------------------------------- 1 | (defproject with-classifiers "0.1.0-SNAPSHOT" 2 | :classifiers {:tests {:source-paths ^:replace ["test"] 3 | :resource-paths ^:replace []}}) 4 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/fixed_and_var_args.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.fixed-and-var-args "Dummy task for tests.") 2 | 3 | (defn fixed-and-var-args [project one two & rest] 4 | (println "a dummy task for tests")) 5 | -------------------------------------------------------------------------------- /resources/leiningen/new/default/hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | target/** 3 | classes/** 4 | checkouts/** 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | .gitignore 12 | .git/** -------------------------------------------------------------------------------- /resources/leiningen/new/plugin/hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | target/** 3 | classes/** 4 | checkouts/** 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | .gitignore 12 | .git/** -------------------------------------------------------------------------------- /resources/leiningen/new/template/hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | target/** 3 | classes/** 4 | checkouts/** 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | .gitignore 12 | .git/** -------------------------------------------------------------------------------- /test_projects/file-not-found-thrower/src/file_not_found_thrower/core.clj: -------------------------------------------------------------------------------- 1 | (ns file-not-found-thrower.core) 2 | 3 | (defn -main 4 | "I don't do a whole lot." 5 | [] 6 | (slurp "haha this file does NOT EXIST")) 7 | -------------------------------------------------------------------------------- /test_projects/java-main/src/java/my/java/Main.java: -------------------------------------------------------------------------------- 1 | package my.java; 2 | 3 | public class Main { 4 | public static void main(String[] args) { 5 | System.out.println("Hello from Java!"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test_projects/sample-reader-cond/test/clj_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-test 2 | (:use [clojure.test] 3 | [selectors :only [record-ran]])) 4 | 5 | (deftest clojure-test 6 | (record-ran :clj-test) 7 | (is true)) 8 | -------------------------------------------------------------------------------- /test_projects/tricky-name/src/org/domain/tricky_name/brunch.clj: -------------------------------------------------------------------------------- 1 | (ns org.domain.tricky-name.brunch) 2 | 3 | (defn -main [& args] 4 | (spit (format "%s/lein-test" (System/getProperty "java.io.tmpdir")) "BRUNCH")) 5 | -------------------------------------------------------------------------------- /leiningen-core/dev-resources/p3.clj: -------------------------------------------------------------------------------- 1 | (defproject middlest "0.0.1" 2 | :description "Test explicit middleware inside a plugin." 3 | :plugins [[lein-maven "0.1.0"]] 4 | :middleware [leiningen.mvn/maven-checkouts]) 5 | -------------------------------------------------------------------------------- /test_projects/sample/checkouts/sample2/test/sample2/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns sample2.core-test 2 | (:use [sample2.core] :reload-all) 3 | (:use [clojure.test])) 4 | 5 | (deftest replace-me ;; FIXME: write 6 | (is false)) 7 | -------------------------------------------------------------------------------- /test_projects/bad-require/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | .lein-deps-sum 10 | .lein-failures 11 | .lein-plugins 12 | .lein-repl-history 13 | -------------------------------------------------------------------------------- /test_projects/java-main/project.clj: -------------------------------------------------------------------------------- 1 | (defproject java-main "0.1.0-SNAPSHOT" 2 | :java-source-paths ["src/java"] 3 | :dependencies [[org.clojure/clojure "1.7.0"]] ;; lein run errors if not there. 4 | :main my.java.Main) 5 | -------------------------------------------------------------------------------- /resources/leiningen/new/app/test.clj: -------------------------------------------------------------------------------- 1 | (ns {{namespace}}-test 2 | (:require [clojure.test :refer :all] 3 | [{{namespace}} :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /test_projects/more-gen-classes/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | .lein-deps-sum 10 | .lein-failures 11 | .lein-plugins 12 | .lein-repl-history 13 | -------------------------------------------------------------------------------- /test_projects/sample/checkouts/sample2/project.clj: -------------------------------------------------------------------------------- 1 | (defproject sample2 "1.0.0-SNAPSHOT" 2 | :description "FIXME: write" 3 | :dependencies [[org.clojure/clojure "1.1.0"] 4 | [org.clojure/clojure-contrib "1.1.0"]]) -------------------------------------------------------------------------------- /resources/leiningen/new/default/test.clj: -------------------------------------------------------------------------------- 1 | (ns {{namespace}}-test 2 | (:require [clojure.test :refer :all] 3 | [{{namespace}} :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /test_projects/sample/checkouts/sample2/README: -------------------------------------------------------------------------------- 1 | # sample2 2 | 3 | FIXME: write description 4 | 5 | ## Usage 6 | 7 | FIXME: write 8 | 9 | ## Installation 10 | 11 | FIXME: write 12 | 13 | ## License 14 | 15 | FIXME: write 16 | -------------------------------------------------------------------------------- /resources/leiningen/new/app/hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | pom.xml 3 | pom.xml.asc 4 | *.jar 5 | *.class 6 | .gitignore 7 | .git/** 8 | 9 | syntax: regexp 10 | ^.nrepl-port 11 | ^.lein-.* 12 | ^target/ 13 | ^classes/ 14 | ^checkouts/ 15 | -------------------------------------------------------------------------------- /test_projects/tricky-name/src/org/domain/tricky_name/munch.clj: -------------------------------------------------------------------------------- 1 | (ns org.domain.tricky-name.munch) 2 | 3 | (defn -main [& args] 4 | (spit (format "%s/lein-test" (System/getProperty "java.io.tmpdir")) 5 | (pr-str :munched args))) 6 | -------------------------------------------------------------------------------- /test_projects/README.txt: -------------------------------------------------------------------------------- 1 | These projects are used for leiningen's test suite, so don't change 2 | any of these values without updating the relevant tests. If you 3 | just want a basic project to work from, generate a new one with 4 | "lein new". 5 | -------------------------------------------------------------------------------- /test_projects/tricky-name/project.clj: -------------------------------------------------------------------------------- 1 | (defproject org.domain/tricky-name "1.0" 2 | :description "One with a tricky group and project name" 3 | :dependencies [[org.clojure/clojure "1.3.0"]] 4 | :main ^{:skip-aot true} org.domain.tricky-name.core) 5 | -------------------------------------------------------------------------------- /test_projects/sample-reader-cond/test/cljc_test.cljc: -------------------------------------------------------------------------------- 1 | (ns cljc-test 2 | (:use #?(:clj [clojure.test] 3 | :cljs [cljs.test]) 4 | [selectors :only [record-ran]])) 5 | 6 | (deftest conditional-test 7 | (record-ran :cljc-test) 8 | (is true)) 9 | -------------------------------------------------------------------------------- /resources/repl-welcome: -------------------------------------------------------------------------------- 1 | Docs: (doc function-name-here) 2 | (find-doc "part-of-name-here") 3 | Source: (source function-name-here) 4 | Javadoc: (javadoc java-object-or-class-here) 5 | Exit: Control+D or (exit) or (quit) 6 | Results: Stored in vars *1, *2, *3, an exception in *e 7 | -------------------------------------------------------------------------------- /test_projects/tricky-name/src/org/domain/tricky_name/core.clj: -------------------------------------------------------------------------------- 1 | (ns org.domain.tricky-name.core) 2 | 3 | (defn -main [& args] 4 | (when-not (empty? args) 5 | (spit (format "%s/lein-test" (System/getProperty "java.io.tmpdir")) 6 | (str "nom:" (first args))) 7 | (recur (rest args)))) 8 | -------------------------------------------------------------------------------- /resources/leiningen/new/plugin/project.clj: -------------------------------------------------------------------------------- 1 | (defproject {{name}} "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :eval-in-leiningen true) 7 | -------------------------------------------------------------------------------- /test_projects/more-gen-classes/README.md: -------------------------------------------------------------------------------- 1 | # more-gen-classes 2 | 3 | A Clojure library designed to ... well, that part is up to you. 4 | 5 | ## Usage 6 | 7 | FIXME 8 | 9 | ## License 10 | 11 | Copyright © 2013 FIXME 12 | 13 | Distributed under the Eclipse Public License, the same as Clojure. 14 | -------------------------------------------------------------------------------- /resources/leiningen/new/template/README.md: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | A Leiningen template for FIXME. 4 | 5 | ## Usage 6 | 7 | FIXME 8 | 9 | ## License 10 | 11 | Copyright © {{year}} FIXME 12 | 13 | Distributed under the Eclipse Public License either version 1.0 or (at 14 | your option) any later version. 15 | -------------------------------------------------------------------------------- /lein-pprint/project.clj: -------------------------------------------------------------------------------- 1 | (defproject lein-pprint "1.1.2" 2 | :description "Pretty-print a representation of the project map." 3 | :url "https://github.com/technomancy/leiningen" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :eval-in-leiningen true) 7 | -------------------------------------------------------------------------------- /resources/leiningen/new/template/project.clj: -------------------------------------------------------------------------------- 1 | (defproject {{name}}/lein-template "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :eval-in-leiningen true) 7 | -------------------------------------------------------------------------------- /resources/leiningen/new/default/project.clj: -------------------------------------------------------------------------------- 1 | (defproject {{raw-name}} "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0"]]) 7 | -------------------------------------------------------------------------------- /test_projects/more-gen-classes/project.clj: -------------------------------------------------------------------------------- 1 | (defproject more-gen-classes "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0"]]) 7 | -------------------------------------------------------------------------------- /test_projects/provided/project.clj: -------------------------------------------------------------------------------- 1 | (defproject provided "0" 2 | :license {:name "Eclipse Public License" 3 | :url "http://www.eclipse.org/legal/epl-v10.html"} 4 | :dependencies [] 5 | :java-source-paths ["src"] 6 | :main provided.core.Example 7 | :profiles {:provided {:dependencies [[org.clojure/clojure "1.4.0"]]}}) 8 | -------------------------------------------------------------------------------- /test_projects/provided/src/provided/core/Example.java: -------------------------------------------------------------------------------- 1 | package provided.core; 2 | 3 | import clojure.lang.RT; 4 | 5 | public class Example { 6 | 7 | public static void 8 | main(String... args) { 9 | System.exit( 10 | RT.intCast( 11 | RT.var("clojure.core", "read-string").invoke("0"))); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /test_projects/sample-no-aot/test/namespace.clj: -------------------------------------------------------------------------------- 1 | (ns ^:integration namespace 2 | (:use [clojure.test] 3 | [selectors :only [record-ran]])) 4 | 5 | (deftest integration-test 6 | (record-ran :integration-ns) 7 | (is true)) 8 | 9 | (deftest ^:int2 int2-integration-test 10 | (record-ran :integration-ns) 11 | (is true)) 12 | -------------------------------------------------------------------------------- /test_projects/sample/test/test_nom_nom_nom.clj: -------------------------------------------------------------------------------- 1 | (ns test-nom-nom-nom 2 | (:use [nom.nom.nom] 3 | [clojure.test])) 4 | 5 | (defn test-ns-hook 6 | [] 7 | (is false)) 8 | 9 | (defn f [x] 10 | (.list x)) 11 | 12 | (deftest should-use-1.1.0 13 | (is (= "1.1.0" (clojure-version))) 14 | (f (java.io.File. "/tmp"))) 15 | -------------------------------------------------------------------------------- /resources/leiningen/new/default/README.md: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | A Clojure library designed to ... well, that part is up to you. 4 | 5 | ## Usage 6 | 7 | FIXME 8 | 9 | ## License 10 | 11 | Copyright © {{year}} FIXME 12 | 13 | Distributed under the Eclipse Public License either version 1.0 or (at 14 | your option) any later version. 15 | -------------------------------------------------------------------------------- /test_projects/file-not-found-thrower/project.clj: -------------------------------------------------------------------------------- 1 | (defproject file-not-found-thrower "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0"]]) 7 | -------------------------------------------------------------------------------- /test_projects/bad-require/project.clj: -------------------------------------------------------------------------------- 1 | (defproject bad-require "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0"]] 7 | :main bad-require.core) 8 | -------------------------------------------------------------------------------- /test_projects/with-aliases2/profiles.clj: -------------------------------------------------------------------------------- 1 | {:user 2 | {:aliases {"echo" ["with-profile" "+a2" "echo"] 3 | "project" ["with-profile" "+a2" "project"] 4 | "projecta" ["with-profile" "+a2" "project" "a"] 5 | "project-set" ["with-profile" "a2" "project"] 6 | "projecta-set" ["with-profile" "a2" "project" "a"]}}} 7 | -------------------------------------------------------------------------------- /leiningen-core/dev-resources/replace-repositories.clj: -------------------------------------------------------------------------------- 1 | (defproject metadata-check "0.1.0" 2 | :description "Check that repositories can be replaced." 3 | :license {:name "Eclipse Public License"} 4 | :dependencies [[robert/hooke "1.1.2"] 5 | [stencil "0.2.0"]] 6 | :repositories ^:replace [["nexus" {:url "https://clojars.org/repo/"}]]) 7 | -------------------------------------------------------------------------------- /test_projects/with-aliases2/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject project-with-aliases "0.1.0-SNAPSHOT" 7 | :a 1 8 | :profiles {:a2 {:a 2}}) 9 | -------------------------------------------------------------------------------- /test_projects/sample-failing/src/nom/nom/nom.clj: -------------------------------------------------------------------------------- 1 | (ns nom.nom.nom 2 | (:gen-class)) 3 | 4 | failure-expected-here-dont-freak-out 5 | 6 | This noming squirrel will cause compilation of this file to fail. 7 | 8 | ,;;:;, 9 | ;;;;; 10 | ,:;;:; ,'=. 11 | ;:;:;' .=" ,'_\ 12 | ':;:;,/ ,__:=@ 13 | ';;:; =./)_ 14 | `"=\_ )_"` 15 | ``'" 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | install: cd leiningen-core; lein install; lein classpath | tail -n 1 > .lein-bootstrap; cd .. 3 | script: bin/lein test 4 | notifications: 5 | irc: "irc.freenode.org#leiningen" 6 | jdk: 7 | - openjdk6 8 | - openjdk7 9 | - oraclejdk7 10 | - oraclejdk8 11 | matrix: 12 | allow_failures: 13 | - oraclejdk7 14 | - oraclejdk8 15 | -------------------------------------------------------------------------------- /lein-pprint/src/leiningen/pprint.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.pprint 2 | (:require [clojure.pprint :as pprint])) 3 | 4 | (defn ^:no-project-needed pprint 5 | "Pretty-print a representation of the project map." 6 | [project & keys] 7 | (if (seq keys) 8 | (doseq [k keys] 9 | (pprint/pprint (project (read-string k)))) 10 | (pprint/pprint project)) 11 | (flush)) 12 | -------------------------------------------------------------------------------- /test_projects/sample-reader-cond/test/selectors.clj: -------------------------------------------------------------------------------- 1 | (ns selectors 2 | (:use [clojure.test] 3 | [clojure.java.io])) 4 | 5 | (defn record-ran [t] 6 | (let [file-name (format "%s/lein-test-ran" 7 | (System/getProperty "java.io.tmpdir"))] 8 | (with-open [w (writer file-name :append true)] 9 | (.write w (str t "\n"))))) 10 | 11 | -------------------------------------------------------------------------------- /test_projects/sample-failing/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject nomnomnom "0.5.0-SNAPSHOT" 7 | :dependencies [[~(symbol "org.clojure" "clojure") ~"1.1.0"]] 8 | :aot [nom.nom.nom]) 9 | -------------------------------------------------------------------------------- /resources/leiningen/new/app/project.clj: -------------------------------------------------------------------------------- 1 | (defproject {{raw-name}} "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0"]] 7 | :main ^:skip-aot {{namespace}} 8 | :target-path "target/%s" 9 | :profiles {:uberjar {:aot :all}}) 10 | -------------------------------------------------------------------------------- /src/leiningen/version.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.version 2 | "Print version for Leiningen and the current JVM." 3 | (:require [leiningen.core.main :as main])) 4 | 5 | (defn ^:no-project-needed version 6 | "Print version for Leiningen and the current JVM." 7 | [project] 8 | (println "Leiningen" (main/leiningen-version) 9 | "on Java" (System/getProperty "java.version") 10 | (System/getProperty "java.vm.name"))) 11 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/core/test/helper.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.core.test.helper) 2 | 3 | (defn abort-msg 4 | "Catches main/abort thrown by calling f on its args and returns its error 5 | message." 6 | [f & args] 7 | (with-out-str 8 | (binding [*err* *out*] 9 | (try 10 | (apply f args) 11 | (catch clojure.lang.ExceptionInfo e 12 | (when-not (:exit-code (ex-data e)) 13 | (throw e))))))) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | *.bak 3 | *.swp 4 | *.class 5 | *asc 6 | *jar 7 | *~ 8 | .lein* 9 | /.classpath 10 | /.nrepl-port 11 | /.project 12 | /.settings 13 | /hs_err_pid*.log 14 | /lein.man 15 | /leiningen-core/.lein-plugins/checksum 16 | /leiningen-core/.nrepl-port 17 | /leiningen-core/dev-resources/target 18 | /lein-pprint/.nrepl-port 19 | /logs 20 | /scratch.clj 21 | /target 22 | /web 23 | /wiki 24 | TAGS 25 | test_projects/*/target 26 | pom.xml 27 | -------------------------------------------------------------------------------- /test_projects/with-resources/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject project-with-resources "0.5.0-SNAPSHOT" 7 | :dependencies [[org.clojure/clojure "1.3.0"] 8 | [janino "2.5.15"]] 9 | 10 | :resource-paths ["resources"]) 11 | -------------------------------------------------------------------------------- /test_projects/sample-reader-cond/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject nomnomnom "0.5.0-SNAPSHOT" 7 | :dependencies [[org.clojure/clojure "1.7.0"] 8 | [janino "2.5.15"]] 9 | :aot :all 10 | :uberjar-exclusions [#"DUMMY"]) 11 | -------------------------------------------------------------------------------- /test_projects/jvm-opts/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject custom/args "0.0.1-SNAPSHOT" 7 | :description "A test project" 8 | :dependencies [[org.clojure/clojure "1.7.0"]] 9 | :profiles {:no-op {} 10 | :ascii {:jvm-opts ["-Dfile.encoding=ASCII"]}}) 11 | -------------------------------------------------------------------------------- /test_projects/sample/src/nom/nom/nom.clj: -------------------------------------------------------------------------------- 1 | (ns nom.nom.nom 2 | (:import [org.jdom.adapters CrimsonDOMAdapter]) 3 | (:gen-class)) 4 | 5 | (when-not (= "1.3.0" (clojure-version)) 6 | (throw (Exception. (str "Not running Clojure 1.3.0: " 7 | (clojure-version))))) 8 | 9 | (def unused-proxy (proxy [Object] [] (toString [] "unused"))) 10 | 11 | (defn -main [& args] 12 | (when-not (empty? args) 13 | (println "NOM! Munched" (first args)) 14 | (recur (rest args)))) 15 | -------------------------------------------------------------------------------- /src/leiningen/plugin.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.plugin 2 | "DEPRECATED. Please use the :user profile instead." 3 | (:require [leiningen.core.main :as main])) 4 | 5 | (defn ^:no-project-needed plugin 6 | "DEPRECATED. Please use the :user profile instead." 7 | [& args] 8 | (main/abort "The plugin task has been removed.\n" 9 | "\nPlease see the upgrade guide for instructions on how to use" 10 | "the user profile to\nspecify plugins instead:" 11 | "https://github.com/technomancy/leiningen/wiki/Upgrading")) 12 | -------------------------------------------------------------------------------- /resources/leiningen/new/template/temp.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.new.{{name}} 2 | (:require [leiningen.new.templates :refer [renderer name-to-path ->files]] 3 | [leiningen.core.main :as main])) 4 | 5 | (def render (renderer "{{name}}")) 6 | 7 | (defn {{name}} 8 | "FIXME: write documentation" 9 | [name] 10 | (let [data {:name name 11 | :sanitized (name-to-path name)}] 12 | (main/info "Generating fresh 'lein new' {{name}} project.") 13 | (->files data 14 | ["src/{{placeholder}}/foo.clj" (render "foo.clj" data)]))) 15 | -------------------------------------------------------------------------------- /leiningen-core/dev-resources/p1.clj: -------------------------------------------------------------------------------- 1 | (defproject leiningen "2.0.0-SNAPSHOT" 2 | :description "Automate Clojure projects without setting your hair on fire." 3 | :url "https://github.com/technomancy/leiningen" 4 | :license {:name "Eclipse Public License"} 5 | :dependencies [[leiningen-core "2.0.0-SNAPSHOT"] 6 | [clucy "0.2.2" :exclusions [org.clojure/clojure]] 7 | [lancet "1.0.1"] 8 | [robert/hooke "1.1.2"] 9 | [stencil "0.2.0"]] 10 | :twelve ~(+ 6 2 4) 11 | :disable-implicit-clean true 12 | :eval-in-leiningen true) 13 | -------------------------------------------------------------------------------- /test/leiningen/test/vcs.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.vcs 2 | (:require [clojure.test :refer :all] 3 | [leiningen.vcs :as vcs])) 4 | 5 | (deftest parsed-args 6 | (testing "VCS tag argument parsing" 7 | (are [args parsed-args] (= (vcs/parse-tag-args args) parsed-args) 8 | [] {:sign? true} 9 | ["v"] {:prefix "v" :sign? true} 10 | ["v" "--sign"] {:prefix "v" :sign? true} 11 | ["--sign"] {:sign? true} 12 | ["--no-sign"] {:sign? false} 13 | ["--no-sign" "v"] {:prefix "v" :sign? false} 14 | ["-s"] {:sign? true} 15 | ["v" "r"] {:prefix "r" :sign? true}))) 16 | -------------------------------------------------------------------------------- /test_projects/native/project.clj: -------------------------------------------------------------------------------- 1 | (defproject project-name "1.0.0-SNAPSHOT" 2 | :description "Test support for transitive native dependencies" 3 | :native-path "nnnative" 4 | :dependencies [[org.clojure/clojure "1.4.0"] 5 | [serial-port "1.0.7"] 6 | [penumbra/lwjgl "2.4.2"] 7 | [com.badlogicgames.gdx/gdx-platform "0.9.9"] 8 | [com.badlogicgames.gdx/gdx-platform "0.9.9" :classifier "natives-desktop"] 9 | [org.clojars.samaaron/rxtx "2.2.0"] 10 | [jriengine "0.8.4"] 11 | [tokyocabinet "1.24.0"]]) 12 | -------------------------------------------------------------------------------- /test/leiningen/test/keys.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.keys 2 | (:require [clojure.test :refer :all])) 3 | 4 | (def df (java.text.SimpleDateFormat. "yyyy-MM-dd")) 5 | 6 | (deftest technomancy-gpg-key 7 | (let [month-before-key-expiry (.parse df "2017-05-14")] 8 | (is (< (System/currentTimeMillis) (.getTime month-before-key-expiry)) 9 | "If this fails, yell at technomancy to generate a new key!"))) 10 | 11 | (deftest clojars-ssl-cert 12 | (let [month-before-cert-expiry (.parse df "2016-07-12")] 13 | (is (< (System/currentTimeMillis) (.getTime month-before-cert-expiry)) 14 | "If this fails, yell at _ato to get a new SSL cert."))) 15 | -------------------------------------------------------------------------------- /test_projects/sample-no-aot/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject nomnomnom "0.5.0-SNAPSHOT" 7 | :dependencies [[org.clojure/clojure "1.3.0"] 8 | [janino "2.5.15"]] 9 | :uberjar-exclusions [#"DUMMY"] 10 | :test-selectors {:default (fn [m] (not (:integration m))) 11 | :integration :integration 12 | :int2 :int2 13 | :no-custom (fn [m] (not (false? (:custom m))))}) 14 | -------------------------------------------------------------------------------- /resources/leiningen/new/app/README.md: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | FIXME: description 4 | 5 | ## Installation 6 | 7 | Download from http://example.com/FIXME. 8 | 9 | ## Usage 10 | 11 | FIXME: explanation 12 | 13 | $ java -jar {{name}}-0.1.0-standalone.jar [args] 14 | 15 | ## Options 16 | 17 | FIXME: listing of options this app accepts. 18 | 19 | ## Examples 20 | 21 | ... 22 | 23 | ### Bugs 24 | 25 | ... 26 | 27 | ### Any Other Sections 28 | ### That You Think 29 | ### Might be Useful 30 | 31 | ## License 32 | 33 | Copyright © {{year}} FIXME 34 | 35 | Distributed under the Eclipse Public License either version 1.0 or (at 36 | your option) any later version. 37 | -------------------------------------------------------------------------------- /src/leiningen/upgrade.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.upgrade 2 | "Upgrade Leiningen to specified version or latest stable." 3 | (:require [leiningen.core.main :as main])) 4 | 5 | ;; This file is only a placeholder. The real upgrade 6 | ;; implementation can be found in the 'lein' script. 7 | 8 | (defn ^:no-project-needed upgrade 9 | "Upgrade Leiningen to specified version or latest stable." 10 | [project & args] 11 | (main/abort "Upgrade is either disabled, or you have tried to call it from a" 12 | "higher order\ntask. If you've installed lein through a package" 13 | "manager, upgrade lein through\nthe package manager's upgrade" 14 | "commands.")) 15 | -------------------------------------------------------------------------------- /src/leiningen/retest.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.retest 2 | "Run only the test namespaces which failed last time around." 3 | (:require [leiningen.test :as test] 4 | [leiningen.core.main :as main])) 5 | 6 | (defn retest 7 | "Run only the test namespaces which failed last time around." 8 | [project & selectors] 9 | (if (:monkeypatch-clojure-test project true) 10 | (apply test/test project 11 | (concat (if (.exists (java.io.File. ".lein-failures")) 12 | (->> (slurp ".lein-failures") 13 | read-string keys sort)) 14 | selectors)) 15 | (main/abort "Cannot retest when :monkeypatch-clojure-test is disabled."))) 16 | -------------------------------------------------------------------------------- /resources/leiningen/new/plugin/README.md: -------------------------------------------------------------------------------- 1 | # {{name}} 2 | 3 | A Leiningen plugin to do many wonderful things. 4 | 5 | ## Usage 6 | 7 | FIXME: Use this for user-level plugins: 8 | 9 | Put `[{{name}} "0.1.0-SNAPSHOT"]` into the `:plugins` vector of your `:user` 10 | profile. 11 | 12 | FIXME: Use this for project-level plugins: 13 | 14 | Put `[{{name}} "0.1.0-SNAPSHOT"]` into the `:plugins` vector of your project.clj. 15 | 16 | FIXME: and add an example usage that actually makes sense: 17 | 18 | $ lein {{unprefixed-name}} 19 | 20 | ## License 21 | 22 | Copyright © {{year}} FIXME 23 | 24 | Distributed under the Eclipse Public License either version 1.0 or (at 25 | your option) any later version. 26 | -------------------------------------------------------------------------------- /src/leiningen/show_profiles.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.show-profiles 2 | "List all available profiles or display one if given an argument." 3 | (:require [clojure.string] 4 | [clojure.pprint :as pprint] 5 | [leiningen.core.project :as project])) 6 | 7 | (defn ^:no-project-needed show-profiles 8 | "List all available profiles or display one if given an argument." 9 | ([project] 10 | (->> (project/read-profiles project) 11 | (keys) 12 | (map (comp #(subs % 1) str)) 13 | (sort) 14 | (clojure.string/join "\n") 15 | (println))) 16 | ([project profile] 17 | (-> (project/read-profiles project) 18 | (get (keyword profile)) 19 | (pprint/pprint)) 20 | (flush))) 21 | -------------------------------------------------------------------------------- /test_projects/uberjar-merging/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject nomnomnom "0.5.0-SNAPSHOT" 7 | :dependencies [[org.clojure/clojure "1.7.0"] 8 | [janino "2.5.15"] 9 | [org.platypope/method-fn "0.1.0"]] 10 | :uberjar-exclusions [#"DUMMY"] 11 | :uberjar-merge-with {#"\.properties$" [slurp str spit]} 12 | :test-selectors {:default (fn [m] (not (:integration m))) 13 | :integration :integration 14 | :int2 :int2 15 | :no-custom (fn [m] (not (false? (:custom m))))}) 16 | -------------------------------------------------------------------------------- /leiningen-core/dev-resources/profile-metadata.clj: -------------------------------------------------------------------------------- 1 | (defproject metadata-check "0.1.0" 2 | :description "Check that profile metadata is retained." 3 | :license {:name "Eclipse Public License"} 4 | :dependencies [[leiningen-core "2.0.0-SNAPSHOT"] 5 | [clucy "0.2.2" :exclusions [org.clojure/clojure]] 6 | [lancet "1.0.1"] 7 | [robert/hooke "1.1.2"] 8 | [stencil "0.2.0"]] 9 | :profiles {:bar {:dependencies ^:please-keep-me [[lancet "1.0.2"] 10 | [stencil "0.3.0"]] 11 | :repositories ^:replace []} 12 | :baz {:dependencies ^:hello [] 13 | :repositories ^:displace [] 14 | :java-opts ["my" "java" "opts"]}}) 15 | -------------------------------------------------------------------------------- /test_projects/sample-no-aot/test/selectors.clj: -------------------------------------------------------------------------------- 1 | (ns selectors 2 | (:use [clojure.test] 3 | [clojure.java.io])) 4 | 5 | (defn record-ran [t] 6 | (let [file-name (format "%s/lein-test-ran" 7 | (System/getProperty "java.io.tmpdir"))] 8 | (with-open [w (writer file-name :append true)] 9 | (.write w (str t "\n"))))) 10 | 11 | (use-fixtures :each (fn [t] (record-ran :fixture) (t))) 12 | 13 | (deftest ^{:integration true} integration-test 14 | (record-ran :integration) 15 | (is true)) 16 | 17 | (deftest regular 18 | (record-ran :regular) 19 | (is true)) 20 | 21 | (deftest ^{:custom false} not-custom 22 | (record-ran :not-custom) 23 | (is true)) 24 | 25 | (deftest ^{:int2 true} integration-2 26 | (record-ran :int2) 27 | (is true)) 28 | -------------------------------------------------------------------------------- /test/leiningen/test/install.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.install 2 | (:require [leiningen.core.user :as user]) 3 | (:use [clojure.test] 4 | [leiningen.install] 5 | [leiningen.test.helper] 6 | [clojure.java.io :only [file]])) 7 | 8 | (deftest ^:online test-install 9 | (delete-file-recursively (m2-dir "nomnomnom" "0.5.0-SNAPSHOT") true) 10 | (install sample-project) 11 | (is (not (empty? (.listFiles (m2-dir "nomnomnom" "0.5.0-SNAPSHOT")))))) 12 | 13 | (def jdom-dir (file local-repo "jdom" "jdom" "1.0")) 14 | 15 | (def tricky-m2-dir (file local-repo "org" "domain" "tricky-name" "1.0")) 16 | 17 | (deftest ^:online test-tricky-name-install 18 | (delete-file-recursively tricky-m2-dir true) 19 | (install tricky-name-project) 20 | (is (not (empty? (.listFiles tricky-m2-dir))))) 21 | -------------------------------------------------------------------------------- /test_projects/sample-profile-meta/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject nomnomnom "0.5.0-SNAPSHOT" 7 | :dependencies [] 8 | :profiles {:default [:leiningen/default :my-leaky :my-provided :my-test] 9 | :my-leaky ^:leaky {:dependencies 10 | [[org.clojure/tools.macro "0.1.2"]]} 11 | :my-test 12 | ^{:pom-scope :test} {:dependencies 13 | [[org.clojure/java.classpath "0.2.2"]]} 14 | :my-provided 15 | ^{:pom-scope :provided} {:dependencies 16 | [[org.clojure/tools.namespace "0.2.6"]]}}) 17 | -------------------------------------------------------------------------------- /leiningen-core/project.clj: -------------------------------------------------------------------------------- 1 | (defproject leiningen-core "2.5.3-SNAPSHOT" 2 | :url "https://github.com/technomancy/leiningen" 3 | :license {:name "Eclipse Public License" 4 | :url "http://www.eclipse.org/legal/epl-v10.html"} 5 | :description "Library for core functionality of Leiningen." 6 | :dependencies [[org.clojure/clojure "1.7.0"] 7 | [bultitude "0.2.8"] 8 | [classlojure "0.6.6"] 9 | [robert/hooke "1.3.0"] 10 | [com.cemerick/pomegranate "0.3.0"] 11 | [org.apache.maven.wagon/wagon-http "2.9"] 12 | [com.hypirion/io "0.3.1"] 13 | [pedantic "0.2.0"]] 14 | :scm {:dir ".."} 15 | :dev-resources-path "dev-resources" 16 | :aliases {"bootstrap" ["with-profile" "base" 17 | "do" "install," "classpath" ".lein-bootstrap"]}) 18 | -------------------------------------------------------------------------------- /src/leiningen/classpath.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.classpath 2 | "Print the classpath of the current project." 3 | (:require [leiningen.core.classpath :as classpath] 4 | [leiningen.core.main :as main] 5 | [clojure.string :as str]) 6 | (:import (org.sonatype.aether.resolution DependencyResolutionException))) 7 | 8 | (defn get-classpath-string [project] 9 | (try 10 | (str/join java.io.File/pathSeparatorChar (classpath/get-classpath project)) 11 | (catch DependencyResolutionException e 12 | (main/abort (.getMessage e))))) 13 | 14 | (defn classpath 15 | "Write the classpath of the current project to output-file. 16 | 17 | With no arguments, print the classpath to stdout. 18 | 19 | Suitable for java's -cp option." 20 | ([project] 21 | (println (get-classpath-string project))) 22 | ([project output-file] 23 | (spit output-file (get-classpath-string project)))) 24 | -------------------------------------------------------------------------------- /test/leiningen/test/jvm_opts.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.jvm-opts 2 | (:require [leiningen.with-profile :refer [with-profile]] 3 | [leiningen.test.helper :refer [jvm-opts-project 4 | with-system-out-str]]) 5 | (:use clojure.test)) 6 | 7 | ;; This is a regression test for technomancy/leiningen#1676 (make sure 8 | ;; that file.encoding can be overriden by profiles.) 9 | (deftest file-encoding-conveyed 10 | (let [exec '(println "system encoding" (System/getProperty "file.encoding")) 11 | run-with #(with-system-out-str 12 | (with-profile jvm-opts-project % "run" 13 | "-m" "clojure.main" 14 | "-e" (pr-str exec)))] 15 | (testing "baseline sane" 16 | (is (.contains (run-with "+no-op") "system encoding UTF-8"))) 17 | (testing "accepts alternative" 18 | (is (.contains (run-with "+ascii") "system encoding ASCII"))))) 19 | -------------------------------------------------------------------------------- /src/leiningen/new/template.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.new.template 2 | (:require [leiningen.new.templates :refer [renderer sanitize year ->files]] 3 | [leiningen.core.main :as main])) 4 | 5 | (defn template 6 | "A meta-template for 'lein new' templates." 7 | [name] 8 | (let [render (renderer "template") 9 | data {:name name 10 | :sanitized (sanitize name) 11 | :placeholder "{{sanitized}}" 12 | :year (year)}] 13 | (main/info "Generating fresh 'lein new' template project.") 14 | (->files data 15 | ["README.md" (render "README.md" data)] 16 | ["project.clj" (render "project.clj" data)] 17 | [".gitignore" (render "gitignore" data)] 18 | [".hgignore" (render "hgignore" data)] 19 | ["src/leiningen/new/{{sanitized}}.clj" (render "temp.clj" data)] 20 | ["resources/leiningen/new/{{sanitized}}/foo.clj" (render "foo.clj")] 21 | ["LICENSE" (render "LICENSE" data)]))) 22 | -------------------------------------------------------------------------------- /test/leiningen/test/search.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.search 2 | (:require [clojure.java.io :as io]) 3 | (:use [clojure.test] 4 | [leiningen.search])) 5 | 6 | #_(deftest test-searchy 7 | (with-redefs [remote-index-url (constantly 8 | (io/resource "sample-index.zip"))] 9 | (ensure-fresh-index ["test" {:url "http://example.com/repo"}]) 10 | (is (= #{"segments.gen" "_0.cfx" "timestamp" "_0.cfs" "segments_2"} 11 | (set (.list (index-location "http://example.com/repo"))))) 12 | (let [results (search-repository ["test" {:url "http://example.com/repo"}] 13 | "hooke" 1)] 14 | (is (= '#{[[robert/hooke "\"1.0.0\""] "Hooke your functions!"] 15 | [[robert/hooke "\"1.0.1\""] "Hooke your functions!"] 16 | [[robert/hooke "\"1.0.2\""] "Hooke your functions!"] 17 | [[robert/hooke "\"1.1.0\""] "Hooke your functions!"]} 18 | (set (map parse-result results))))))) 19 | -------------------------------------------------------------------------------- /src/leiningen/new/plugin.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.new.plugin 2 | (:require [leiningen.new.templates :refer [renderer sanitize year ->files]] 3 | [leiningen.core.main :as main])) 4 | 5 | (defn plugin 6 | "A leiningen plugin project template." 7 | [^String name] 8 | (let [render (renderer "plugin") 9 | unprefixed (if (.startsWith name "lein-") 10 | (subs name 5) 11 | name) 12 | data {:name name 13 | :unprefixed-name unprefixed 14 | :sanitized (sanitize unprefixed) 15 | :year (year)}] 16 | (main/info (str "Generating a fresh Leiningen plugin called " name ".")) 17 | (->files data 18 | ["project.clj" (render "project.clj" data)] 19 | ["README.md" (render "README.md" data)] 20 | [".gitignore" (render "gitignore" data)] 21 | [".hgignore" (render "hgignore" data)] 22 | ["src/leiningen/{{sanitized}}.clj" (render "name.clj" data)] 23 | ["LICENSE" (render "LICENSE" data)]))) 24 | -------------------------------------------------------------------------------- /test_projects/with-aliases/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (defproject project-with-aliases "0.1.0-SNAPSHOT" 7 | :aliases {"p" ["echo" "p"] 8 | "a2p" ["with-profile" "+a2" "p"] 9 | "pp" ["with-profile" "+a2" "echo" "pp"] 10 | "ppp" ["with-profile" "+a2" "echo" "ppp"] 11 | "echo" ["echo" "hello"] 12 | 13 | "project" ["project"] 14 | "projecta" ["project" "a"] 15 | 16 | "pa2project" ["with-profile" "+a2" "project"] 17 | "pa2projecta" ["with-profile" "+a2" "project" "a"] 18 | 19 | "a2project" ["with-profile" "a2" "project"] 20 | "a2projecta" ["with-profile" "a2" "project" "a"]} 21 | :a 1 22 | :profiles {:a2 {:aliases {"q" ["echo" "q"] 23 | "inp-projecta" ["project" "a"]} 24 | :a 2}}) 25 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/core/test/mirrors.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.core.test.mirrors 2 | (:require [clojure.test :refer :all] 3 | [cemerick.pomegranate :as pom] 4 | [leiningen.core.project :refer [defproject init-project]])) 5 | 6 | 7 | ;; Regression test for Issue #1555 8 | (defproject mirrors-work-ok-with-plugins-project "0.0.0" 9 | ;; we need to use a sequence of pairs rather than a map to 10 | ;; reproduce the bug; IRL maps got converted to seqs somehow or 11 | ;; another anyhow, but apparently not by init-project. 12 | :mirrors [["central" {:name "foo" 13 | :url "https://repo1.maven.org/maven2/"}]] 14 | ;; Have to have a plugin to reproduce 15 | :plugins [[lein-pprint "1.1.1"]]) 16 | 17 | (deftest ^:online mirrors-work-ok-with-plugins 18 | ;; turn off add-classpath so we don't actually mutate the classpath; 19 | ;; we're still hitting the internet, and there's probably something 20 | ;; in pomegranate we could redef to prevent that, but I haven't 21 | ;; figured out what it is exactly. 22 | (with-redefs [pom/add-classpath (constantly nil)] 23 | (is (init-project project)))) 24 | -------------------------------------------------------------------------------- /src/leiningen/install.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.install 2 | "Install the current project to the local repository." 3 | (:require [cemerick.pomegranate.aether :as aether] 4 | [leiningen.core.project :as project] 5 | [leiningen.core.main :as main] 6 | [leiningen.jar :as jar] 7 | [leiningen.pom :as pom] 8 | [clojure.java.io :as io]) 9 | (:import (java.util.jar JarFile) 10 | (java.util UUID))) 11 | 12 | (defn install 13 | "Install jar and pom to the local repository; typically ~/.m2." 14 | [project] 15 | (when (not (or (:install-releases? project true) 16 | (pom/snapshot? project))) 17 | (main/abort "Can't install release artifacts when :install-releases?" 18 | "is set to false.")) 19 | (let [jarfiles (jar/jar project) 20 | pomfile (pom/pom project) 21 | local-repo (:local-repo project)] 22 | (aether/install 23 | :coordinates [(symbol (:group project) (:name project)) 24 | (:version project)] 25 | :artifact-map jarfiles 26 | :pom-file (io/file pomfile) 27 | :local-repo local-repo) 28 | (main/info (str "Installed jar and pom into " (if local-repo 29 | local-repo "local repo") ".")))) 30 | -------------------------------------------------------------------------------- /bin/issues.clj: -------------------------------------------------------------------------------- 1 | ;; This is just a one-off tool to classify/summarize issues programmatically. 2 | 3 | (try (require 'tentacles.issues) 4 | (catch java.io.FileNotFoundException _ 5 | (cemerick.pomegranate/add-dependencies 6 | :repositories [["clojars" {:url "https://clojars.org/repo/"}]] 7 | :coordinates '[[tentacles "0.2.7"]]) 8 | (require 'tentacles.issues))) 9 | 10 | (defn labeled? [label issue] (some #(= (:name %) label) (:labels issue))) 11 | (def low-priority? #{1566 1544 1319 1363 1155}) 12 | (def order ["2.4.3" "other" "Enhancement" "docs" "low" "3.0.0"]) 13 | 14 | (defn categorize [i] 15 | (cond (labeled? "Windows" i) nil 16 | (:title (:milestone i)) (:title (:milestone i)) 17 | (labeled? "Enhancement" i) "Enhancement" 18 | (labeled? "docs" i) "docs" 19 | (low-priority? (:number i)) "low" 20 | :else "other")) 21 | 22 | (defn report [] 23 | (doseq [[category issues] (->> (tentacles.issues/issues 24 | "technomancy" "leiningen") 25 | (group-by categorize) 26 | (sort-by #(.indexOf order (key %)))) 27 | :when category] 28 | (println "\n#" category) 29 | (doseq [i issues] 30 | (println (:number i) "-" (:title i))))) 31 | -------------------------------------------------------------------------------- /test/leiningen/test/update_in.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.update-in 2 | (:refer-clojure :exclude [update-in]) 3 | (:use clojure.test leiningen.update-in)) 4 | 5 | (defn- prj-map [p] (with-meta p {:without-profiles p})) 6 | 7 | (deftest test-update-in 8 | (doseq 9 | [[in-args task-form] 10 | (->> [[(prj-map {:version "1.0.0"}) 11 | ":" "assoc" ":version" "\"2.0.0\"" "--" "jar"] 12 | ["jar" (prj-map {:version "2.0.0"})] 13 | 14 | [(prj-map {:repl-options {:port 1}}) 15 | ":repl-options:port" "inc" "--" "repl" ":headless"] 16 | ["repl" (prj-map {:repl-options {:port 2}}) ":headless"] 17 | 18 | [(prj-map {:dependencies [['clojure.core "1.7.0"]]}) 19 | ":dependencies" "conj" "[slamhound \"1.1.3\"]" "--" "repl"] 20 | ["repl" (prj-map {:dependencies [['clojure.core "1.7.0"] 21 | ['slamhound "1.1.3"]]})]] 22 | (partition 2))] 23 | (let [[in-prj key-path f & args] in-args 24 | [keys-vec f f-args [task-name & task-args]] 25 | (parse-args key-path f args) 26 | out-prj (update-project in-prj keys-vec f f-args)] 27 | (is (= task-form (concat [task-name out-prj] task-args))) 28 | (is (= (meta (second task-form)) (meta out-prj)))))) 29 | -------------------------------------------------------------------------------- /src/leiningen/new/app.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.new.app 2 | "Generate a basic application project." 3 | (:require [leiningen.new.templates :refer [renderer year project-name 4 | ->files sanitize-ns name-to-path 5 | multi-segment]] 6 | [leiningen.core.main :as main])) 7 | 8 | (defn app 9 | "An application project template." 10 | [name] 11 | (let [render (renderer "app") 12 | main-ns (multi-segment (sanitize-ns name)) 13 | data {:raw-name name 14 | :name (project-name name) 15 | :namespace main-ns 16 | :nested-dirs (name-to-path main-ns) 17 | :year (year)}] 18 | (main/info "Generating a project called" name "based on the 'app' template.") 19 | (->files data 20 | ["project.clj" (render "project.clj" data)] 21 | ["README.md" (render "README.md" data)] 22 | ["doc/intro.md" (render "intro.md" data)] 23 | [".gitignore" (render "gitignore" data)] 24 | [".hgignore" (render "hgignore" data)] 25 | ["src/{{nested-dirs}}.clj" (render "core.clj" data)] 26 | ["test/{{nested-dirs}}_test.clj" (render "test.clj" data)] 27 | ["LICENSE" (render "LICENSE" data)] 28 | "resources"))) 29 | -------------------------------------------------------------------------------- /lein-pprint/README.md: -------------------------------------------------------------------------------- 1 | # lein-pprint 2 | 3 | Pretty-print a representation of the project map. 4 | 5 | This is a sample of how a simple plugin would work. 6 | 7 | ## Usage 8 | 9 | Add `[lein-pprint "1.1.1"]` to `:plugins`. 10 | 11 | $ lein pprint 12 | 13 | ```clj 14 | {:compile-path "/home/phil/src/leiningen/lein-pprint/classes", 15 | :group "lein-pprint", 16 | :source-path ("/home/phil/src/leiningen/lein-pprint/src"), 17 | :dependencies nil, 18 | :target-path "/home/phil/src/leiningen/lein-pprint/target", 19 | :name "lein-pprint", 20 | :root "/home/phil/src/leiningen/lein-pprint", 21 | :version "1.0.0", 22 | :jar-exclusions [#"^\."], 23 | :test-path ("/home/phil/src/leiningen/lein-pprint/test"), 24 | :repositories 25 | (["central" {:url "https://repo1.maven.org/maven2"}] 26 | ["clojars" {:url "http://clojars.org/repo/"}]), 27 | :uberjar-exclusions [#"^META-INF/DUMMY.SF"], 28 | :eval-in :leiningen, 29 | :plugins [[lein-swank "1.4.0-SNAPSHOT"]], 30 | :resources-path 31 | ("/home/phil/src/leiningen/lein-pprint/dev-resources" 32 | "/home/phil/src/leiningen/lein-pprint/resources"), 33 | :native-path "/home/phil/src/leiningen/lein-pprint/native", 34 | :description "Pretty-print a representation of the project map."} 35 | ``` 36 | 37 | ## License 38 | 39 | Copyright © 2012 Phil Hagelberg 40 | 41 | Distributed under the Eclipse Public License, the same as Clojure. 42 | -------------------------------------------------------------------------------- /test_projects/sample/project.clj: -------------------------------------------------------------------------------- 1 | ;; This project is used for leiningen's test suite, so don't change 2 | ;; any of these values without updating the relevant tests. If you 3 | ;; just want a basic project to work from, generate a new one with 4 | ;; "lein new". 5 | 6 | (def clj-version "1.3.0") 7 | 8 | (defproject nomnomnom "0.5.0-SNAPSHOT" 9 | :description "A test project" 10 | :url "http://leiningen.org" 11 | :license {:name "Eclipse Public License" 12 | :url "http://www.eclipse.org/legal/epl-v10.html"} 13 | :dependencies [[~(symbol "org.clojure" "clojure") ~clj-version] 14 | [rome ~(str "0." "9")] 15 | [ring "1.0.0"]] 16 | :plugins [[codox "0.6.4"]] 17 | :main nom.nom.nom 18 | :global-vars {*warn-on-reflection* true} 19 | :jar-exclusions [#"^META-INF"] 20 | :filespecs [{:type :fn :fn (fn [p] {:type :bytes :path "bytes.clj" 21 | :bytes (str "[:bytes \"are\" " 22 | (:name p) "]")})}] 23 | :test-selectors {:integration :integration 24 | :default (complement :integration) 25 | :random (fn [_] (> (rand) ~(float 1/2)))} 26 | :repositories {"other" "http://example.com/repo"} 27 | :deploy-repositories {"snapshots" ~(format "file://%s/lein-repo" 28 | (System/getProperty "java.io.tmpdir"))}) 29 | -------------------------------------------------------------------------------- /test/leiningen/test/do.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.do 2 | (:refer-clojure :exclude [do]) 3 | (:use [clojure.test] 4 | [leiningen.do])) 5 | 6 | (deftest test-group-args-empty-args 7 | (is (= [] (group-args [])))) 8 | 9 | (deftest test-group-args-single-task 10 | (is (= [["pom"]] (group-args ["pom"])))) 11 | 12 | (deftest test-group-args-without-args 13 | (is (= [["clean"] ["deps"] ["test"]] 14 | (group-args ["clean," "deps," "test"])))) 15 | 16 | (deftest test-group-args-with-args 17 | (is (= [["test" "test-core"] ["version"]] 18 | (group-args ["test" "test-core," "version"])))) 19 | 20 | (deftest test-group-args-with-long-chain 21 | (is (= [["help" "help"] ["help" "version"] ["version"] 22 | ["test" "test-compile"]] 23 | (group-args '("help" "help," "help" "version," "version," 24 | "test" "test-compile"))))) 25 | 26 | (deftest test-group-existing-collections 27 | (is (= [["clean"] ["test" ":integration"] '("deploy" "clojars")] 28 | (group-args ["clean" ["test" ":integration"] 29 | '("deploy" "clojars")]))) 30 | (is (= [["foo" "bar"] ["baz" "quux"]] 31 | (group-args [["foo" "bar"] ["baz" "quux"]]))) 32 | (is (= [["foo" "bar"] ["baz"]] 33 | (group-args [["foo" "bar"] "baz"]))) 34 | (is (= [["combinations"] ["work"] ["as" "well"]] 35 | (group-args ["combinations," "work" ["as" "well"]])))) 36 | -------------------------------------------------------------------------------- /src/leiningen/do.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.do 2 | "Higher-order task to perform other tasks in succession." 3 | (:refer-clojure :exclude [do]) 4 | (:require [leiningen.core.main :as main])) 5 | 6 | (defn- conj-to-last [coll x] 7 | (update-in coll [(dec (count coll))] conj x)) 8 | 9 | (defn- butlast-char 10 | "Removes the last character in the string." 11 | [s] 12 | (subs s 0 (dec (count s)))) 13 | 14 | (defn- pop-if-last 15 | "Pops the collection if (pred (peek coll)) is truthy." 16 | [coll pred] 17 | (if (pred (peek coll)) 18 | (pop coll) 19 | coll)) 20 | 21 | (defn ^:internal group-args 22 | ([args] (-> (reduce group-args [[]] args) 23 | (pop-if-last empty?))) 24 | ([groups arg] 25 | (cond (coll? arg) (-> (pop-if-last groups empty?) 26 | (conj arg [])) 27 | (.endsWith arg ",") (-> groups 28 | (conj-to-last (butlast-char arg)) 29 | (conj [])) 30 | :else (conj-to-last groups arg)))) 31 | 32 | (defn ^:no-project-needed ^:higher-order do 33 | "Higher-order task to perform other tasks in succession. 34 | 35 | Each comma-separated group should be a task name followed by optional arguments. 36 | 37 | USAGE: lein do test, compile :all, deploy private-repo" 38 | [project & args] 39 | (doseq [arg-group (group-args args)] 40 | (main/resolve-and-apply project arg-group))) 41 | -------------------------------------------------------------------------------- /src/leiningen/new/default.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.new.default 2 | "Generate a library project." 3 | (:require [leiningen.new.templates :refer [renderer year project-name 4 | ->files sanitize-ns name-to-path 5 | multi-segment]] 6 | [leiningen.core.main :as main])) 7 | 8 | (defn default 9 | "A general project template for libraries. 10 | 11 | Accepts a group id in the project name: `lein new foo.bar/baz`" 12 | [name] 13 | (let [render (renderer "default") 14 | main-ns (multi-segment (sanitize-ns name)) 15 | data {:raw-name name 16 | :name (project-name name) 17 | :namespace main-ns 18 | :nested-dirs (name-to-path main-ns) 19 | :year (year)}] 20 | (main/info "Generating a project called" name "based on the 'default' template.") 21 | (main/info "The default template is intended for library projects, not applications.") 22 | (main/info "To see other templates (app, plugin, etc), try `lein help new`.") 23 | (->files data 24 | ["project.clj" (render "project.clj" data)] 25 | ["README.md" (render "README.md" data)] 26 | ["doc/intro.md" (render "intro.md" data)] 27 | [".gitignore" (render "gitignore" data)] 28 | [".hgignore" (render "hgignore" data)] 29 | ["src/{{nested-dirs}}.clj" (render "core.clj" data)] 30 | ["test/{{nested-dirs}}_test.clj" (render "test.clj" data)] 31 | ["LICENSE" (render "LICENSE" data)] 32 | "resources"))) 33 | -------------------------------------------------------------------------------- /bash_completion.bash: -------------------------------------------------------------------------------- 1 | _lein_completion() { 2 | local cur prev tasks 3 | COMPREPLY=() 4 | cur="${COMP_WORDS[COMP_CWORD]}" 5 | prev="${COMP_WORDS[COMP_CWORD-1]}" 6 | tasks="check classpath clean compile deploy deps help install jar javac new pom repl retest run search swank test trampoline uberjar upgrade version with-profile" 7 | 8 | case "${prev}" in 9 | check | classpath | clean | deploy | deps | install | jar | javac | new | pom | repl | swank | uberjar | version) 10 | COMPREPLY=() 11 | ;; 12 | help) 13 | # Show tasks again, but only once; don't infinitely recurse 14 | local prev2="${COMP_WORDS[COMP_CWORD-2]}" 15 | if [ "$prev2" == "help" ]; then 16 | COMPREPLY=() 17 | else 18 | COMPREPLY=( $(compgen -W "${tasks}" -- ${cur}) ) 19 | fi 20 | ;; 21 | test | retest ) 22 | # list project's test namespaces: 23 | local namespaces=$(find test/ -type f -name "*.clj" -exec sed -n 's/^(ns[ ]*//p' '{}' '+') 24 | COMPREPLY=( $(compgen -W "${namespaces}" -- ${cur}) ) 25 | ;; 26 | run | compile) 27 | # list project's src namespaces: 28 | local namespaces=$(find src/ -type f -name "*.clj" -exec sed -n 's/^(ns[ ]*//p' '{}' '+') 29 | COMPREPLY=( $(compgen -W "${namespaces}" -- ${cur}) ) 30 | ;; 31 | lein) 32 | COMPREPLY=( $(compgen -W "${tasks}" -- ${cur}) ) 33 | ;; 34 | esac 35 | 36 | return 0 37 | } 38 | complete -F _lein_completion lein 39 | -------------------------------------------------------------------------------- /src/leiningen/check.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.check 2 | "Check syntax and warn on reflection." 3 | (:require [leiningen.core.eval :as eval] 4 | [leiningen.core.main :as main] 5 | [bultitude.core :as b] 6 | [clojure.java.io :as io])) 7 | 8 | (defn check 9 | "Check syntax and warn on reflection." 10 | ([project] 11 | (let [source-files (map io/file (:source-paths project)) 12 | nses (b/namespaces-on-classpath :classpath source-files 13 | :ignore-unreadable? false) 14 | action `(let [failures# (atom 0)] 15 | (doseq [ns# '~nses] 16 | ;; load will add the .clj, so can't use ns/path-for. 17 | (let [ns-file# (-> (str ns#) 18 | (.replace \- \_) 19 | (.replace \. \/))] 20 | (binding [*out* *err*] 21 | (println "Compiling namespace" ns#)) 22 | (try 23 | (binding [*warn-on-reflection* true] 24 | (load ns-file#)) 25 | (catch ExceptionInInitializerError e# 26 | (swap! failures# inc) 27 | (.printStackTrace e#))))) 28 | (if-not (zero? @failures#) 29 | (System/exit @failures#)))] 30 | (try 31 | (binding [eval/*pump-in* false] 32 | (eval/eval-in-project project action)) 33 | (catch clojure.lang.ExceptionInfo e 34 | (main/abort "Failed.")))))) 35 | -------------------------------------------------------------------------------- /test/leiningen/test/javac.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.javac 2 | (:use [clojure.test] 3 | [clojure.java.io :only [file]] 4 | [leiningen.javac :only [javac normalize-javac-options]] 5 | [leiningen.test.helper :only [delete-file-recursively 6 | #_dev-deps-project]])) 7 | 8 | (deftest test-javac-options-normalization 9 | (testing "that Leiningen 2 style options are returned unmodified" 10 | (are [arg] (= arg (normalize-javac-options arg)) 11 | ["-target" "1.6" "-source" "1.6"] 12 | ["-deprecation" "-g"])) 13 | (testing "conversion of Leiningen 1 style options that are supported" 14 | (are [old new] (= new (normalize-javac-options old)) 15 | {:debug false} ["-g:none"] 16 | {:debug "off"} ["-g:none"] 17 | ;; overriden by :compile-path 18 | {:destdir "clazzez"} [] 19 | {:encoding "utf8"} ["-encoding" "utf8"] 20 | {:debugLevel "source,lines"} ["-g:source,lines"])) 21 | (testing "conversion of multiple Leiningen 1 style options" 22 | ;; Cannot assume argument order from hash maps 23 | (are [old new] (= new 24 | (apply hash-map (normalize-javac-options old))) 25 | {:source "1.5" :target "1.5"} {"-target" "1.5" "-source" "1.5"} 26 | {:source 1.5 "target" 1.5} {"-target" "1.5" "-source" "1.5"}))) 27 | 28 | (deftest ^:disabled ; not really; need to fix this 29 | test-javac 30 | #_(delete-file-recursively (:compile-path dev-deps-project) true) 31 | #_(javac dev-deps-project) 32 | (is (.exists (file "test_projects/dev-deps-only/classes" 33 | "dev_deps_only" "Junk.class"))) 34 | (is (.exists (file "test_projects/dev-deps-only/classes" 35 | "dev_deps_only" "Junk2.class")))) 36 | -------------------------------------------------------------------------------- /src/leiningen/update_in.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.update-in 2 | "Perform arbitrary transformations on your project map." 3 | (:refer-clojure :exclude [update-in]) 4 | (:require [leiningen.core.main :as main] 5 | [leiningen.core.project :as project] 6 | [leiningen.core.utils :as utils] 7 | [clojure.core :as clj])) 8 | 9 | (defn ^:internal parse-args [key-path f args] 10 | (let [[f-args [_ & task+args]] (split-with #(not= "--" %) args)] 11 | [(mapv keyword (rest (.split key-path ":"))) 12 | (utils/require-resolve (read-string f)) 13 | (mapv read-string f-args) 14 | task+args])) 15 | 16 | (defn ^:internal update-project [project keys-vec f args] 17 | (let [f #(apply apply (concat (if (seq keys-vec) 18 | [clj/update-in % keys-vec f] 19 | [f %]) 20 | args 21 | [nil]))] 22 | (-> (vary-meta (f project) clj/update-in [:without-profiles] f) 23 | (project/load-plugins) 24 | (project/activate-middleware)))) 25 | 26 | (defn ^:higher-order ^:no-project-needed update-in 27 | "Perform arbitrary transformations on your project map. 28 | 29 | Acts a lot like calling `clojure.core/update-in` on your project map 30 | and then invoking a task on it, but with a few differences. Instead of 31 | a vector of keys for reaching into nested maps, just mash keywords 32 | together like \":repl-options:port\". A single \":\" refers to the map 33 | root. Provide the arguments to f (which must be a resolvable var) 34 | followed by \"--\", and then the task name and arguments to the task: 35 | 36 | $ lein update-in :dependencies conj \"[slamhound \\\"1.1.3\\\"]\" -- repl" 37 | [project key-path f & args] 38 | (let [[keys-vec f f-args task+args] (parse-args key-path f args)] 39 | (main/resolve-and-apply (update-project project keys-vec f f-args) 40 | task+args))) 41 | -------------------------------------------------------------------------------- /leiningen-core/resources/clojars.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFMTCCBBmgAwIBAgIDE2MtMA0GCSqGSIb3DQEBBQUAMDwxCzAJBgNVBAYTAlVT 3 | MRcwFQYDVQQKEw5HZW9UcnVzdCwgSW5jLjEUMBIGA1UEAxMLUmFwaWRTU0wgQ0Ew 4 | HhcNMTQwNjExMTEwOTI1WhcNMTYwODEyMDgwNDUxWjCBvjEpMCcGA1UEBRMgemhk 5 | RHptM2Q5Q3A5YzlFOG1BMEFHbS8vS1RscFp3c1cxEzARBgNVBAsTCkdUMzU0OTEz 6 | NDcxMTAvBgNVBAsTKFNlZSB3d3cucmFwaWRzc2wuY29tL3Jlc291cmNlcy9jcHMg 7 | KGMpMTQxLzAtBgNVBAsTJkRvbWFpbiBDb250cm9sIFZhbGlkYXRlZCAtIFJhcGlk 8 | U1NMKFIpMRgwFgYDVQQDEw93d3cuY2xvamFycy5vcmcwggEiMA0GCSqGSIb3DQEB 9 | AQUAA4IBDwAwggEKAoIBAQDbitTo6RveJGbzz+jLt/N7Yc8+EE4Q6+qJxzFdtz1p 10 | wjbwnR4TpUvpymfznZyWLfHe3pOuZ5WHQgUmlFfly3uUFb5ItnyGVR2MGgfSk8Xz 11 | lAJ4zQH8W5/jURUmVeB7qhjzBYHtOh+GUib6rhw5QDpnRA9rgPYEGJVbE29byU+I 12 | dU+JsvogGv9Xux09eeFs8AGMhe+XA66PN7E2qvL/siLd4EBkUVtDLGsbSeyQRLmD 13 | q64DV9XV1S9x/A7vOJ56VOCMc4hkjla1xf2zgfolZIsrdlfhHlgeg8sQz2euZbdv 14 | X3qpRZYWPxbU+97dGdha2yaCMfsjh+m6tlXSBwG5ki59AgMBAAGjggG3MIIBszAf 15 | BgNVHSMEGDAWgBRraT1qGEJK3Y8CZTn9NSSGeJEWMDAOBgNVHQ8BAf8EBAMCBaAw 16 | HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCcGA1UdEQQgMB6CD3d3dy5j 17 | bG9qYXJzLm9yZ4ILY2xvamFycy5vcmcwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDov 18 | L3JhcGlkc3NsLWNybC5nZW90cnVzdC5jb20vY3Jscy9yYXBpZHNzbC5jcmwwHQYD 19 | VR0OBBYEFPSo41HyT0umW48l4dgqBEsJMKyJMAwGA1UdEwEB/wQCMAAweAYIKwYB 20 | BQUHAQEEbDBqMC0GCCsGAQUFBzABhiFodHRwOi8vcmFwaWRzc2wtb2NzcC5nZW90 21 | cnVzdC5jb20wOQYIKwYBBQUHMAKGLWh0dHA6Ly9yYXBpZHNzbC1haWEuZ2VvdHJ1 22 | c3QuY29tL3JhcGlkc3NsLmNydDBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYwMzAx 23 | BggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2Nw 24 | czANBgkqhkiG9w0BAQUFAAOCAQEAmNJqmMoXCHZhgE5epRSScr/aKzoNECmJ0Tx/ 25 | X+pOD7tl+aLzZZeRk7vTKcQpdyIDoiU/NMS35WEBDIj5EZeSk66skGR0WwILO8sA 26 | dw3SwX8jSy1IybMvP2CpLyo4rBeL+uY0fpVTN5bzN6YMka3JiyLfKnekiH5Um1jP 27 | qxYitgsnTP4B4VOy5bhXGG0XXr3OD4odpdi32wMyhhwnhbn8X7aGqj/AjNV7auno 28 | qslaJJYsAj09GvDOe2AVxRbTFmsk0EfEUfrlT5LVnTXjxCxRNMrQOAPXDnNEu8or 29 | QPFF1rFdRnUvypZzsqk7kE4NivVT+n6zJ8Pt4BaLAPR7Amdy1w== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/core/test/user.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.core.test.user 2 | (:use clojure.test 3 | leiningen.core.user)) 4 | 5 | (deftest resolving-repo-creds 6 | (with-redefs [credentials (constantly {#"^https://clojars\.org/.*" 7 | {:username "u" :password "p" 8 | :passphrase "looooong" 9 | :private-key-file "./somewhere"}})] 10 | (testing "Literal creds unmolested" 11 | (is (= (resolve-credentials {:url "https://clojars.org/repo" 12 | :username "easily" :password "stolen"}) 13 | {:url "https://clojars.org/repo" 14 | :username "easily" :password "stolen"}))) 15 | (testing "Lookup in environment" 16 | (with-redefs [getenv {"LEIN_USERNAME" "flynn" 17 | "CUSTOMENV" "flotilla"}] 18 | (is (= (resolve-credentials {:url "https://clojars.org/repo" 19 | :username :env 20 | :password :env/customenv}) 21 | {:url "https://clojars.org/repo" 22 | :username "flynn" :password "flotilla"})))) 23 | (testing "Check multiple locations" 24 | (with-redefs [getenv {"LEIN_USERNAME" "flynn" 25 | "CUSTOMENV" "flotilla"}] 26 | (is (= (resolve-credentials {:url "https://clojars.org/repo" 27 | :username [:gpg :env] 28 | :password [:env/customenv :gpg]}) 29 | {:url "https://clojars.org/repo" 30 | :username "u" :password "flotilla"})))) 31 | (testing "Custom keys unmolested (and :creds expanded)" 32 | (is (= (resolve-credentials {:url "https://clojars.org/repo" 33 | :creds :gpg 34 | :foo [:gpg "0x00D85767"]}) 35 | {:url "https://clojars.org/repo" 36 | :username "u" :password "p" 37 | :passphrase "looooong" :private-key-file "./somewhere" 38 | :foo [:gpg "0x00D85767"]}))))) 39 | -------------------------------------------------------------------------------- /doc/lein.1: -------------------------------------------------------------------------------- 1 | .\"to render: groff -Tascii -man doc/lein.1 > lein.man" 2 | .TH LEININGEN 1 "2011 June 30" 3 | .SH NAME 4 | lein \- Automate Clojure projects 5 | 6 | .SH SYNOPSIS 7 | 8 | .B lein 9 | [\fITASK\fR [\fIARGS\fR] 10 | 11 | .SH DESCRIPTION 12 | 13 | Leiningen is for automating Clojure projects without setting your hair 14 | on fire. 15 | 16 | Working on Clojure projects with tools designed for Java can be an 17 | exercise in frustration. With Leiningen, you just write Clojure. 18 | 19 | .SH TASKS 20 | 21 | .B lein help 22 | will show the complete list of tasks, while 23 | .B lein help TASK 24 | shows usage for a specific one. 25 | 26 | .B lein help tutorial 27 | has a detailed walk-through of the various tasks, but the most 28 | commonly-used are: 29 | 30 | .TP 31 | \fBlein new NAME\fR 32 | generate a new project skeleton 33 | .TP 34 | \fBlein test [TESTS]\fR 35 | run the tests in the TESTS namespaces, or all tests 36 | .TP 37 | \fBlein repl\fR 38 | launch an interactive REPL session in a networked REPL server 39 | .TP 40 | \fBlein uberjar\fR 41 | package up the project and its dependencies as a standalone .jar file 42 | .TP 43 | \fBlein install\fR 44 | install a project into your local repository 45 | .TP 46 | \fBlein deploy [REPOSITORY]\fR 47 | deploy a library to a remote repository 48 | 49 | .SH CONFIGURATION 50 | 51 | Leiningen reads its configuration from the 52 | .B project.clj 53 | file in your project root. Either use 54 | .B lein new 55 | to create a fresh project from which to work, or see the exhaustive 56 | list of configuration options with 57 | .B lein help sample. 58 | 59 | You can customize your project map further with profiles; see 60 | .B lein help profiles 61 | 62 | .SH BUGS 63 | 64 | Check https://github.com/technomancy/leiningen/issues to see if your 65 | problem is a known issue. If not, please open a new issue on that site 66 | or join the mailing list at 67 | http://librelist.com/leiningen/. Please include the output of 68 | .B lein version 69 | as well as your 70 | .B project.clj 71 | file and as much of the relevant code from your project as possible. 72 | 73 | .SH COPYING 74 | 75 | Copyright 76 | .if t \(co 77 | .if n (C) 78 | 2009-2013 Phil Hagelberg and contributors. 79 | 80 | Distributed under the Eclipse Public License, the same as Clojure 81 | uses. See the file /usr/share/doc/leiningen/copyright. 82 | 83 | .SH AUTHOR 84 | This manpage is written by Phil Hagelberg 85 | -------------------------------------------------------------------------------- /bin/release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u 4 | 5 | RELEASE_VERSION=$1 6 | CURRENT_VERSION="$RELEASE_VERSION-SNAPSHOT" 7 | 8 | # Would like to use `lein release` here, but we don't have a way to 9 | # update the bash scripts or watch for boot slowdowns that way. Maybe 10 | # try adding lein-shell? 11 | 12 | if [ ! -x `which lein-stable` ]; then 13 | echo "Install a stable version of Leiningen as lein-stable." 14 | exit 1 15 | fi 16 | 17 | grep $RELEASE_VERSION NEWS.md || (echo "Add $RELEASE_VERSION to NEWS.md" && exit 1) 18 | 19 | lein vcs assert-committed 20 | 21 | for f in bin/lein bin/lein-pkg bin/lein.bat project.clj leiningen-core/project.clj; do 22 | sed -i s/$CURRENT_VERSION/$RELEASE_VERSION/ $f 23 | done 24 | 25 | rm -rf target classes leiningen-core/target leiningen-core/classes leiningen-core/lib 26 | rm -rf $HOME/.lein/self-installs/leiningen-$RELEASE_VERSION-standalone.jar 27 | 28 | LEIN_ROOT=$PWD 29 | 30 | cd leiningen-core 31 | lein-stable do clean, bootstrap 32 | cd .. 33 | 34 | bin/lein uberjar 35 | RELEASE_JAR=$PWD/target/leiningen-$RELEASE_VERSION-standalone.jar 36 | cp $RELEASE_JAR $HOME/.lein/self-installs 37 | 38 | cp bin/lein /tmp/lein-$RELEASE_VERSION 39 | cd /tmp 40 | 41 | if [ ! -r test-project ]; then 42 | ./lein-$RELEASE_VERSION new test-project 43 | fi 44 | 45 | cd test-project 46 | 47 | echo "Running a few invocations in order to check boot time..." 48 | 49 | time ../lein-$RELEASE_VERSION run -m clojure.main/main -e nil 50 | time ../lein-$RELEASE_VERSION run -m clojure.main/main -e nil 51 | time ../lein-$RELEASE_VERSION run -m clojure.main/main -e nil 52 | 53 | echo "Check that these are about the same boot times as with the last version." 54 | echo "Run this in a project: time lein-stable run -m clojure.main/main -e nil" 55 | echo "Are these acceptable times? (~3s) [Y\n]" 56 | read CONTINUE 57 | case "$CONTINUE" in 58 | y|Y|"") 59 | gpg -ab $RELEASE_JAR;; 60 | *) 61 | echo "Aborted." 62 | exit 1;; 63 | esac 64 | 65 | cd $LEIN_ROOT 66 | 67 | git commit -a -m "Release $RELEASE_VERSION" 68 | git tag -s $RELEASE_VERSION -m "Release $RELEASE_VERSION" 69 | git push && git push --tags && git push origin master:stable 70 | 71 | echo "Upload $RELEASE_JAR and $RELEASE_JAR.asc to GitHub releases for $RELEASE_VERSION." 72 | echo "Copy this version's section of NEWS.md to the GitHub release description." 73 | 74 | rm -rf target leiningen-core/target 75 | echo "Test self-install. If things are good, run this:" 76 | echo "$ lein deploy clojars && cd leiningen-core && lein deploy clojars" 77 | echo "And announce it on the mailing list." 78 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | ;; This is Leiningen's own project configuration. See doc/TUTORIAL.md 2 | ;; file as well as sample.project.clj for help writing your own. 3 | 4 | (defproject leiningen "2.5.3-SNAPSHOT" 5 | :description "Automate Clojure projects without setting your hair on fire." 6 | :url "https://github.com/technomancy/leiningen" 7 | :license {:name "Eclipse Public License" 8 | :url "http://www.eclipse.org/legal/epl-v10.html"} 9 | :dependencies [[leiningen-core "2.5.3-SNAPSHOT"] 10 | [org.clojure/data.xml "0.0.3"] 11 | [commons-io "2.4"] 12 | [bultitude "0.2.8"] 13 | [stencil "0.3.5" :exclusions [org.clojure/core.cache]] 14 | [org.apache.maven.indexer/indexer-core "4.1.3" 15 | :exclusions [org.apache.maven/maven-model 16 | org.sonatype.aether/aether-api 17 | org.sonatype.aether/aether-util 18 | org.sonatype.sisu/sisu-inject-plexus 19 | jakarta-regexp]] 20 | [reply "0.3.7" :exclusions [ring/ring-core 21 | org.thnetos/cd-client]] 22 | [org.clojure/tools.nrepl "0.2.10"] 23 | [clojure-complete "0.2.3"] 24 | ;; bump versions of various common transitive deps 25 | [slingshot "0.10.3"] 26 | [cheshire "5.5.0"] 27 | [clj-http "0.9.2" :exclusions [crouton]] 28 | [net.cgrand/parsley "0.9.3" :exclusions [org.clojure/clojure]]] 29 | ;; checkout-deps don't work with :eval-in :leiningen 30 | :profiles {:dev {:resource-paths ["leiningen-core/dev-resources"] 31 | :test-paths ["leiningen-core/test"]} 32 | :uberjar {:aot [#"leiningen" 33 | leiningen.core.ssl ; lazy-loaded 34 | cemerick.pomegranate 35 | classlojure.core 36 | clojure.tools.nrepl 37 | clj-http.core 38 | ;; to avoid compile warnings at runtime: 39 | clj-http.client]}} 40 | :test-selectors {:default (complement :disabled) 41 | :offline (comp (partial not-any? identity) 42 | (juxt :online :disabled))} 43 | :source-paths ["leiningen-core/src" "src"] 44 | :aot [leiningen.search] 45 | ;; work around Clojure bug http://dev.clojure.org/jira/browse/CLJ-1034 46 | :uberjar-exclusions [#"^data_readers.clj$"] 47 | :eval-in :leiningen) 48 | -------------------------------------------------------------------------------- /test/leiningen/test/help.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.help 2 | (:use [leiningen.help] 3 | [clojure.test])) 4 | 5 | (def formatted-docstring @#'leiningen.help/formatted-docstring) 6 | (def get-subtasks-and-docstrings-for 7 | @#'leiningen.help/get-subtasks-and-docstrings-for) 8 | (def formatted-help @#'leiningen.help/formatted-help) 9 | (def resolve-task @#'leiningen.help/resolve-task) 10 | 11 | (deftest blank-subtask-help-for-pom 12 | (let [subtask-pom (apply subtask-help-for (resolve-task "pom"))] 13 | (is (= nil subtask-pom)))) 14 | 15 | (deftest subtask-help-for-new 16 | (let [subtask-help (apply subtask-help-for (resolve-task "new"))] 17 | (is (re-find #"Subtasks available" subtask-help)) 18 | (is (re-find #"default\s+A general project template." subtask-help)) 19 | (is (re-find #"plugin\s+A leiningen plugin project template." subtask-help)) 20 | (is (re-find #"template\s+A meta-template for 'lein new' templates." 21 | subtask-help)))) 22 | 23 | (deftest subtask-help-for-new-default 24 | (let [subtask-help (help-for-subtask "new" "default")] 25 | (is (re-find #"^A general project template." subtask-help)) 26 | (is (re-find #"Arguments: \(\[name\]\)" subtask-help)))) 27 | 28 | (deftest test-docstring-formatting 29 | (is (= "This is an 30 | AWESOME command 31 | For real!" 32 | (formatted-docstring 33 | "install" 34 | "This is an\n AWESOME command\nFor real!" 5)))) 35 | 36 | (deftest test-formatted-help 37 | (is (= "install This is an 38 | AWESOME command 39 | For real!" 40 | (formatted-help "install" "This is an\nAWESOME command\nFor real!" 15)))) 41 | 42 | (deftest ^:disabled test-get-subtasks 43 | (let [m (get-subtasks-and-docstrings-for (second (resolve-task "plugin")))] 44 | (is (= ["install" "uninstall"] 45 | (sort (keys m)))))) 46 | 47 | (deftest test-alias-docstrings 48 | (testing "default alias docstrings" 49 | (is (re-find #"is an alias for" (help-for {} "--version"))) 50 | (is (re-find #"is an alias" (help-for {} "-o"))) 51 | (is (re-find #"not found" (help-for {} "not-a-task")))) 52 | (testing "own alias docstrings" 53 | (let [custom-aliases {:aliases {"foobar" ^{:doc "Foos the bar."} 54 | ["foo" "bar"], 55 | "vsn" "version" 56 | "multipart" ["multi" "part"]}}] 57 | (is (re-find #"is an alias for" (help-for custom-aliases "vsn"))) 58 | (is (re-find #"is an alias" (help-for custom-aliases "multipart"))) 59 | (is (re-find #"Foos the bar\." (help-for custom-aliases "foobar"))) 60 | (is (re-find #"not found" (help-for custom-aliases "not-a-task")))))) 61 | -------------------------------------------------------------------------------- /src/leiningen/trampoline.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.trampoline 2 | "Run a task without nesting the project's JVM inside Leiningen's." 3 | (:refer-clojure :exclude [trampoline]) 4 | (:require [clojure.string :as string] 5 | [leiningen.core.eval :as eval] 6 | [leiningen.core.main :as main] 7 | [leiningen.core.project :as project] 8 | [clojure.java.io :as io] 9 | [clojure.pprint :as pprint])) 10 | 11 | (def ^:dynamic *trampoline?* false) 12 | 13 | (defn- trampoline-file [] 14 | (System/getenv "TRAMPOLINE_FILE")) 15 | 16 | (defn- win-batch? [] 17 | (if-let [t (trampoline-file)] 18 | (.endsWith t ".bat"))) 19 | 20 | (defn- quote-arg [arg] 21 | (if (win-batch?) 22 | (format "\"%s\"" arg) 23 | (format "'%s'" arg))) 24 | 25 | (defn trampoline-command-string [project forms profiles] 26 | ;; each form is (do init & body) 27 | (let [forms (map rest forms) ;; strip off do 28 | inits (map first forms) 29 | rests (mapcat rest forms) 30 | command (eval/shell-command project (concat '(do) inits rests))] 31 | (string/join " " (map quote-arg command)))) 32 | 33 | (defn write-trampoline [project forms profiles] 34 | (let [command (trampoline-command-string project forms profiles) 35 | trampoline (trampoline-file)] 36 | (main/debug "Trampoline command:" command) 37 | (.mkdirs (.getParentFile (io/file trampoline))) 38 | (spit trampoline command))) 39 | 40 | (defn ^:higher-order trampoline 41 | "Run a task without nesting the project's JVM inside Leiningen's. 42 | 43 | Calculates the Clojure code to run in the project's process for the 44 | given task and allows Leiningen's own JVM process to exit before 45 | running it rather than launching a subprocess of Leiningen's JVM. 46 | 47 | Use this to save memory or to work around stdin issues. 48 | 49 | Note that this can be unpredictable on account of collapsing all 50 | eval-in-project calls into one run. For example, tasks chained 51 | together under different profiles end up all running together." 52 | 53 | [project task-name & args] 54 | (when (= :leiningen (:eval-in project)) 55 | (main/info "Warning: trampoline has no effect with :eval-in-leiningen.")) 56 | (binding [*trampoline?* true] 57 | (main/apply-task (main/lookup-alias task-name project) 58 | (-> (assoc project :eval-in :trampoline) 59 | (vary-meta update-in [:without-profiles] assoc 60 | :eval-in :trampoline)) 61 | args)) 62 | (if (seq @eval/trampoline-forms) 63 | (write-trampoline @eval/trampoline-project 64 | @eval/trampoline-forms 65 | @eval/trampoline-profiles) 66 | (main/abort task-name "did not run any project code for trampolining."))) 67 | -------------------------------------------------------------------------------- /test/leiningen/test/release.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.release 2 | (:require [clojure.test :refer :all] 3 | [clojure.pprint :as pprint] 4 | [leiningen.release :refer :all])) 5 | 6 | (def invalid-semver-version-values 7 | [["1.0" "1.0"] 8 | ["derpin" "derpin"]]) 9 | 10 | (def valid-semver-version-values 11 | [["1.0.0" 12 | {:major 1 13 | :minor 0 14 | :patch 0 15 | :qualifier nil} 16 | {:major "2.0.0-SNAPSHOT" 17 | :minor "1.1.0-SNAPSHOT" 18 | :patch "1.0.1-SNAPSHOT"}] 19 | 20 | ["1.2.3" 21 | {:major 1 22 | :minor 2 23 | :patch 3 24 | :qualifier nil} 25 | {:major "2.0.0-SNAPSHOT" 26 | :minor "1.3.0-SNAPSHOT" 27 | :patch "1.2.4-SNAPSHOT"}] 28 | 29 | ["1.2.3-herp" 30 | {:major 1 31 | :minor 2 32 | :patch 3 33 | :qualifier "herp"} 34 | {:major "2.0.0-SNAPSHOT" 35 | :minor "1.3.0-SNAPSHOT" 36 | :patch "1.2.4-SNAPSHOT" 37 | :release "1.2.3"}] 38 | 39 | ["1.0.0-SNAPSHOT" 40 | {:major 1 41 | :minor 0 42 | :patch 0 43 | :qualifier "SNAPSHOT"} 44 | {:major "2.0.0-SNAPSHOT" 45 | :minor "1.1.0-SNAPSHOT" 46 | :patch "1.0.1-SNAPSHOT" 47 | :release "1.0.0" 48 | :alpha "1.0.0-alpha1" 49 | :beta "1.0.0-beta1" 50 | :rc "1.0.0-RC1"}] 51 | 52 | ["1.0.0-alpha1" 53 | {:major 1 54 | :minor 0 55 | :patch 0 56 | :qualifier "alpha1"} 57 | {:major "2.0.0-SNAPSHOT" 58 | :minor "1.1.0-SNAPSHOT" 59 | :patch "1.0.1-SNAPSHOT" 60 | :release "1.0.0" 61 | :alpha "1.0.0-alpha2" 62 | :beta "1.0.0-beta1" 63 | :rc "1.0.0-RC1"}]]) 64 | 65 | (deftest test-string->semantic-version 66 | (testing "Testing semantic version string parsing" 67 | (doseq [[args expected] valid-semver-version-values] 68 | (testing (format "with valid version strings: %s" args) 69 | (is (= (string->semantic-version args) expected)))) 70 | 71 | (testing "with invalid version strings." 72 | (doseq [[semver-test-data] invalid-semver-version-values] 73 | (is (nil? (string->semantic-version semver-test-data))))))) 74 | 75 | (deftest test-parse-semver-version 76 | (testing "Testing semantic version string parsing" 77 | (doseq [[args expected] valid-semver-version-values] 78 | (testing (format "with valid version strings: %s" args) 79 | (is (= (parse-semantic-version args) expected)))) 80 | 81 | (testing "with invalid version strings." 82 | (doseq [[semver-test-data] invalid-semver-version-values] 83 | (is (thrown-with-msg? Exception #"Unrecognized version string" 84 | (binding [leiningen.core.main/*exit-process?* false 85 | *err* (java.io.StringWriter.)] 86 | (parse-semantic-version semver-test-data)))))))) 87 | 88 | (deftest version-map->string-valid 89 | (doseq [[string parsed bumps] valid-semver-version-values] 90 | (is (= string (version-map->string parsed))) 91 | (doseq [[level string] bumps] 92 | (is (= (merge {:qualifier nil} (bump-version-map parsed level)) 93 | (parse-semantic-version string)))))) 94 | -------------------------------------------------------------------------------- /zsh_completion.zsh: -------------------------------------------------------------------------------- 1 | #compdef lein 2 | 3 | # Lein ZSH completion function 4 | # Drop this somewhere in your $fpath (like /usr/share/zsh/site-functions) 5 | # and rename it _lein 6 | 7 | _lein() { 8 | if (( CURRENT > 2 )); then 9 | # shift words so _arguments doesn't have to be concerned with second command 10 | (( CURRENT-- )) 11 | shift words 12 | # use _call_function here in case it doesn't exist 13 | _call_function 1 _lein_${words[1]} 14 | else 15 | _values "lein command" \ 16 | "change[Rewrite project.clj by applying a function.]" \ 17 | "check[Check syntax and warn on reflection.]" \ 18 | "classpath[Print the classpath of the current project.]" \ 19 | "clean[Remove all files from project's target-path.]" \ 20 | "compile[Compile Clojure source into .class files.]" \ 21 | "deploy[Build and deploy jar to remote repository.]" \ 22 | "deps[Download all dependencies.]" \ 23 | "do[Higher-order task to perform other tasks in succession.]" \ 24 | "help[Display a list of tasks or help for a given task.]" \ 25 | "install[Install the current project to the local repository.]" \ 26 | "jar[Package up all the project's files into a jar file.]" \ 27 | "javac[Compile Java source files.]" \ 28 | "new[Generate project scaffolding based on a template.]" \ 29 | "plugin[DEPRECATED. Please use the :user profile instead.]" \ 30 | "pom[Write a pom.xml file to disk for Maven interoperability.]" \ 31 | "release[Perform :release-tasks.]" \ 32 | "repl[Start a repl session either with the current project or standalone.]" \ 33 | "retest[Run only the test namespaces which failed last time around.]" \ 34 | "run[Run a -main function with optional command-line arguments.]" \ 35 | "search[Search remote maven repositories for matching jars.]" \ 36 | "show-profiles[List all available profiles or display one if given an argument.]" \ 37 | "test[Run the project's tests.]" \ 38 | "trampoline[Run a task without nesting the project's JVM inside Leiningen's.]" \ 39 | "uberjar[Package up the project files and dependencies into a jar file.]" \ 40 | "update-in[Perform arbitrary transformations on your project map.]" \ 41 | "upgrade[Upgrade Leiningen to specified version or latest stable.]" \ 42 | "vcs[Interact with the version control system.]" \ 43 | "version[Print version for Leiningen and the current JVM.]" \ 44 | "with-profile[Apply the given task with the profile(s) specified.]" 45 | fi 46 | } 47 | 48 | _lein_plugin() { 49 | _values "lein plugin commands" \ 50 | "install[Download, package, and install plugin jarfile into ~/.lein/plugins]" \ 51 | "uninstall[Delete the plugin jarfile: \[GROUP/\]ARTIFACT-ID VERSION]" 52 | } 53 | 54 | 55 | _lein_namespaces() { 56 | if [ -f "./project.clj" -a -d "$1" ]; then 57 | _values "lein valid namespaces" \ 58 | $(find "$1" -type f -name "*.clj" -exec awk '/^\(ns */ {gsub("\\)", "", $2); print $2}' '{}' '+') 59 | fi 60 | } 61 | 62 | 63 | _lein_run() { 64 | _lein_namespaces "src/" 65 | } 66 | 67 | _lein_test() { 68 | _lein_namespaces "test/" 69 | } 70 | 71 | -------------------------------------------------------------------------------- /test/leiningen/test/deploy.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.deploy 2 | (:use [clojure.test] 3 | [clojure.java.io :only [file]] 4 | [leiningen.deploy] 5 | [leiningen.test.helper :only [delete-file-recursively 6 | tmp-dir sample-project]])) 7 | 8 | (defn- repo-path 9 | [relative-repo-path] 10 | (clojure.string/replace 11 | (format "%s/%s" tmp-dir relative-repo-path) 12 | "\\" "/")) ;make path delimiters look the same / even under Windows 13 | 14 | (defn- repo-url 15 | [absolute-repo-path] 16 | (str "file://" absolute-repo-path)) 17 | 18 | (defn- deploy-snapshots 19 | [project relative-repo-path & [explicit-deploy-repo?]] 20 | (let [repo-path (repo-path relative-repo-path) 21 | repo-url (repo-url repo-path)] 22 | (delete-file-recursively repo-path :silently) 23 | (deploy project (if explicit-deploy-repo? 24 | repo-url 25 | "snapshots")) 26 | (let [dir (file repo-path "nomnomnom/nomnomnom/0.5.0-SNAPSHOT/") 27 | files (.list dir)] 28 | (is (seq files)) 29 | ;; TODO: this is vulnerable to the y3k bug! 30 | (is (seq (filter #(re-find #"nomnomnom-0.5.0-2\d{7}\." %) files)))))) 31 | 32 | (deftest ^:online test-deploy 33 | (testing "simple deployment to `snapshots` already defined in project.clj" 34 | (deploy-snapshots sample-project "lein-repo"))) 35 | 36 | (deftest ^:online test-deploy-custom-url 37 | (testing "deployment to a repo specified as a URL argument to `deploy`" 38 | (deploy-snapshots sample-project "lein-custom-repo" true))) 39 | 40 | (deftest ^:online test-deploy-repositories-key 41 | (testing "preferring repository in :deploy-repositories over :repositories" 42 | (deploy-snapshots (assoc sample-project 43 | :deploy-repositories 44 | {"snapshots" {:url (-> "deploy-only-repo" 45 | repo-path repo-url)}}) 46 | "deploy-only-repo"))) 47 | 48 | (deftest signing 49 | (testing "GPG invocation" 50 | (is (= (signing-args "foo.jar" nil) 51 | ["--yes" "-ab" "--" "foo.jar"])) 52 | (is (= (signing-args "foo.jar" {:gpg-key "123456"}) 53 | ["--yes" "-ab" "--default-key" "123456" "--" "foo.jar"]))) 54 | (testing "Key selection" 55 | (is (= (:gpg-key (signing-opts {:signing {:gpg-key "key-project"}} 56 | ["repo" {:signing {:gpg-key "key-repo"}}])) 57 | "key-repo")) 58 | (is (= (:gpg-key (signing-opts {:signing {:gpg-key "key-project"}} 59 | ["repo" {}])) 60 | "key-project"))) 61 | (testing "Whether to sign" 62 | (is (= (sign-for-repo? ["foo" {:sign-releases true}]) true)) 63 | (is (= (sign-for-repo? ["foo" {:sign-releases false}]) false)) 64 | (is (= (sign-for-repo? ["foo" {}]) true)))) 65 | 66 | (deftest validate-input 67 | (testing "Fail if project data is missing" 68 | (is (thrown? clojure.lang.ExceptionInfo (deploy nil)))) 69 | (testing "Fail if project data is missing" 70 | (is (thrown? clojure.lang.ExceptionInfo (deploy nil "snapshots"))))) -------------------------------------------------------------------------------- /test/leiningen/test/uberjar.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.uberjar 2 | (:require [leiningen.uberjar :refer :all] 3 | [clojure.test :refer :all] 4 | [clojure.java.io :refer [delete-file]] 5 | [clojure.java.shell :refer [sh]] 6 | [clojure.xml :as xml] 7 | [leiningen.test.helper :refer [sample-no-aot-project 8 | uberjar-merging-project 9 | provided-project]]) 10 | (:import (java.io File FileOutputStream) 11 | (java.util.zip ZipFile))) 12 | 13 | (deftest test-uberjar 14 | (uberjar sample-no-aot-project) 15 | (let [filename (str "test_projects/sample-no-aot/target/" 16 | "nomnomnom-0.5.0-SNAPSHOT-standalone.jar") 17 | uberjar-file (File. filename)] 18 | (is (= true (.exists uberjar-file))) 19 | (when (.exists uberjar-file) 20 | (let [entries (->> (ZipFile. uberjar-file) 21 | .entries 22 | enumeration-seq 23 | (map (memfn getName)) 24 | set)] 25 | (.deleteOnExit uberjar-file) 26 | (is (entries "nom/nom/nom.clj")) 27 | (is (entries "org/codehaus/janino/Compiler$1.class")) 28 | (is (not (some #(re-find #"dummy" %) entries))))))) 29 | 30 | (deftest test-uberjar-merge-with 31 | (uberjar uberjar-merging-project) 32 | (let [filename (str "test_projects/uberjar-merging/target/" 33 | "nomnomnom-0.5.0-SNAPSHOT-standalone.jar") 34 | uberjar-file (File. filename)] 35 | (is (= true (.exists uberjar-file))) 36 | (when (.exists uberjar-file) 37 | (.deleteOnExit uberjar-file) 38 | (with-open [zf (ZipFile. uberjar-file)] 39 | (is (= '{nomnomnom/identity clojure.core/identity 40 | mf/i nomnomnom/override 41 | mf/s method.fn/static} 42 | (->> (.getEntry zf "data_readers.clj") 43 | (.getInputStream zf) 44 | slurp read-string))))))) 45 | 46 | (deftest test-components-merger 47 | (let [file1 (str "test_projects/uberjar-components-merging/components1.xml") 48 | file2 (str "test_projects/uberjar-components-merging/components2.xml") 49 | readxml (components-merger 0) 50 | combine (components-merger 1) 51 | writexml (components-merger 2) 52 | combined-xml (combine (readxml file1) (readxml file2)) 53 | expected-xml (xml/parse "test_projects/uberjar-components-merging/expected-components.xml") 54 | result-file "test_projects/uberjar-components-merging/result-components.xml" 55 | out-file (FileOutputStream. (File. result-file))] 56 | (writexml out-file combined-xml) 57 | (is (= expected-xml (xml/parse result-file))) 58 | (delete-file result-file true))) 59 | 60 | ;; TODO: this breaks on Java 6 61 | (deftest ^:disabled test-uberjar-provided 62 | (let [bootclasspath "-Xbootclasspath/a:leiningen-core/lib/clojure-1.4.0.jar" 63 | filename "test_projects/provided/target/provided-0-standalone.jar" 64 | _ (uberjar provided-project)] 65 | (is (= 1 (:exit (sh "java" "-jar" filename)))) 66 | (is (= 0 (:exit (sh "java" bootclasspath "-jar" filename)))))) 67 | -------------------------------------------------------------------------------- /test/leiningen/test/new/templates.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.new.templates 2 | (:use clojure.test 3 | leiningen.new.templates) 4 | (:require [leiningen.test.helper :refer [abort-msg] :as lthelper] 5 | [leiningen.core.user :as user] 6 | [clojure.java.io :as io]) 7 | (:import [java.io File])) 8 | 9 | (defn- getenv [s] 10 | (System/getenv s)) 11 | 12 | (deftest line-separators 13 | (testing "that nothing changes when we're on unix systems" 14 | (with-redefs [user/getprop (constantly "\n")] 15 | (is (= (fix-line-separators "foo") "foo")) 16 | (is (= (fix-line-separators "bar\nbaz") "bar\nbaz")) 17 | (is (= (fix-line-separators "quux\n\n\nsycorax") "quux\n\n\nsycorax")))) 18 | 19 | (testing "that newlines are correctly converted on '\\r\\n' systems" 20 | (with-redefs [user/getprop (constantly "\r\n")] 21 | (is (= (fix-line-separators "foo") "foo")) 22 | (is (= (fix-line-separators "bar\nbaz") "bar\r\nbaz")) 23 | (is (= (fix-line-separators "quux\n\n\nsycorax") 24 | "quux\r\n\r\n\r\nsycorax")))) 25 | 26 | (testing "that other bizarre systems get same treatment" 27 | (with-redefs [user/getprop (constantly "\t\t")] 28 | (is (= (fix-line-separators "foo") "foo")) 29 | (is (= (fix-line-separators "bar\nbaz") "bar\t\tbaz")) 30 | (is (= (fix-line-separators "quux\n\n\nsycorax") 31 | "quux\t\t\t\t\t\tsycorax")))) 32 | 33 | (testing "that one can override the normal system newline" 34 | (with-redefs [user/getprop (constantly "\r\n") 35 | user/getenv (fn [s] (if (= s "LEIN_NEW_UNIX_NEWLINES") 36 | "y" 37 | (getenv s)))] 38 | (is (= (fix-line-separators "foo") "foo")) 39 | (is (= (fix-line-separators "bar\nbaz") "bar\nbaz")) 40 | (is (= (fix-line-separators "quux\n\n\nsycorax") "quux\n\n\nsycorax"))))) 41 | 42 | (deftest project-names 43 | (is (= (project-name "org.example/foo.bar") "foo.bar")) 44 | (is (= (project-name "example") "example")) 45 | (is (= (sanitize-ns "org.example/foo-bar") "org.example.foo-bar")) 46 | (is (= (sanitize-ns "foo-bar") "foo-bar")) 47 | (is (= (sanitize-ns "foo_bar") "foo-bar"))) 48 | 49 | (deftest namespaces 50 | (is (= (multi-segment "foo") "foo.core")) 51 | (is (= (multi-segment "foo" "api") "foo.api")) 52 | (is (= (multi-segment "multi.segment" "last") "multi.segment"))) 53 | 54 | (deftest paths 55 | (is (= (name-to-path "foo-bar.baz") (lthelper/fix-path-delimiters "foo_bar/baz")))) 56 | 57 | (deftest renderers 58 | (is (.contains (abort-msg (renderer "my-template") "boom" {}) 59 | "Template resource 'leiningen/new/my_template/boom' not found.")) 60 | (is (.contains (abort-msg (renderer "my-template") "boom") 61 | "Template resource 'leiningen/new/my_template/boom' not found."))) 62 | 63 | (deftest slurp-resource-compatibility ; can be removed in 3.0.0 64 | (is (= (slurp-resource "leiningen/new/template/temp.clj") 65 | (slurp-resource (io/resource "leiningen/new/template/temp.clj"))))) 66 | 67 | 68 | (deftest files 69 | (testing "that files marked as executable are set executable" 70 | (let [file (File/createTempFile "lein" "template") 71 | path [(.getName file) (.getAbsolutePath file) :executable true]] 72 | (binding [*dir* (.getParentFile file) 73 | *force?* true] 74 | (.deleteOnExit file) 75 | (->files {} path) 76 | (is (.canExecute file)))))) 77 | -------------------------------------------------------------------------------- /test_projects/uberjar-components-merging/components1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.apache.maven.doxia.Doxia 5 | org.apache.maven.doxia.DefaultDoxia 6 | Simple implementation of the Doxia interface: 7 | uses a ParserManager to lookup a parser. 8 | 9 | 10 | org.apache.maven.doxia.parser.manager.ParserManager 11 | parserManager 12 | 13 | 14 | 15 | 16 | org.apache.maven.doxia.macro.Macro 17 | echo 18 | org.apache.maven.doxia.macro.EchoMacro 19 | A simple macro that prints out the key and value of some supplied parameters. 20 | 21 | 22 | org.apache.maven.doxia.macro.manager.MacroManager 23 | org.apache.maven.doxia.macro.manager.DefaultMacroManager 24 | Default implementation of <code>MacroManager</code> 25 | 26 | 27 | org.apache.maven.doxia.macro.Macro 28 | macros 29 | 30 | 31 | 32 | 33 | org.apache.maven.doxia.macro.Macro 34 | snippet 35 | org.apache.maven.doxia.macro.snippet.SnippetMacro 36 | A macro that prints out the content of a file or a URL. 37 | 38 | 39 | org.apache.maven.doxia.macro.Macro 40 | swf 41 | org.apache.maven.doxia.macro.SwfMacro 42 | Macro for embedding Flash (SWF) within Maven documentation. 43 | 44 | 45 | org.apache.maven.doxia.macro.Macro 46 | toc 47 | org.apache.maven.doxia.macro.toc.TocMacro 48 | Macro to display a <code>Table Of Content</code> in a given <code>Sink</code>. 49 | 50 | 51 | org.apache.maven.doxia.module.site.manager.SiteModuleManager 52 | org.apache.maven.doxia.module.site.manager.DefaultSiteModuleManager 53 | Simple implementation of the SiteModuleManager interface. 54 | 55 | 56 | org.apache.maven.doxia.module.site.SiteModule 57 | siteModules 58 | 59 | 60 | 61 | 62 | org.apache.maven.doxia.parser.manager.ParserManager 63 | org.apache.maven.doxia.parser.manager.DefaultParserManager 64 | Simple implementation of the <code>ParserManager</code> interface. 65 | 66 | 67 | org.apache.maven.doxia.parser.Parser 68 | parsers 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /pcmpl-lein.el: -------------------------------------------------------------------------------- 1 | ;;; pcmpl-lein.el --- pcomplete for Leiningen tasks; works with eshell 2 | 3 | ;; Copyright (C) 2011 Phil Hagelberg 4 | ;; 5 | ;; Author: Phil Hagelberg 6 | ;; URL: http://github.com/technomancy/leiningen 7 | ;; Version: 0.1 8 | ;; Keywords: eshell completion 9 | ;; Created: 2011-01-15 10 | 11 | ;; This file is not part of GNU Emacs or Leiningen. 12 | 13 | ;;; Commentary: 14 | 15 | ;; Provides completion of leiningen tasks using pcomplete, suitable 16 | ;; for eshell. Does not support custom :source-path or :test-path. 17 | 18 | ;;; License: 19 | 20 | ;; This program is free software; you can redistribute it and/or 21 | ;; modify it under the terms of the GNU General Public License 22 | ;; as published by the Free Software Foundation; either version 3 23 | ;; of the License, or (at your option) any later version. 24 | ;; 25 | ;; This program is distributed in the hope that it will be useful, 26 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | ;; GNU General Public License for more details. 29 | ;; 30 | ;; You should have received a copy of the GNU General Public License 31 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 32 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 33 | ;; Boston, MA 02110-1301, USA. 34 | 35 | ;;; Code: 36 | 37 | (require 'cl) 38 | (require 'pcomplete) 39 | (require 'esh-util) 40 | 41 | (defvar pcmpl-lein-tasks-alist nil 42 | "Cached alist of project roots to task lists.") 43 | 44 | (defvar pcmpl-lein-project-root nil) 45 | 46 | (defun pcmpl-lein-tasks () 47 | (or (cdr (assoc pcmpl-lein-project-root pcmpl-lein-tasks-alist)) 48 | (let* ((help (progn (message "Getting Leiningen task list...") 49 | (shell-command-to-string "lein help"))) 50 | (tasks (split-string help "\n")) 51 | (tasks (subseq tasks 4 -3)) 52 | (tasks (mapcar (lambda (line) 53 | (substring line 0 (string-match " " line))) 54 | tasks))) 55 | ;; OHAI MEMOIZE. 56 | (add-to-list 'pcmpl-lein-tasks-alist 57 | (cons pcmpl-lein-project-root tasks)) 58 | tasks))) 59 | 60 | (defun pcmpl-lein-namespaces-dir () 61 | (let ((task (cadr pcomplete-args))) 62 | (cond ((equal "test" task) "test") 63 | ((or (equal "run" task) (equal "compile" task)) "src")))) 64 | 65 | (defun pcmpl-lein-transform-filename (file) 66 | (subst-char-in-string ?/ ?. 67 | (substring file (+ (length pcmpl-lein-project-root) 68 | (length namespaces-dir) 1) -4))) 69 | 70 | (defun pcmpl-lein-namespaces-in-dir (file) 71 | (if (not (file-directory-p file)) 72 | (if (string-match "\\.clj$" file) 73 | (pcmpl-lein-transform-filename file)) 74 | (eshell-flatten-list (mapcar 'pcmpl-lein-namespaces-in-dir 75 | (directory-files file t "^[^\\.]"))))) 76 | 77 | (defun pcmpl-lein-namespaces () 78 | (let ((namespaces-dir (pcmpl-lein-namespaces-dir))) 79 | (when namespaces-dir 80 | (pcmpl-lein-namespaces-in-dir namespaces-dir)))) 81 | 82 | ;;;###autoload 83 | (defun pcomplete/lein () 84 | (let ((pcmpl-lein-project-root (expand-file-name 85 | (locate-dominating-file 86 | default-directory "project.clj")))) 87 | (pcomplete-here (pcmpl-lein-tasks)) 88 | (if (not (string= "run" (cadr pcomplete-args))) 89 | (pcomplete-here (pcmpl-lein-namespaces)) 90 | (pcomplete-here (list "-m")) 91 | (pcomplete-here (pcmpl-lein-namespaces))))) 92 | 93 | (provide 'pcmpl-lein) 94 | ;;; pcmpl-lein.el ends here 95 | -------------------------------------------------------------------------------- /src/leiningen/with_profile.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.with-profile 2 | "Apply the given task with the profile(s) specified." 3 | (:require [clojure.string :as string] 4 | [leiningen.core.main :as main] 5 | [leiningen.core.project :as project] 6 | [robert.hooke :as hooke])) 7 | 8 | (defn ^:internal with-profiles* 9 | "Apply the given task with a comma-separated profile list." 10 | [project profiles task-name args] 11 | (hooke/with-scope 12 | (let [project (and project (project/set-profiles project profiles)) 13 | task-name (main/lookup-alias task-name project)] 14 | (main/apply-task task-name project args)))) 15 | 16 | (defn profiles-in-group 17 | [project profile-group] 18 | (let [profiles (.split profile-group ",") 19 | prefixes (map first profiles)] 20 | (cond 21 | (every? #{\+ \-} prefixes) 22 | (distinct 23 | (reduce (fn [result profile] 24 | (let [pm (first profile), profile (keyword (subs profile 1)) 25 | profiles (project/expand-profile project profile)] 26 | (if (= \+ pm) 27 | (concat result profiles) 28 | (remove (set profiles) result)))) 29 | (mapcat (partial project/expand-profile project) 30 | (:active-profiles (meta project))) 31 | profiles)) 32 | 33 | (not-any? #{\+ \-} prefixes) 34 | (distinct 35 | (mapcat (comp #(project/expand-profile project %) keyword) 36 | profiles)) 37 | 38 | :else 39 | (throw 40 | (ex-info 41 | "Profiles in with-profile must either all be qualified, or none qualified" 42 | {:exit-code 1}))))) 43 | 44 | 45 | (defn- apply-task-with-profiles 46 | [project profiles task-name args failures multi-group] 47 | (when multi-group 48 | (main/info (format "Performing task '%s' with profile(s): '%s'" 49 | task-name 50 | (string/join "," (map name profiles))))) 51 | (binding [main/*exit-process?* false] 52 | (try 53 | (with-profiles* project profiles task-name args) 54 | (catch Exception e 55 | (main/info 56 | (format "Error encountered performing task '%s' with profile(s): '%s'" 57 | task-name (string/join "," (map name profiles)))) 58 | (if (and (:exit-code (ex-data e)) (not main/*debug*)) 59 | (main/info (.getMessage e)) 60 | (.printStackTrace e)) 61 | (swap! failures inc))))) 62 | 63 | (defn ^:no-project-needed ^:higher-order with-profile 64 | "Apply the given task with the profile(s) specified. 65 | 66 | Comma-separated profiles may be given to merge profiles and perform the task. 67 | Colon-separated profiles may be given for sequential profile task application. 68 | 69 | A profile list may either be a list of profiles to use, or may specify the 70 | profiles to add or remove from the active profile list using + or - prefixes. 71 | 72 | For example: 73 | 74 | lein with-profile user,dev test 75 | lein with-profile -dev test 76 | lein with-profile +1.4:+1.4,-dev:base,user test 77 | 78 | To list all profiles or show a single one, see the show-profiles task. 79 | For a detailed description of profiles, see `lein help profiles`." 80 | [project profiles task-name & args] 81 | (let [profile-groups (seq (.split profiles ":")) 82 | failures (atom 0) 83 | result (->> profile-groups 84 | (map (partial profiles-in-group project)) 85 | (mapv #(apply-task-with-profiles 86 | project % task-name args failures 87 | (> (count profile-groups) 1))))] 88 | (when (pos? @failures) 89 | (main/abort)) 90 | result)) 91 | -------------------------------------------------------------------------------- /leiningen-core/README.md: -------------------------------------------------------------------------------- 1 | # Leiningen Core 2 | 3 | This library provides the core functionality of Leiningen. This 4 | consists of the task execution implementation, project configuration, 5 | and helper functions. The built-in tasks and the launcher scripts are 6 | kept in the main `leiningen` project. 7 | 8 | More detailed [API reference](http://leiningen.org/reference.html) is 9 | available. 10 | 11 | ## Namespaces 12 | 13 | * **leiningen.core.main** contains the `-main` entry point along with 14 | task handling functions like `apply-task` and `resolve-task`. 15 | * **leiningen.core.project** has `read` and `defproject` for getting a 16 | project map from `project.clj` files. It also handles applying 17 | profiles to the project map and loading plugins. 18 | * **leiningen.core.classpath** is where the project's classpath is 19 | calculated. It handles Maven dependencies as well as checkout 20 | dependencies. 21 | * **leiningen.core.eval** houses the `eval-in-project` function which 22 | implements the isolation of project code from Leiningen's own code. 23 | * **leiningen.core.user** just has a handful of functions which handle 24 | user-level configuration. 25 | 26 | ## Running Tasks 27 | 28 | When Leiningen is invoked, it first reads the `project.clj` file and 29 | applies any active profiles to the resulting project map. (See 30 | Leiningen's own readme for a description of how profiles work.) Then 31 | it looks up the task which was invoked. Tasks are just functions named 32 | after the task they implement and defined in the `leiningen.the-task` 33 | namespace. They usually take a project map as their argument, but can 34 | also run outside the context of a project. See the 35 | [plugin guide](https://github.com/technomancy/leiningen/blob/stable/doc/PLUGINS.md) 36 | for more details on how tasks are written. The `apply-task` function 37 | looks up the task function, checks to make sure it can be applied to 38 | the provided arguments, and then calls it. 39 | 40 | ## Project Isolation 41 | 42 | When you launch Leiningen, it must start an instance of Clojure to 43 | load itself. But this instance must not affect the project that you're 44 | building. It may use a different version of Clojure or other 45 | dependencies from Leiningen itself, and Leiningen's code should not be 46 | visible to the project's functions. 47 | 48 | Leiningen currently implements this by launching a sub-process using 49 | `leiningen.core.eval/eval-in-project`. Any code that must execute 50 | within the context of the project (AOT compilation, test runs, repls) 51 | needs to go through this function. Before the process is launched, the 52 | project must be "prepped", which consists of running all the tasks 53 | named in the project's `:prep-tasks` key. This defaults to `javac` and 54 | `compile`, but `defproject` or profiles may add additional tasks as 55 | necessary. All prep tasks must be cheap to call if nothing has changed 56 | since their last invocation. 57 | 58 | The sub-process (referred to as the "project JVM") is an entirely new 59 | invocation of the `java` command with its own classpath calculated 60 | from functions in the `leiningen.core.classpath` namespace. It can 61 | even use a different version of the JVM from Leiningen if the 62 | `:java-cmd` key is provided. It can only communicate with Leiningen's 63 | process via the file system, sockets, and its exit code. 64 | 65 | The exception to this rule is when `:eval-in-leiningen` in 66 | `project.clj` is true, as is commonly used for Leiningen plugins. 67 | Since Leiningen plugins are intended to be used inside Leiningen 68 | itself, there's no need to enforce this isolation. 69 | 70 | ## License 71 | 72 | Copyright © 2011-2014 Phil Hagelberg and 73 | [contributors](https://www.ohloh.net/p/leiningen/contributors). 74 | 75 | Distributed under the Eclipse Public License, the same as Clojure. 76 | -------------------------------------------------------------------------------- /test/leiningen/test/run.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.run 2 | (:require [leiningen.core.project :as project] 3 | [leiningen.javac] 4 | [clojure.java.io :as io] 5 | [leiningen.test.helper :as helper 6 | :refer [bad-require-project tmp-dir tricky-name-project 7 | java-main-project file-not-found-thrower-project 8 | with-system-out-str with-system-err-str]]) 9 | (:use [clojure.test] 10 | [leiningen.run])) 11 | 12 | (def out-file (format "%s/lein-test" tmp-dir)) 13 | 14 | (deftest test-arg-map 15 | (let [parse-args #'leiningen.run/parse-args] 16 | (is (= (:main (parse-args ["-m" "my-main"])) 17 | "my-main")) 18 | (is (= ((juxt :main :args) (parse-args ["-m" "my-main" "-m" "foo"])) 19 | ["my-main" ["-m" "foo"]])) 20 | (is (= (:arg-conversion (parse-args ["-m" "my-main"])) 21 | :stringify)) 22 | (is (= (:arg-conversion (parse-args ["-m" "my-main" "--quote-args"])) 23 | :quote)) 24 | (is (= (:arg-conversion (parse-args ["--quote-args" "-m" "my-main"])) 25 | :quote)) 26 | (is (= (:args (parse-args ["--" "--quote-args" "-m" "my-main"])) 27 | ["--quote-args" "-m" "my-main"])) 28 | (is (= (:args (parse-args ["--" "--" "-m" "my-main"])) 29 | ["--" "-m" "my-main"])))) 30 | 31 | (use-fixtures :each (fn [f] 32 | (f) 33 | (io/delete-file out-file :silently))) 34 | 35 | (deftest test-basic 36 | (run tricky-name-project "/unreadable") 37 | (is (= "nom:/unreadable" (slurp out-file)))) 38 | 39 | (deftest test-alt-main 40 | (run tricky-name-project "-m" "org.domain.tricky-name.munch" "/unreadable") 41 | (is (= ":munched (\"/unreadable\")" (slurp out-file)))) 42 | 43 | (deftest test-valid-namespace-argument 44 | (is (re-find #"Option -m requires a valid namespace argument, not -1\." 45 | (helper/abort-msg run tricky-name-project "-m" "-1")))) 46 | 47 | (deftest test-nonexistant-ns-error-message 48 | (is (re-find #"Can't find 'nonexistant.ns' as \.class or \.clj for lein run" 49 | (with-system-err-str 50 | (try (run tricky-name-project "-m" "nonexistant.ns") 51 | (catch Exception _)))))) 52 | 53 | (deftest test-escape-args 54 | (run tricky-name-project "--" ":bbb") 55 | (is (= "nom::bbb" (slurp out-file))) 56 | (run tricky-name-project "--" "-m") 57 | (is (= "nom:-m" (slurp out-file)))) 58 | 59 | (deftest test-bad-require-error-msg 60 | (let [e-msg (with-system-err-str 61 | (try (run bad-require-project) 62 | (catch clojure.lang.ExceptionInfo e nil)))] 63 | ;; Don't throw the old ClassNotFoundException 64 | (is (not (re-find #"ClassNotFoundException: bad-require.core" e-msg))) 65 | ;; Do show a relevant error message 66 | (is (re-find #"FileNotFoundException" e-msg)) 67 | (is (re-find #"this/namespace/does/not/exist.clj" e-msg)))) 68 | 69 | (deftest test-run-java-main 70 | (leiningen.javac/javac java-main-project) 71 | (let [out-result (with-system-out-str (run java-main-project))] 72 | (is (= (.trim out-result) ;; To avoid os-specific newline handling 73 | "Hello from Java!")))) 74 | 75 | ;; Regression test for https://github.com/technomancy/leiningen/issues/1469 76 | (deftest file-not-found-exception-test 77 | (let [s (with-system-err-str 78 | (try (run file-not-found-thrower-project 79 | "-m" "file-not-found-thrower.core") 80 | (catch clojure.lang.ExceptionInfo e nil)))] 81 | ;; testing that the true exception is printed immediately and 82 | ;; the inappropriate error message "Can't find 83 | ;; 'file-not-found-thrower.core' as .class or .clj for lein run: 84 | ;; please check the spelling." is not 85 | (is (.contains s "Exception in thread \"main\" java.io.FileNotFoundException")))) 86 | -------------------------------------------------------------------------------- /src/leiningen/vcs.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.vcs 2 | "Interact with the version control system." 3 | (:require [clojure.java.io :as io] 4 | [bultitude.core :as b] 5 | [leiningen.core.eval :as eval] 6 | [leiningen.core.main :as main])) 7 | 8 | ;; TODO: make pom task use this ns by adding a few more methods 9 | 10 | (def supported-systems (atom [:git])) 11 | 12 | (defn uses-vcs [project vcs] 13 | (let [vcs-dir (io/file (:root project) (str "." (name vcs)))] 14 | (and (.exists vcs-dir) vcs))) 15 | 16 | (defn which-vcs [project & _] 17 | (or (:vcs project) (some (partial uses-vcs project) @supported-systems))) 18 | 19 | (defn parse-tag-args [args] 20 | (loop [parsed-args {:sign? true} 21 | args args] 22 | (case (first args) 23 | ("--sign" "-s") (recur (assoc parsed-args :sign? true) (rest args)) 24 | "--no-sign" (recur (assoc parsed-args :sign? false) (rest args)) 25 | nil parsed-args ;; We're finished and can exit 26 | (recur (assoc parsed-args :prefix (first args)) (rest args))))) 27 | 28 | ;;; Methods 29 | 30 | (defmulti push "Push to your remote repository." 31 | which-vcs :default :none) 32 | 33 | (defmulti commit "Commit changes to current repository." 34 | which-vcs :default :none) 35 | 36 | (defmulti tag "Apply a version control tag. Takes an optional tag prefix." 37 | which-vcs :default :none) 38 | 39 | (defmulti assert-committed "Abort if uncommitted changes exist." 40 | which-vcs :default :none) 41 | 42 | 43 | ;;; VCS not found 44 | 45 | (defn- unknown-vcs [task] 46 | (binding [*out* *err*] 47 | (println (str "Unknown VCS detected for 'vcs " task "'"))) 48 | (System/exit 1)) 49 | 50 | (defmethod push :none [project & [args]] (unknown-vcs "push")) 51 | 52 | (defmethod commit :none [project] (unknown-vcs "commit")) 53 | 54 | (defmethod tag :none [project] (unknown-vcs "tag")) 55 | 56 | (defmethod assert-committed :none [project] (unknown-vcs "assert-committed")) 57 | 58 | 59 | ;;; Git 60 | 61 | (defmethod push :git [project & args] 62 | (binding [eval/*dir* (:root project)] 63 | (apply eval/sh-with-exit-code "Couldn't push to the remote" "git" "push" args) 64 | (apply eval/sh-with-exit-code "Couldn't push tags to the remote" "git" "push" "--tags" args))) 65 | 66 | (defmethod commit :git [project] 67 | (binding [eval/*dir* (:root project)] 68 | (eval/sh-with-exit-code "Couldn't commit" "git" "commit" "-a" "-m" (str "Version " (:version project))))) 69 | 70 | (defmethod tag :git [{:keys [root version]} & args] 71 | (binding [eval/*dir* root] 72 | (let [{:keys [sign? prefix]} (parse-tag-args args) 73 | tag (if prefix 74 | (str prefix version) 75 | version) 76 | cmd (->> ["git" "tag" (when sign? "--sign") tag "-m" (str "Release " version)] 77 | (filter some?))] 78 | (apply eval/sh-with-exit-code "Couldn't tag" cmd)))) 79 | 80 | (defmethod assert-committed :git [project] 81 | (binding [eval/*dir* (:root project)] 82 | (when (re-find #"Changes (not staged for commit|to be committed)" 83 | (with-out-str (eval/sh-with-exit-code "Couldn't get status" "git" "status"))) 84 | (main/abort "Uncommitted changes in" (:root project) "directory.")))) 85 | 86 | 87 | 88 | (defn- not-found [subtask] 89 | (partial #'main/task-not-found (str "vcs " subtask))) 90 | 91 | (defn- load-methods [] 92 | (doseq [n (b/namespaces-on-classpath :prefix "leiningen.vcs.")] 93 | (swap! supported-systems conj (keyword (last (.split (name n) "\\.")))) 94 | (require n))) 95 | 96 | (defn ^{:subtasks [#'push #'commit #'tag #'assert-committed]} vcs 97 | "Interact with the version control system." 98 | [project subtask & args] 99 | (load-methods) 100 | (let [subtasks (:subtasks (meta #'vcs) {}) 101 | [subtask-var] (filter #(= subtask (name (:name (meta %)))) subtasks)] 102 | (apply (or subtask-var (not-found subtask)) project args))) 103 | -------------------------------------------------------------------------------- /test/leiningen/test/test.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.test 2 | (:refer-clojure :exclude [test]) 3 | (:require [clojure.test :refer :all] 4 | [leiningen.test :refer :all] 5 | [leiningen.test.helper :refer [tmp-dir sample-no-aot-project 6 | sample-reader-cond-project 7 | sample-failing-project 8 | with-system-err-str]] 9 | [clojure.java.io :as io] 10 | [leiningen.core.main :as main] 11 | [leiningen.core.project :as project])) 12 | 13 | (use-fixtures :each 14 | (fn [f] 15 | (f) 16 | (.delete (java.io.File. tmp-dir "lein-test-ran")))) 17 | 18 | (defn runs [] 19 | (let [ran-file (io/file tmp-dir "lein-test-ran")] 20 | (and (.exists ran-file) 21 | (-> ran-file 22 | (slurp) 23 | (.split "\n") 24 | (->> (map read-string) 25 | (frequencies)))))) 26 | 27 | (defn ran? [] (-> (runs) keys set)) 28 | 29 | (deftest test-project-selectors 30 | (is (= #{:default :integration :int2 :no-custom} 31 | (set (keys (:test-selectors sample-no-aot-project))))) 32 | (is (every? ifn? (map eval (vals (:test-selectors sample-no-aot-project)))))) 33 | 34 | (deftest test-default-selector 35 | (test sample-no-aot-project ":default") 36 | (is (= (ran?) #{:regular :int2 :not-custom :fixture}))) 37 | 38 | (deftest fixture-runs-appropriate-number-of-times 39 | ;; Issue #1269 40 | (test sample-no-aot-project) 41 | ;; Because three tests ran 42 | (is (= 3 ((runs) :fixture)))) 43 | 44 | (deftest test-no-args-defaults-to-default-selector 45 | (test sample-no-aot-project) 46 | (is (= (ran?) #{:regular :int2 :not-custom :fixture}))) 47 | 48 | (deftest test-basic-selector 49 | (test sample-no-aot-project ":integration") 50 | (is (= (ran?) #{:integration :integration-ns :fixture}))) 51 | 52 | (deftest test-complex-selector 53 | (test sample-no-aot-project ":no-custom") 54 | (is (= (ran?) #{:integration :integration-ns :regular :int2 :fixture}))) 55 | 56 | (deftest test-two-selectors 57 | (test sample-no-aot-project ":integration" ":int2") 58 | (is (= (ran?) #{:integration :integration-ns :int2 :fixture}))) 59 | 60 | (deftest test-override-namespace-selector 61 | (test sample-no-aot-project ":int2") 62 | (is (= (ran?) #{:integration-ns :int2 :fixture}))) 63 | 64 | (deftest test-only-selector 65 | (test sample-no-aot-project ":only" "selectors/regular") 66 | (is (= (ran?) #{:regular :fixture}))) 67 | 68 | (deftest test-namespace-argument 69 | (test sample-no-aot-project "selectors") 70 | (is (= (ran?) #{:regular :not-custom :int2 :fixture}))) 71 | 72 | (deftest test-reader-conditional-tests 73 | (test sample-reader-cond-project) 74 | (is (= (ran?) #{:clj-test :cljc-test}))) 75 | 76 | (deftest test-invalid-namespace-argument 77 | (is (.contains 78 | (with-system-err-str 79 | (try 80 | (test sample-no-aot-project "boom") 81 | (catch clojure.lang.ExceptionInfo e 82 | (when-not (:exit-code (ex-data e)) 83 | (throw e))))) 84 | "java.io.FileNotFoundException: Could not locate"))) 85 | 86 | (deftest test-file-argument 87 | (let [file (io/file (first (:test-paths sample-no-aot-project)) "selectors.clj")] 88 | (test sample-no-aot-project (.getPath file))) 89 | (is (= (ran?) #{:regular :not-custom :int2 :fixture}))) 90 | 91 | (deftest test-unreadable-test-fails 92 | (let [project (project/merge-profiles sample-failing-project 93 | [{:aot ^:replace [] 94 | :dependencies ^:replace 95 | [['org.clojure/clojure "1.7.0"]]}])] 96 | (binding [main/*exit-process?* false] 97 | (is (= "EOF while reading" (try (test project) false 98 | (catch Exception e 99 | (.getMessage e)))))))) 100 | -------------------------------------------------------------------------------- /leiningen-core/dev-resources/leiningen/downloads.clj: -------------------------------------------------------------------------------- 1 | (use '[cemerick.pomegranate :only (add-dependencies)]) 2 | 3 | (add-dependencies :coordinates '[[clj-aws-s3 "0.3.6"] 4 | [tentacles "0.2.4"]] 5 | :repositories (merge cemerick.pomegranate.aether/maven-central 6 | {"clojars" "http://clojars.org/repo"})) 7 | 8 | (ns leiningen.downloads 9 | "Calculate download statistics from logs." 10 | (:require [aws.sdk.s3 :as s3] 11 | [clojure.java.io :as io] 12 | [tentacles.repos :as repo] 13 | [clojure.pprint :refer [pprint]] 14 | [leiningen.core.main :as main]) 15 | (:import (java.io File))) 16 | 17 | (defn ^:internal aws-cred [] 18 | 19 | ;; in order to run, you need to define a map with the appropriate AWS 20 | ;; credentials in ~/.secrets/leiningen_downloads_aws_cred.clj: 21 | 22 | ;; {:access-key "AWS_ACCESS_KEY" 23 | ;; :secret-key "AWS_SECRET_KEY"} 24 | (let [f (File. (System/getenv "HOME") 25 | "/.secrets/leiningen_downloads_aws_cred.clj")] 26 | (if (.exists f) 27 | (read-string (slurp f)) 28 | (main/abort "Missing credentials file:" f)))) 29 | 30 | (defn- list-all-objects 31 | [bucket & [objects next-marker]] 32 | (let [response (s3/list-objects (aws-cred) bucket {:marker next-marker}) 33 | truncated? (:truncated? response) 34 | next-marker (:next-marker response) 35 | objects (concat objects (:objects response))] 36 | (if (not truncated?) 37 | objects 38 | (recur bucket [objects next-marker])))) 39 | 40 | (defn- fetch-all-objects 41 | [bucket] 42 | (for [object (list-all-objects bucket)] 43 | (do 44 | (println (str "Processing: " (:key object))) 45 | (s3/get-object (aws-cred) bucket (:key object))))) 46 | 47 | (defn- file-for-line 48 | [line] 49 | (let [[_ file] (re-find #"\"GET ([^ ]+) " line)] 50 | (if file 51 | (last (.split file "/"))))) 52 | 53 | (defn- ip-for-line 54 | [line] 55 | (re-find #"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b" line)) 56 | 57 | (defn- status-for-line 58 | [line] 59 | (second (re-find #"\" (\d\d\d)" line))) 60 | 61 | (defn- parse-files 62 | [content] 63 | (with-open [rdr (io/reader content)] 64 | (doall (for [line (line-seq rdr)] 65 | {:file (file-for-line line) 66 | :status (status-for-line line) 67 | :ip (ip-for-line line)})))) 68 | 69 | (defn- s3-downloads 70 | [] 71 | (flatten 72 | (for [logfile (map :content (fetch-all-objects "leiningen-logs"))] 73 | (filter #(and (get % :file) ;; file is present 74 | (re-find #"\.jar\b" (get % :file)) ;; file is a jar 75 | (= "200" (get % :status))) ;; and only HTTP 200 responses 76 | (parse-files logfile))))) 77 | 78 | (defn- github-downloads 79 | [] 80 | (reverse 81 | (sort-by #(first (vals %)) 82 | (filter #(re-find #"\.jar$" (first (keys %))) 83 | (let [downloads {}] 84 | (for [download (repo/downloads "technomancy" "leiningen")] 85 | (assoc downloads 86 | (:name download) 87 | (:download_count download)))))))) 88 | 89 | (defn ^:no-project-needed downloads [project] 90 | (let [s3-downloads (s3-downloads) 91 | s3-download-count (count s3-downloads) 92 | github-downloads (github-downloads) 93 | github-download-count 94 | (reduce + (map #(first (vals %)) github-downloads))] 95 | (println (str "GitHub Downloads: " github-download-count)) 96 | (println (str "S3 Downloads: " s3-download-count)) 97 | (println (str "Unique IP Addresses (S3 Downloads Only): " 98 | (count (distinct (map :ip s3-downloads))))) 99 | (println (str "Total Downloads: " 100 | (+ github-download-count s3-download-count))) 101 | (print "\n\n") 102 | (println "GitHub downloads by file:") 103 | (print "\n\n") 104 | (pprint github-downloads) 105 | (print "\n\n") 106 | (println "S3 downloads by file:") 107 | (print "\n\n") 108 | (pprint (frequencies (map :file s3-downloads))) 109 | (println ""))) ;; need this last println for some reason or else 110 | ;; the above doesn't print out using lein run... 111 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/core/test/eval.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.core.test.eval 2 | (:require [clojure.test :refer :all] 3 | [leiningen.core.eval :refer :all] 4 | [clojure.java.io :as io] 5 | [clojure.set :as set] 6 | [leiningen.core.classpath :as classpath] 7 | [leiningen.test.helper :as lthelper] 8 | [leiningen.core.project :as project]) 9 | (:import (java.io File))) 10 | 11 | (def project {:dependencies '[[org.clojure/clojure "1.3.0"]] 12 | :root "/tmp/lein-sample-project" 13 | :repositories project/default-repositories 14 | :target-path "/tmp/lein-sample-project/target" 15 | :source-paths ["/tmp/lein-sample-project/src"] 16 | :resource-paths ["/tmp/lein-sample-project/resources"] 17 | :test-paths ["/tmp/lein-sample-project/test"] 18 | :compile-path "/tmp/lein-sample-project/classes" 19 | :name "test" :group "test" :version "1.0.0"}) 20 | 21 | (deftest test-eval-in-project 22 | (doseq [where [:subprocess :leiningen :classloader]] 23 | (let [file (File/createTempFile "lein-eval-test" "")] 24 | (eval-in-project (assoc project :eval-in where 25 | :prep-tasks []) 26 | `(spit ~(.getPath file) (eval "{:foo \"bar\"}"))) 27 | (is (= "{:foo \"bar\"}" (slurp file))) 28 | (.delete file)))) 29 | 30 | (deftest test-classpath-directories-created 31 | (doseq [path (concat (:source-paths project) 32 | (:test-paths project) 33 | (:resource-paths project))] 34 | (let [file (File/createTempFile "lein-eval-test" "")] 35 | (eval-in-project project 36 | `(do (.mkdirs (clojure.java.io/file ~path)) 37 | (spit ~(str path "/foo.txt") "Hello World") 38 | (when-let [f# (clojure.java.io/resource "foo.txt")] 39 | (spit ~(.getPath file) (slurp f#)))) 40 | `(require 'clojure.java.io)) 41 | (is (= "Hello World" (slurp file))) 42 | (.delete (io/file (str path "/foo.txt"))) 43 | (.delete (io/file path)) 44 | (.delete file)))) 45 | 46 | (deftest test-jvm-opts 47 | (is (= ["-Dhello=\"guten tag\"" "-XX:+HeapDumpOnOutOfMemoryError"] 48 | (get-jvm-opts-from-env (str "-Dhello=\"guten tag\" " 49 | "-XX:+HeapDumpOnOutOfMemoryError"))))) 50 | 51 | (deftest test-file-encoding-in-jvm-args 52 | (is (contains? 53 | (set (#'leiningen.core.eval/get-jvm-args project)) 54 | (str "-Dfile.encoding=" (System/getProperty "file.encoding"))))) 55 | 56 | (deftest test-get-jvm-args-with-proxy-settings 57 | ;; Mock get-proxy-settings to return test values 58 | (with-redefs [classpath/get-proxy-settings 59 | (fn ([] {:host "foo.com" :port 8080}) 60 | ([https] {:host "secure-foo.com", :port 443}))] 61 | (let [args (set (shell-command project 'repl))] 62 | (is (and (contains? args "-Dhttp.proxyHost=foo.com") 63 | (contains? args "-Dhttp.proxyPort=8080") 64 | (contains? args "-Dhttps.proxyHost=secure-foo.com") 65 | (contains? args "-Dhttps.proxyPort=443")))))) 66 | 67 | (deftest test-java-agent 68 | (let [p {:java-agents '[[com.newrelic.agent.java/newrelic-agent "2.18.0" 69 | :bootclasspath true] 70 | [nodisassemble "0.1.2" :options "hello"]] 71 | :dependencies '[[slamhound "1.3.0"]] 72 | :repositories project/default-repositories} 73 | [newrelic newrelic-bootcp nodisassemble] (classpath-arg p)] 74 | (is (.endsWith newrelic (lthelper/fix-path-delimiters 75 | (str "/com/newrelic/agent/java/newrelic-agent" 76 | "/2.18.0/newrelic-agent-2.18.0.jar")))) 77 | (is (re-find #"bootclasspath.*newrelic.*jar" newrelic-bootcp)) 78 | (is (re-find #"-javaagent:.*nodisassemble-0.1.2.jar=hello" nodisassemble)))) 79 | 80 | (deftest test-sh-with-exit-code-successful-command 81 | (with-redefs [sh (constantly 0)] 82 | (is (= 0 (sh-with-exit-code "Shouldn't see me." "ls"))))) 83 | 84 | (deftest test-sh-with-exit-code-failed-command 85 | (with-redefs [sh (constantly 1)] 86 | (is (thrown-with-msg? Exception #"Should see me. ls exit code: 1" (sh-with-exit-code "Should see me" "ls"))))) 87 | -------------------------------------------------------------------------------- /src/leiningen/clean.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.clean 2 | "Remove all files from project's target-path." 3 | (:require [clojure.java.io :as io] 4 | [leiningen.core.utils :as utils] 5 | [leiningen.core.main :as main]) 6 | (:import [java.io IOException])) 7 | 8 | (defn real-directory? 9 | "Returns true if this file is a real directory, false if it is a symlink or a 10 | normal file." 11 | [f] 12 | (if (= :windows (utils/get-os)) 13 | (.isDirectory f) 14 | (and (.isDirectory f) 15 | (not (utils/symlink? f))))) 16 | 17 | (defn delete-file-recursively 18 | "Delete file f. If it's a directory, recursively delete all its contents. 19 | Raise an exception if any deletion fails unless silently is true." 20 | [f & [silently]] 21 | (let [f (io/file f)] 22 | (when (real-directory? f) 23 | (doseq [child (.listFiles f)] 24 | (delete-file-recursively child silently))) 25 | (.setWritable f true) 26 | (io/delete-file f silently))) 27 | 28 | (defn- ancestor? 29 | "Is a an ancestor of b?" 30 | [a b] 31 | (let [hypothetical-ancestor (.getCanonicalPath (io/file a)) 32 | hypothetical-descendant (.getCanonicalPath (io/file b))] 33 | (and (.startsWith hypothetical-descendant hypothetical-ancestor) 34 | (not (= hypothetical-descendant hypothetical-ancestor))))) 35 | 36 | (defn- protected-paths 37 | "Returns a set of leiningen project source directories and important files." 38 | [project] 39 | (let [root-dir (:root project)] 40 | (->> [:source-paths :java-source-paths :test-paths :resource-paths] 41 | (select-keys project) 42 | (mapcat val) 43 | (list* (io/file root-dir "doc") 44 | (io/file root-dir "project.clj")) 45 | (map io/file) 46 | (map #(.getCanonicalPath %)) 47 | set))) 48 | 49 | (defn- protected-path? 50 | "Is path one of the leiningen project files or directories (which we expect to 51 | be version controlled), or a descendant?" 52 | [project path] 53 | (let [protected-paths (protected-paths project)] 54 | (or (protected-paths (.getCanonicalPath (io/file path))) 55 | (some #(ancestor? % path) protected-paths)))) 56 | 57 | (defn- protect-clean-targets? 58 | "Returns the value of :protect in the metadata map for the :clean-targets 59 | value." 60 | [project] 61 | (-> project :clean-targets meta (get :protect true))) 62 | 63 | (defn- error-msg [& args] 64 | (apply str (concat args 65 | "\nCheck :clean-targets" 66 | " or override this behavior by adding metadata ->" 67 | "\n :clean-targets ^{:protect false} [...targets...]"))) 68 | 69 | (defn- sanity-check 70 | "Ensure that a clean-target string refers to a directory that is sensible to 71 | delete." 72 | [project clean-target] 73 | (when (and (string? clean-target) 74 | (protect-clean-targets? project)) 75 | (cond (not (ancestor? (:root project) clean-target)) 76 | (main/abort (error-msg "Deleting path outside of the project root [\"" 77 | clean-target "\"] is not allowed.")) 78 | (protected-path? project clean-target) 79 | (main/abort (error-msg "Deleting non-target project paths [\"" 80 | clean-target "%s\"] is not allowed."))))) 81 | 82 | (defn- with-parent-target-path 83 | "Assoc the :target-path sans the profile suffix, if any format 84 | specifier is detected in the raw :target-path" 85 | [project] 86 | (if-let [tp (->> project meta :without-profiles :target-path (re-find #"(.*?)/[^/]*%") second)] 87 | (assoc project :target-path (if (.isAbsolute (io/file tp)) 88 | tp 89 | (str (io/file (:root project) tp)))) 90 | project)) 91 | 92 | (defn clean 93 | "Removes all files from paths in clean-targets for a project" 94 | [project] 95 | (let [project (with-parent-target-path project)] 96 | (doseq [target-key (:clean-targets project)] 97 | (when-let [target (cond (vector? target-key) (get-in project target-key) 98 | (keyword? target-key) (target-key project) 99 | (string? target-key) target-key)] 100 | (doseq [f (flatten [target])] 101 | (sanity-check project f) 102 | (delete-file-recursively f :silently)))))) 103 | -------------------------------------------------------------------------------- /bin/lein-pkg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This variant of the lein script is meant for downstream packagers. 4 | # It has all the cross-platform stuff stripped out as well as the 5 | # logic for running from a source checkout and self-install/upgrading. 6 | 7 | export LEIN_VERSION="2.5.3-SNAPSHOT" 8 | 9 | if [ "$(whoami)" = "root" ] && [ "$LEIN_ROOT" = "" ]; then 10 | echo "WARNING: You're currently running as root; probably by accident." 11 | echo "Press control-C to abort or Enter to continue as root." 12 | echo "Set LEIN_ROOT to disable this warning." 13 | read _ 14 | fi 15 | 16 | # cd to the project root, if applicable 17 | NOT_FOUND=1 18 | ORIGINAL_PWD="$PWD" 19 | while [ ! -r "$PWD/project.clj" ] && [ "$PWD" != "/" ] && [ $NOT_FOUND -ne 0 ]; do 20 | cd .. 21 | if [ "$(dirname "$PWD")" = "/" ]; then 22 | NOT_FOUND=0 23 | cd "$ORIGINAL_PWD" 24 | fi 25 | done 26 | 27 | # Support $JAVA_OPTS for backwards-compatibility. 28 | JVM_OPTS=${JVM_OPTS:-"$JAVA_OPTS"} 29 | JAVA_CMD=${JAVA_CMD:-"java"} 30 | 31 | # User init 32 | export LEIN_HOME="${LEIN_HOME:-"$HOME/.lein"}" 33 | 34 | for f in "/etc/leinrc" "$LEIN_HOME/leinrc" ".leinrc"; do 35 | if [ -e "$f" ]; then 36 | source "$f" 37 | fi 38 | done 39 | 40 | grep -E -q '^\s*:eval-in\s+:classloader\s*$' project.clj 2> /dev/null && 41 | LEIN_JVM_OPTS="${LEIN_JVM_OPTS:-'-Xms64m -Xmx512m'}" 42 | 43 | # If you're not using an uberjar you'll need to list each dependency 44 | # and add them individually to the classpath/bootclasspath as well. 45 | 46 | LEIN_JAR=/usr/share/java/leiningen-$LEIN_VERSION-standalone.jar 47 | 48 | # Do not use installed leiningen jar during self-compilation 49 | if ! { [ "$1" = "compile" ] && 50 | grep -qsE 'defproject leiningen[[:space:]]+"[[:digit:].]+"' \ 51 | project.clj ;}; then 52 | CLASSPATH="$CLASSPATH":"$LEIN_JAR" 53 | BOOTCLASSPATH="-Xbootclasspath/a:$LEIN_JAR" 54 | fi 55 | 56 | # apply context specific CLASSPATH entries 57 | if [ -f .lein-classpath ]; then 58 | CLASSPATH="$(cat .lein-classpath):$CLASSPATH" 59 | fi 60 | 61 | if [ -n "$DEBUG" ]; then 62 | echo "Leiningen's classpath: $CLASSPATH" 63 | fi 64 | 65 | # Which Java? 66 | 67 | export JAVA_CMD="${JAVA_CMD:-"java"}" 68 | export LEIN_JAVA_CMD="${LEIN_JAVA_CMD:-$JAVA_CMD}" 69 | 70 | if [[ "$(basename "$LEIN_JAVA_CMD")" == *drip* ]]; then 71 | export DRIP_INIT="$(printf -- '-e\n(require (quote leiningen.repl))')" 72 | fi 73 | 74 | # Support $JAVA_OPTS for backwards-compatibility. 75 | export JVM_OPTS="${JVM_OPTS:-"$JAVA_OPTS"}" 76 | 77 | if ([ "$LEIN_FAST_TRAMPOLINE" != "" ] || [ -r .lein-fast-trampoline ]) && 78 | [ -r project.clj ]; then 79 | INPUTS="$* $(cat project.clj) $(test -f "$LEIN_HOME/profiles.clj" && cat "$LEIN_HOME/profiles.clj")" 80 | INPUT_CHECKSUM=$(echo "$INPUTS" | shasum - | cut -f 1 -d " ") 81 | # Just don't change :target-path in project.clj, mkay? 82 | TRAMPOLINE_FILE="target/trampolines/$INPUT_CHECKSUM" 83 | else 84 | TRAMPOLINE_FILE="/tmp/lein-trampoline-$$" 85 | trap "rm -f $TRAMPOLINE_FILE" EXIT 86 | fi 87 | 88 | if [ "$1" = "upgrade" ]; then 89 | echo "This version of Leiningen was installed with a package manager, but" 90 | echo "the upgrade task is intended for manual installs. Please use your" 91 | echo "package manager's built-in upgrade facilities instead." 92 | exit 1 93 | fi 94 | 95 | if [ "$INPUT_CHECKSUM" != "" ] && [ -r "$TRAMPOLINE_FILE" ]; then 96 | if [ -n "$DEBUG" ]; then 97 | echo "Fast trampoline with $TRAMPOLINE_FILE." 98 | fi 99 | exec sh -c "exec $(cat $TRAMPOLINE_FILE)" 100 | else 101 | export TRAMPOLINE_FILE 102 | "$LEIN_JAVA_CMD" \ 103 | -client -XX:+TieredCompilation \ 104 | "${BOOTCLASSPATH[@]}" \ 105 | -Dfile.encoding=UTF-8 \ 106 | -Dmaven.wagon.http.ssl.easy=false \ 107 | $LEIN_JVM_OPTS \ 108 | -Dleiningen.original.pwd="$ORIGINAL_PWD" \ 109 | -Dleiningen.script="$0" \ 110 | -classpath "$CLASSPATH" \ 111 | clojure.main -m leiningen.core.main "$@" 112 | 113 | EXIT_CODE=$? 114 | 115 | if [ -r "$TRAMPOLINE_FILE" ] && [ "$LEIN_TRAMPOLINE_WARMUP" = "" ]; then 116 | TRAMPOLINE="$(cat $TRAMPOLINE_FILE)" 117 | if [ "$INPUT_CHECKSUM" = "" ]; then 118 | rm $TRAMPOLINE_FILE 119 | fi 120 | exec sh -c "exec $TRAMPOLINE" 121 | else 122 | exit $EXIT_CODE 123 | fi 124 | fi 125 | -------------------------------------------------------------------------------- /leiningen-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | leiningen-core 5 | leiningen-core 6 | jar 7 | 2.5.2 8 | leiningen-core 9 | Library for core functionality of Leiningen. 10 | https://github.com/technomancy/leiningen 11 | 12 | 13 | Eclipse Public License 14 | http://www.eclipse.org/legal/epl-v10.html 15 | 16 | 17 | 18 | scm:git:git://github.com/technomancy/leiningen.git 19 | scm:git:ssh://git@github.com/technomancy/leiningen.git 20 | 6c7de27414532a6727e183392221758e20c65ba7 21 | 22 | https://github.com/technomancy/leiningen 23 | 24 | 25 | src 26 | test 27 | 28 | 29 | resources 30 | 31 | 32 | 33 | 34 | dev-resources 35 | 36 | 37 | resources 38 | 39 | 40 | target 41 | target/classes 42 | 43 | 44 | 45 | 46 | central 47 | https://repo1.maven.org/maven2/ 48 | 49 | false 50 | 51 | 52 | true 53 | 54 | 55 | 56 | clojars 57 | https://clojars.org/repo/ 58 | 59 | true 60 | 61 | 62 | true 63 | 64 | 65 | 66 | 67 | 68 | org.clojure 69 | clojure 70 | 1.7.0 71 | 72 | 73 | bultitude 74 | bultitude 75 | 0.2.8 76 | 77 | 78 | classlojure 79 | classlojure 80 | 0.6.6 81 | 82 | 83 | robert 84 | hooke 85 | 1.3.0 86 | 87 | 88 | com.cemerick 89 | pomegranate 90 | 0.3.0 91 | 92 | 93 | org.apache.maven.wagon 94 | wagon-http 95 | 2.9 96 | 97 | 98 | com.hypirion 99 | io 100 | 0.3.1 101 | 102 | 103 | pedantic 104 | pedantic 105 | 0.2.0 106 | 107 | 108 | org.clojure 109 | tools.nrepl 110 | 0.2.6 111 | 112 | 113 | org.clojure 114 | clojure 115 | 116 | 117 | test 118 | 119 | 120 | clojure-complete 121 | clojure-complete 122 | 0.2.3 123 | 124 | 125 | org.clojure 126 | clojure 127 | 128 | 129 | test 130 | 131 | 132 | 133 | 134 | 138 | -------------------------------------------------------------------------------- /doc/TEMPLATES.md: -------------------------------------------------------------------------------- 1 | # Writing Templates 2 | 3 | Suppose you've written a fabulously popular library, used the world 4 | over by adoring fans. For the purposes of this document, let's say 5 | this library is called "liquid-cool". If using liquid-cool takes a bit 6 | of setup, or if you'd just like to give your users a little guidance 7 | on how one might best create a new project which uses liquid-cool, you 8 | might want to provide a template for it (just like how `lein` already 9 | provides built-in templates for "app", "plugin", and so on). 10 | 11 | Let's assume your library's project dir is `~/dev/liquid-cool`. Create 12 | a template for it like so: 13 | 14 | cd ~/dev 15 | lein new template liquid-cool --to-dir liquid-cool-template 16 | 17 | Your new template would look like: 18 | 19 | liquid-cool-template 20 | ├── LICENSE 21 | ├── project.clj 22 | ├── README.md 23 | └── src 24 | └── leiningen 25 | └── new 26 | ├── liquid_cool 27 | │   └── foo.clj 28 | └── liquid_cool.clj 29 | 30 | Note that you'll now have a new and separate project named 31 | "liquid-cool-template". It will have a group-id of "liquid-cool", and 32 | an artifact-id of "lein-template". 33 | 34 | > All lein templates have an artifact-id of "lein-template", and are 35 | > differentiated by their group-id, which always should match the 36 | > project for which they provide a template. 37 | 38 | ## Structure 39 | 40 | The files that your template will provide to users are in 41 | `src/leiningen/new/liquid_cool`. lein-newnew starts you off with just 42 | one, named "foo.clj". You can see it referenced in 43 | `src/leiningen/new/liquid_cool.clj`, right underneath the 44 | `->files data` line. 45 | 46 | You can delete `foo.clj` if you like (and its corresponding line in 47 | `liquid_cool.clj`), and start populating that 48 | `src/leiningen/new/liquid_cool` directory with the files you wish to be 49 | part of your template. For everything you add, make sure the 50 | `liquid_cool.clj` file receives corresponding entries in that `->files` 51 | call. For examples to follow, have a look inside [the \*.clj files for 52 | the built-in 53 | templates](https://github.com/technomancy/leiningen/tree/stable/resources/leiningen/new). 54 | 55 | While developing a template, if you're in the template project 56 | lein-newnew will pick it up and you'll be able to test it. However, if 57 | you want to use it on your system without putting it on clojars, just 58 | `lein install` your template. 59 | 60 | ## Templating System 61 | 62 | lein-newnew uses [stencil][] for templating, which implements the 63 | language-agnostic templating system [Mustache][]. All the available tag types 64 | can be found in the [Mustache manual][mustache-manual]; we will only go through 65 | the most common tag type here. 66 | 67 | Suppose we want to add in a standard markdown readme file where the input name 68 | is the main header of the file. To be able to do so, we must do two things: 69 | Ensure that the input name is contained within the `data` mapped to the key X, 70 | and that we have a template file which looks up the key X by wrapping it in 71 | double mustaches like so: `{{X}}`. As for our input name, `data` already 72 | contains the line `:name name`, which means we can lookup the input name by 73 | writing `{{name}}` in the template file. To try it out, save the following 74 | contents in the file `src/leiningen/new/liquid_cool/README.md`: 75 | 76 | ```markdown 77 | # {{name}} 78 | 79 | This is our readme! 80 | ``` 81 | 82 | And add the following line right underneath the `->files data` line: 83 | 84 | ```clj 85 | ["README.md" (render "README.md" data)] 86 | ``` 87 | 88 | Now, if we for instance say `lein new liquid-cool liquid-cool-app`, the newly 89 | generated project will contain a file named `README.md` where the header is 90 | `liquid-cool-app`. 91 | 92 | [stencil]: https://github.com/davidsantiago/stencil 93 | [Mustache]: http://mustache.github.io/ 94 | [mustache-manual]: http://mustache.github.io/mustache.5.html 95 | 96 | ## Distributing your Template 97 | 98 | Templates are just maven artifacts. Particularly, they need only be on 99 | the classpath when `lein new` is called. So, as a side-effect, you 100 | can just put your templates in a jar and toss them on clojars and have 101 | people install them like normal Leiningen plugins. 102 | 103 | In Leiningen 2.x, templates get dynamically fetched if they're not 104 | found. So for instance `lein new heroku myproject` will find the 105 | latest version of the `heroku/lein-template` project from Clojars and 106 | use that. 107 | 108 | Users of Leiningen 1.x (1.6.2 or later) can also use the template if 109 | they install the `lein-newnew` plugin: 110 | 111 | $ lein plugin install lein-newnew 0.3.6 112 | $ lein new foo 113 | $ lein new plugin lein-foo 114 | -------------------------------------------------------------------------------- /leiningen-core/test/leiningen/core/test/classpath.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.core.test.classpath 2 | (:use [clojure.test] 3 | [leiningen.core.classpath]) 4 | (:require [clojure.java.io :as io] 5 | [clojure.set :as set] 6 | [leiningen.core.user :as user] 7 | [leiningen.test.helper :as lthelper] 8 | [leiningen.core.project :as project])) 9 | 10 | (use-fixtures :once 11 | (fn [f] 12 | ;; Can't have user-level profiles interfering! 13 | (with-redefs [user/profiles (constantly {}) 14 | user/credentials (constantly nil)] 15 | (f)))) 16 | 17 | (defn m2-file [f] 18 | (io/file (System/getProperty "user.home") ".m2" "repository" f)) 19 | 20 | (def project {:dependencies '[[org.clojure/clojure "1.3.0"] 21 | [ring/ring-core "1.0.0" 22 | :exclusions [commons-codec]]] 23 | :checkout-deps-shares [:source-paths :resource-paths 24 | :compile-path #(lthelper/pathify (str (:root %) "/foo"))] 25 | :repositories project/default-repositories 26 | :root "/tmp/lein-sample-project" 27 | :target-path "/tmp/lein-sample-project/target" 28 | :source-paths ["/tmp/lein-sample-project/src"] 29 | :resource-paths ["/tmp/lein-sample-project/resources"] 30 | :test-paths ["/tmp/lein-sample-project/test"]}) 31 | 32 | (deftest test-resolve-deps 33 | (doseq [f (reverse (file-seq (io/file (:root project))))] 34 | (when (.exists f) (io/delete-file f))) 35 | (is (= #{(m2-file "org/clojure/clojure/1.3.0/clojure-1.3.0.jar") 36 | (m2-file "commons-io/commons-io/1.4/commons-io-1.4.jar") 37 | (m2-file "javax/servlet/servlet-api/2.5/servlet-api-2.5.jar") 38 | (m2-file "ring/ring-core/1.0.0/ring-core-1.0.0.jar") 39 | (m2-file (str "commons-fileupload/commons-fileupload/1.2.1/" 40 | "commons-fileupload-1.2.1.jar"))} 41 | (set (resolve-dependencies :dependencies project))))) 42 | 43 | (deftest test-dependency-hierarchy 44 | (doseq [f (reverse (file-seq (io/file (:root project))))] 45 | (when (.exists f) (io/delete-file f))) 46 | (is (= '{[org.clojure/clojure "1.3.0"] nil 47 | [ring/ring-core "1.0.0" 48 | :exclusions [[commons-codec]]] 49 | {[commons-fileupload "1.2.1"] nil 50 | [commons-io "1.4"] nil 51 | [javax.servlet/servlet-api "2.5"] nil}} 52 | (dependency-hierarchy :dependencies project)))) 53 | 54 | (def directories 55 | (vec (map lthelper/pathify 56 | ["/tmp/lein-sample-project/test" 57 | "/tmp/lein-sample-project/src" 58 | "/tmp/lein-sample-project/resources"]))) 59 | 60 | (def libs 61 | #{(str (m2-file "commons-io/commons-io/1.4/commons-io-1.4.jar")) 62 | (str (m2-file "javax/servlet/servlet-api/2.5/servlet-api-2.5.jar")) 63 | (str (m2-file "ring/ring-core/1.0.0/ring-core-1.0.0.jar")) 64 | (str (m2-file "commons-fileupload/commons-fileupload/1.2.1/commons-fileupload-1.2.1.jar")) 65 | (str (m2-file "org/clojure/clojure/1.3.0/clojure-1.3.0.jar"))}) 66 | 67 | (deftest test-classpath 68 | (let [classpath (get-classpath project)] 69 | (is (= (set classpath) (into libs directories))) 70 | (is (= directories (take 3 classpath))) 71 | (is (= libs (set (drop 3 classpath)))))) 72 | 73 | (deftest test-checkout-deps 74 | (let [d1 (io/file (:root project) "checkouts" "d1")] 75 | (try 76 | (.mkdirs d1) 77 | (spit (io/file d1 "project.clj") 78 | (pr-str '(defproject hello "1.0"))) 79 | (is (= (for [path ["src" "dev-resources" "resources" 80 | "target/classes" "foo"]] 81 | (lthelper/pathify (format "/tmp/lein-sample-project/checkouts/d1/%s" path))) 82 | (#'leiningen.core.classpath/checkout-deps-paths project))) 83 | (finally 84 | ;; can't recur from finally 85 | (dorun (map #(.delete %) (reverse (file-seq d1)))))))) 86 | 87 | (deftest test-add-auth 88 | (with-redefs [user/credentials (constantly 89 | {"https://sekrit.info/repo" 90 | {:username "milgrim" :password "reindur"}}) 91 | user/profiles (constantly {:auth {:repository-auth 92 | {#"clojars" 93 | {:username "flynn" 94 | :password "flotilla"}}}})] 95 | (is (= [["clojars" {:url "http://clojars.org/repo" 96 | :username "flynn" :password "flotilla"}] 97 | ["sonatype" {:url "https://oss.sonatype.org/"}] 98 | ["internal" {:password "reindur" :username "milgrim" 99 | :url "https://sekrit.info/repo"}]] 100 | (map add-repo-auth 101 | [["clojars" {:url "http://clojars.org/repo"}] 102 | ["sonatype" {:url "https://oss.sonatype.org/"}] 103 | ["internal" {:url "https://sekrit.info/repo" 104 | :username :gpg :password :gpg}]]))))) 105 | -------------------------------------------------------------------------------- /test/leiningen/test/helper.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.helper 2 | (:require [leiningen.core.project :as project] 3 | [leiningen.core.user :as user] 4 | [leiningen.core.test.helper :as helper] 5 | [clojure.java.io :as io]) 6 | (:import (java.io ByteArrayOutputStream PrintStream FileDescriptor 7 | FileOutputStream))) 8 | 9 | ;; TODO: fix 10 | (def local-repo (io/file (System/getProperty "user.home") ".m2" "repository")) 11 | 12 | (def tmp-dir (System/getProperty "java.io.tmpdir")) 13 | 14 | (defn m2-dir [n v] 15 | (io/file local-repo 16 | (if (string? n) n (or (namespace n) (name n))) (name n) v)) 17 | 18 | (defn- read-test-project [name] 19 | (with-redefs [user/profiles (constantly {})] 20 | (let [project (project/read (format "test_projects/%s/project.clj" name))] 21 | (project/init-project 22 | (project/project-with-profiles-meta 23 | project (merge @project/default-profiles (:profiles project))))))) 24 | 25 | (def with-resources-project (read-test-project "with-resources")) 26 | 27 | (def with-aliases-project (read-test-project "with-aliases")) 28 | 29 | (def with-aliases2-project (read-test-project "with-aliases2")) 30 | 31 | (def sample-project (read-test-project "sample")) 32 | 33 | (def sample-failing-project (read-test-project "sample-failing")) 34 | 35 | (def sample-no-aot-project (read-test-project "sample-no-aot")) 36 | 37 | (def sample-profile-meta-project (read-test-project "sample-profile-meta")) 38 | 39 | (def sample-reader-cond-project (read-test-project "sample-reader-cond")) 40 | 41 | (def tricky-name-project (read-test-project "tricky-name")) 42 | 43 | (def native-project (read-test-project "native")) 44 | 45 | (def provided-project (read-test-project "provided")) 46 | 47 | (def uberjar-merging-project (read-test-project "uberjar-merging")) 48 | 49 | (def overlapped-sourcepaths-project (read-test-project "overlapped-sourcepaths")) 50 | 51 | (def more-gen-classes-project (read-test-project "more-gen-classes")) 52 | 53 | (def bad-require-project (read-test-project "bad-require")) 54 | 55 | (def java-main-project (read-test-project "java-main")) 56 | 57 | (def file-not-found-thrower-project (read-test-project "file-not-found-thrower")) 58 | 59 | (def jvm-opts-project (read-test-project "jvm-opts")) 60 | 61 | (def with-classifiers-project (read-test-project "with-classifiers")) 62 | 63 | (defn abort-msg 64 | "Catches main/abort thrown by calling f on its args and returns its error 65 | message." 66 | [f & args] 67 | (apply helper/abort-msg f args)) 68 | 69 | ;; grumble, grumble; why didn't this make it into clojure.java.io? 70 | (defn delete-file-recursively 71 | "Delete file f. If it's a directory, recursively delete all its contents. 72 | Raise an exception if any deletion fails unless silently is true." 73 | [f & [silently]] 74 | (System/gc) ; This sometimes helps release files for deletion on windows. 75 | (let [f (io/file f)] 76 | (if (.isDirectory f) 77 | (doseq [child (.listFiles f)] 78 | (delete-file-recursively child silently))) 79 | (io/delete-file f silently))) 80 | 81 | 82 | (defn fix-path-delimiters [input-str] 83 | (clojure.string/replace input-str "/" java.io.File/separator)) 84 | 85 | ;; So paths would work under Windows too, which adds a drive letter and changes 86 | ;; the path separator. 87 | (defn pathify 88 | "Converts paths to absolute paths. Will throw if not, because if the path is 89 | not absolute, then .getAbsolutePath will resolve them relative to current 90 | directory." [in-str-or-file] 91 | (cond (or 92 | (nil? in-str-or-file) 93 | (not (or 94 | (.startsWith in-str-or-file "/") 95 | (and 96 | (>= (.length in-str-or-file) 3) 97 | (= ":\\" (.substring in-str-or-file 1 3)))))) 98 | (throw (RuntimeException. (str "Bad usage, passed: `" in-str-or-file "`."))) 99 | :else 100 | (.getAbsolutePath (io/as-file in-str-or-file)))) 101 | 102 | (defn entries 103 | "Returns a lazy seq of all the entries in a zipfile." 104 | [zipfile] 105 | (enumeration-seq (.entries zipfile))) 106 | 107 | (defn walkzip 108 | "Applies f to all ZipEntries in the ZipFile filename and returns the result as 109 | a vector." 110 | [filename f] 111 | (with-open [z (java.util.zip.ZipFile. filename)] 112 | (reduce #(conj %1 (f %2)) [] (entries z)))) 113 | 114 | (defn noerr-fixture [f] 115 | (binding [*err* (java.io.StringWriter.)] 116 | (f))) 117 | 118 | (defmacro with-system-out-str 119 | "Like with-out-str, but for System/out." 120 | [& body] 121 | `(try (let [o# (ByteArrayOutputStream.)] 122 | (System/setOut (PrintStream. o#)) 123 | ~@body 124 | (.toString o#)) 125 | (finally 126 | (System/setOut 127 | (-> FileDescriptor/out FileOutputStream. PrintStream.))))) 128 | 129 | (defmacro with-system-err-str 130 | "Like with-out-str, but for System/err." 131 | [& body] 132 | `(try (let [o# (ByteArrayOutputStream.)] 133 | (System/setErr (PrintStream. o#)) 134 | ~@body 135 | (.toString o#)) 136 | (finally 137 | (System/setErr 138 | (-> FileDescriptor/err FileOutputStream. PrintStream.))))) 139 | -------------------------------------------------------------------------------- /src/leiningen/deps.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.deps 2 | "Download all dependencies." 3 | (:require [leiningen.core.classpath :as classpath] 4 | [leiningen.core.main :as main] 5 | [leiningen.core.eval :as eval] 6 | [leiningen.core.project :as project] 7 | [leiningen.core.user :as user] 8 | [leiningen.core.utils :as utils] 9 | [cemerick.pomegranate.aether :as aether] 10 | [clojure.pprint :as pp] 11 | [clojure.java.io :as io]) 12 | (:import (org.sonatype.aether.resolution DependencyResolutionException))) 13 | 14 | (defn- walk-deps 15 | ([deps f level] 16 | (doseq [[dep subdeps] deps] 17 | (f dep level) 18 | (when subdeps 19 | (walk-deps subdeps f (inc level))))) 20 | ([deps f] 21 | (walk-deps deps f 0))) 22 | 23 | (defn- print-dep [dep level] 24 | (println (apply str (repeat (* 2 level) \space)) (pr-str dep))) 25 | 26 | 27 | 28 | (declare check-signature) 29 | 30 | (defn- fetch-key [signature err] 31 | (if (or (re-find #"Can't check signature: public key not found" err) 32 | (re-find #"Can't check signature: No public key" err)) 33 | (let [key (second (re-find #"using \w+ key ID (.+)" err)) 34 | {:keys [exit]} (user/gpg "--recv-keys" "--" key)] 35 | (if (zero? exit) 36 | (check-signature signature) 37 | :no-key)) 38 | :bad-signature)) 39 | 40 | (defn- check-signature [signature] 41 | (let [{:keys [err exit]} (user/gpg "--verify" "--" (str signature))] 42 | (if (zero? exit) 43 | :signed ; TODO distinguish between signed and trusted 44 | (fetch-key signature err)))) 45 | 46 | (defn- get-signature [project dep] 47 | (let [dep-map (assoc (apply hash-map (drop 2 dep)) 48 | ;; TODO: check pom signature too 49 | :extension "jar.asc") 50 | dep (into (vec (take 2 dep)) (apply concat dep-map))] 51 | (try (->> (aether/resolve-dependencies 52 | :repositories (:repositories project) 53 | :mirrors (:mirrors project) 54 | :coordinates [dep]) 55 | (aether/dependency-files) 56 | (filter #(.endsWith (.getName %) ".asc")) 57 | (first)) 58 | (catch DependencyResolutionException _)))) 59 | 60 | (defn- verify [project dep _] 61 | (let [signature (get-signature project dep) 62 | status (if signature 63 | (check-signature signature) 64 | :unsigned)] 65 | ;; TODO: support successful exit code only on fully-signed deps 66 | (println status (pr-str dep)))) 67 | 68 | 69 | (def tree-command 70 | "A mapping from the tree-command to the dependency key it should print a tree 71 | for." 72 | {":tree" :dependencies 73 | ":plugin-tree" :plugins}) 74 | 75 | 76 | 77 | (defn print-implicits [project type] 78 | (when-let [implicits (seq (filter utils/require-resolve 79 | (project/plugin-vars project type)))] 80 | (println (str "Implicit " (name type) ":")) 81 | (doseq [i implicits] (println " " i)))) 82 | 83 | 84 | 85 | (defn deps 86 | "Show details about dependencies. 87 | 88 | lein deps :tree 89 | 90 | Show the full dependency tree for the current project. Each dependency is only 91 | shown once within a tree. 92 | 93 | lein deps :plugin-tree 94 | 95 | Show the full dependency tree for the plugins in the current project. 96 | 97 | lein deps :verify 98 | 99 | Check signatures of each dependency. ALPHA: subject to change. 100 | 101 | lein deps :implicits 102 | 103 | List the implicit middleware and hooks that will be activated by the current 104 | set of plugins. Useful for debugging unexplained behaviour. 105 | 106 | lein deps 107 | 108 | Force Leiningen to download the dependencies it needs. This usage is 109 | deprecated as it should happen automatically on demand. 110 | 111 | Normally snapshot dependencies will be checked once every 24 hours; to 112 | force them to be updated, use `lein -U $TASK`." 113 | ([project] 114 | (deps project nil)) 115 | ([project command] 116 | (try 117 | (cond (= ":implicits" command) 118 | (do (print-implicits project :middleware) 119 | (print-implicits project :hooks)) 120 | (tree-command command) 121 | (let [project (project/merge-profiles 122 | project 123 | [{:pedantic? (quote ^:displace warn)}]) 124 | hierarchy (classpath/dependency-hierarchy 125 | (tree-command command) 126 | project)] 127 | (walk-deps hierarchy print-dep)) 128 | (= command ":verify") 129 | (if (user/gpg-available?) 130 | (walk-deps (classpath/dependency-hierarchy :dependencies project) 131 | (partial verify project)) 132 | (main/abort (str "Could not verify - gpg not available.\n" 133 | "See `lein help gpg` for how to setup gpg."))) 134 | :else (classpath/resolve-dependencies :dependencies project)) 135 | (catch DependencyResolutionException e 136 | (main/abort (.getMessage e)))))) 137 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Leiningen is the most widely-contributed-to Clojure project. We 4 | welcome potential contributors and do our best to try to make it easy 5 | to help out. Contributors who have had a single patch accepted may 6 | request commit rights as well as a free 7 | [sticker](http://twitpic.com/2e33r1). 8 | 9 | Discussion occurs both in the 10 | [#leiningen channel on Freenode](irc://chat.freenode.net#leiningen) 11 | and on the [mailing list](http://librelist.com/browser/leiningen/). To 12 | join the mailing list, simply email `leiningen@librelist.org`; your 13 | first message to that address will subscribe you without being posted. 14 | 15 | Please report issues on the 16 | [GitHub issue tracker](https://github.com/technomancy/leiningen/issues) 17 | or the mailing list. Sending bug reports to personal email addresses 18 | is inappropriate. Simpler issues appropriate for first-time 19 | contributors looking to help out are tagged "newbie". 20 | 21 | Patches are preferred as patches from `git format-patch` on the 22 | mailing list or as GitHub pull requests. Please use topic branches 23 | when sending pull requests rather than committing directly to master 24 | in order to minimize unnecessary merge commit clutter. Direct pull 25 | requests towards the master branch, not the stable branch. 26 | 27 | Leiningen is [mirrored at Gitorious](https://gitorious.org/leiningen/leiningen) 28 | and [tested on Travis](http://travis-ci.org/technomancy/leiningen). 29 | 30 | ## Codebase 31 | 32 | The definitions of the various tasks reside in `src/leiningen` in the 33 | top-level project. The underlying mechanisms for things like 34 | `project.clj` parsing, classpath calculation, and subprocess launching 35 | are implemented inside the `leiningen-core` subproject. 36 | 37 | See the 38 | [readme for the leiningen-core library](https://github.com/technomancy/leiningen/blob/master/leiningen-core/README.md) 39 | and `doc/PLUGINS.md` for more details on how Leiningen's codebase is 40 | structured. 41 | 42 | Try to be aware of the conventions in the existing code, except the 43 | one where we don't write tests. Make a reasonable attempt to avoid 44 | lines longer than 80 columns or function bodies longer than 20 45 | lines. Don't use `when` unless it's for side-effects. Don't introduce 46 | new protocols. Use `^:internal` metadata to mark vars which can't be 47 | private but shouldn't be considered part of the public API. 48 | 49 | ## Bootstrapping 50 | 51 | You don't need to "build" Leiningen per se, but when you're developing on a 52 | checkout you will need to get its dependencies in place and compile some of the 53 | tasks. Assuming you are in Leiningen's project root, you can do that like this: 54 | 55 | ```bash 56 | $ cd leiningen-core 57 | $ lein bootstrap 58 | $ cd .. 59 | $ bin/lein compile 60 | ## or the bat file on Windows. 61 | ``` 62 | 63 | The `lein` command is a stable release of Leiningen on your `$PATH` – preferably 64 | the newest one. If you don't have a stable `lein` installed, simply check out 65 | the `stable` branch and copy `bin/lein` to somewhere on your `$PATH`, then 66 | switch your branch back. 67 | 68 | If you want to use your development copy for everyday usage, symlink 69 | `bin/lein` to somewhere on your `$PATH`. You'll want to rename your 70 | stable installation to keep them from interfering; typically you can 71 | name that `lein2` or `lein-stable`. 72 | 73 | When dependencies in Leiningen change, you may have to do `rm .lein-classpath` 74 | in the project root, though in most cases this will be done automatically. If 75 | dependencies in leiningen-core change, you have to redo the `lein bootstrap` 76 | step mentioned earlier. 77 | 78 | Using `bin/lein` alone from the master branch without a full checkout 79 | is not supported. If you want to just grab a shell script to work 80 | with, use the `stable` branch. 81 | 82 | ### Uberjar from Master 83 | 84 | Since a development version is not uberjared, it can be rather slow compared to 85 | a stable release. If this is annoying and you depend on a recent fix or 86 | enhancement, you can build an uberjar from master as follows: 87 | 88 | ```bash 89 | $ bin/lein uberjar 90 | # The last line should contain the location of the standalone. 91 | $ cp target/leiningen-2.5.2-SNAPSHOT-standalone.jar $HOME/.lein/self-installs 92 | $ cp bin/lein $HOME/bin/lein-master 93 | ``` 94 | 95 | Here, 2.5.2-SNAPSHOT is the version we've built, and we have `$HOME/bin` on our 96 | $PATH. 97 | 98 | Note that changes on master won't be visible in the uberjared version unless you 99 | overwrite both the lein script and a freshly created uberjar. 100 | 101 | ## Tests 102 | 103 | Before you're asking for a pull request, we would be very happy if you ensure 104 | that the changes you've done doesn't break any of the existing test cases. While 105 | there is a test suite, it's not terribly thorough, so don't put too much trust 106 | in it. Patches which add test coverage for the functionality they change are 107 | especially welcome. 108 | 109 | To run the test cases, run `bin/lein test` in the root directory: This will test 110 | both `leiningen-core` and `leiningen` itself. Do not attempt to run the tests 111 | with a stable version of Leiningen, as the namespaces conflict and you may end 112 | up with errors during the test run. 113 | -------------------------------------------------------------------------------- /leiningen-core/src/leiningen/core/ssl.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.core.ssl 2 | (:require [cemerick.pomegranate.aether :as aether] 3 | [clojure.java.io :as io] 4 | [leiningen.core.user :as user]) 5 | (:import java.security.KeyStore 6 | java.security.KeyStore$TrustedCertificateEntry 7 | java.security.Security 8 | java.security.cert.CertificateFactory 9 | javax.net.ssl.KeyManagerFactory 10 | javax.net.ssl.SSLContext 11 | javax.net.ssl.TrustManagerFactory 12 | javax.net.ssl.X509TrustManager 13 | java.io.FileInputStream 14 | org.apache.http.config.RegistryBuilder 15 | org.apache.http.conn.socket.PlainConnectionSocketFactory 16 | org.apache.http.conn.ssl.BrowserCompatHostnameVerifier 17 | org.apache.http.conn.ssl.SSLConnectionSocketFactory 18 | org.apache.http.impl.conn.PoolingHttpClientConnectionManager 19 | org.apache.maven.wagon.providers.http.HttpWagon)) 20 | 21 | (defn ^TrustManagerFactory trust-manager-factory [^KeyStore keystore] 22 | (doto (TrustManagerFactory/getInstance "PKIX") 23 | (.init keystore))) 24 | 25 | (defn default-trust-managers [] 26 | (let [tmf (trust-manager-factory nil) 27 | tms (.getTrustManagers tmf)] 28 | (filter #(instance? X509TrustManager %) tms))) 29 | 30 | (defn key-manager-props [] 31 | (let [read #(java.lang.System/getProperty %)] 32 | (merge {:file (read "javax.net.ssl.keyStore") 33 | :type (read "javax.net.ssl.keyStoreType") 34 | :provider (read "javax.net.ssl.keyStoreProvider") 35 | :password (read "javax.net.ssl.keyStorePassword")} 36 | (-> (user/profiles) :user :key-manager-properties)))) 37 | 38 | (defn key-manager-factory [{:keys [file type provider password]}] 39 | (let [type (or type (KeyStore/getDefaultType)) 40 | fis (if-not (empty? file) (FileInputStream. file)) 41 | pwd (and password (.toCharArray password)) 42 | store (if provider 43 | (KeyStore/getInstance type provider) 44 | (KeyStore/getInstance type))] 45 | (.load store fis pwd) 46 | (when fis (.close fis)) 47 | (doto (KeyManagerFactory/getInstance 48 | (KeyManagerFactory/getDefaultAlgorithm)) 49 | (.init store pwd)))) 50 | 51 | (defn default-trusted-certs 52 | "Lists the CA certificates trusted by the JVM." 53 | [] 54 | (mapcat #(.getAcceptedIssuers %) (default-trust-managers))) 55 | 56 | (defn read-certs 57 | "Read one or more X.509 certificates in DER or PEM format." 58 | [f] 59 | (let [cf (CertificateFactory/getInstance "X.509") 60 | in (io/input-stream (or (io/resource f) (io/file f)))] 61 | (.generateCertificates cf in))) 62 | 63 | (defn make-keystore 64 | "Construct a KeyStore that trusts a collection of certificates." 65 | [certs] 66 | (let [ks (KeyStore/getInstance "jks")] 67 | (.load ks nil nil) 68 | (doseq [[i cert] (map vector (range) certs)] 69 | (.setEntry ks (str i) (KeyStore$TrustedCertificateEntry. cert) nil)) 70 | ks)) 71 | 72 | ;; TODO: honor settings from project.clj, not just user profile 73 | (defn make-sslcontext 74 | "Construct an SSLContext that trusts a collection of certificates." 75 | [trusted-certs] 76 | (let [ks (make-keystore trusted-certs) 77 | kmf (key-manager-factory (key-manager-props)) 78 | tmf (trust-manager-factory ks)] 79 | (doto (SSLContext/getInstance "TLS") 80 | (.init (.getKeyManagers kmf) (.getTrustManagers tmf) nil)))) 81 | 82 | (alter-var-root #'make-sslcontext memoize) 83 | 84 | (defn https-registry 85 | "Constructs a registry map that uses a given SSLContext for https." 86 | [context] 87 | (let [factory (SSLConnectionSocketFactory. context (BrowserCompatHostnameVerifier.))] 88 | {"https" factory 89 | "http" PlainConnectionSocketFactory/INSTANCE})) 90 | 91 | (defn ^:deprecated https-scheme 92 | "Constructs a registry map that uses a given SSLContext for https. 93 | 94 | DEPRECATED: Use https-registry instead." 95 | ([context port] 96 | (if (not= port 443) ;; TODO: Should we support this? 97 | (throw (ex-info "Specifying port for https-scheme is not possible anymore." 98 | {:context context :port port})) 99 | (https-scheme context))) 100 | ([context] 101 | (binding [*out* *err*] 102 | (println "https-scheme is deprecated, use https-registry instead")) 103 | (https-registry context))) 104 | 105 | (defn- map->registry 106 | "Creates a Registry based of the given map." 107 | [m] 108 | (let [rb (RegistryBuilder/create)] 109 | (doseq [[scheme conn-sock-factory] m] 110 | (.register rb scheme conn-sock-factory)) 111 | (.build rb))) 112 | 113 | (defn override-wagon-registry! 114 | "Override the registry scheme used by the HTTP Wagon's Connection 115 | manager (used for Aether)." 116 | [registry] 117 | (let [cm (PoolingHttpClientConnectionManager. (map->registry registry))] 118 | (HttpWagon/setPoolingHttpClientConnectionManager cm))) 119 | 120 | (defn ^:deprecated register-scheme 121 | "Override the registry scheme used by the HTTP Wagon's Connection 122 | manager (used for Aether). 123 | 124 | DEPRECATED: Use override-wagon-registry! instead." 125 | [scheme] 126 | (binding [*out* *err*] 127 | (println "register-scheme is deprecated, use override-wagon-registry! instead")) 128 | (override-wagon-registry! scheme)) 129 | -------------------------------------------------------------------------------- /test/leiningen/test/with_profile.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.with-profile 2 | (:require [clojure.test :refer :all] 3 | [leiningen.core.main :as main] 4 | [leiningen.test.helper 5 | :refer [with-aliases-project 6 | with-aliases2-project] 7 | :as lthelper] 8 | [leiningen.with-profile :refer :all])) 9 | 10 | (defn- prj-map 11 | ([p] (prj-map p [:default])) 12 | ([p a] 13 | (let [p {:profiles p} 14 | m {:without-profiles p, :active-profiles a 15 | :profiles (:profiles p)}] 16 | (with-meta p m)))) 17 | 18 | (deftest test-profiles-in-group 19 | (doseq [[project pgroup expected] 20 | [[(prj-map {}) "+foo" [:base :system :user :provided :dev :foo]] 21 | [(prj-map {:default [:base :dev]}) "+foo" [:base, :dev, :foo]] 22 | [(prj-map {:default [:base :dev]}) "-dev" [:base]] 23 | [(prj-map {:default [:base :dev]}) "-dev,+foo" [:base, :foo]] 24 | [(prj-map {:default [:base :dev]}) "-default,+foo" [:foo]] 25 | [(prj-map {:default [:base :dev], :foo [:bar :baz]}) 26 | "-default,+foo" [:bar :baz]] 27 | [(prj-map {:default [:base :dev], :dev [:foo] 28 | :foo [:bar :baz], :baz [:zap]}) 29 | "-default,+foo" [:bar :zap]] 30 | ;; TODO: drop support for partially-composite profiles in 3.0 31 | [(prj-map {:default [:base :dev], :foo [:bar {:gross true}]}) 32 | "-default,+foo" [:foo]]]] 33 | (is (= expected (profiles-in-group project pgroup)))) 34 | (testing "no +/- prefixes in arg" 35 | (let [project (prj-map {:default [:base :dev] :foo [:bar :baz] 36 | :bar [:one :two] :baz [:three :four]})] 37 | (doseq [[pgroup expected] 38 | [["foo" [:one :two :three :four]] 39 | ["bar" [:one :two]] 40 | ["baz" [:three :four]] 41 | ["bar,baz" [:one :two :three :four]] 42 | ["baz,bar" [:three :four :one :two]]]] 43 | (is (= expected (profiles-in-group project pgroup))))))) 44 | 45 | (deftest default-project-with-profiles 46 | (testing "outside project directory" 47 | (let [project (main/default-project)] 48 | (is (= nil (main/resolve-and-apply project ["project" "a"])) 49 | "base project") 50 | (testing "with a profile" 51 | (let [project (vary-meta project assoc-in [:profiles :a2] {:a 2})] 52 | (is (= [2] 53 | (main/resolve-and-apply project 54 | ["with-profile" "+a2" "project" "a"])) 55 | "applies profile")))))) 56 | 57 | (deftest with-profiles-aliases-test 58 | (testing "recursive aliases" 59 | (testing "no with-profile" 60 | (is (= 1 (main/resolve-and-apply with-aliases-project ["project" "a"])) 61 | "recursive alias") 62 | (is (= 1 (main/resolve-and-apply with-aliases-project ["projecta"])) 63 | "two level alias")) 64 | 65 | (testing "with-profile added profile" 66 | (is (= [2] (main/resolve-and-apply 67 | with-aliases-project ["with-profile" "+a2" "project" "a"])) 68 | "recursive partial alias") 69 | (is (= [2] (main/resolve-and-apply 70 | with-aliases-project ["with-profile" "+a2" "projecta"])) 71 | "recursive full alias")) 72 | 73 | (testing "with-profile set profile" 74 | (is (= [2] (main/resolve-and-apply 75 | with-aliases-project ["with-profile" "a2" "project" "a"])) 76 | "recursive partial alias") 77 | (is (= [2] (main/resolve-and-apply 78 | with-aliases-project ["with-profile" "a2" "projecta"])) 79 | "recursive full alias")) 80 | 81 | 82 | (testing "with-profile added in alias" 83 | (is (= [2] 84 | (main/resolve-and-apply with-aliases-project ["pa2project" "a"])) 85 | "recursive partial alias") 86 | (is (= [2] 87 | (main/resolve-and-apply with-aliases-project ["pa2projecta"])) 88 | "recursive full alias")) 89 | 90 | (testing "with-profile set in alias" 91 | (is (= [2] (main/resolve-and-apply 92 | with-aliases-project ["a2project" "a"])) 93 | "recursive partial alias") 94 | (is (= [2] (main/resolve-and-apply with-aliases-project ["a2projecta"])) 95 | "recursive full alias")) 96 | 97 | 98 | (testing "with alias in profile" 99 | (is (= [2] (main/resolve-and-apply 100 | with-aliases-project ["with-profile" "+a2" "inp-projecta"])) 101 | "with profile added") 102 | (is (= [2] (main/resolve-and-apply 103 | with-aliases-project ["with-profile" "a2" "inp-projecta"])) 104 | "with profile set"))) 105 | 106 | (testing "recursive across profiles" 107 | (is (= [2] (main/resolve-and-apply with-aliases2-project ["project" "a"])) 108 | "partial alias from user profile") 109 | (is (= [[2]] (main/resolve-and-apply with-aliases2-project ["projecta"])) 110 | "full alias from user profile") 111 | (is (= [2] (main/resolve-and-apply 112 | with-aliases2-project ["project-set" "a"])) 113 | "partial alias from user profile") 114 | ;; this should probably be [[2]] 115 | (is (= [2] (main/resolve-and-apply with-aliases2-project ["projecta-set"])) 116 | "full alias from user profile"))) 117 | -------------------------------------------------------------------------------- /test/leiningen/test/compile.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.compile 2 | (:refer-clojure :exclude [compile]) 3 | (:use [clojure.test] 4 | [clojure.java.io :only [file]] 5 | [clojure.java.shell :only [with-sh-dir]] 6 | [leiningen.compile] 7 | [leiningen.test.helper :only [sample-project delete-file-recursively 8 | sample-failing-project 9 | sample-reader-cond-project 10 | tricky-name-project 11 | more-gen-classes-project 12 | with-system-err-str]]) 13 | (:require [leiningen.core.eval :as eval] 14 | [leiningen.core.main :as main])) 15 | 16 | (use-fixtures :each (fn [f] 17 | (delete-file-recursively 18 | (file "test_projects" "sample" "target") true) 19 | (delete-file-recursively 20 | (file "test_projects" "sample-failing" "target") true) 21 | (f))) 22 | 23 | (deftest ^:online test-compile 24 | (compile sample-project "nom.nom.nom") 25 | (is (.exists (file "test_projects" "sample" "target" 26 | "classes" "nom" "nom" "nom.class"))) 27 | (is (thrown? Exception (compile sample-failing-project)))) 28 | 29 | (deftest ^:online test-compile-all 30 | (compile sample-project ":all") 31 | (is (.exists (file "test_projects" "sample" "target" 32 | "classes" "nom" "nom" "nom.class")))) 33 | 34 | (deftest test-compile-regex 35 | (compile more-gen-classes-project "#\"\\.ba.$\"") 36 | (is (.exists (file "test_projects" "more-gen-classes" "target" 37 | "classes" "more_gen_classes" "bar.class"))) 38 | (is (.exists (file "test_projects" "more-gen-classes" "target" 39 | "classes" "more_gen_classes" "baz.class"))) 40 | (is (not (.exists (file "test_projects" "more-gen-classes" "target" 41 | "classes" "more_gen_classes" "foo.class"))))) 42 | 43 | (deftest test-compile-cljc 44 | (compile sample-reader-cond-project) 45 | (is (.exists (file "test_projects" "sample-reader-cond" "target" 46 | "classes" "nom" "nom" "clj__init.class"))) 47 | (is (.exists (file "test_projects" "sample-reader-cond" "target" 48 | "classes" "nom" "nom" "cljc__init.class")))) 49 | 50 | (def eip-check (atom false)) 51 | 52 | (deftest ^:online test-plugin 53 | (reset! eip-check false) 54 | (eval/eval-in-project (assoc sample-project 55 | :eval-in :leiningen 56 | :skip-shutdown-agents true 57 | :main nil) 58 | `(reset! eip-check true)) 59 | (is @eip-check)) 60 | 61 | (deftest ^:online test-cleared-transitive-aot 62 | (compile (assoc sample-project :clean-non-project-classes true) "nom.nom.nom") 63 | (eval/eval-in-project sample-project '(require 'nom.nom.nom)) 64 | (let [classes (seq (.list (file "test_projects" "sample" "target" 65 | "classes" "nom" "nom")))] 66 | (doseq [r [#"nom\$fn__\d+.class" #"nom\$loading__\d+__auto__.class" 67 | #"nom\$_main.class" #"nom.class" #"nom__init.class"]] 68 | (is (some (partial re-find r) classes) (format "missing %s" r)))) 69 | (is (not (.exists (file "test_projects" "sample" "target" 70 | "classes" "sample2" "core.class")))) 71 | (is (not (.exists (file "test_projects" "sample" "target" 72 | "classes" "sample2" "alt.class"))))) 73 | 74 | (deftest ^:online test-cleared-transitive-aot-by-regexes 75 | (compile (assoc sample-project :clean-non-project-classes [#"core"]) 76 | "nom.nom.check") 77 | (let [classes (seq (.list (file "test_projects" "sample" "target" 78 | "classes" "nom" "nom")))] 79 | (doseq [r [#"check\$loading__\d+__auto__.class" 80 | #"check\$_main.class" #"check.class" #"check__init.class"]] 81 | (is (some (partial re-find r) classes) (format "missing %s" r)))) 82 | (is (not (.exists (file "test_projects" "sample" "target" 83 | "classes" "sample2" "core.class")))) 84 | (is (.exists (file "test_projects" "sample" "target" "classes" 85 | "sample2" "alt__init.class")))) 86 | 87 | (deftest ^:online test-injection 88 | (eval/eval-in-project (assoc sample-project 89 | :injections ['(do (ns inject.stuff) 90 | (def beef :hot))]) 91 | '#'inject.stuff/beef)) 92 | 93 | ;; (deftest test-compile-java-main 94 | ;; (compile dev-deps-project)) 95 | 96 | (deftest bad-aot-test 97 | (is (re-find #"does\.not\.exist|does\/not\/exist" 98 | (with-system-err-str 99 | (try 100 | (compile (assoc sample-project 101 | :aot '[does.not.exist])) 102 | (catch clojure.lang.ExceptionInfo _)))))) 103 | 104 | (deftest compilation-specs-tests 105 | (is (= '[foo bar] (compilation-specs ["foo" "bar"]))) 106 | (is (= [:all] (compilation-specs [":all"]) (compilation-specs [:all]))) 107 | (is (every? #'leiningen.compile/regex? 108 | (compilation-specs ["#\"foo\"" #"bar" "#\"baz\""]))) 109 | (testing "that regexes are compiled first" 110 | (let [spec (compilation-specs '[foo #"baz" bar #"quux"])] 111 | (is (every? symbol? (drop-while #'leiningen.compile/regex? spec)))))) 112 | -------------------------------------------------------------------------------- /src/leiningen/release.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.release 2 | "Perform :release-tasks." 3 | (:require [clojure.java.io :as io] 4 | [leiningen.core.main :as main] 5 | [leiningen.core.project :as project])) 6 | 7 | (def ^:dynamic *level* :patch) 8 | 9 | (defn string->semantic-version [version-string] 10 | "Create map representing the given version string. Returns nil if the 11 | string does not follow guidelines setforth by Semantic Versioning 2.0.0, 12 | http://semver.org/" 13 | ;; ..[-] 14 | (let [version-map (->> (re-matches #"(\d+)\.(\d+)\.(\d+).*" version-string) 15 | (drop 1) 16 | (map #(Integer/parseInt %)) 17 | (zipmap [:major :minor :patch])) 18 | qualifier (last (re-matches #".*-(.+)?" version-string))] 19 | (if-not (empty? version-map) 20 | (merge version-map {:qualifier qualifier})))) 21 | 22 | (defn parse-semantic-version [version-string] 23 | "Create map representing the given version string. Aborts with exit code 1 24 | if the string does not follow guidelines setforth by Semantic Versioning 2.0.0, 25 | http://semver.org/" 26 | (or (string->semantic-version version-string) 27 | (main/abort "Unrecognized version string:" version-string))) 28 | 29 | (defn version-map->string 30 | "Given a version-map, return a string representing the version." 31 | [version-map] 32 | (let [{:keys [major minor patch qualifier]} version-map] 33 | (if qualifier 34 | (str major "." minor "." patch "-" qualifier) 35 | (str major "." minor "." patch)))) 36 | 37 | (defn next-qualifier [sublevel qualifier] 38 | (let [pattern (re-pattern (str sublevel "([0-9]+)")) 39 | [_ n] (and qualifier (re-find pattern qualifier))] 40 | (str sublevel (inc (Integer. (or n 0)))))) 41 | 42 | (defn bump-version-map 43 | "Given version as a map of the sort returned by parse-semantic-version, return 44 | a map of the version incremented in the level argument. Add qualifier unless 45 | releasing non-snapshot." 46 | [{:keys [major minor patch qualifier]} level] 47 | (case (keyword (name level)) 48 | :major {:major (inc major) :minor 0 :patch 0 :qualifier "SNAPSHOT"} 49 | :minor {:major major :minor (inc minor) :patch 0 :qualifier "SNAPSHOT"} 50 | :patch {:major major :minor minor :patch (inc patch) :qualifier "SNAPSHOT"} 51 | :alpha {:major major :minor minor :patch patch 52 | :qualifier (next-qualifier "alpha" qualifier)} 53 | :beta {:major major :minor minor :patch patch 54 | :qualifier (next-qualifier "beta" qualifier)} 55 | :rc {:major major :minor minor :patch patch 56 | :qualifier (next-qualifier "RC" qualifier)} 57 | :release {:major major :minor minor :patch patch})) 58 | 59 | (defn bump-version 60 | "Given a version string, return the bumped version string - 61 | incremented at the indicated level. Add qualifier unless releasing 62 | non-snapshot. Level defaults to *level*." 63 | [version-str & [level]] 64 | (-> version-str 65 | (parse-semantic-version) 66 | (bump-version-map (or level *level*)) 67 | (version-map->string))) 68 | 69 | 70 | 71 | (defn ^{:subtasks []} release 72 | "Perform release tasks. 73 | 74 | The default list of release tasks is as follows: 75 | 76 | :release-tasks [[\"vcs\" \"assert-committed\"] 77 | [\"change\" \"version\" 78 | \"leiningen.release/bump-version\" \"release\"] 79 | [\"vcs\" \"commit\"] 80 | [\"vcs\" \"tag\"] 81 | [\"deploy\"] 82 | [\"change\" \"version\" \"leiningen.release/bump-version\"] 83 | [\"vcs\" \"commit\"] 84 | [\"vcs\" \"push\"]] 85 | 86 | First change the version stored in project.clj, then commit that change, tag 87 | this commit to with the release version indicated, deploy to the Maven release 88 | repository, then change to the next snapshot version in project.clj, commit 89 | that change, and push to the default remote version control repository. 90 | 91 | A key point to note is that this default set of :release-tasks requires a clean 92 | working directory as far as the current version control system is concerned. 93 | This ensures that the `vcs commit` tasks will only save changes made to 94 | project.clj made by the `change version` tasks. 95 | 96 | This behavior can be overridden by setting :release-tasks a vector in which 97 | every element is either a task name or a collection in which the first element 98 | is a task name and the rest are arguments to that task. 99 | 100 | The release task takes a single argument which should be one of :major, 101 | :minor, :patch, :alpha, :beta, or :rc to indicate which version level to 102 | bump. If none is given, it defaults to :patch." 103 | ([project] (release project (str *level*))) 104 | ([project level] 105 | (binding [*level* (read-string level)] 106 | (doseq [task (:release-tasks project)] 107 | (let [current-project (project/init-project (project/read))] 108 | (main/resolve-and-apply current-project task)))))) 109 | 110 | ;; support existing release plugin: 111 | ;; https://github.com/technomancy/leiningen/issues/1544 112 | (when-let [[resource] (-> (.getContextClassLoader (Thread/currentThread)) 113 | (.getResources "leiningen/release.clj") 114 | (enumeration-seq) (distinct) (rest) (seq))] 115 | (clojure.lang.Compiler/load (io/reader resource) 116 | "leiningen/release.clj" (str resource))) 117 | -------------------------------------------------------------------------------- /test/leiningen/test/jar.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.jar 2 | (:require [clojure.java.io :as io] 3 | [leiningen.core.main :as main] 4 | [leiningen.core.project :as project] 5 | [leiningen.core.utils :refer [platform-nullsink]] 6 | [leiningen.test.helper :as helper]) 7 | (:use [clojure.test] 8 | [leiningen.jar]) 9 | (:import (java.util.jar JarFile))) 10 | 11 | (def long-line 12 | (apply str (repeat 10000 "a"))) 13 | 14 | (def mock-project-1 {:name "mock-project" :version "1.0" 15 | :main 'foo.one-two.three-four.bar 16 | :manifest [ 17 | ["hello" "world"] 18 | [:my-section-1 {"C" "D" "S" "T"}] 19 | ["A" "B"] 20 | [:my-section-2 [["E" "F"] ["X" "Y"]] ] 21 | ["G" "H"] 22 | ["long-line" long-line] 23 | ]}) 24 | 25 | (def mock-project-2 {:name "mock-project" :version "1.0" 26 | :main 'foo.one-two.three-four.bar 27 | :manifest { 28 | "hello" "world" 29 | :my-section-1 {"C" "D" "S" "T"} 30 | "A" "B" 31 | :my-section-2 [["E" "F"] ["X" "Y"]] 32 | "G" "H" 33 | "long-line" long-line 34 | }}) 35 | 36 | (deftest test-manifest 37 | (doseq [mock-project [mock-project-1 mock-project-2]] 38 | (let [mm (-> mock-project 39 | make-manifest 40 | manifest-map)] 41 | (is (= {"Main-Class" "foo.one_two.three_four.bar", "hello" "world"} 42 | (select-keys mm ["hello" "Main-Class"]))) 43 | (is (= #{"Manifest-Version" "Main-Class" "hello" "A" "G" "Created-By" "Built-By" 44 | "Build-Jdk" "long-line"} 45 | (-> mm keys set))) 46 | (is (= (get mm "long-line") long-line)) 47 | (is (= #{"my-section-1" "my-section-2"} 48 | (-> mock-project 49 | make-manifest 50 | .getEntries 51 | keys 52 | set)))))) 53 | 54 | (deftest test-jar-fails 55 | (binding [*err* (java.io.PrintWriter. (platform-nullsink))] 56 | (is (thrown? Exception (jar helper/sample-failing-project))))) 57 | 58 | (deftest test-directory-entries-added-to-jar 59 | (with-out-str 60 | (let [jar (first (vals (jar helper/with-resources-project))) 61 | entry-names (set (helper/walkzip jar #(.getName %)))] 62 | (is (entry-names "nested/dir/")) 63 | (is (not (some #(.startsWith % "/") entry-names)))))) 64 | 65 | (deftest test-profile-added-to-jar 66 | (with-out-str 67 | (let [project (-> helper/with-resources-project 68 | (project/add-profiles 69 | {:test-jar {:resource-paths ^:replace []}}) 70 | (project/merge-profiles [:test-jar])) 71 | jar (first (vals (jar project))) 72 | entry-names (set (helper/walkzip jar #(.getName %)))] 73 | (is (not (entry-names "nested/dir/sample.txt")))))) 74 | 75 | (deftest test-no-aot-jar-succeeds 76 | (with-out-str 77 | (is (jar helper/sample-no-aot-project)))) 78 | 79 | (deftest test-classifier-jar-succeeds 80 | (is (= 1 (count (:classifiers helper/with-classifiers-project))) 81 | "test project has a classifier") 82 | (is (= 1 (count (classifier-jars helper/with-classifiers-project nil))) 83 | "test project produces a classifier jar") 84 | (with-out-str 85 | (is (jar helper/with-classifiers-project) 86 | "jar runs correctly") 87 | (is (= 2 (count (jar helper/with-classifiers-project))) 88 | "jar produces two jar files"))) 89 | 90 | (deftest ^:online test-no-deps-jar 91 | (let [[coord jar-file] (first 92 | (jar (dissoc helper/sample-project 93 | :dependencies :main)))] 94 | (is (.exists (io/file jar-file))) 95 | (is (= coord [:extension "jar"])))) 96 | 97 | (deftest overlapped-paths 98 | (let [info-logs (atom [])] 99 | (with-redefs [main/info (fn [& args] (swap! info-logs conj args))] 100 | (let [result (jar helper/overlapped-sourcepaths-project)] 101 | (is result) 102 | (is (not-any? #(re-find #"Warning" %) (mapcat identity @info-logs))))))) 103 | 104 | (def mock-project mock-project-1) 105 | 106 | (deftest test-write-jar 107 | (testing (str "Confirm that a warning is output when the Main-Class is not " 108 | "part of the output jar file") 109 | (let [out-str (with-out-str 110 | (write-jar mock-project 111 | "/dev/null" 112 | [{:type :bytes 113 | :path "foo/one_two.class" 114 | :bytes ""} 115 | {:type :bytes 116 | :path "foo/one_two_too.class" 117 | :bytes ""}]))] 118 | (is (.contains out-str 119 | "Warning: The Main-Class specified does not exist")))) 120 | 121 | (testing (str "Confirm that a warning is NOT output when the Main-Class is " 122 | "not part of the output jar file") 123 | (let [out-str (with-out-str 124 | (write-jar mock-project 125 | "/dev/null" 126 | [{:type :bytes 127 | :path "foo/one_two.class" 128 | :bytes ""} 129 | {:type :bytes 130 | :path "foo/one_two_too.class" 131 | :bytes ""} 132 | {:type :bytes 133 | :path "foo/one_two/three_four/bar.class" 134 | :bytes ""}]))] 135 | (is (not (.contains out-str 136 | "Warning: The Main-Class specified does not exist")))))) 137 | -------------------------------------------------------------------------------- /test/leiningen/test/new.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.new 2 | (:require [leiningen.new :as new]) 3 | (:use [clojure.test] 4 | [clojure.java.io :only [file]] 5 | [leiningen.test.helper :only [delete-file-recursively abort-msg]])) 6 | 7 | (deftest test-new-with-just-project-name 8 | (leiningen.new/new nil "test-new-proj") 9 | (is (= #{"README.md" "project.clj" "resources" "src" "core.clj" "test" 10 | "doc" "intro.md" "test_new_proj" "core_test.clj" ".gitignore" 11 | ".hgignore" "LICENSE"} 12 | (set (map (memfn getName) (rest (file-seq (file "test-new-proj"))))))) 13 | (delete-file-recursively (file "test-new-proj") :silently)) 14 | 15 | (deftest test-new-with-group-and-project-name 16 | (leiningen.new/new nil "orgname/a-project") 17 | (is (= #{"src" "a_project_test.clj" "project.clj" "a_project.clj" "orgname" 18 | "resources" "test" ".gitignore" "README.md" "doc" "intro.md" 19 | "LICENSE" ".hgignore"} 20 | (set (map (memfn getName) 21 | (rest (file-seq (file "a-project"))))))) 22 | (delete-file-recursively (file "a-project") :silently)) 23 | 24 | (deftest test-new-with-explicit-default-template 25 | (leiningen.new/new nil "default" "test-new-proj") 26 | (is (= #{"README.md" "project.clj" "src" "core.clj" "test" "resources" 27 | "doc" "intro.md" "test_new_proj" "core_test.clj" ".gitignore" 28 | "LICENSE" ".hgignore"} 29 | (set (map (memfn getName) (rest (file-seq (file "test-new-proj"))))))) 30 | (delete-file-recursively (file "test-new-proj") :silently)) 31 | 32 | (deftest test-new-with-app-template 33 | (leiningen.new/new nil "app" "test-new-app") 34 | (is (= #{"README.md" "project.clj" "src" "core.clj" "test" "resources" 35 | "doc" "intro.md" "test_new_app" "core_test.clj" ".gitignore" 36 | "LICENSE" ".hgignore"} 37 | (set (map (memfn getName) (rest (file-seq (file "test-new-app"))))))) 38 | (delete-file-recursively (file "test-new-app") :silently)) 39 | 40 | (deftest test-new-with-plugin-template 41 | (leiningen.new/new nil "plugin" "test-new-plugin") 42 | (is (= #{"README.md" "project.clj" "src" "leiningen" "test_new_plugin.clj" ".gitignore" 43 | "LICENSE" ".hgignore"} 44 | (set (map (memfn getName) (rest (file-seq (file "test-new-plugin"))))))) 45 | (delete-file-recursively (file "test-new-plugin") :silently)) 46 | 47 | (deftest test-new-with-template-template 48 | (leiningen.new/new nil "template" "test-new-template") 49 | (is (= #{"README.md" "project.clj" "src" "leiningen" "new" "resources" 50 | "test_new_template.clj" "test_new_template" "foo.clj" ".gitignore" 51 | "LICENSE" ".hgignore"} 52 | (set (map (memfn getName) (rest (file-seq (file "test-new-template"))))))) 53 | (delete-file-recursively (file "test-new-template") :silently)) 54 | 55 | (deftest test-new-with-nonexistent-template 56 | (is (re-find 57 | #"Could not find template zzz" 58 | (with-redefs [leiningen.new/resolve-remote-template (constantly false)] 59 | (abort-msg leiningen.new/new nil "zzz" "my-zzz"))))) 60 | 61 | (deftest test-new-with-*-jure-project-name 62 | (is (re-find 63 | #"names such as clojure .* are not allowed" 64 | (with-redefs [leiningen.new/resolve-remote-template (constantly false)] 65 | (abort-msg leiningen.new/new nil "awesomejure"))))) 66 | 67 | (deftest test-new-with-clojure-project-name 68 | (is (re-find 69 | #"clojure can't be used as a project name" 70 | (with-redefs [leiningen.new/resolve-remote-template (constantly false)] 71 | (abort-msg leiningen.new/new nil "clojure"))))) 72 | 73 | (deftest test-new-with-show-describes-a-template 74 | (is (re-find 75 | #"^A general project template for libraries" 76 | (with-out-str 77 | (leiningen.new/new nil ":show" "default")))) 78 | (is (re-find 79 | #"^A general project template for libraries" 80 | (with-out-str 81 | (leiningen.new/new nil "default" ":show"))))) 82 | 83 | (deftest test-new-with-to-dir-option 84 | (leiningen.new/new nil "test-new-proj" "--to-dir" "my-proj") 85 | (is (= #{"README.md" "project.clj" "src" "core.clj" "test" "resources" 86 | "doc" "intro.md" "test_new_proj" "core_test.clj" ".gitignore" 87 | "LICENSE" ".hgignore"} 88 | (set (map (memfn getName) (rest (file-seq (file "my-proj"))))))) 89 | (delete-file-recursively (file "my-proj") :silently)) 90 | 91 | (deftest test-new-with-force-option 92 | (.mkdir (file "test-new-proj")) 93 | (leiningen.new/new nil "test-new-proj" "--force") 94 | (is (= #{"README.md" "project.clj" "src" "core.clj" "test" "resources" 95 | "doc" "intro.md" "test_new_proj" "core_test.clj" ".gitignore" 96 | "LICENSE" ".hgignore"} 97 | (set (map (memfn getName) (rest (file-seq (file "test-new-proj"))))))) 98 | (delete-file-recursively (file "test-new-proj") :silently)) 99 | 100 | (deftest test-new-with-to-dir-and-force-option 101 | (.mkdir (file "my-proj")) 102 | (leiningen.new/new nil "test-new-proj" "--to-dir" "my-proj" "--force") 103 | (is (= #{"README.md" "project.clj" "src" "core.clj" "test" "resources" 104 | "doc" "intro.md" "test_new_proj" "core_test.clj" ".gitignore" 105 | "LICENSE" ".hgignore"} 106 | (set (map (memfn getName) (rest (file-seq (file "my-proj"))))))) 107 | (delete-file-recursively (file "my-proj") :silently)) 108 | 109 | (deftest test-new-generates-in-the-current-directory 110 | (let [original-pwd (System/getProperty "leiningen.original.pwd") 111 | new-pwd (file original-pwd "subdir") ;; TODO: make rand temp dir instead 112 | _ (.mkdir new-pwd) 113 | new-pwd (str new-pwd)] 114 | ;; Simulate being in a directory other than the project's top-level dir 115 | (System/setProperty "leiningen.original.pwd" new-pwd) 116 | 117 | (leiningen.new/new nil "test-new-proj") 118 | (is (= #{"README.md" "project.clj" "src" "core.clj" "test" "resources" 119 | "doc" "intro.md" "test_new_proj" "core_test.clj" ".gitignore" 120 | "LICENSE" ".hgignore"} 121 | (set (map (memfn getName) (rest (file-seq (file new-pwd "test-new-proj"))))))) 122 | (System/setProperty "leiningen.original.pwd" original-pwd) 123 | (delete-file-recursively (file new-pwd) :silently))) 124 | --------------------------------------------------------------------------------